ADR-0098: Org Chart × Role System — Marriage at the Evidence Level, Never in Authorization¶
- Status: accepted
- Date: 2026-07-27
- Deciders: Nicolas Burri ("D2 Option 1 · D3 sounds good too · B sounds good to me"), from the design discussion he queued after his current system's audit ("the organizational chart was a critical point… I am wondering if we can marry this chart to our role system but I am not sure at all if this is a good idea").
- Relates to: ADR-0054 (owner is a ROLE — the decision that already de-personalized document responsibility), ADR-0052/0053 (acknowledgement fan-out — the "communicated" evidence), ADR-0089 (recurring obligations — the snapshot cadence), ADR-0096 (the authority model the derived view renders), the setup-status read (unstaffed-role detection this view aggregates), REQ-NOT-004 (the future escalation scheduler the primary bit will feed).
Context¶
Two DIFFERENT pains were behind the ask, and they need two different answers:
- The auditor's pain was expressiveness: the chart could not properly represent staff positions (Stabstellen), nor the distinction between line organization (disciplinary reporting) and functional/technical authority (who decides what). The auditor also insisted each role have a dedicated "main" person — accountable that the role's jobs get done (not necessarily doing them): the RACI responsible-vs-accountable distinction, which ISO 13485 §5.5.2 itself instantiates by requiring the management representative to be an individual.
- Nicolas's pain was synchronization: chart, Jira structures and documents were three independent sources of truth, hand-synced for audit day. One QA member leaving cost half a day (diagram + Jira + a hunt across living documents for her responsibilities) — and the same again two weeks later when her successor arrived.
An org chart fuses two things: hierarchy (reporting lines) and authority (who may do what). LQMS holds the second as live, enforced data; it holds nothing of the first. The sync pain existed because people's names were written in several places; the expressiveness pain because one drawing had to carry facts of different kinds.
Decisions¶
-
Marriage at the evidence level ONLY. Authorization stays pure. Reporting lines never enter the role system, and no org-chart concept ever grants, implies or inherits a permission — reporting ≠ permission, and the role system's flatness (authority only by explicit grant) is what makes its answers audit-proof. The same contamination rule applies to accountability (decision 4): the primary holder has exactly the rights any other holder has.
-
The hierarchy chart is a controlled DOCUMENT, and it is DE-PERSONALIZED. It draws functions and their relations only — line reporting, staff positions in their conventional representation, functional-authority annotations where wanted — and never a person's name. It lives in the QMS scope (the third lane), as a draw.io SVG attachment (renders in the reading view AND in PDF export; mermaid degrades to a source block on paper, ADR-0074 §7). Consequence of de-personalization: personnel churn never touches the chart; it changes only when the ORGANIZATION changes — rare, and exactly what a controlled release with review and acknowledgement fan-out is for. "Who holds each function" is answered by decision 3, never by the drawing.
-
A DERIVED authority/staffing view + a quarterly snapshot RECORD. The view renders, from live staffing and policy data: role × scope × current holders × what the role actually permits (the review policies ARE the functional-authority definition — rendered, not claimed), with unstaffed-but-granted roles flagged loudly (aggregating the setup-status machinery). It cannot drift because it IS the enforcement. A recurring obligation (ADR-0089, quarterly) releases a point-in-time snapshot as a controlled RECORD — the evidence pair the audit pack carries: chart (structure) beside snapshot (occupancy + authority).
-
One
primarybit on the existing staffing assignment (role_assignment): per scope × role, AT MOST one holder may be marked primary; a role may have none. Zero authorization semantics — it feeds the derived view and snapshot (answering the auditor's "main person" natively) and, later, the REQ-NOT-004 reminder/escalation scheduler as the escalation target. Person-bindings thus stay in exactly ONE place — the staffing data — which is the principle (with ADR-0054's owner-as-role) that turns Nicolas's half-day personnel change into: deactivate → the gap flags name what is now unstaffed/primary-less → staff the successor. -
The light org layer (units, positions, reporting edges) is DEFERRED with a named trigger. It would buy automation of the drawing, not truth — while moving the hand-maintained drift source inside the tool. It becomes worth building when a proven need appears: an auditor hard-requiring generated names-in-boxes, or a demonstrated matrix-org / multi-chart need. If built, it references USERS (marriage at the user level) and never touches authorization.
Consequences¶
- ISO 13485 §5.5.1 mapping: defined = the chart document + the role grants; documented = controlled release of both; communicated = the acknowledgement records the chart's release fans out. §5.5.2's individual management representative = the QMS scope's Quality Manager role with its primary holder.
- Migration:
primaryflag onrole_assignment+ a partial unique index per (scope, role); assignment surface (admin users page /POST …/assignments) gains the flag; audited (ROLE_ASSIGNEDgains the before/after). - New read surface: the derived authority/staffing view (VIEW-gated per scope; the GLOBAL aggregation follows the audit-coupling posture) + its snapshot export into the recurring obligation's completion record.
- Seed/wizard: the QMS lane's folder tree already carries the chapter home for the chart document; the snapshot obligation is a seed/preset candidate ("review org chart quarterly").
- Implementation is a registered queue item — it does NOT start with this ADR.
Realization notes (2026-07-27 night slice)¶
- Functional-authority disclosure is CONDITIONAL, not widened. The per-type policy
composition ("which transitions this role may perform, per document type") was already a
deliberate CONFIGURE_REVIEW_POLICY-gated disclosure (the setup-status read withholds it from
VIEW callers by documented design). The authority view honors that boundary: VIEW callers get
everything except
grants; the payload statesfunctionalAuthorityDisclosedexplicitly — "no grants" and "you may not see the grants" are different facts. Decided (Nicolas, 2026-07-28): the boundary STAYS — VIEW holders do not see per-type grants; widening would need a new decision with a demonstrated reader. The snapshot's intended puller (a QM) holds the permission, so the evidence use-case is complete either way. - The cascade nuance §4's wording does not cover: the partial unique index bounds primaries per ASSIGNMENT scope, and holders resolve through the V038 descendant-or-self cascade — so a role assigned at GLOBAL and at a project, each with its own primary, would surface BOTH in the project view. The view renders every primary it finds (no invented tie-break); does not arise in practice (the composite FK requires activation at the assignment scope). Amend here if a resolution rule is ever wanted.
- Audit: the assign act's
ROLE_ASSIGNEDgains theprimaryHolderbefore/after as anticipated; the standalone re-point act is its ownPRIMARY_HOLDER_SETevent (an act that assigns nothing must not writeROLE_ASSIGNED). Both name people in clear text. The snapshot export is audited like every derived export.
Clarification 2026-08-05 — snapshot format, and the permission vocabulary¶
From Nicolas's hands-on with the authority view (register feedback-2026-08-05-dossier.md A1/A2):
- The snapshot download is a PDF; markdown stays behind
?format=md(A2 — he expected a PDF). §3 chose markdown deliberately, for two reasons: the snapshot is attached to a record and read beside its sources, and text stays diffable quarter-over-quarter. The first reason is answered better by PDF — it is the auditor-facing evidence form the dossier and audit pack already speak, and the ADR-0074 render path exists. The second reason no longer needs the artifact: A2-RESHAPED makes the quarter-over-quarter question a change list read off the audit trail, not a diff of two snapshots. So the DEFAULT flipped and the markdown wire stayed; only the default moved. The PDF is typeset from the very markdown themdform serves, not from a parallel model — two renderings of one piece of evidence must not be able to state different facts, and a second model over the same composition is how they would eventually come to. Determinism therefore still lives where §3 put it, in the markdown. - Permission descriptions travel on their own read:
GET /api/permissions(A1 — the permission chips look clickable and explain nothing). The tooltip's text ispermission.description, which V004 declaresNOT NULLbeside the permission's own definition: the tooltip IS the definition, so the "hard to keep up to date" worry dissolves structurally rather than by discipline. Delivered as a separate, additive, authentication-gated vocabulary read rather than by enriching this ADR's payload (permissions: [string]→[{name, description}]), which would have broken every current consumer and repeated fixed sentences once per role per scope on every view read. The catalog is English only — one column, no i18n; a consumer renders it as data. - Registered, NOT decided here: rewriting those descriptions as help text (migration V077) forced
an enforcement audit of the catalog, and seven permissions turned out to be enforced nowhere
(
SEARCH,UPLOAD,VIEW_AUDIT,ASK_LLM,MANAGE_LLM_POLICY,EXPORT_BACKUP,ERASE_PERSONAL_DATA) whileASSIGN_ROLESis consumed only as the escalation-recipient predicate, not as the staffing gate its name claims. V077 makes each description say so rather than promise an authority the installation does not govern — the V071/V073 lesson. Whether they are retired (V073-style) or their gates are built is a decision with a real design question behind it, and is deliberately not taken by a wording pass.
(Resolved 2026-08-05, V078 — Nicolas: "good catch. Agree". The seven SPLIT:SEARCH,UPLOADandVIEW_AUDITwere retired because their acts ship gated byVIEW/EDIT_DRAFT/VIEWrespectively — keepingVIEW_AUDITwould have narrowed the activity trail's deliberately-decided disclosure boundary.ASK_LLM,MANAGE_LLM_POLICY,EXPORT_BACKUPandERASE_PERSONAL_DATAwere kept reserved, with V077's honest descriptions: they name roadmap obligations, not gates something else already covers.ASSIGN_ROLESneeded nothing. See ADR-0012's 2026-08-05 amendment.)