docs(adr-0026): promote to accepted + resolve open questions #216
Reference in New Issue
Block a user
Delete Branch "docs/adr-0026-accept-and-tighten-lifecycle"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
ADR-0026 shipped as
proposedin #213 with two open questions explicitly flagged for the acceptance pass. This PR resolves both and promotes the ADRproposed → accepted.No code changes — same shape as #205 (ADR-0025 acceptance).
What lands
docs/decisions/0026-person-user-portal-data-model.mdstatus: proposed → accepted; the Lifecycle section rewritten to drop email-based dedup; new "Seeding the org hierarchy" subsection picking inline-migration over a separate seed script; Consequences section reworked to match the new dedup choice.docs/decisions/README.mdproposed → accepted.CLAUDE.md0001 → 0026 accepted; new Architecture bullet for the portal-side data model; the@RequireScopePrisma-resolver roadmap entry now references ADR-0026 as accepted with the two-PR implementation phasing.Open questions resolved
Q1 —
Persondedup at first sign-inDecision: v1 looks up by
entraOidonly; no email-based merging.The original ADR-0026 specified an email-based dedup at sign-in: if a Person row with matching email existed, the new User attached to it; otherwise a fresh Person was created. That path is fragile in two ways:
info@mailbox at a small partner organisation, error in an upstream feed. Auto-merge silently corrupts the golden record without surfacing a conflict.The accepted shape: the provisioner trusts
entraOidas the only natural key. IfUser.entraOid = Xexists, refreshlastSignInAtand return. Otherwise, create a freshPerson+ linkedUserin one transaction (Person.source = 'self-signin', names from the EntradisplayNamesplit, email from the Entra claim).ADR-0027 inherits the reconciliation problem: when Pléiades / Acteurs+ syncs run after v1 sessions exist, two Person rows may describe the same human (one
self-signin, onepleiades). The merge becomes an operator-confirmed flow surfaced in the admin UI, not an automated email-based join.Q2 — Seed placement for
Region/Delegation/EtablissementDecision: inline in the Prisma migration as reference data.
Two candidates:
prisma migrate deploygets the same starting set.prisma/seed.ts— schema ships empty tables; a seed script is run on demand.The org hierarchy is reference data — slow-moving, every environment needs it, none of the rows are environment-specific. Inline in the migration is the conservative posture: one atomic deploy ships the schema and the rows that make the schema usable. The v1 seed is intentionally small (only the codes the test tenant exercises — a handful of établissements + Délégation 33 + Région 75 Nouvelle-Aquitaine +
siege). ADR-0027's Pléiades sync becomes the authoritative source once it ships.Test-tenant
user_scopesrows for the 19 personas are not in this inline seed — they are environment-specific and ride inprisma/seed.ts(implementation PR 2).Notes for the reviewer
CLAUDE.md Architecture bullet for ADR-0026. Added at the same level of detail as the ADR-0025 bullet that precedes it (decision summary + sees-link). Implementation hasn't shipped yet — when PR 1 lands, the bullet won't need re-editing, only the "Still on the roadmap" entry will move into "Shipped on
main".The Consequences section's Bad-because was rewritten, not just trimmed. The original wording said
Person.emailas the v1 dedup key was fragile, and pointed at ADR-0027 for the fix. With the new dedup choice the fragility is gone — but a new failure mode appears: a Person created viaself-signinand later imported from Pléiades look like duplicates until reconciled. The new Bad-because makes that explicit so a future reader understands the v1 cost. ADR-0027 owns the merge flow.No code changes, so no
pnpm ci:checkimpact.pnpm exec prettier --checkclean on the three touched files.Test plan
pnpm exec prettier --check— clean on the three touched files.What's next
Per ADR-0026's
§More Informationphasing — now unblocked:Person.source/Etablissement.kindcatalogue constants + drift-gate extension +PersonAndUserProvisionercalled fromSessionEstablisher+ updatedPrincipalBuilderpopulatingPrincipal.user.{id, personId}from the real rows.PrismaScopeResolverreplacingStubScopeResolver+/admin/users/:id/scopesadmin-app screen +prisma/seed.tspopulating the 19 test personas'user_scopespernotes/test-tenant-role-assignments.md.Pull request closed