---
name: ba-create-prd
description: >
  Synthesises the upstream BA `.md` tree of ONE module (menu, actors, use cases,
  business rules, RBAC, data model, screens) into a Product Requirements Document
  on disk: `prd.md` (product framing: context, goals, non-goals, MoSCoW, technical
  constraints), three phase slices, per-screen `pagespecs/*.md` (each carrying a
  machine spec block), and `claude.md`. User stories and acceptance criteria are
  NOT in the PRD — they live under each UC in `<section>/use-case.md` as the
  test-scaffolder's input. The output drives development through `/ba-develop`.
  Run after the BA phases (and `/ba-audit-pre-dev` GO), before development.
allowed-tools: [Read, Write, Edit, Glob, Grep]
---

# ba-create-prd — Product Requirements Document (per module)

You produce the **PRD of ONE module** by reading its already-written BA `.md`
docs and synthesising the dev-facing artifacts. You do **not** invent anything —
every PRD element traces to an upstream doc. If an upstream doc is missing or
thin, surface the gap (route back to the owning phase) rather than fabricating.

## File model — read the module subtree, write the PRD files

There is no database and no action blocks. State is the `.smartstack/ba/` tree.

1. **Read the module subtree** `.smartstack/ba/<APP>/<MODULE>/`:
   - `index.md` (+ the app and project `index.md`) → context + the **out-of-scope
     cascade** (project → app → module).
   - `<APP>/acteur.md` → actor roles (referenced from UC actors and RBAC).
   - `<MODULE>/use-case.md` (per section) → behaviour scope + each UC's
     `**Acceptance Criteria**` field (the test contract — not duplicated in the PRD).
   - `<MODULE>/règles-métier.md` → validation rules + valid/invalid examples
     (consumed by the entities slice as domain invariants and by the API slice
     as FluentValidation rules).
   - `<MODULE>/rbac.md` → permissions (API slice).
   - `<MODULE>/entité.md` → entities/attributes/relationships/computed (entities slice).
   - `<MODULE>/<section>/screen.md` → screens (frontend slice + pagespecs).
2. **Write** the PRD files (below) with the Write tool — one file at a time, no
   monolithic blob. The `pagespecs/<Entity>.<view>.md` fenced ```json blocks are
   the machine artifacts the dev pipeline reads directly — there is no separate
   cache to generate.

### Files written (per module, under `.smartstack/ba/<APP>/<MODULE>/`)

| File | Content | Source |
|------|---------|--------|
| `prd.md` | `## Context`, `## Goals`, `## Non-goals` (≥3), `## MoSCoW`, `## Technical constraints` | index out-of-scope + use cases + rules |
| `prd.entities.md` | `# Phase: Entities` — every entity, attributes, relationships, computed formulas, invariants, migrations (combined domain+data) as `- [ ]` bullets | `entité.md` + `règles-métier.md` |
| `prd.api.md` | `# Phase: API` — every permission → HTTP method+route, business handlers + validators. **Workflow transitions (SK-001):** for every entity with a Status field, include a `## Workflow: {Entity} Status` section listing state→state transitions with actor, action code, guard rules, and flow parameters. **Cross-module deps (SK-004/005):** when a BR references an entity from an undelivered module, annotate the handler with `crossModuleDeps: ["{moduleName}"]` and flag conditional matrices as `needsRefinement: true` with a note. | `rbac.md` + `règles-métier.md` + `<section>/use-case.md` |
| `prd.frontend.md` | `# Phase: Frontend` — every screen code → route + referenced entity | `screen.md` files |
| `pagespecs/<Entity>.<view>.md` | one per screen: a fenced ```json machine block (the pageSpec) + a human body | `screen.md` + `entité.md` + `rbac.md` |
| `claude.md` | the generated-project module CLAUDE.md (≤ ~50 lines) | template below |

The three `prd.*.md` slices are **self-contained and disjoint** (a dev subagent
loads exactly one). The `pagespecs/<Entity>.<view>.md` fenced ```json blocks ARE
the machine artifacts: `/ba-develop` reads them directly and feeds
each to `scaffold-component` — there is no separate `prd.json` to generate. All
PRD files are plain `.md`, the source of truth and the committed deliverable.

**Entities slice — relationships carry the FK contract.** In `prd.entities.md`,
EVERY relationship (any scope) MUST be written so the dev pipeline can build the
`scaffold-entity` `relations[]` deterministically — cardinality + target + FK
name + cascade + **scope**, e.g.
- `- [ ] Rel: Opportunity *→1 Contact — FK ContactId, scope same-module, onDelete restrict`
- `- [ ] Rel: Invoice *→1 Client — FK ClientId, scope cross-module, onDelete restrict`
- `- [ ] Rel: Employee *→1 Department — FK DepartmentId, scope core (ref_Departments), onDelete restrict`

Copy the scope + cascade from the `entité.md` **Relations** line (default
`restrict`). **A cross-table reference is ALWAYS a FK — cross-module / Core refs
are NO LONGER plain `Guid` bullets.** The ONLY plain-`Guid` `*Id` columns are the
identity/audit allowlist (`CreatedByUserId`, … — `lib/fk-allowlist.ts`). The
per-entity `TenantId` FK is emitted automatically from the entity's tenant mode —
no `Rel:` line needed. Phase 1 forwards every relationship (with its scope) to the
generator, which emits the real FK + `OnDelete` (cross-schema for `core`) — audit
DEV-DAT-008 (blocking) verifies the constraint actually landed.

## `prd.md` — product framing

- `## Non-goals` ≥ 3, each a **negated** statement. **Recopy every non-empty
  out-of-scope** from the project/app/module `index.md` cascade as a non-goal
  (paraphrase ok, stay faithful) — Claude Code cannot infer intent from omission.
- `## Goals` ≥ 2 action-verb statements describing what the module delivers.
- `## MoSCoW` lists Must / Should / Could / Won't priorities mined from the
  upstream UCs + index out-of-scope.
- `## Technical constraints` names the target stack constraints specific to this
  module (DB provider, integrations, performance budgets). Cross-module
  constraints belong in the project root `claude.md`, not here.

> **No `## User Stories` / `## Acceptance Criteria` sections.** Use cases own the
> behaviour (in each `<section>/use-case.md`) and **each UC carries its own
> `**Acceptance Criteria**` field** — that is the single source of truth the
> test-scaffolder consumes during `/ba-develop`. The PRD never paraphrases UCs
> into stories; it frames the product (context, goals, non-goals, scope priorities,
> technical constraints).

## `pagespecs/<Entity>.<view>.md` — the scaffold-component contract

For **every screen** in the module's `screen.md` files, write one pagespec file.
The machine block is fenced ```json (so the dev pipeline parses it natively — no
extra dependency); the human body explains it. Required fields,
all traced to the upstream docs (copy verbatim — never invent a code):

```json
{
  "screenCode": "SCR-CRM-PIPELINE-OPPORTUNITES-001",
  "appCode": "crm",
  "module": "pipeline",
  "section": "opportunites",
  "entity": "Opportunity",
  "view": "list",
  "permission": "pipeline.opportunites.read",
  "linkedUseCases": ["UC-CRM-PIPELINE-OPPORTUNITES-001"],
  "linkedBusinessRules": ["BR-001"],
  "screenType": "SmartListView",
  "columns": [{"key":"amount","labelKey":"list.columns.amount","formatHint":"currency","sortable":true,"filterable":false,"searchable":false,"isComputed":false}],
  "actions": [
    {"code":"create","scope":"header","labelKey":"list.create","permission":"pipeline.opportunites.create","variant":"primary"},
    {"code":"syncFromPce","kind":"api","scope":"header","endpoint":"sync-from-proconcept","httpMethod":"POST","labelKey":"list.actions.syncFromPce","permission":"pipeline.opportunites.execute","variant":"secondary","ucReference":"UC-CRM-PIPELINE-OPPORTUNITES-007"},
    {"code":"open","kind":"navigate","scope":"row","targetScreen":"SCR-CRM-PIPELINE-OPPORTUNITES-002","targetRoute":"routes.opportunites.detail(item.id)","labelKey":"list.actions.open","permission":"pipeline.opportunites.read"}
  ],
  "filters": [{"field":"stage","control":"select","labelKey":"list.filters.stage"}],
  "i18nKeys": {"fr":{"list.title":"Opportunités"},"en":{"list.title":"[en] Opportunités"},"it":{"list.title":"[it] Opportunités"},"de":{"list.title":"[de] Opportunités"}},
  "needsRefinement": false,
  "refinementNotes": []
}
```

Rules (the dev pipeline `scaffold-component` consumes this verbatim):
- `screenCode` = the `SCR-…` heading from `screen.md`, verbatim.
- `appCode`/`module`/`section` = lowercase folder codes; `entity` = PascalCase
  from `entité.md`; `view` from the SmartComponent type (list/detail/form/
  dashboard/kanban/card/app-home/module-home/section-home).
- `permission` = `module.section[.resource].action` from `rbac.md` (3 segments
  for a section, 4 for a resource; no app prefix — added at controller boundary).
- `columns`/`actions`/`filters` mined from `screen.md` (1:1, drop nothing) and
  enriched from `entité.md` (formatHint from attribute type; isComputed from a
  non-empty `Calculé` formula → never editable).
- **Responsive columns (optional per column)** — the list table is rendered by
  `<ResponsiveDataTable>`: the `code` + `label/libellé` columns (and the actions
  column) stay visible at every width, and the remaining columns appear
  progressively as the viewport widens, with long cell values clipped + a tooltip.
  `scaffold-component` derives this automatically (code/label always; the rest get
  `md → lg → xl` by order), so you normally emit NOTHING. To override a column,
  add `"minBreakpoint":"sm|md|lg|xl"` (min width at which it appears) or
  `"priority":"always"` (pin it visible), and `"truncate":false` to opt a column
  out of the ellipsis+tooltip. Only set these when the default order is wrong.
- **Custom actions propagation** — the `- **Actions personnalisées** :`
  sub-bullets of `screen.md` map 1:1 to `actions[]` entries with the FULL
  custom-action schema (`code`, `kind`, `scope`, `endpoint`, `httpMethod`,
  `payloadDto`, `responseDto`, `targetScreen`, `targetRoute`, `labelKey`,
  `permission`, `variant`, `ucReference`, `guardRules`, `workflowTransition`,
  `crossModuleDeps`). The canonical Zod schema lives in
  `lib/page-spec-actions.ts` (`PageCustomActionSchema`) — `/ba-audit-prd`
  validates each entry against it. Defaults applied when missing:
  - `kind` = `"api"` (legacy fallback for standard CRUD codes).
  - `endpoint` = kebab-case of `code` (e.g. `syncFromPce` → `sync-from-pce`).
  - `httpMethod` = `"POST"`.
  - `payloadDto` / `responseDto` = `null` unless the linked UC main flow
    declares parameters or returns a typed payload.
  - `variant` = `"secondary"` (CRUD `create` keeps `"primary"`, `delete` keeps
    `"danger"`).
  - For `kind: "navigate"`, resolve `targetScreen: SCR-…` against the screen
    registry of the project to compute a `targetRoute` of shape
    `routes.<sectionFolder>.<view>(<arg>)` — view defaults to `detail(item.id)`
    for row scope and `list()` for header scope when the target is a
    `SmartListView`.
  - **Workflow detection** — when an action's linked `BR: BR-…` resolves to a
    rule whose `Type` field is `workflow` and whose flow lists a status transition,
    populate `workflowTransition: { fromStatus, toStatus, flowParameters }`.
    Same parsing rules as Phase 2 SK-001/002 (see `ba-develop/SKILL.md`).
  - **Cross-module dependency** — when the linked UC references an entity from
    another module not yet in this `prd.entities.md`, add the foreign module
    name to `crossModuleDeps: ["<module>"]` and set `needsRefinement: true`
    with a note. Phase 2 emits a `// BLOCKED[CROSS-MODULE: …]` TODO so the
    audit catches the deferral.
  - The `endpoint` written here is the EXACT string that scaffold-controller
    will emit in `[HttpVerb("<endpoint>")]` AND scaffold-api-client will emit
    in `apiClient.<verb>('/api/.../<endpoint>')`. Never derive separately
    downstream — frontend and backend stay aligned by construction.
- `i18nKeys`: 4 locales (`fr/en/it/de`), parallel key sets; FR authored, others
  may be `"[en] <fr>"` placeholders with `needsRefinement: true`.
  Mandatory keys by view type:
  - **List**: `list.title`, `list.subtitle`, `list.search`, `list.loading`,
    `list.empty`, `list.error`, `list.create`, `list.columns.{field}`,
    `list.filters.{field}`
  - **Detail**: `detail.loading`, `detail.notFound`, `detail.fields.{field}`
  - **Form**: `form.createTitle`, `form.editTitle`, `form.save`, `form.cancel`,
    `form.fields.{field}`
  - **Dashboard**: `dashboard.title`, `dashboard.subtitle`, `dashboard.loading`,
    `dashboard.error`, `dashboard.widget.{key}` (one per widget)
  - **Home** (app-home/module-home/section-home): `home.title`, `home.subtitle`,
    `home.kpis`, `home.navigation`, `home.widgets.{key}` (per widget),
    `home.quicklinks.{key}` (per quickLink)
- For **home views** (`app-home`/`module-home`/`section-home`) AND the
  **dashboard view** (`SmartDashboard`), include a `widgets` array mined from
  `screen.md`'s `Widgets` (home views additionally include `quickLinks`):
  - `"widgets": [{"key":"total","label":"Clients","type":"kpi","entity":"Client",
    "aggregation":"count","col":3,"labelKey":"home.widgets.total"}]` — for a
    **dashboard** widget use `"labelKey":"dashboard.widget.{key}"`, any `type` in
    `kpi|counter|chart-line|chart-bar|chart-pie|list`, a `col` (1-12) and an
    optional `permission` (cross-module widgets).
  - `"quickLinks": [{"key":"to-list","label":"Annuaire clients","icon":"list",
    "screenTarget":"SCR-CRM-CLIENTS-LIST-001","labelKey":"home.quicklinks.to-list"}]`
    (home views only)
  `scaffold-component` renders dashboard widgets through `<WidgetRenderer>` / `<DashboardGrid>`
  and home widgets as KPI cards + navigation buttons. Omitting a dashboard's
  `widgets` produces an empty grid.
- Set `needsRefinement: true` + a note for any inferred field.
- Synthesise a `detail` view per `list`+`form` pair (`needsRefinement: true`,
  note "detail not captured by BA").

## `claude.md` — the module's generated-project CLAUDE.md

```markdown
# {ModuleLabel} — CLAUDE.md
## Stack
- .NET 10 + EF Core 10 · React 18 + TS strict · Shoelace · Zustand · React Router v6 · PostgreSQL/SQL Server
## Architecture
- Backend Clean Architecture (Domain/Application/Infrastructure/Api); Frontend 5-layer (pages/business/services/stores/components). Mutations via business hooks.
## Conventions
- Entities under Domain/Entities/<ModuleCode>/; snake_case columns, schema {module}; permissions {app}.{module}.{section}.{action} via [RequirePermission]; tests colocated.
## Module-specific decisions
<copy the module's technical constraints>
## Non-goals
<copy prd.md non-goals so every dev session sees them>
```

Fill from the module's docs; keep it to one screen.

## Self-check before finishing

- ≥ 3 negated non-goals, including every upstream out-of-scope.
- `## Goals` / `## MoSCoW` / `## Technical constraints` populated (no User Stories
  / Acceptance Criteria sections — those live in `<section>/use-case.md` under
  each UC).
- Every entity from `entité.md` appears in `prd.entities.md`.
- Every screen from `screen.md` has a `pagespecs/*.md` (+ synthesised detail) and
  each pagespec's fenced ```json block parses and copies its `screenCode`,
  `permission`, `entity` and upstream codes verbatim.
- Each slice non-empty, self-contained, disjoint.

## Hand off

Acknowledge in one line, then: the PRD must pass `/ba-audit-prd` (dev-ready score
≥ 80) before development. On a GO, develop with `/ba-develop`.

> Note: regeneration preserves the upstream UC + AC ids (they live in
> `use-case.md`, not in the PRD). The PRD itself has no story/AC ids to renumber.
> Don't invent features absent from the upstream BA — surface the gap and route
> back to the owning phase.
