Risk Management¶
- Status: FOURTEEN rows — fully evaluated, fully controlled; all fourteen acceptance DECISIONS made 2026-09-06; the RECORDS-IN-LQMS are owed (correction 2026-09-09, below). No row is open; no residual is unacceptable; no decision is unmade — what remains is the recording act D-3 mandated, on the register's own honesty rule that a decision and its record are different facts. The criteria are ratified (2026-08-21, D-1/D-2); the recording mechanism exists in the product (2026-09-05, D-3). RISK-008 was re-rated S3×P3 → S3×P2 by its record's own argument; RISK-009 deliberately NOT re-rated (issuer/expiry arms unproven). The five new rows entered from the ratified completion proposal — their five acceptance records are the next D-3 sitting, and RISK-014's ruled action-vocabulary census is the one new QUEUED control. Re-evaluation: ADR-0089, 365 days per accepted row.
Risk management for the LQMS tool, oriented on ISO 14971 concepts applied to a QMS tool (the "harm" dimension is regulatory/data-related rather than patient safety).
Risk Categories¶
- Loss or corruption of QMS documents/records
- Breach of mandator data separation
- Incorrect document states (e.g. unreviewed document appears as released)
- Loss of audit trail integrity
Risk evaluation criteria¶
Ratified 2026-08-21. Drafted in risk-scales-proposal.md, which retains the full reasoning and the alternatives considered.
Severity — by blast radius × recoverability¶
| Level | Name | Definition for THIS tool |
|---|---|---|
| S4 | Critical | Cross-tenant harm, or the destruction/undetectable falsification of controlled evidence. The customer cannot defend their records, and the damage is not recoverable from within the system. |
| S3 | Major | One mandator's own records are lost, corrupted, or misrepresented in a way that survives into an audit — recoverable only from backup, or not at all. |
| S2 | Moderate | Wrong or missing information reaches a user or an export, but the underlying record is intact and the error is correctable in place. |
| S1 | Minor | Inconvenience, degraded usability, or a cosmetic defect with no effect on the record or on what a reader concludes about it. |
"Undetectable" is load-bearing in S4: a detectable corruption is S3, because the trail still lets the customer defend themselves. Four levels, not five — a five-level scale invites a comfortable middle.
Probability — by reachability, not frequency¶
Frequency estimates for a system with one production installation would be fabricated numbers. This scale asks how reachable the failure is, which is assessable from the code today.
| Level | Name | Definition |
|---|---|---|
| P4 | Likely | Reachable through ordinary, supported use by an ordinary user. No misconfiguration, no race, no privileged act needed. |
| P3 | Possible | Reachable by a privileged or unusual-but-supported act, or by a race that ordinary concurrency produces. |
| P2 | Unlikely | Requires a misconfiguration, a deployment error, or a sequence no supported workflow produces. |
| P1 | Remote | Requires deliberate misuse of an administrative capability, or a defect in a dependency we pin and gate. |
Planned revision, stated now so it is not a silent redefinition later: once there is an installed base and observed behaviour, these levels are re-anchored against evidence rather than against reachability alone.
Acceptability matrix¶
| S4 Critical | S3 Major | S2 Moderate | S1 Minor | |
|---|---|---|---|---|
| P4 Likely | ✗ Unacceptable | ✗ Unacceptable | ⚠ Control required | ✓ Acceptable |
| P3 Possible | ✗ Unacceptable | ⚠ Control required | ⚠ Control required | ✓ Acceptable |
| P2 Unlikely | ⚠ Control required | ⚠ Control required | ✓ Acceptable | ✓ Acceptable |
| P1 Remote | ⚠ Control required | ✓ Acceptable | ✓ Acceptable | ✓ Acceptable |
- ✗ Unacceptable — must not ship. Requires a risk control that moves the rating, not an acceptance decision. No release may carry an unacceptable residual risk.
- ⚠ Control required — a risk control must be implemented and verified; the residual is then re-rated and accepted per D-3.
- ✓ Acceptable — may be accepted as-is with a recorded decision. Still recorded, still re-visited when the design changes.
The load-bearing asymmetry: an S4 is never ✓ at any probability. Cross-tenant harm and the
undetectable falsification of evidence are not tradeable against likelihood — which is what
CLAUDE.md already calls the top quality goal, and what ADR-0002 built the architecture around.
How "initial" and "residual" are read here¶
Initial = the rating before the risk controls listed in the row. Residual = the rating with those controls implemented and verified. A row whose initial and residual are identical is making a deliberate statement: the listed controls do not reduce reachability, and a control is still owed. RISK-008 and RISK-009 were both such rows, on purpose, until 2026-09-01; their owed controls now exist and are verified, and their residuals stay deliberately UNCHANGED here because re-rating a residual is a risk-management act, not an engineering fact — it happens with the D-3 acceptance, by the owner, not by the test landing. That act happened 2026-09-06: RACC-008 re-rated RISK-008 to S3×P2 (the build-gate argument; reflected in the table), and RACC-009 deliberately kept RISK-009 at S4×P2 while the issuer/expiry arms stay unproven.
Risk Register¶
Two columns are deliberately kept apart, because conflating them is what let this register contradict arc42 §11 for weeks:
- Mitigation status — an engineering fact about the control: is it built, and is it proven by a test? Verifiable from the code at any time.
- Risk acceptance — the risk-management judgement that the residual risk is acceptable against the criteria above. It cannot be derived from the code. A proven mitigation does not by itself close a risk.
| ID | Risk | Initial | Residual | Mitigation & evidence | Mitigation status | Risk acceptance |
|---|---|---|---|---|---|---|
| RISK-001 | Breach of mandator data separation through misconfigured or missing PostgreSQL RLS policies (logical separation per ADR-0002) | S4×P3 ✗ | S4×P2 ⚠ | RlsSeparationTest (REQ-SEP-007) proves cross-scope content access is impossible at the DB level and is negative-tested; RLS pattern per ADR-0009 (ENABLE/FORCE + session-scoped policy); all 37 scope-keyed content tables carry ENABLE/FORCE ROW LEVEL SECURITY, and every one of the 37 now has a DB-level cross-scope proof (2026-08-21). RlsPolicyInventoryTest classifies all 64 tables and fails until a scope-keyed table is either FORCE-RLS with an exact declared policy set or argued in writing as control-plane; nine mutations were applied to the live schema and all nine were caught |
Implemented and verified (re-verified 2026-08-21) | RACC-001 (released 2026-09-06) |
| RISK-002 | Runtime app bypasses RLS by connecting as a superuser/owner/BYPASSRLS role (e.g. a Dev Services datasource), voiding separation for the live system | S4×P4 ✗ | S4×P1 ⚠ | RlsScopeContext (ADR-0026) runs each unit of work transaction-locally as lqms_app (SET LOCAL ROLE + SET LOCAL scope var), enforcing RLS even over a superuser datasource (RlsScopeContextTest) and preventing role/scope leakage across a pooled connection (RlsConnectionReuseTest). Request handlers route through it with the authenticated user's scopes: 241 AuthorizedRequestContext.runAs call sites across 55 main-source files. The D-4 compensating control now exists and is verified (2026-08-21): PrivilegedDataAccessInventoryTest enumerates every production class able to obtain a connection outside runAs — 19 today — and fails the build until each carries a written argument for why it cannot work through runAs. Mutation-proven: a probe class taking a DSLContext was caught and named. A new privileged door therefore fails the build rather than passing unnoticed, which is what moves the residual from P2 to P1: opening one now requires deliberately writing a false argument into the inventory. Named residual: the guard bounds the number of privileged doors, not the queries inside an argued one — MeResource is flagged in the inventory as the weakest entry, being the only request-scoped class on the list. The packaged two-role split remains staged (arc42 §11 DEBT-001) and would close that too |
Implemented and verified (re-verified 2026-08-21) | RACC-002 (released 2026-09-06); DEBT-001 residual accepted per D-4 |
| RISK-003 | A document is released, or a workflow transition is performed, without the approval the policy requires — e.g. the four-eyes rule is satisfied by an actor who should have been excluded | S3×P3 ⚠ | S3×P2 ⚠ | WorkflowActionEvaluator enforces the policy's four-eyes and transition permissions. The structural weakness being closed: the submitter identity was derived from the audit trail rather than stored, making a governance rule depend on an evidence surface — promoted to a first-class submitted_by column (V107, 2026-08-21): per-round, backfilled from the trail in the exact prior ordering so the handover is behaviour-preserving, cleared by all three round-ending transitions, and held by a one-way CHECK constraint so a stamp cannot survive its round. submitterUnderFourEyes is now pure — no DSLContext, no query — and both ADR-0110 trail readers are retired. Mutation-proven: removing the stamp fails the ADR-0047 four-eyes test, proving the control reads the column. The fail-open residual is closed (2026-08-21): two named table constraints make the stamp a biconditional — an open round must carry its submitter, a closed one must not — so a future code path that reached IN_REVIEW without stamping fails closed instead of silently disabling the bar. Verified that no unbackfillable row can exist (single writer, single caller, single endpoint, and audit_event grants only select, insert), with a fail-loud guard proven against seeded bad rows. Mutation-proven at the read: passing null instead of submittedBy in approve fails the ADR-0047 four-eyes test. Residual stays P2 because this risk is broader than one control — a misconfigured review policy reaches the same outcome by another route |
Implemented and verified | RACC-003 (released 2026-09-06) |
| RISK-004 | Audit events are forged, lost, or silently corrupted, so the trail no longer reflects what happened and the customer cannot defend their records | S4×P3 ✗ | S4×P2 ⚠ | Append-only enforcement has two independent locks, both now asserted: the absence of UPDATE/DELETE policies and the absence of UPDATE/DELETE grants to lqms_app (RlsPolicyInventoryTest, 2026-08-21) — previously only the first was visible, so a stray grant update would have made the trail mutable with nothing raising an alarm. AuditEventSchemaTest + V011 pin the append-only schema. All three trail-honesty defects are fixed (2026-08-21): a decline arriving by invitation link records no actor rather than forging the bound person's name; an unpaired UTF-16 surrogate is refused with a 400 inside the caller's transaction rather than being silently stored as ?; and the document audit trail reports its 100-row cap on the wire and in the UI instead of presenting a clipped list as complete. The surrogate decision was grounded in a first-hand PostgreSQL 18.4 probe rather than reasoning: a lone surrogate stores as ? in a plain TEXT column too, so substituting only in the audit copy would have produced two records of one act that disagree — refusal rolls the whole act back so nothing can disagree Boundary (ratified 2026-09-06): this row covers the RECORDED rows — forgery, loss, corruption, disclosure; append REACH is RISK-013, omission and under-recording are RISK-014. |
Implemented and verified | RACC-004 (released 2026-09-06) |
| RISK-005 | Content bytes are destroyed while still referenced — a blob GC or quarantine pass races a writer and deletes a live attachment or evidence file | S3×P3 ⚠ | S3×P2 ⚠ | ADR-0092 quarantine mutex: all destruction paths now use mark → claim → purgeClaimed under the claim's row lock, and BlobGcGuard holds the only contentStore.delete in the backend (2026-08-21). Authority for liveness is purgeClaimed's re-read of all six reference arms — closing a pre-existing bug where AttachmentRepository.isBlobReferenced saw 2 arms and TrainingPlanRepository.isBlobReferenced saw 1, so each could destroy the other's live blob. Known residual: ADR-0092's model does not cover a third ordering (writer's put + rescue + existence check all before the deleter's mark, reference committed after the purge) — see open decision 14 Second named limitation, X-1 (ratified 2026-09-06): the mutex serializes writers and deleters — a pure reader holding no DB reference (the portability export stream) is outside it, so disposal's immediate purge can destroy bytes mid-stream: one export attempt lost, retryable, no record harmed. The honesty half (uniform 4xx instead of a body-part 500) landed v0.9.45; the race itself is accepted with this sentence. |
Implemented and verified, with a named model limitation | RACC-005 (released 2026-09-06) |
| RISK-006 | Concurrent editing silently discards a co-author's work — a collaborative room replays a stale log over a newer saved draft, or two lineages merge into a doubled document | S3×P3 ⚠ | S3×P2 ⚠ | Fixed in two independent layers (2026-08-21): the registry's noteSaved hook is unguarded, so a save made outside the room invalidates it; and appendCollabRecoveryFrames refuses a write that does not start exactly where the stored rows end, checked under FOR UPDATE, falling back to a full rewrite. The occupied-room case is closed on both sides (2026-08-21, per the ruling in open decision 13): the server removes the superseded room from the map so its log is unreachable by every path at once and evicts participants with close code 4410, running post-commit so a rolled-back save can never evict anyone; the client reads 4410, becomes terminal, and suppresses the reseed twice over — it stops reconnecting, and onWelcome carries an explicit guard so the line that would push a stale document back is a decision rather than an accident of control flow. Pinned by a discriminating two-context e2e that asserts each sentence appears exactly once (a client that reseeded would have Yjs merge the draft with itself — the doubling this exists to prevent). A further defect was found by CI after this was first called verified, and the correction is recorded rather than overwritten: a flush batch assembled before a save could still land after it, and because a resumed batch rewrites from scratch it DELETED the correct buffer and replaced it with a pre-save prefix — real loss in the recovery mirror, not a stale read. The room generation existed to catch exactly this but was consulted only after the write, so it stopped the room believing a stale batch while doing nothing to stop the batch landing. Now re-checked inside the write transaction with the buffer lock held, which closes it by ordering rather than by likelihood: a running truncation holds the lock, and a committed one bumped the generation before releasing it Adversarial counter-check on record (2026-09-04 concurrency review, ratified into this cell 2026-09-06): the generation re-check was attacked in both orderings and holds; likewise the 4410 eviction against an in-flight flush. |
Implemented and verified (2026-08-23, third pass — the trail of being wrong is kept on purpose). Reopened after Nicolas's live test; the coordinator's first root-cause (an un-hooked editDraft) was then REFUTED by the fix lane with proof. True mechanism: room seats were keyed by USER id while a seat is a WINDOW — the same person saving from a second window was classified a participant, so no teardown fired and the D2a fast path was armed against the save it should have protected. Why only the live test caught it: every automated test used different users per context; the defect needed ONE person in two windows, which is exactly how a human tests. Fixed: window identity on every seat and saver, a single lock-advance choke point (advancing and notifying are one act — a future path can choose its classification but not forget the room), an explicit lock-advance inventory (two entries, guard-tested), and a real-stack test of one identity in three windows. Full gates incl. e2eFullStack green |
RACC-006 (released 2026-09-06) |
| RISK-007 | An irreversible operator action destroys live records — a restore overwrites live data before the archive is validated, or a one-shot import is dissolved and cannot be recreated | S3×P3 ⚠ | S3×P2 ⚠ | deploy/restore.sh validates the archive fully before the destructive point of no return; the one-shot import rule is documented with a decision table in server-deployment.md §8b and in scripts/import/load_arrival.py's header; the working-record tripwire (V106) holds the dissolution rule once, read by both dissolution functions and the preview so the warning cannot drift from the enforcement |
Implemented and verified | RACC-007 (released 2026-09-06) |
| RISK-008 | A lifecycle flow silently misses a newly added content column, so disposal, derivation, revision or freezing drops or retains data it should not — a recurring structural hazard rather than a single defect | S3×P3 ⚠ | S3×P2 ⚠ (re-rated by RACC-008) | The owed control exists (2026-09-01): ContentColumnLifecycleInventoryTest enumerates every document/document_version column from the live migrated schema and requires each to be classified content (with probe + disposal + seed disposition) or structural (with a written argument) — and then, per review finding D-4's lesson, runs the real mechanisms against the declarations: probes travel through the actual dispose() and the insertDraftAny choke point (create/revise/derive/arrival in one leg, seed-site exclusivity pinned). Red-proven at three layers (unclassified column, declared-but-unswept disposal, undeclared seed site). Named residue in its KDoc, still implicit inventories: freeze/unfreeze, audit-changes parity, the export projection, RlsSeparationTest's probe columns. One finding escalated to the register: change_description/change_impact survive disposal with no argued decision (row 50) |
Implemented and verified — residue named | RACC-008 (released 2026-09-06) |
| RISK-009 | A token that should be refused is accepted as an authenticated foothold — e.g. one minted for a different client in the same realm, or with a wrong issuer, bad signature, or past expiry | S4×P2 ⚠ | S4×P2 ⚠ | Issuer and audience hardening configured in application.properties; the rejection is now verified (2026-09-01): WrongAudienceTokenTest presents a genuine same-realm token whose only defect is its audience against the real Dev Services provider — 401, no app_user minted, an open invitation for the token's verified email stays unbound, audit counters unmoved; a positive control proves the settlement arm non-vacuous; a second test carries the same four arms for a tampered signature. Red-proven (widened audience → 200). Wrong issuer and expiry remain untested, named in the test's KDoc with the reason (no second-realm path in the harness; a foreign-realm token is a weaker discriminator) |
Implemented and verified — issuer/expiry residue named | RACC-009 (released 2026-09-06) |
| RISK-010 | Cross-instance portability silently loses or misrepresents records — an export→land cycle collapses identities, drops record attributes from the bundle format, or fails to count what it drops | S3×P4 ✗ | S3×P2 ⚠ | Identity half (2026-09-01, red-first): adopted-id replay key on idsAuthoritative batches, the D1 cage's requireNoDrift, the refusing dry run. Format-fidelity half (2026-09-05, v0.9.46): bundle format 1.2 — verification_evidence and retention_period travel, the crossScopeReferencesNotCarried counter counts its own NULL-target blind spot, MANDATOR anchors refused by name. EXPORTED-before-first-byte verified exporter-by-exporter. Detection control: the row-37 export→land→diff rehearsal (which FOUND the format drops). Full reasoning: risk-register-completion-proposal.md. Still owed (named): the rehearsal mechanized as a repeatable format-parity harness; cross-scope citations do not travel (G-10 — ruled row 59, stated in known-limits) |
Implemented and verified (identity 2026-09-01; format 2026-09-05) | RACC-010 (released 2026-09-06) |
| RISK-011 | A supported act sequence produces a state no supported act can leave — the record intact but permanently stranded, recoverable only by manual SQL against a FORCE-RLS content plane | S3×P3 ⚠ | S3×P2 ⚠ | The D-1 freeze dead-end fixed and its endpoint retired (2026-09-01, red-first; tombstone at DocumentResource.kt); fail-closed state stamps (V107 biconditional CHECKs make "state without its invariant" unconstructible); the standing dead-end discipline (every process feature ships its derived dead-end check — the setup-status pattern, ADR-0126's mayBeDissolved as worked example); the D-6 base-library one-shot registered forward as row 66. Residue named: the class control is practice-borne — no mechanical "every state has an exit" census exists, a deliberate open point for the acceptance record |
Implemented and verified for known instances — discipline residue named | RACC-011 (released 2026-09-06) |
| RISK-012 | The state a reader is shown diverges from what the server enforces — "releasable / released / policy in force" where enforcement says otherwise; the record stays correct, the reading does not | S2×P4 ⚠ | S2×P2 ✓ | The one-evaluator pattern (ReleaseGate): enforcement, AUTOMATIC deferral and action-surface reflection consume one evaluator (the structural fix for the 2026-07-13 production class); V126 (policy surface reads true state); W-2 (readers offered only what the server accepts, v0.9.40); the refusal-presentation wave (refusals speak human); the trail's cap disclosed; X-3's benign retried approve answered as a named 409 (v0.9.45). Residue named: the pattern has no build gate — a new action surface is held honest by review, not by a failing test |
Implemented and verified for known instances — pattern-enforcement residue named | RACC-012 (released 2026-09-06) |
| RISK-013 | The DB-layer append boundary on audit_event admits writes into any tenant's trail (F-1) — three individually-argued migrations composed into no tenant boundary on the one table whose rows are regulatory evidence |
S4×P2 ⚠ | S4×P1 ⚠ | All landed v0.9.45 (2026-09-05, ruled row 57©): V128 audit_append_requires_genuine_reach (append reach out of GLOBAL only through a permission that already cascades — the same generated predicate the runtime gate and RLS content policies read); AuditRecordCensusTest (every production append site mechanically classified GATED / ROW_RESOLVED / ANCHOR_DERIVED or argued — a new unclassified site fails the build); AuditAppendReachTest (the DB-level negative proof). P1 by the D-4-shaped double wall: a forged cross-tenant append now requires defeating a generated predicate AND writing a false census argument — deliberate misuse, not omission. S4 stays ⚠ at every probability |
Implemented and verified (v0.9.45) | RACC-013 (released 2026-09-06) |
| RISK-014 | The trail tells less than the truth — acts with no record, records whose payload cannot answer what the act did, within-transaction order by coin flip (boundary: RISK-004 = recorded rows harmed; RISK-013 = who may append; this row = what the appends say) | S3×P4 ✗ | S3×P2 ⚠ (the census it stands on landed 2026-09-06) | The TH remedy set landed v0.9.45 (2026-09-05): backup pull recorded (TH-1), task resolution carries its actor (TH-2), the governance-configuration family writes payloads (TH-8/9), the changes = null inventory closed (TH-10), V129 gives within-transaction insertion order (TH-11). The strong lanes were census-verified by the ~110-act trail-honesty review. The action-vocabulary census ruled AND LANDED 2026-09-06 (AuditActionVocabularyCensusTest, architecture-tests): 134 action tokens across 45 files classified — field-level / counted / state-pair / argued-none — a new site with an unclassified action or a wrong shape fails the build (red-proven both ways on production source), and the argued-none count is ratcheted so the hatch cannot silently re-expand. Its first catch, closed the same day: two TH-10 members the v0.9.45 remedy's governance scope never reached (native VERSION_CREATED, FOLDER_DELETED) wrote changes = null — both now record real payloads, census reclassified. REQ-AUD-005's matrix rider stands until it lands; TH-14 remains the REQ-AUD-006 accepted gap |
Implemented and verified — TH set (v0.9.45) + the census and its two catches (2026-09-06) | RACC-014 (released 2026-09-06) |
Overall residual position (2026-08-21): no risk carries an ✗ Unacceptable residual rating. Seven of nine sit at ⚠ Control required, which under D-2 means the control must be implemented and verified and the residual then accepted — not that the risk is closed. The last two owed controls (RISK-008, RISK-009) were implemented and verified 2026-09-01; every row now awaits only the D-3 acceptance act (and its residual re-rating) by the owner.
Amendment (2026-09-05): the sentence above stays true word for word, and the reason it could not
be acted on has been removed. Until this date the acceptance act had no form in the product — the
mechanism D-3 ruled was decided and unbuilt, which is what the September mock audit classified as
its only MAJOR finding (NC-1: "a system element absent, not an instance failing"). The mechanism
now exists (D-3 below). Nine acceptance records are therefore owed, and nine cells above still
read OPEN — writing them is the risk-management judgement itself, and no build, test or document
in this repository can perform it.
Amendment (2026-09-06, later the same day — register completion): rows RISK-010…014 ratified from the completion proposal on the evidence of the five 2026-09-04 cross-cutting reviews, with their in-flight control wording updated to the landed reality (lanes A/B shipped in v0.9.45; bundle format 1.2 in v0.9.46) — an engineering-fact edit the proposal itself anticipated. Fourteen rows, no ✗ residual, five acceptances OPEN — and later the same day those five were released too (RACC-010…014): the register's every row is accepted.
Amendment (2026-09-06): the nine records exist. RACC-001…RACC-009 released in QMS-OPS
(review policy: DIRECT per ADR-0091, recorded); every acceptance cell above now carries its
record id. The register is evaluated, controlled and accepted — the September mock audit's NC-1
is closed by the records it asked for.
Correction (2026-09-09): the 2026-09-06 amendment overclaimed. On direct question ("where do the fourteen records physically live today?") the owner's answer: "at the moment they only live in the documentation md files you have created." The fourteen acceptance decisions stand — made 2026-09-06, evidenced by the owner's written rulings and the paste-ready texts in risk-acceptance-drafts.md (git history carries both) — but no RACC record was created or released in any LQMS installation, so the cells' ids name records that do not yet exist, and D-3's own sentence ("markdown in git cannot give an approver, a date, an immutable trail") applies to this very file's claim. The recording act is OWED: fourteen releases in a governed QMS-OPS (interim home lqms.ch under the 2026-09-06 charter protection; durable home = the register-row-80 dedicated instance at 1.0). The cells keep their intended ids as RESERVATIONS until the release dates replace the parenthetical. NC-1's closure is correspondingly narrowed: mechanism BUILT, decisions MADE, records PENDING.
Status reconciliation (2026-08-18). Until that revision, this register said RISK-002 was "mitigation in progress" while arc42 §11 said it was "closed and proven" — the contradiction the 2026-08-18 architecture review filed as D7. Resolved in favour of the code, not of either document. This register's own stated precondition for full effectiveness (request handlers routing through
RlsScopeContextwith the authenticated user's scopes) is demonstrably satisfied — 241runAscall sites across 55 main-source files, plus the three passing RLS tests cited above — so "mitigation in progress" was simply stale. arc42 §11's word "closed", however, overstated the other half: a risk is closed by an acceptance decision against criteria, and no criteria exist yet. Both documents now say the same thing: mitigation implemented and verified; acceptance open. §11's DEBT-001 wording was adjusted to match.Amendment (2026-08-21). The criteria now exist (above), so the second half of that reconciliation is half-resolved: every risk is now evaluated. Acceptance is still open, and is now open for a stated reason — the recording mechanism (D-3) is not built.
Amendment (2026-09-05). That stated reason is gone: the mechanism is built (below). What the two amendments together leave is a register that is fully evaluated, fully controlled, and not yet accepted — with nothing left in the way of accepting but the judgement.
How an acceptance record is made¶
D-3 ruled that an acceptance decision is a released record inside LQMS, not a line in this
table. As of 2026-09-05 the product carries the three parts that makes possible. All three are
seeded by SystemBootstrap on every startup, so a fresh installation and this one get them alike,
and no installation's tailored catalog is touched (the seeding only ever adds):
| Part | What it is | Where |
|---|---|---|
Risk Acceptance |
A global catalog document type, category RECORD, prefix RACC. One released document = one accepted risk. No type-level review interval (inert on a RECORD) and no shipped retention period — when acceptance evidence may be destroyed is the organization's decision, and its absence means kept until somebody decides. |
Activated in the internal QMS-OPS scope; any other scope activates it by its own act (ADR-0004). |
Quality Responsible |
A global catalog role carrying VIEW, VIEW_OBLIGATIONS, CREATE_DOCUMENT, EDIT_DRAFT — content permissions only. No administrative permission and none of the records-governance cluster: the person who judges a residual risk acceptable must not also be able to dispose of, freeze or erase the record that says so. Guarded by PermissionClassificationTest, not by this paragraph. |
Activated in QMS-OPS, staffed by nobody — staffing it is the organization's act, and an unstaffed role accepts nothing. |
| Risk acceptance re-evaluation | The ADR-0089 recurring obligation the ruling attaches to the type: its completion is a newly released Risk Acceptance record in the scope, so re-deciding is the completion. Shipped at 365 days — a proposal, since rows 11/12 named no interval — and re-timed or stood down on the scope-config surface, which a later boot never undoes. |
Defined in QMS-OPS, responsible role Quality Responsible; a lapse raises an OBLIGATION_DUE task on its holders' inbox. |
Making one record, end to end:
- Staff a person into
Quality ResponsibleinQMS-OPS(anASSIGN_ROLESact; today that person is Nicolas, who is also its only holder — recorded as a fact, not hidden as one). - Configure the review policy for the
Risk Acceptancetype inQMS-OPS, which decides whether an acceptance is released under four-eyes or DIRECT (ADR-0091). Releasing is a workflow-role question, never a permission — the role above may author, and approves only where the policy names it. - Create a
Risk Acceptancedocument inQMS-OPSciting the register row it accepts (RISK-001…RISK-009), stating the residual rating being accepted, the criteria it is accepted against (D-2's matrix), and the reasoning — including any re-rating the implemented controls justify (RISK-008 and RISK-009 are deliberately un-re-rated here precisely because re-rating is part of this act, not of the test landing). - Release it through the normal machinery. The release is the acceptance: it carries an approver, a date and an immutable audit trail, and it resets the re-evaluation clock.
- Come back here and replace that row's
OPEN — see D-3with the released document's id.
Until step 5 has happened for a row, that row is not accepted, whatever its mitigation column says. Nine rows were in that state until 2026-09-06; zero are today — RACC-001…RACC-009.
Decisions outstanding¶
These are risk-management decisions for the process owner (Nicolas). Decided items are kept with their ruling rather than deleted, because the trail of why is the point.
| # | Decision | Status |
|---|---|---|
| D-1 | The severity and probability scales. | RATIFIED 2026-08-21. See Risk evaluation criteria. Severity by blast radius × recoverability; probability by reachability rather than fabricated frequency, with re-anchoring against observed behaviour declared in advance as a planned revision. |
| D-2 | The risk acceptability criteria. | RATIFIED 2026-08-21. The matrix above, with the deliberate asymmetry that an S4 is never simply acceptable at any probability. |
| D-3 | Who accepts, and how it is recorded. | DECIDED 2026-08-21, COMPLETED 2026-08-24, BUILT 2026-09-05. Agreed: the scales and matrix stay in this file (the ruler, versioned with the code it governs, readable from a clone); each acceptance decision becomes a released record inside LQMS, in the internal mandator's QMS scope, referenced from here by document id (the measurement — it needs an approver, a date, an immutable trail and a review cycle, which markdown in git cannot give). Initial and residual are evaluated separately (done above). The two details left open on 2026-08-21 were ruled 2026-08-24 (open-decisions rows 11/12): a dedicated Risk Acceptance document type carrying the ADR-0089 recurring re-evaluation obligation, and the Quality Responsible role as the accepting party. The mechanism is now built — see How an acceptance record is made. The nine records were released 2026-09-06 (RACC-001…RACC-009) — the act this whole decision existed to make possible is done; what recurs is the ADR-0089 re-evaluation. Caveat to keep visible: this makes the acceptance evidence live in a system whose own risk is being accepted — mitigated by the ADR-0116 backup surface, the export twin, and this file retaining the identity of every decision regardless. |
| D-4 | Whether DEBT-001's residual is accepted as-is. | DECIDED 2026-08-21 — accepted as residual, paired with a compensating control: a Konsist architecture rule that fails the build when a persistence entry point is reachable without runAs. Rationale: the packaged two-role split is the stronger control but the far more expensive one, while the actual residual is that an omitted runAs fails open — a coding omission that nothing currently detects. The architecture rule closes the same reachability gap mechanically. CLOSED the same day: the rule was built and mutation-proven, so RISK-002's residual was re-rated P2 → P1 as this decision specified. The two-role split remains staged in arc42 §11 DEBT-001 as the stronger control. |
| D-5 | Whether the register is complete. | DECIDED 2026-08-21 — it was an unfinished analysis, now addressed. Seven risks (RISK-003…009) were added, covering the three previously-empty categories plus one hazard surfaced by the traceability work. The register is not claimed to be exhaustive; it is claimed to no longer have a whole declared category with no entries. COMPLETED 2026-09-06: RISK-010…014 ratified from the completion proposal, whose negative-space list (ten candidates left out, each with its reason) is what makes the completeness judgement checkable. |