ADR-0117: Role Documentation — a Link, a Finding, Per-Source Recognizers; Substitution Rejected¶
- Status: accepted (2026-08-17 — his "I read the doc and agree with your recommendations" on
docs/planning/role-documentation-options.md, and the substitution discussion closed the same morning: "That closes it, record it in the ADR as described") - Date: 2026-08-17
- Deciders: Nicolas Burri
- Relates to: ADR-0114 (roles as functions — the title≠function separation this decision leans
on), ADR-0098 (
is_primarystaffing + the de-personalized org chart), ADR-0106 (the finding/reflection grammar the check reuses), ADR-0112 §2 / register #73 (the deactivation grammar the link's lifecycle follows), register #47 + #108, the options notedocs/planning/role-documentation-options.md. - Refined by: ADR-0130 + ADR-0131 — this ADR's named trigger FIRED on 2026-09-01, and a reader of this ADR alone would otherwise answer "does LQMS have substitution?" backwards. The acting-as refusal stands and is load-bearing: ADR-0131 §1 leans on it, because the substitute self-staffs in his OWN name, which is exactly why four-eyes survives. What changed is that the sanctioned shape this ADR itself named — time-boxed staffing — was built: ADR-0130 §4 is the mechanism (timed self-staffing, expiry enforced in the authorization predicate), ADR-0131 the declared per-scope adoption matrix, ruled into 1.0.
- Realization status (2026-09-04): this ADR's own decided feature — the
described_bylink on the role activation, the ROLE_WITHOUT_DESCRIPTION setup finding and the per-source recognizers — is still unbuilt, and its timing is now open-decisions.md row 62. ADR-0121 and ADR-0130 both build arguments on top of that machinery.
Context¶
Register #47 began as an import discovery: the organization's complete role model lives as 19
released Rollenbeschreibungen under ORG-QM-Organigramm — and the PULSEMED repair proved roles
adopted by true name are only half-grounded while nothing connects a staffed role to the document
that describes its duties. His generalization challenge reframed the feature: how much of the
"derive from the Organigramm tree" idea is one corpus's shape? Should the system opinionatedly
enforce that every relevant role in a project is documented? The durable answer is a MODEL fact
plus a CHECK, with any tree-parsing demoted to one importer recognizer among possible many.
Decision¶
-
The model fact (D-1a): a role's describing document lives on the ACTIVATION.
role_activationgains a nullabledescribed_by_document_id— "in THIS scope, this role is described by THAT document". Set three ways: by hand on the roles/authority surface (the generalization floor — any project, any setup), by the IMPORT when a recognizer identifies role-description documents in an arriving corpus, or by pointing at a document in an ACCESSIBLE describing scope (the org-QMS case: a project's PLE described by the org QMS's Rollenbeschreibung — cross-scope, VIEW-checked at read like every cross-scope fact). Per-scope, not per-catalog-role: the same catalog role can mean locally different duties, and the check below is a per-scope question. -
The opinionated check (D-2a): a setup-status finding, not a gate.
ROLE_WITHOUT_DESCRIPTION— every role that is ACTIVATED and STAFFED in a scope must carry a described-by link to a RELEASED document the scope can reach; violations appear in the setup-status dead-end list with the fix named (link a document, or release the describing one). State not paths, warn not block — the standing finding grammar. Staffed-and-activated keeps the noise honest: an unstaffed role is already its own finding. -
The importer recognizer (D-3): one per source convention, contained. For the imported corpus: a page under
ORG-QM-Organigrammwhose title matches a role name. A future corpus ships a different recognizer — or none, and the operator links by hand (D-1a's manual path is the floor that makes every import workable). A role adopted by true name AND matched to its description arrives fully grounded — the α-family loop closes. -
Substitution/deputies: REJECTED as a mechanism — see the case analysis below. The former D-4 "recorded non-goal" is upgraded to a decided refusal with a named trigger.
Substitution: rejected — the staffing model IS the substitution mechanism¶
The strongest real-world counter-case, walked to ground (the executive-board (GL) case, his account 2026-08-17): one fully trained upper-management member in the QMS; during his vacation the team lead is his company-level substitute and takes on his QMS duties. Three constraints — he must not be marked as GL (he isn't), the GL-pinned tasks must stay pinned to upper management, and the process must not block during the absence.
A title-based system (the legacy Confluence QMS) needs a substitution rule set precisely because it cannot separate "is GL" from "performs GL's duties" — its only other move is relabeling a person, a lie about the org. This system already separates the two (ADR-0114: roles are functions; staffing records who performs them; ADR-0098 keeps the org chart de-personalized truth). Against that model, all three constraints dissolve:
- Staffing ≠ title. Staffing the person into the GL-function role (non-primary, beside the
is_primaryholder) states exactly what is true: he performs that function here, now. The describing document (D-1) states the deputy arrangement in controlled content; an auditor reads "primary: X; deputy by documented arrangement: Y" — reality, verbatim. - Rights stay pinned to the role. Nothing is granted to the deputy's own role; one person gains a second function; every other holder of his title is untouched.
- Nothing blocks. Standing staffing plus documented convention covers the absence — the way paper Stellvertreterregelungen actually operate.
- Competence is accounted, not asserted. Training records travel with the person: a deputy staffed into duties he is not trained for is honestly visible — the one thing the legacy rule set could never check.
Acting-as / impersonation is refused on trail-honesty grounds: the trail must never blur the actor and the authority source ("A, acting for B" is the ambiguity this product refuses everywhere else — the releaser stamps, provenance framing, one true trail). That refusal is worldview, not cost.
Named trigger, so the refusal stays falsifiable: if standing-staffing-plus-convention ever proves insufficient — a customer or auditor demanding mechanically absence-bound deputization — the sanctioned shape is TIME-BOXED STAFFING (a staffing row with a validity window, own-name authority, audited at both ends, expiring by itself). Small, trail-honest, and never acting-as.
Rejected alternatives¶
- D-1b — catalog-level describing document (one per role installation-wide): wrong the first time two projects describe one role differently; makes the org QMS the only possible home.
- D-2b — a hard gate (cannot staff an undescribed role): the opinionated principle's strong form, but it would have blocked the clean rebirth's first staffing act on day one; premature until describing documents are the norm rather than the goal.
- Substitution as a mechanism — rejected with the case analysis above.
Consequences¶
- The check lands dormant-friendly: zero links = every staffed role flagged once — exactly the to-do list #47 wanted the system to surface.
- The link is the activation's child: it leaves with the activation on deactivation (the #73 /
ADR-0112 §2 grammar); the realization sweeps the
role_activationlifecycle flows for the new column (dissolve/derive/reflect/RLS tests — the standing schema-extension discipline). - Realization is one slice, queued behind the running v0.9.13 wave; requirement rows and the matrix entry ride the realization per the v1.8 verified-at-minting discipline.