---
name: doc-templates
description: Canonical markdown skeletons for the six BA docs + index + rollup variant. The single format contract that authors (definer skills), audits, and create-prd all parse. Loaded on demand, not every turn.
phase: '*'
kind: reference
section_label: '_WORKFLOW — DOC TEMPLATES'
---

# BA doc templates — the canonical file shapes

Every file under `.smartstack/ba/` follows one of the skeletons below. The first
line is always an anchor comment (`<!-- ba:… -->`); item codes are `###`
headings so they are greppable. Write content in the user's language; keep codes
and anchors verbatim. See `ba-files.md` for the authority matrix (which level
authors vs rolls up each doc).

---

## `index.md` — every node (project / app / module / section / resource)

```markdown
<!-- ba:node kind=node level=application code=CRM -->
# CRM — Gestion commerciale

## Contexte
Application de suivi commercial pour les équipes de vente : prospects,
opportunités, devis. Sert les commerciaux et leurs managers ; s'arrête à la
signature — la facturation et le recouvrement sont hors de son champ.
- **Sources** : SRC-001 §1

## Hors-périmètre
- La facturation est gérée dans BILLING.
- La signature électronique passe par un service tiers (hors v1).

## Enfants
- [PIPELINE](./PIPELINE/index.md) — Pipeline d'opportunités
- [CONTACTS](./CONTACTS/index.md) — Annuaire de contacts
```

`level=project` for the root `.smartstack/ba/index.md`; its `## Enfants` lists
the applications.

- `## Contexte` — REQUIRED at app/module/section: **3-5 sentences carrying
  WHO / WHAT / LIMITS** (MENU-007: err empty, warn under ~120 chars). The
  optional closing line `- **Sources** : SRC-NNN §n` cites the registered
  client source that grounds it (`.smartstack/sources/` — see the `source.md`
  section below); omitted when no source applies.
- `## Hors-périmètre` — **MANDATORY at app and module level** (MENU-008 err).
  `out_of_scope` (the bullets) cascades into PRD non-goals downstream — keep
  it accurate. When no exclusion exists, the explicit empty marker
  `_Aucune exclusion connue à ce stade._` replaces the bullets: absence is an
  ASSERTION, not an omission (the PRD does NOT copy the marker as a
  non-goal). Encouraged on sections, not required.

### Module-level `index.md` — with optional `depends=` attribute

After `/ba-create-ba-order` (Phase 1.5), module-level `index.md` files gain a
`depends=` attribute in their anchor and a `## Dépendances` section:

```markdown
<!-- ba:node kind=node level=module code=BILLING depends=ERP/REFERENCES,ERP/CUSTOMERS -->
# BILLING — Invoicing

## Contexte
Invoices management for customers and suppliers.

## Hors-périmètre
- No online payment v1.

## Dépendances
- [REFERENCES](../REFERENCES/index.md) — uses currencies, countries, statuses
- [CUSTOMERS](../CUSTOMERS/index.md) — invoices linked to customers

## Enfants
- [invoices](./invoices/index.md) — Invoices
```

For a module **without** dependencies:

```markdown
## Dépendances
_Aucune — module fondationnel._
```

`## Dépendances` is placed between `## Hors-périmètre` (or `## Contexte` if
no out-of-scope section) and `## Enfants`. Only module-level `index.md` carries
this section — app/section/resource levels do not.

---

## `acteur.md` — authoritative at Application

```markdown
<!-- ba:acteur level=application code=CRM -->
# Acteurs — CRM

### BA-001-AC-001 — Commercial
- **Type** : internal
- **Description** : Gère son portefeuille de prospects et opportunités.
- **Périmètre** : CRM (toute l'application)
- **Origine** : conversation (2026-…)
- **Sources** : SRC-001 §1

### BA-001-AC-002 — Manager commercial
- **Type** : internal
- **Description** : Supervise l'équipe, valide les remises.
- **Périmètre** : CRM / PIPELINE
- **Origine** : code (rôle détecté)
```

`type` ∈ `internal | external | system`. Both `Description` and `Origine`
(traceability) are required. Actor codes are `BA-{analysisSeq}-AC-{NNN}`.
The optional `- **Sources**` line (after `Origine`) cites the registered
client source(s) grounding the actor — see the `source.md` section below.

---

## `use-case.md` — authoritative at Section

```markdown
<!-- ba:use-case level=section code=opportunites -->
# Cas d'usage — CRM / PIPELINE / opportunites

### UC-CRM-PIPELINE-OPPORTUNITES-001 — Créer une opportunité
- **Acteur principal** : BA-001-AC-001 (Commercial)
- **Acteurs secondaires** : —
- **Préconditions** : le prospect existe.
- **Flux principal** :
  1. Le commercial ouvre le formulaire.
  2. Il saisit le montant estimé et l'échéance.
  3. Le système crée l'opportunité au statut NOUVELLE.
- **Flux alternatifs** :
  - ALT-1 : montant inconnu → enregistrement en brouillon.
- **Exceptions** :
  - EXC-1 : prospect archivé → refus avec message.
- **Postconditions** : opportunité visible dans le pipeline.
- **Sources** : SRC-001 §1
- **Acceptance Criteria** :
  - [ ] AC-01 — POST /api/opportunities avec un body valide renvoie 201 + l'identifiant créé.
  - [ ] AC-02 — POST avec un montant négatif renvoie 400 et le code d'erreur `amount-positive`.
  - [ ] AC-03 — La nouvelle opportunité apparaît dans GET /api/opportunities du même owner avec statut NOUVELLE.
```

UC codes are `UC-{APP}-{MOD}-{SEC}-NNN` (section code UPPERCASED in the code).

AC codes are **local to the UC** : `AC-01`, `AC-02`, … numbered from 01 (zero-padded
2 digits), sequential, no gaps. The globally unique reference is `<UC-code>#AC-NN`
(e.g. `UC-CRM-PIPELINE-OPPORTUNITES-001#AC-02`). Each AC is a single, testable
assertion that the test-scaffolder turns into a backend `[Fact]` (trait
`<UC-code>#AC-NN`, blocking DEV-TEST-001/008 coverage gate) at
code-generation time — there is NO Playwright half today; UI-only verification
belongs to the /uat axis. Write at least one AC per `user-goal` UC
(audit `UC-012`); subfunction-level UCs may legitimately have none.

---

## `règles-métier.md` — authoritative at the rule's deepest scope

```markdown
<!-- ba:rules level=module code=PIPELINE -->
# Règles métier — CRM / PIPELINE

### BR-001 — Remise plafonnée
- **Type** : validation
- **Sévérité** : err
- **Portée** : CRM / PIPELINE
- **Condition** : QUAND une remise > 20 % est saisie ALORS exiger l'aval manager.
- **Expression** : `Discount <= 0.20 || Approval.ManagerId != null`
- **Code d'erreur** : `pipeline.discount.cap`
- **Cas valides** : remise 15 % sans aval ; remise 25 % avec aval manager.
- **Cas invalides** : remise 25 % sans aval.
- **Cas d'usage liés** : UC-CRM-PIPELINE-OPPORTUNITES-002 (étape 3)
- **Sources** : SRC-001 §2

### BR-002 — Numéro d'opportunité
- **Type** : numbering
- **Sévérité** : err
- **Portée** : CRM / PIPELINE
- **Condition** : QUAND une opportunité est créée ALORS son numéro = `OPP-{YY}-{SEQ:4}` (séquence par tenant, reset annuel, gapless, immuable).
- **Expression** : `format = "OPP-{YY}-{SEQ:4}" ; scope = tenant ; reset = yearly ; gapless = true ; immutable = true`
- **Implémentation** : AUCUNE — alloué par le socle (`CodedEntitySaveHandler`) à l'insert. Cette règle est la SPEC du `**Code pattern**` de l'entité : ne rien coder (ni service, ni compteur, ni validateur d'unicité). Le code d'erreur ne sert que le chemin d'un code PROPOSÉ (`ISuppliedCodeGuard`), jamais un validateur maison.
- **Code d'erreur** : `pipeline.opportunity.reference.duplicate`
- **Cas valides** : 1re de 2026 → `OPP-26-0001` ; suivante → `OPP-26-0002` ; 1re de 2027 → `OPP-27-0001`.
- **Cas invalides** : deux créations concurrentes → jamais le même numéro (GARANTI par le socle — allocation atomique, ne pas ré-implémenter de vérification d'unicité) ; numéro modifié après création → rejeté (immuable par construction — `Code` sans setter public).
- **Cas d'usage liés** : UC-CRM-PIPELINE-OPPORTUNITES-001 (postcondition)
```

Rules feed FluentValidation + form error messages downstream — keep the valid/
invalid examples concrete and testable.

Optional field `- **Enforcement** : plateforme|manuel — <raison>` — the
AUTHORED opt-out from the scaffolded-enforcement gates (PRD-129 /
DEV-TEST-009): the rule is enforced by the platform itself or by a human
process, never by generated code. Name the real channel; an absent field
means the generated app must enforce the rule.

---

## `rbac.md` — authoritative at Module

```markdown
<!-- ba:rbac level=module code=PIPELINE -->
# RBAC — CRM / PIPELINE

| Acteur | Permission (`module.section[.resource].action`) | Portée |
|--------|------------------------------------------|--------|
| BA-001-AC-001 (Commercial) | `pipeline.opportunites.read` | toutes |
| BA-001-AC-001 (Commercial) | `pipeline.opportunites.create` | les siennes |
| BA-001-AC-002 (Manager commercial) | `pipeline.opportunites.approve` | équipe |

<!-- ba:rbac-floor BEGIN -->
… (machine-owned — written by `derive-permission-floor`, never by hand: the
default permission floor per menu node, seeded automatically without grants)
<!-- ba:rbac-floor END -->

<!-- ba:rbac-derived-lookups BEGIN -->
… (machine-owned — written by `derive-lookup-grants`, never by hand)
<!-- ba:rbac-derived-lookups END -->
```

Human rows are `module.section[.resource].action` within the module (3 segments
for a section, 4 for a resource); the app prefix is added at the page/controller
boundary, not here (prohibition n°10 of `/ba-create-rbac` — the two machine
blocks are the only app-qualified exceptions). The permission FLOOR (access/
lookup/read/create/update/delete/execute per section+resource, access per
app/module) exists by construction — human rows carry the GRANTS and the
extra actions (approve, export, `.read.all`, …).

---

## `entité.md` — authoritative at Module

```markdown
<!-- ba:entité level=module code=PIPELINE -->
# Modèle de données — CRM / PIPELINE

### ENT-001 — Opportunity (agrégat racine)
- **Préfixe table** : `pipeline_`
- **Portée** : strict — données isolées par tenant (`optional` / `none` : voir attributes.md § tenantMode)
- **Traçabilité** : UC-CRM-PIPELINE-OPPORTUNITES-001, BR-001, BR-002

| Attribut | Type | Contraintes | Calculé |
|----------|------|-------------|---------|
| Id | Guid | PK | — |
| Amount | decimal(18,2) | ≥ 0 | — |
| Stage | enum | NOUVELLE/GAGNEE/PERDUE | — |
| WeightedAmount | decimal(18,2) | — | `Amount * Probability` |

- **Relations** : Opportunity *→1 Contact — FK ContactId, scope same-module, onDelete restrict.
- **Index** : (Stage), (ContactId).
- **Code pattern** : `OPP-{YY}-{SEQ:4}` — scope tenant, reset annuel, gapless (voir BR-002). Alloué par le socle : AUCUNE entité compteur à modéliser. OPT-IN : seulement si l'entité passe le test de décision (référencée HORS de l'UI — téléphone/email, document sortant, numérotation légale, dossier long) ; la plupart des entités n'ont PAS de code. Deux facettes optionnelles de plus sur la même ligne : `libellé « Référence »` (le nom MÉTIER du code dans l'UI/i18n — la colonne reste `Code`, alias EN `label "Reference"`) et `surchargeable à la création` (un code fourni — import, reprise, suggestion retouchée — est accepté au CREATE via `ISuppliedCodeGuard`, JAMAIS en update ; alias EN `supplied on create`).
- **Affichage** : Code — le champ qui NOMME une ligne partout (colonnes lookup, combobox, recherche).
- **Code décidé** : la référence dictée au téléphone — décision utilisateur du 2026-09-01
  <!-- sur une entité (lookup) UNIQUEMENT, et seulement si l'utilisateur l'a tranché -->
- **Valeurs initiales** : clé `Key` — les lignes fixées par le métier, semées par `scaffold-seed` :

  | Key | Label | ThresholdDays | Enabled |
  |-----|-------|---------------|---------|
  | expertise | Expertise périodique | 30 | true |
  | vignette | Vignette autoroutière | 15 | true |

### ENT-002 — Employee (agrégat racine)
- **Préfixe table** : `hr_`
- **Traçabilité** : UC-HR-EMPLOYEES-DOSSIER-001, BR-005
- **Personne** : mandatory — identité via auth_Users (FirstName, LastName, Email)

| Attribut | Type | Contraintes | Calculé |
|----------|------|-------------|---------|
| Id | Guid | PK | — |
| HiredAt | datetime | requis | — |
| Status | enum | ACTIVE/ON_LEAVE/TERMINATED | — |

- **Relations** : Employee *→1 User — FK UserId, scope core (auth_Users), onDelete restrict.
- **Index** : (Status).
```

Entity codes are `ENT-NNN`. The role in parentheses is the classification
(`agrégat racine` / `composant` / `lookup` / `technical`). Computed attributes
carry a non-empty **Calculé** formula (no DB column, no setter). A `technical`
entity (outbox, journal d'intégration, log d'audit) is isolated by nature — give
it an `- **Isolation** : by-design — <raison>` line so audit DM-009 treats the
absence of relations as intentional, not a modeling gap. A **compteur de codes
n'en est jamais un** : le `- **Code pattern**` suffit, le socle alloue (voir
`create-data-model/levels/attributes.md` § Code pattern). Un code TAPÉ par
l'utilisateur (référentiel : `TypeAffaire`, un code service) garde son nom
métier et se déclare `- **Code saisi** : <Attribut>` (alias EN
`**Typed code**`) — c'est ce qui dit à DM-021 que l'espèce a été DÉCIDÉE ; un
attribut code-like (Référence, Numéro, Matricule… — lexique
`code-like-lexicon:v1`) string + unique sans classification est un warn.

**Une valeur de référence ne porte pas de code.** Sur une entité `(lookup)`, le
**libellé** est son identité et sa clé naturelle : ni code alloué, ni code saisi
par défaut. Seul l'UTILISATEUR peut décider qu'une table de référence en porte
un, et il l'écrit — DATÉ — `- **Code décidé** : <ce que le code nomme> — décision
utilisateur du <AAAA-MM-JJ>` (alias EN `**Decided code**`). Cette ligne
**outrepasse la règle** : aucun audit ne rediscute ce code (DM-022). Un agent ne
la pose JAMAIS de sa propre initiative — elle transcrit une décision prise. Une
ligne qui MENTIONNE la décision sans être analysable (cellule de tableau, puce
sans date réelle) est un `err`, jamais un silence. Un code décidé est SAISI,
jamais ALLOUÉ : un `**Code pattern**` sur une table de référence reste un défaut.

The optional `- **Affichage** : <Attribut>` line names the field that LABELS a
row of this entity everywhere — lookup combobox `displayName`, every column
that references the entity, what lookup search matches on. REQUIRED whenever
the entity has no attribute in the display family
(`Name/Label/Code/Title/Libelle/Titre/Reference/Number/Numero` — SSOT
`lib/display-field.ts`) and no projected identity (`**Personne**` entities
resolve `FirstName + LastName` automatically): without it `scaffold-business`
now FAILS CLOSED instead of silently promoting the first string column
(the incident: ten entities labelled by a phone number, a free comment or a
raw GUID). `- **Affichage** : Id` is the conscious opt-out (GUID label
accepted). Downstream: `/ba-develop` Phase 2 passes it as `displayNameExpr`.

The optional `- **Valeurs initiales**` line declares the rows an entity is
BORN with — business-fixed reference rows (the 9 alert types, the 6 transition
reasons). It names the natural key (`clé <Attribut>`) and carries a markdown
table whose columns are the entity's attributes. Downstream, Phase 1 of
`/ba-develop` feeds it VERBATIM to `scaffold-seed` (`referenceData[]` — the
`{Module}ReferenceDataSeedDataProvider`), and `audit-dev-api DEV-API-030`
treats an entity with no create endpoint, no seed AND no declared feeding
action as UNPOPULATABLE (err). Never combine it with a `- **Code pattern**`
key: the seed table cannot pre-assign an allocated Code — use another natural
key. (The `surchargeable à la création` facet covers API-supplied codes at
CREATE — imports/reprises — never seed rows.)

The optional `- **Personne**` line (after `Traçabilité`) records the person
mode of an entity extending `core.auth_Users` — `mandatory` (FK `UserId` NOT
NULL, identity fields NOT redeclared locally, like ENT-002) or `optional`
(FK nullable + local identity fields with fallback). It pairs with a
`Relations … *→1 User — FK UserId, scope core (auth_Users)` entry; downstream
phases (PRD `Person:`/`Proj:` lines → DTO projection) parse it. Absent = not a
person — valid ONLY for entities that trip no C-4 person trigger. A TRIGGERED
entity (person-word name, or ≥2 identity attributes) must persist its decision
explicitly; the deliberate decorrelation is written
`- **Personne** : none — décision client : <raison>` (the sanctioned override
DM-018c accepts — silence is an err).

---

## `jeu-de-test.md` — OPTIONAL, authoritative at Module

The business TEST DATASET — the SECOND seed tier. Setup rows an application
needs in every environment are `**Valeurs initiales**` on the entity
(entité.md); THIS file carries 5-8 realistic, FICTITIOUS rows per business
entity, validated by the client, seeded in dev/test/qual only
(`SmartStack:EnableDevSeeding`), used as acceptance-test fixtures and rendered
by a BA simulator instead of hash noise. Written by `/ba-create-test-data`;
checked by `create-test-data/cli/derive-test-data --mode check` (audit
DM-029..032); consumed by `/ba-develop` Phase 1 (`scaffold-seed testData[]`).
Filename deliberately ASCII.

```markdown
<!-- ba:jeu-de-test level=module code=ANNUAIRE -->
# Jeu de test — CLIENT / ANNUAIRE
> Personnes et sociétés FICTIVES — aucune donnée réelle.
- **Date de référence** : 2026-09-15

### JT-001 — Client (ENT-001)
- **Clé** : `Nom`

| Nom | Organisation | TypeClient | Segment | Statut | Responsable | Note |
|-----|--------------|------------|---------|--------|-------------|------|
| Direction Marketing | ACME SA | Grand compte | Industrie | Actif | Commercial | BR-003 : nom ≠ raison sociale |
| Atelier Lausanne | Garage du Léman Sàrl | PME | Services | Archivé | BA-001-AC-001 | BR-004 : archivé, lecture seule |
```

Grammar (`lib/ba-test-data.ts` is the parser — tolerant, fail-closed on loss):
- heading STRICTLY `### JT-NNN — <Entité> (ENT-NNN)` — nothing after the
  parenthesis (the CLI's and any external reader's heading regexes refuse it);
- `- **Clé** : \`<Attribut>\`` is MANDATORY — upsert key, citation key,
  unique per tenant (the display attribute is the natural choice);
- columns = the entity's PascalCase attributes and its FK relations
  (`TypeClient`, `TypeClientId` or the target entity's name); `Note` is
  RESERVED and ignored by every reader (cite the BR / UC a row exists for);
  never `Id`, never a computed attribute, never `Code` on a coded entity
  (the engine allocates it) unless the pattern says `surchargeable à la
  création`;
- enum cells VERBATIM; dates absolute `AAAA-MM-JJ` (a real calendar date),
  read relative to `**Date de référence**` — never "today"; booleans
  `oui` / `non`; numbers plain;
- 5-8 rows per block, one table per block, one block per entity;
- a reference table (`**Valeurs initiales**`) has NO block — those rows ARE
  its dataset (DM-031).

### Références entre modules — utilisateur, client, facture

**A row belongs to the module that owns the entity; other modules cite it by
its key, never by copy.** The file is per module; the dataset is a graph of
the project. The relation written in entité.md says where the target lives:

| Relation `scope` | The cell carries | Resolved against |
|---|---|---|
| `same-module` | the target row's key, or its display value (`**Affichage**`) | this file, or the target's `**Valeurs initiales**` |
| `cross-module (APP/MOD)` | idem | `APP/MOD/jeu-de-test.md` (or its Valeurs initiales) — the OWNER's rows, whatever the audit scope |
| `core` — `User` | an ACTOR of acteur.md — code `BA-001-AC-002` or label `Commercial` | at seed: the actor's role → the module's test user (by email) |
| `core` — `TenantOrganisation` | its `Name` (fictitious) | at seed, by Name — retried at every startup (the socle seeds its demo Core rows after the client providers) |
| `core` — anything else | leave the cell empty | not resolvable in v1 |

- Reference by VALUE, never by position or Guid — `derive-test-data` normalizes
  a cell to `{ ref: "APP/MOD/Entity", keyField, key }`; the Guid only exists at
  seed time, inside the tenant. An entity `Portée: none` is global and citable
  by every tenant.
- Authoring order = the BA order (`/ba-create-ba-order`): a cited module first.
  Seed order follows: `Order = 200 + rank` (a cited module's provider runs
  before a citing one); a cycle between blocks or modules is REFUSED.
- A citation the owner cannot resolve is the OWNER's row to add: same
  application → `/ba-create-test-data` on that module (the file stays the
  owner's); another application → report it, never write there.
- A key renamed in the owner breaks the citations — the citing module's check
  and DM-030 (project scope) say it; no automatic rewrite.
- One `Date de référence` per file; the check warns when a cited module's
  differs (relative dates would drift).

---

## `screen.md` — authoritative at Section / Resource

```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
- **Sources** : SRC-001 §1
- **Colonnes** : Amount, Stage, Contact
- **Filtres** : Stage (select), période (date-range)
- **Actions** : create (header), edit (row), delete (row)
```

Screen codes are `SCR-{APP}-…-NNN`. The SmartComponent type (`SmartListView`,
`SmartForm`, `SmartDashboard`, …) is named in the heading. create-prd mines
these to build per-page specs.

---

## Rollup variant — non-authoritative levels

At a level where a doc is a rollup (per the authority matrix), write a short
aggregated view marked with the rollup comment, linking to the authoritative
children. **Never hand-author content here** — regenerate it whenever the
authoritative source changes.

```markdown
<!-- ba:acteur level=module code=PIPELINE -->
<!-- ba:rollup auto -->
# Acteurs — actifs sur PIPELINE
> Vue agrégée — source faisant autorité : [../acteur.md](../acteur.md)

- BA-001-AC-001 (Commercial)
- BA-001-AC-002 (Manager commercial)
```

A placeholder (node created by create-menu, concept not yet defined) is the
minimal form — anchor + a one-line note:

```markdown
<!-- ba:rules level=section code=opportunites -->
_À définir lors de la phase « règles métier »._
```

---

## Audit verdict — `_audit/<dimension>.md`

The `/ba-audit-*` skills read the tree, apply their rule set, and write a verdict
to an `_audit/` subfolder of the scope they audit (e.g. an app-scoped menu audit
→ `.smartstack/ba/<APP>/_audit/menu.md`; a module-scoped data-model audit →
`.smartstack/ba/<APP>/<MODULE>/_audit/entité.md`; a project-scoped actors audit →
`.smartstack/ba/_audit/actors.md`). This replaces the Studio `[ACTION]{auditReport}`
envelope — there are no i18n label codes, no `persist`, no frontend.

```markdown
<!-- ba:audit dimension=menu scope=CRM -->
# Audit menu — CRM
_2026-05-21 · Verdict : ⚠️ 1 warn · 0 err · 3 ok_

## ✅ Conforme
- **MENU-001** — chaque module correspond bien à son application (2 modules vérifiés).
- **MENU-003** — aucun module ne duplique le Core SmartStack.
- **MENU-004** — aucun module orphelin, aucune app/module vide.

## ⚠️ Avertissements
- **MENU-002** — le concept « Contacts » apparaît dans **CRM** et **SALES**.
  - Pourquoi : risque de doublon de données et de logique.
  - → Clarifier la frontière ou fusionner via `/ba-create-menu`.

## ❌ Bloquants
- _(aucun)_
```

- The header line carries the verdict counts; the rule codes (`MENU-001`, …) stay
  **bold** so they remain greppable, but they are explained in business terms.
- Findings are grouped by severity (`ok` / `warn` / `err`). For each non-`ok`
  finding give **what's wrong** (offending codes in bold), **why it matters**, and
  a `→` **concrete fix** naming the skill to re-run (e.g. `/ba-create-menu`).
- The skill also prints a 3–6 line summary in the chat; the file is the durable
  record. A downstream phase/dev gate that needs "did the audit pass?" reads the
  `Verdict :` header (`0 err` = pass).


## `source.md` — authoritative in `.smartstack/sources/SRC-NNN/` (SIBLING root, committed)

Written ONLY by `/ba-create-sources`' `cli/ingest` (the model judges, the CLI
renders — never hand-edit). Parsed by `lib/ba-sources.ts`; the `sources` audit
dimension (SRC-001..007) is the verdict. The BA artefacts cite it with the
whole-word grammar `SRC-NNN` / `SRC-NNN §n`.

```markdown
<!-- ba:source code=SRC-001 kind=file fingerprint=a1b2c3d4e5f6 status=ingested -->
# SRC-001 — Cahier des charges v2

## Métadonnées
- **Origine** : `docs/cdc-v2.pdf` (copie : `raw/cdc-v2.pdf`)
- **Format** : pdf · **Ingéré le** : 2026-09-02
- **Tags** : facturation, processus-vente
- **Portée pressentie** : CRM, CRM/PIPELINE

## Résumé
{5-15 lignes, précises et factuelles — ce que le client DIT, pas ce qu'on infère}

## Points saillants
### §1 — Processus de vente [processus-vente]
{2-6 lignes}
### §2 — Plafond de remise [regles] (p. 12) — extrait verbatim
> « Toute remise supérieure à 20 % requiert la validation du manager. »

## Ce que cette source ne couvre PAS
- Rien sur les permissions ni les rôles.
```

- Anchor `status` ∈ `ingested | blocked/needs-export | blocked/unreadable |
  superseded` — a `blocked/*` source is a TYPED admission (code reserved,
  content unread); « ingested, 0 extracts » must never exist.
- `§n` anchors are the citable grain; a decision-bearing passage is a
  **verbatim blockquote** with a locator `(p. 12)`.
- The citation line in the BA artefacts (acteur/use-case/règles/screen items,
  `## Contexte` of `index.md`) is:

```markdown
- **Sources** : SRC-001 §2, SRC-003
```

  It is OPTIONAL per item (only items actually grounded in a source carry it)
  but AUDITED both ways: every cited code/anchor must resolve (SRC-004), and a
  module whose scoped sources are never cited errs (SRC-005). The lesson of
  the removed UC back-references (`Règles liées` — written by no phase, read
  by no audit) is the design constraint here: this field is written by every
  phase (Write protocols) AND read by audit-ba. **Full overwrite rule**: a
  Write re-emits every existing `**Sources**` line or the citation is lost.
