# kinds/entity.md — one entity (a table), after the fact

The playbook `/ba-change` loads for `kind: entity`. The frame is in
`SKILL.md`; this file carries what is specific to a **new entity** — the
biggest change a finished module can take: a table, its screens, its
pagespecs, its seed.

Owner document: `<APP>/<MODULE>/entité.md` (full re-Write, every other block
verbatim). Grammar: `_workflow/doc-templates.md` § entité.md;
`ba-create-data-model` Step 0 (trace audit) → Step 1 (the block);
`levels/attributes.md`, `levels/relationships.md`. Code: `allocation.next`
— `ENT-NNN`, module-wide (cited by `screen.md` `**Entité** : X (ENT-NNN)`).

## 1. Analyse the request

One grouped AskUserQuestion:

| Question | Why it matters |
|---|---|
| **What** is it — the business concept, its PascalCase name, one sentence? Is it an aggregate root, a component of another aggregate, a reference table (lookup)? | classification decides prefix, display field, seed tier |
| **Who upstream asks for it** — which use case / rule names it? | verbatim trace (BLOCKED otherwise — DM-011) |
| **Its key attributes** — identity, label, amounts / dates / statuses, the FKs to other entities (which module, which onDelete)? | Relations drag lookup grants; a status drags the state chain |
| **Where does it live** — a satellite of an existing section (shown as a tab on its parent's detail + its own list/form) or a NEW menu section? | a new section is `/ba-create-menu`'s job, out of this change |

Also settle: is it a **person** (→ `Personne` link to `auth_Users`, CODE-007)?
Does it carry a **business code** (→ `**Code pattern**`, the socle allocates)?
Which attribute **names a row** (`**Affichage**`)?

## 2. Challenge it

- **Who upstream asks for it?** `report.trace` — no hit = BLOCKED; write the
  use case or the rule first.
- **Is it already there?** `existing.exact` (same name) → nothing to add;
  `existing.similar` (`Prospect` vs `ProspectContact`) → the same aggregate?
- **Is it a Core entity?** `User`, `Tenant`, `TenantOrganisation`,
  `Department`, `Office`, … are the platform's — the CLI BLOCKS
  (`core-entity-collision`): reference them through a `scope core` relation.
  A **reserved name** (`Notification`, `Ticket`, `Workflow`, `AuditLog`, …) is
  blocked too, with what to use instead.
- **Is it a platform capability?** A document / attachment / search index /
  counter table is a service, not an entity (DM-019 / CODE-006) — the CLI
  warns; the playbook steers to the capability.
- **Is it an entity or an attribute?** « La catégorie de l'opportunité » with
  three fixed values is an enum attribute; with a managed list it is a
  reference table (lookup) — then its label is its identity, no code unless
  the user decides it (DM-022).
- **Is it a satellite or a section?** A satellite lives under an existing
  section (tab on the parent's 360 view + its own list/detail/form with
  `routeFamily` / `routeParent`); a first-class business object nobody
  parents is a new section — `/ba-create-menu`, then `/ba-loop` on the
  module, not this change.
- **Who reads it?** The section's actors need rows at the section grain
  (`/ba-change` kind=permission) — and its FK producers become lookups
  (derived by the mandatory post-step).

## 3. Author

The full block (doc-templates § entité.md):

```markdown
### <allocation.next> — <Name> (agrégat racine | composant | référentiel)
- **Préfixe table** : `<module>_`
- **Portée** : strict — données isolées par tenant
- **Traçabilité** : UC-…, BR-…
- **Sources** : SRC-NNN §n                     (when the registry exists)

| Attribut | Type | Contraintes | Calculé |
|----------|------|-------------|---------|
| Id | Guid | PK | — |
| … | … | … | … |

- **Relations** : <Name> *→1 <Parent> — FK <Parent>Id, scope same-module, onDelete restrict.
- **Index** : (<ParentId>), (Status).
- **Affichage** : <Label>
```

Then the full re-Write (SKILL.md Step 3), the verify (`expectCode` = the new
code, `baselineCount` = `existing.count`), and the MANDATORY post-step
`derive-lookup-grants --mode derive`.

## 4. Verify

`report.verify.ok` true: the code is found, the entity count moved by +1, no
duplicate code, nothing `lost` (a refused attribute row, an unreadable
`**Index**`, a `**Relations**` entry the grammar could not read).

## 5. Propagate — what a new entity drags along

1. **Lookup grants** — `derive-lookup-grants --mode derive`, mandatory after
   EVERY entité.md Write (RBAC-008).
2. **Where it lives** — satellite: rows at the section grain (kind=permission);
   new section: out of scope, `/ba-create-menu` first.
3. **Screens** — list / detail / form through `/ba-change` kind=screen, one at
   a time; the parent's detail gains an `Onglet lié` (`derive-related-tabs
   --mode derive` suggests it). SCR-002, SCR-009/014, SCR-024.
4. **Pagespecs** — one per screen through `/ba-create-prd` § Single-pagespec
   entry point, `routeFamily` + `routeParent` on the satellite, the
   `prd.entities.md` bullet; then `derive-nav-resources` (collisions are
   blocking, DEV-UI-046), `derive-related-tabs --mode validate` with
   `pagespecs: true`, `derive-rule-links --mode backfill`, `derive-uc-coverage`.
   Never `/ba-create-prd` on a module with pagespecs.
5. **Hand-off** — Phase 0 (the nav resource + floor of a satellite, additive
   seed), Phase 1 (DEV-DOM-001: the entity has no `.cs` → scaffold-entity +
   the EF migration through `/efcore`, a new table is a 🟢 verdict), Phase 2a/2b
   (controller + screen endpoints), Phase 3 (the added pagespecs), Phase 4.

## 6. Audit

`audit-ba` with every dimension of the module (`data-model`, `rules`, `rbac`,
`screens`, `cross-dimension`, `cross-ref-code` with `projectRoot`). What an
err means here: DM-011 (no upstream reference — the trace was lost), DM-013
(bare Guid), DM-018 / CODE-001 (Core duplicate), DM-019 / CODE-006
(capability re-modelled), CODE-007 (person without the auth link), SCR-002
(no screen), RBAC-002 (nobody reaches it).

## Modify variant (op=modify)

- A **renamed entity** renames the table, the class, the DTOs, the routes —
  a destructive migration (`/efcore` 🟡/🔴, the user's call) and a
  `previousCodes=`-style rename nothing records for entities: announce it,
  prefer keeping the name.
- A changed **classification** (aggregate → lookup) changes prefix, display
  field, seed tier and code rules (DM-015, DM-022).
- Changed **relations** re-run the lookup-grants post-step and the related
  tabs (`derive-related-tabs --mode validate`).
- Verify expects delta **0**.
