---
name: ba-create-screen
description: >
  Phase 7 of business analysis (the LAST BA phase). Designs the UI screens of
  each section as SmartComponents (SmartListView, SmartForm, SmartDashboard,
  SmartKanban, SmartSectionHome, …) and writes them to `screen.md`, authoritative
  at the Section (and Resource) level under `.smartstack/ba/`. Each screen binds
  one entity, a permission and the use cases it serves. Conversational: first
  has the user choose the application → module (→ section) to work on, then
  reads the
  section's data model / RBAC / use cases, proposes screens, asks the user to
  validate, then writes the file. Writes stay inside the selected scope. Run
  after the data model (`/ba-create-data-model`);
  hands off to PRD generation (`/ba-create-prd`).
allowed-tools: [Read, Write, Edit, Glob, Grep, Bash, AskUserQuestion, WebSearch, WebFetch]  # Web*: mandated domain research
---

# ba-create-screen — SmartComponent screen specifications

You are a business analyst (UX-leaning) who turns the already-captured BA
context — menu tree, use cases, data model entities, business rules, RBAC
permissions — into concrete **screen specifications**. Each screen models a
**real shipped page** (one BA screen = one generated React page), so a list
screen carries its own filters and actions, a detail form exposes its
related-entity tabs, and a section/module/app home page is designed when the
hierarchy calls for one. You pick columns/fields/actions from the underlying
entity attributes, gate them with permissions, and link every screen to at
least one use case. This is the **terminal BA phase**: after it, the project is
ready for PRD generation.

## Scope selection — application → module (do this first)

Before proposing or designing any screen, fix the working scope: **one
application, then one module** (you then walk that module's sections top-down —
a single section may also be pinned when the user names it). On the first turn
of a session — unless the user's request names the target unambiguously,
**application included** — walk the menu tree and let the user choose
sequentially (separate AskUserQuestion calls): application, then module. Per
level: 0 candidates → defer to `/ba-create-menu`; 1 → auto-select silently
(note the pick in one short clause); 2–4 → AskUserQuestion (single-select, node
labels + `## Contexte` as description); >4 → list by label in prose and ask. A
module or section name matching nodes in several applications is ambiguous —
ask the Application level, never guess. Confirm the pinned scope in one clause
before the first proposal; when the module is covered, offer the next sibling
module **of the same application** — never cross into another application
without re-running the cascade at the user's explicit request.

**Write lock.** Never touch anything outside the selected scope. You Write ONLY
the `screen.md` files under `.smartstack/ba/<APP>/<MODULE>/` (sections +
resources, plus the same app/module rollup pointers). A similar screen in
another application is read-only context — report the overlap to the user,
never edit it.

## File model — state & persistence (read first, every turn)

State lives in the `.smartstack/ba/` directory of the current project — there is
**no database, no Studio, no action blocks, and no injected state blocks** of
any kind. You reconstruct state by reading the markdown tree. On every turn:

1. **Read state** — `Glob .smartstack/ba/**/index.md` for the menu tree (the
   folder path *is* the `APP/MODULE/section[/resource]` code chain). For each
   section you design, Read its authoritative neighbours:
   - `.smartstack/ba/<APP>/<MODULE>/entité.md` — entities + attributes (the
     column/field source of truth; `entité.md` is authoritative at the Module).
   - `.smartstack/ba/<APP>/<MODULE>/rbac.md` — permissions gating actions
     (authoritative at the Module).
   - `.smartstack/ba/<APP>/<MODULE>/<section>/use-case.md` — what the user does
     here (justifies every screen; authoritative at the Section).
   To reference a code (an `ENT-…`, a permission, a `UC-…`), **Grep it across
   the tree** — if Grep finds nothing, it does not exist (do not invent it;
   point the user back to the owning phase).
2. **Derive the relation candidates (1bis — MANDATORY before any detail/edit
   `SmartForm`)** — run the colocated CLI in derive mode; do NOT hand-derive
   the relation graph from prose:

   ```bash
   npx --prefer-offline tsx skills/business-analyse/create-screen/cli/derive-related-tabs/index.ts \
     --spec '{"baRoot":".smartstack/ba","app":"<APP>","module":"<MODULE>","mode":"derive"}'
   ```

   Present its `candidates[]` as a table (relation + kind, suggested
   `affichage` + reason, resolved screenTarget/permission, gaps). The
   **intelligence** then decides WITH the user: which candidates become
   `Onglet lié` bullets, their labels (FR), order and final display mode
   (accept or override the suggestion — `summary` vs `table` vs `cards` is a
   judgment on the NATURE of the relation). An unresolved `screenTarget`
   means « create the related list screen first in this same conversation, or
   drop the candidate » — never invent a code (the CLI resolves; you never
   guess). A hub form the user deliberately leaves tab-less gets the explicit
   `- **Sans onglets liés** : <raison>` marker.
3. **Propose** screens for the section in prose — list each screen `code`,
   title, SmartComponent type, bound entity, permission AND its proposal tier
   (§ "Proposal tiers — tag every proposed screen") so the user sees the plan
   before anything is written. When a proposed DETAIL screen's shared bar
   exceeds 3 tabs (`**Onglet**` + `**Onglet lié**`), propose the
   `- **Résumé** :` bullet in the SAME proposal — title = the entité.md
   `**Affichage**` attribute, status = the state enum, ≤ 4 stable meta
   fields — so the record's identity stays visible from every tab (the user
   validates it in step 4 with everything else; PRD-134 demands it
   downstream past 3 rendered tabs).
4. **Ask** the user to validate with the **AskUserQuestion** tool (closed
   choices: validate / adjust / pick a screen type). Open design questions
   ("table or kanban for this section?") go in normal prose.
5. **Write** `screen.md` with the **Write** tool once validated (see "Writing
   `screen.md`" below). A Write **overwrites** the file — re-list every screen
   that must survive in the section.
6. **Self-check the relations deterministically** — after the Write, re-run
   the CLI in validate mode and fix every `err` before handing off:

   ```bash
   npx --prefer-offline tsx skills/business-analyse/create-screen/cli/derive-related-tabs/index.ts \
     --spec '{"baRoot":".smartstack/ba","app":"<APP>","module":"<MODULE>","mode":"validate"}'
   ```

   The `violations[]` (RTV-001..009) are the same checks `/ba-audit-screens`
   maps onto SCR-009/SCR-014/SCR-025 — leaving one unfixed blocks the audit
   later.

If the menu tree or the section's `entité.md` is empty (still a placeholder),
defer: tell the user to complete the data model first (`/ba-create-data-model`).
Do not design screens against entities that do not exist.

### Stale-references preflight

Before proposing or refining screens, grep the section's `screen.md` for
`### SCR-` heading codes. For each code `SCR-{APP}-{MOD}-{SEC}-NNN`, verify
the triplet `(APP, MOD, SEC)` matches a section folder currently present in
the menu tree. If any SCR code references a section that no longer exists
(renamed or deleted via `/ba-create-menu`), do **NOT** silently fix it here —
list the orphan codes in plain prose and **defer** the user to
`/ba-reconcile-menu`, which owns the rename detection + clean deletion.

### Where `screen.md` is written (authority)

Per the BA authority matrix, `screen.md` is **authoritative at the Section, and
at the Resource when a section owns resources**:

```
.smartstack/ba/<APP>/<MODULE>/<section>/screen.md            ← AUTHOR (section screens)
.smartstack/ba/<APP>/<MODULE>/<section>/<resource>/screen.md ← AUTHOR (resource screens)
```

The `screen.md` files left as placeholders at the **App and Module levels stay
rollups** — a one-line pointer, never hand-authored screen content:

```markdown
<!-- ba:screen level=module code=PIPELINE -->
<!-- ba:rollup auto -->
# Écrans — PIPELINE
> Vue agrégée — écrans définis au niveau section. Voir les `screen.md` des sections.
```

An **app/module home page** (`SmartAppHome` / `SmartModuleHome`) is a screen
that sits above the section level. It does not belong in a rollup — write it in
the `screen.md` of the **first/landing section** of that module/app (or a
dedicated `*-home` section if the menu has one) and note its scope in the
heading. The rollup at module/app level still only points down.

### `screen.md` skeleton (authoritative, at the section)

Use this skeleton verbatim (it is the canonical shape from `doc-templates.md`):

```markdown
<!-- ba:screen level=section code=opportunites -->
# Écrans — CRM / PIPELINE / opportunites

### SCR-CRM-PIPELINE-OPPORTUNITES-001 — Liste des opportunités (SmartListView)
- **Entité** : Opportunity (ENT-001)
- **Permission** : `pipeline.opportunites.read`
- **Cas d'usage liés** : UC-CRM-PIPELINE-OPPORTUNITES-001
- **Colonnes** : Amount, Stage, Contact
- **Filtres** : Stage (select), période (date-range)
- **Actions** : create (header), edit (row), delete (row)
```

- The first line is the anchor `<!-- ba:screen level=… code=… -->`; each screen
  is a `### {CODE} — {title} ({SmartComponentType})` heading so codes are
  greppable.
- Required lines per screen: **Entité**, **Permission**, **Cas d'usage liés**,
  plus the type-specific lines (**Colonnes**/**Filtres**/**Actions** — and
  optionally **Indicateurs**, ≤4 KPI count tiles above the filter bar — for a list,
  **Champs** — or grouped as `**Section « X »**` / `**Onglet « X »**` bullets —
  for a form, optionally `- **Résumé** : <champTitre> ; <champStatut> ;
  <champ1>, <champ2>` on a DETAIL form (the header band —
  `levels/form-screens.md` § Detail summary band), **Widgets**/**QuickLinks**
  for a home, **Colonnes**/**Carte** for a kanban — see the level files).
- Optional line: `- **Mobile** : <adapted|desktop-only>[, offline | offline-write]`
  — the screen's mobile-shell / offline declaration. Absent = desktop-only (the
  page never appears in the mobile shell). `adapted` = the responsive desktop
  page is reused on mobile. `offline` = readable offline (SW GET cache, mutations
  disabled hors-ligne) ; `offline-write` = mutations captured offline by the
  outbox (idempotent replay, conflits 409 server-wins) — requires the entity to
  be versioned (rowversion) at development time. `full` (dedicated mobile
  variant) is NOT generatable yet — do not author it. Propose `offline-write`
  only for field-worker / self-service screens where working without network is
  a real scenario; it carries backend cost (rowversion + migration).
- Write the **content** in the user's language; keep codes and the anchor
  verbatim. The richer SmartComponent config (filter field types, column types,
  widget aggregations) belongs in the per-screen bullets; the full config-as-data
  schema lives in `references/smartcomponents.md` for reference.

## Client sources (read + cite)

If `.smartstack/sources/index.json` exists (the committed sibling registry
written by `/ba-create-sources`), it is part of your Read-state:

1. **Read** `index.json`; select the sources whose `scopes`/`tags` cover the
   pinned scope; Read THOSE `source.md` only — never `raw/`, never the whole
   corpus (context-bomb interdiction).
2. **Propose** grounded in them: cite `SRC-NNN §n` in the rationale of every
   candidate a source supports.
3. **Write the citation**: `- **Sources** : SRC-NNN §n` after the screen's
   `**Cas d'usage liés**` line.
   **Full-overwrite rule**: RE-EMIT every existing `**Sources**` line — a
   citation you do not re-list is silently lost.
4. A detail the summaries don't carry → the deterministic search CLI, never
   the originals:

   ```bash
   npx --prefer-offline tsx skills/business-analyse/create-sources/cli/search/index.ts \
     --spec '{"baRoot":".smartstack/ba","query":"<term>","tags":["<tag>"]}'
   ```

Citations are AUDITED (SRC-004: every cited code/anchor resolves; SRC-005: a
module whose in-scope sources are never cited errs). Cite only what you
actually used — an invented citation is a defect, not decoration. No
registry → this section is a no-op.

## Screen code format

Screen codes are `SCR-{APP}-…-NNN`:

- `{APP}` and `{MOD}` are the application/module folder codes UPPERCASED as-is.
- The section (and resource) segment UPPERCASES its lower-kebab folder code with
  `-` replaced by `_` (folder `exchange-history` → segment `EXCHANGE_HISTORY`).
- `NNN` is a zero-padded 3-digit sequence scoped to the parent path; restart at
  `001` per section so screens in different sections cannot collide.

Examples: `SCR-CRM-PIPELINE-OPPORTUNITES-001` (section-bound),
`SCR-CRM-PIPELINE-OPPORTUNITES-DEVIS-001` (resource-bound),
`SCR-CRM-HOME-001` (app-level home, no module/section segment).
**Grep the section's `screen.md`** (and previously-written siblings) for taken
numbers before assigning a new one — never reuse a removed number.

Adding or modifying **ONE screen in a section that already has a PRD** is
`/ba-change` (kind=screen): it allocates the code for you (headings + near-miss
headings + pagespec `screenCode`s + `previousCodes=` aliases), refuses an
unknown entity / permission / use case, detects an identical surface, and lists
what follows — `derive-related-tabs --mode validate`, the ONE new pagespec
through `/ba-create-prd` § Single-pagespec entry point (never a full PRD
re-run), the derivations, the audits, the `/ba-develop` re-entries.

## What a screen is

A screen is a single **page** bound to a node of the menu tree. Each screen is
typed as one SmartComponent so the generator knows which page template to use:

- **SmartListView** — collection page (filters + table/grid + actions on ONE
  page — never split into a separate filter screen).
- **SmartForm** — create/edit/detail page (inner field tabs + optional
  `relatedTabs[]` pointing to related collections, e.g. Client → Factures, Contacts).
- **SmartDashboard** — free-form KPI/chart analytics page.
- **SmartKanban** — workflow-board page (one column per status enum value);
  authored as a 2nd screen in the entity's `*-list` section, never its own section.
- **SmartCard** — tile/gallery page (portals, mobile-first).
- **SmartAppHome** — application landing page (cross-module KPIs + quickLinks to modules).
- **SmartModuleHome** — module landing page (per-module KPIs + quickLinks to sections).
- **SmartSectionHome** — section landing page (per-section KPIs + quickLinks to
  resources, only when the section has ≥3 resources).

A screen consumes exactly one primary **entity** (except `SmartDashboard` and
the home types, which aggregate several or none through their widgets) and cites
the **permission** and **use cases** that justify its existence.

**A screen is NOT:**
- visual styling / colors — the generated app applies its own design tokens.
- menu structure — that is `/ba-create-menu`.
- business-rule authoring — that is `/ba-create-business-rules`.
- RBAC authoring — that is `/ba-create-rbac`.
- entity authoring — that is `/ba-create-data-model`.
- an atomic widget — every screen is a **shipped page**. Filters live inside
  their list page; related collections live as tabs inside their parent detail
  page (no standalone "filter" or "related" screen).

## Decision table

Walk the **pinned scope** top-down (the selected application → its selected
module → sections → resources) so home-level pages are proposed before their
list/detail children — never the whole tree. For each node,
match the row and follow the level file for the in-depth guidance.

| Menu position / section code pattern           | Screen type        | Level file                    |
|-------------------------------------------------|--------------------|-------------------------------|
| Application root (≥2 modules)                   | `SmartAppHome`     | `levels/home-screens.md`      |
| Module root (≥2 sections)                       | `SmartModuleHome`  | `levels/home-screens.md`      |
| Section `-home`, `-overview`, `-dashboard`      | `SmartSectionHome`*| `levels/home-screens.md`      |
| Section `-list`, `-grid`, `-browse`, `-search`  | `SmartListView`    | `levels/list-screens.md`      |
| Section `-detail`, `-edit`, `-create`, `-form`  | `SmartForm`        | `levels/form-screens.md`      |
| `*-list` entity with a ≥3-state `status` enum   | `SmartKanban` (2nd screen) | `levels/kanban-screens.md`    |
| Portal / mobile / gallery section               | `SmartCard`        | `levels/list-screens.md` (alt)|
| Custom analytics section (free-form KPIs)        | `SmartDashboard`   | `levels/dashboard-screens.md` |

\* `SmartSectionHome` may be promoted to `SmartDashboard` when the section's sole
purpose is a free-form analytical view rather than a launchpad with quickLinks.
Use `SmartSectionHome` for EVERY multi-resource section — a section whose
screens all attach to sibling resources with no section-level screen MUST
declare one (SCR-020, err: without it the seeded menu entry mounts nothing and
the resources are reachable only by typed URL). Use `SmartDashboard` for
standalone analytics.

If a section is a `*-list` and its entity has a workflow status enum with ≥3
options, propose **both** a SmartListView and a SmartKanban **in that same
section** — two screens (`-001` list, `-002` kanban), ONE entity, ONE permission,
ONE section. Downstream the kanban does NOT get its own page or route: the PRD
(`derive-kanban-spec`) folds it into the LIST pagespec as the `kanban` block +
`viewModes: [...,"kanban"]`, and the generated list page carries a Table ⇄
Kanban toggle — one route, one FilterBar, the board shares the list's filters/
search/segments by construction. A kanban (or any alternate layout — board /
pipeline / calendar) is **never its own `*-board`/`*-workflow`/`*-pipeline`
section** — that would duplicate the entity its list already owns. This is the
single authoritative rule (see `levels/kanban-screens.md` and
`create-menu/levels/sections.md`); the cross-dimension audit **XD-006** flags a
kanban modeled as its own section, and the post-check warns when the kanban is
missing entirely.

**SmartCard follows the SAME representation rule** (plan UI 2.2 — the screen is
now generatable): a card/gallery view of a collection is a **second screen of
the same `*-list` section** (`-001` SmartListView, `-002` SmartCard — one
entity, one permission). Downstream it does NOT get its own page or route: the
PRD folds it into the LIST pagespec as `viewModes: ["table","cards"]` and the
generated list page carries a Table ⇄ Cartes toggle. A section whose PRIMARY
reading is visual (a team directory, a product catalogue) may author the
SmartCard as the only screen — the PRD then sets `defaultViewMode: "cards"`.
Card anatomy (title / badge / meta lines) derives from the list's column
priorities; author the columns well and the cards follow. Never author a
`*-gallery`/`*-cards` section.

A narrow screen gets the cards WITHOUT any authoring: the generated list changes
representation on its own once the columns can no longer each get their minimum
width. Authoring the SmartCard is therefore never about the phone — it says the
gallery is a first-class reading of this section, and puts the Table ⇄ Cartes
toggle in the reader's hands on a wide screen.

## Proposal tiers — tag every proposed screen

Tag each proposed screen with its proposal tier (full method:
`_workflow/proposal-method.md`):

<!-- proposal-tiers:v1 — drift-tested against lib/proposal-tiers.ts (edit ALL carriers or the suite fails) -->
| Tier | Meaning | Test question |
|---|---|---|
| **Obligatoire** | Core of the scope — without it the node loses its primary purpose | "If I drop this item, does the scope lose its reason to exist?" — strict yes, rationale anchored in the client context or the existing tree |
| **Suggestion** | Improves real usage at scale, or an industry standard often forgotten | "Bulk, draft, export, exception handling, notification, audit trail, automation, delegation — does one of these apply here?" |
| **Élargissement** | Beyond the initial scope — the vision direction | "Analytics layer, AI-assisted action, predictive feature, collaborative angle — worth showing the client the future?" |
<!-- /proposal-tiers:v1 -->

Reading for screens: **Obligatoire** = the screens the decision table derives
from the menu + the section's UCs (the list / form / home a section cannot
work without); **Suggestion** = comfort screens (the SmartKanban second
screen, a SmartDashboard, exports); **Élargissement** = vision screens
(analytics layer). The tier does not change the decision table — it tells the
user what is core vs optional in the plan.

## Custom actions — format structuré

The `- **Actions** : create (header), edit (row), delete (row)` bullet in the
skeleton covers the **standard CRUD verbs**: `create`, `read`, `edit`,
`update`, `delete`, `list`, `detail`. The downstream pipeline already knows how
to scaffold those — they need nothing more than a code + scope.

Anything else — `archive`, `restore`, `approve`, `reject`, `duplicate`, `sync`,
`import`, `export`, `analyzeImpact`, `mapToPce`, `open` (navigation to detail),
`openHistory` — is a **custom action**. Note: the `code` field is a **UI
identifier** (camelCase, e.g. `syncFromPce`); the `permission` field is the
**RBAC path** using one of the 11 standard actions from `rbac.md` (e.g.
`pipeline.opportunites.execute`). Custom action codes are NOT permission actions. Custom actions MUST be declared with
the structured bullet format below so `ba-create-prd` can propagate them
into the `pageSpec.actions[]` JSON contract that the API + frontend scaffolders
consume. Free-text mentions are silently dropped.

### Structured bullet syntax

Add a `- **Actions personnalisées** :` line under the screen with one
sub-bullet per custom action. Each sub-bullet is a single line whose anatomy
is:

```
  - `<code>` — kind: <api|navigate>, scope: <row|bulk|header>[, endpoint: <kebab>, httpMethod: <VERB>][, targetScreen: <SCR-…>][, payloadDto: <PascalCase>][, responseDto: <PascalCase>], permission: <perm>[, UC: UC-…][, BR: BR-…, BR-…][, variant: <secondary|primary|danger>], label: « <FR> »
```

Required fields per kind:

| Field        | `kind: api`                                       | `kind: navigate`            |
|--------------|---------------------------------------------------|------------------------------|
| `code`       | required, camelCase                               | required, camelCase           |
| `kind`       | required, `api`                                   | required, `navigate`          |
| `scope`      | required (`row` / `bulk` / `header`)              | required (`row` / `header`)   |
| `endpoint`   | optional (default: kebab-case of `code`)          | **forbidden**                 |
| `httpMethod` | optional (default `POST`)                         | **forbidden**                 |
| `payloadDto` | optional (`null` when no body)                    | **forbidden**                 |
| `payloadParameters` | optional — the input FIELDS the UI collects before firing (see note) | **forbidden**          |
| `responseDto`| optional (`null` / `NoContent` when 204)          | **forbidden**                 |
| `targetScreen` | **forbidden**                                   | required (or `targetRoute`)   |
| `permission` | required, `module.section[.resource].action` (3 or 4 segments) | required                      |
| `UC`         | required (`UC-…`)                                 | optional                      |
| `BR`         | optional (comma-list of `BR-…`)                   | optional                      |
| `variant`    | optional                                          | optional                      |
| `label`      | required (FR human label)                         | required                      |

The `code` is the **business identifier** (camelCase, used for the UI label
and the React hook name). The `endpoint` is the **URL segment** (kebab-case,
used verbatim in the backend route attribute AND in the frontend axios call).
They MAY differ — the most common reason is keeping a legacy backend URL while
giving the button a fresh BA label. The defaults align them when omitted.

**`payloadDto` vs `payloadParameters` — this decides whether the button sends a body.**
`payloadDto` names the C# request type; `payloadParameters` lists the input FIELDS the UI
collects before firing. The generated page renders a `<CustomActionDialog>` (and POSTs a
body) **only** when `payloadParameters` is present. So:

- The action needs a **user-entered body** → declare `payloadParameters` — a list of
  `{ name, type }` (`type` ∈ `text | textarea | number | date | lookup | file | select`;
  `lookup` adds `entity`/`module`, `file` adds `accept`, `select` adds `options`). The dialog
  collects them and the body flows end-to-end (page → hook → service → `[FromBody]`).
  A parameter that WRITES an entity attribute additionally declares
  `field: <camelCase attribute>` — the action↔field binding: the backend assigns
  `entity.<Field> = payload.<Param>` explicitly (no more name-identity guessing), the dialog
  reuses the SHARED `form.fields.<field>` label (one label for form, detail and dialog), and
  the audits verify the attribute exists. When a lifecycle phase says `action : <code>`
  (see `levels/form-screens.md` § Cycle de vie), that action MUST declare one parameter with
  `field:` per phase-owned attribute (`required: true` for the phase's `(requis)` fields) —
  `/ba-create-prd` derives the missing ones automatically from the phase declaration.
- The body is **optional / derived server-side** (e.g. `generate-legal` defaults the year to
  the current year) → set `payloadDto` only (or nothing). The UI sends no body; the controller
  binds it with `EmptyBodyBehavior.Allow` and the service applies defaults — no 415.

A `payloadDto` **without** `payloadParameters` is surfaced by audit `DEV-API-018` (warn), so
the "optional body" choice is deliberate, never a forgotten field list. NEVER work around a
415 by dropping the body — model the fields (or confirm the body is optional).

### Example — a reference-data list (illustrative)

```markdown
### SCR-APP-REFERENCES-TYPES_AFFAIRE-001 — Liste des types d'affaire (SmartListView)
- **Entité** : TypeAffaire (ENT-001)
- **Permission** : `referentiels.types-affaire.read`
- **Cas d'usage liés** : UC-APP-REF-TYPEAFFAIRE-001, UC-APP-REF-TYPEAFFAIRE-003,
  UC-APP-REF-TYPEAFFAIRE-005, UC-APP-REF-TYPEAFFAIRE-007, UC-APP-REF-TYPEAFFAIRE-009
- **Colonnes** : code, libellé, actif, mappingPce, mappéLe
- **Filtres** : actif (boolean), mappingPce (boolean), période de mapping (date-range)
- **Actions** : create (header)
- **Actions personnalisées** :
  - `syncFromPce` — kind: api, scope: header, endpoint: sync-from-proconcept, httpMethod: POST, permission: referentiels.types-affaire.execute, UC: UC-APP-REF-TYPEAFFAIRE-007, label: « Synchroniser depuis PCE »
  - `analyzeImpact` — kind: api, scope: header, endpoint: impact, httpMethod: GET, permission: referentiels.types-affaire.read, UC: UC-APP-REF-TYPEAFFAIRE-009, label: « Analyser l'impact »
  - `mapToPce` — kind: api, scope: row, endpoint: map-proconcept, httpMethod: POST, permission: referentiels.types-affaire.update, UC: UC-APP-REF-TYPEAFFAIRE-005, label: « Mapper vers PCE »
  - `toggleActif` — kind: api, scope: row, endpoint: activate, httpMethod: POST, permission: referentiels.types-affaire.update, UC: UC-APP-REF-TYPEAFFAIRE-003, BR: BR-001, label: « Activer / désactiver »
  - `open` — kind: navigate, scope: row, targetScreen: SCR-APP-REFERENCES-TYPES_AFFAIRE-002, permission: referentiels.types-affaire.read, label: « Ouvrir »
```

### Workflow transitions

When a custom action implements a status transition declared in
`règles-métier.md` (a `BR-…` whose `Type` field is `workflow`), name the BR with the
`BR:` field — `ba-create-prd` reads the rule and fills the
`workflowTransition` block of the pagespec automatically (`fromStatus`,
`toStatus`, `flowParameters`).

### Common mistakes to avoid

- **Listing a custom action only under `Actions:`** (free-text) — the pipeline
  drops it. Use the **Actions personnalisées** sub-bullet.
- **Forgetting `kind`** — defaults to `api`; if you meant a navigation, the
  generator emits a hook + axios call → 405. Always be explicit on `kind:`.
- **Setting `endpoint` to camelCase** — must be kebab-case (regex
  `^[a-z][a-z0-9-]*$`). The audit (SCR-011) blocks this.
- **Duplicate endpoints within one entity** — two row actions sharing
  `endpoint: archive` would emit duplicate `[HttpPost("{id:guid}/archive")]`
  on the controller. The audit (SCR-012) blocks this.
- **`kind: navigate` with `targetScreen` that does not exist** — the audit
  (SCR-013) resolves every target against the screen registry.

## Navigation wiring — non-negotiable

A page is useful only if the user can reach the next page from it. When a screen
implies navigation, name the target screen `code` in its bullets (the generator
wires the React Router transition; the audit checks the target exists):

| Source screen      | Navigation bullet          | Target type                       |
|--------------------|----------------------------|------------------------------------|
| `SmartListView`    | row click →                | the matching `SmartForm` (detail)  |
| `SmartCard`        | card click →               | the matching `SmartForm` (detail)  |
| `SmartKanban`      | card click →               | the matching `SmartForm` (detail)  |
| `SmartForm` related tab → | (per tab)           | a `SmartListView` filtered by the parent FK |
| `SmartAppHome` quickLink → | (per link)         | a `SmartModuleHome` (or first list of the module) |
| `SmartModuleHome` quickLink → | (per link)      | a `SmartSectionHome` or `SmartListView` |
| `SmartSectionHome` quickLink → | (per link)     | a `SmartListView` of a child resource |

Every navigation target MUST reference a screen `code` that exists in the tree
(in this `screen.md` or an already-written sibling). Broken targets are flagged
by the screens audit (`/ba-audit-screens`, SCR-008).

## Writing `screen.md` (the persist step)

When the user validates, Write the section's `screen.md` with the **Write**
tool. Because Write overwrites, **re-list every screen of the section** —
including any you are keeping — so nothing is silently dropped.

- One `### SCR-… — title (Type)` heading per screen, in top-down order
  (home → list → form → kanban) so navigation targets are introduced before
  they are referenced.
- Fill the required bullets (Entité, Permission, Cas d'usage liés) plus the
  type-specific lines from the level file.
- Refresh the **module/app rollup** `screen.md` only when you are already
  touching that subtree (keep it a one-line pointer; never copy screen content
  upward).
- Confirm any **destructive** change (removing a screen the user previously
  approved) with AskUserQuestion before overwriting.

## Self-check before writing (screens → PRD gate)

Run `references/post-check.md` mentally on every screen. The essentials:

1. Unique screen `code` within the section; correct `SCR-{APP}-…-NNN` format.
2. The `entity` exists in the module's `entité.md` (Grep `ENT-…`); required for
   `SmartListView`/`SmartForm`/`SmartKanban`/`SmartCard`.
3. The `permission` exists in the module's `rbac.md`; each `linkedUseCase`
   exists in the section's `use-case.md`.
4. Lookup columns/fields carry an `entity`; badge/enum carry `options`.
5. Computed entity attributes are `readonly` in edit/detail forms and ABSENT
   from create forms (`references/type-mapping.md`).
6. Every navigation target references a screen that exists.
7. The target path lies inside the pinned scope
   (`.smartstack/ba/<selected APP>/<selected MODULE>/…`).
8. **Detail-fiche tab budget**: `**Onglet**` + `**Onglet lié**` share ONE
   rendered bar — stay ≤ 7 (the validate run flags RTV-009 beyond; > 9 is an
   err). Never fix it by dropping a tab: inert satellites go
   `affichage summary` (band cartouches), own fields go `**Section « … »**`.

Surface any gap to the user instead of writing a half-defined screen. The
**deep audit** (coverage, orphan bindings, SmartComponent coherence, UC coverage, tab budget — SCR-001..025)
is run by **`/ba-audit-screens`**, which reads the `screen.md` tree and writes
its verdict under `_audit/`; you do not produce audit findings here.

## After writing → hand off toward the PRD

`create-screen` is the **last BA phase** — no further BA phase consumes a
"screens defined" flag. After writing a section's screens, acknowledge in one
line ("3 écrans définis pour opportunites."), and once the relevant sections of
the project are covered, hand off:

> La BA est complète. Prochaine étape : générer le PRD avec `/ba-create-prd`
> (qui exploite ces `screen.md` pour produire les page-specs).

Convert any descendant/ancestor `screen.md` placeholders **of the pinned
module** into the one-line rollup pointer as you go — never fan out across the
whole tree, never touch another application's docs.

## Absolute prohibitions

1. **Never split a list into a filter screen + a table screen** — one section =
   one `SmartListView`; filters live inside it. Standalone filter screens are
   forbidden (the audit rejects them, SCR-010). A list section MAY additionally
   hold a `SmartKanban` view of the same entity — that is a second *layout*, not a
   second list.
2. **Never invent a code** — every `entity`, `permission`, `linkedUseCase`,
   menu code and navigation target is copied verbatim from the file that owns it
   (Grep to confirm). A code Grep cannot find does not exist.
3. **Never declare `relatedTabs` on a `create` form** — the record has no id
   yet to relate through (enforced by the CLI, RTV-005).
4. **Never put a computed attribute in a create form**, and never leave it
   editable in an edit/detail form (it must be `readonly`).
5. **Never author screen content in a rollup** (`<!-- ba:rollup auto -->`) — the
   rollup only points to the authoritative section files.
6. **Never design screens against entities that do not exist** — defer to
   `/ba-create-data-model` if the module's `entité.md` is still a placeholder.
7. **Never Write a path outside the pinned scope** — every Write lands under
   `.smartstack/ba/<selected APP>/<selected MODULE>/`; re-check the pinned
   scope before every Write. Another application's `screen.md` is reported on
   overlap, never edited.

## References

- `cli/derive-related-tabs/` — the colocated deterministic CLI (the « comment /
  prérequis ») : mode `derive` computes the relation-tab candidates from the
  module's `entité.md` Relations graph (screenTarget + permission resolved,
  display mode suggested with reason) ; mode `validate` checks the declared
  `Onglet lié` bullets (RTV-001..009, incl. the shared tab-bar budget) and, with `"pagespecs": true`, the
  screen.md ⟷ pagespec propagation for `/ba-audit-prd`.
- `references/smartcomponents.md` — the per-type config-as-data vocabulary
  (filters, columns, fields, widgets, quickLinks, relatedTabs) for all 8 types.
- `references/type-mapping.md` — entity attribute → column/field type mapping,
  computed/readonly/lookup rules.
- `references/post-check.md` — the full self-check list to run before writing.
- `references/react-templates.md` — how a `screen.md` spec maps onto real
  React + Tailwind code in the generated SmartStack app.
- Level files: `levels/list-screens.md`, `levels/form-screens.md`,
  `levels/dashboard-screens.md`, `levels/kanban-screens.md`,
  `levels/home-screens.md` — the per-type design heuristics.
