9. Architecture Decisions¶
Architecture decisions are documented as ADRs in docs/decisions/. ADRs are immutable once accepted; a later ADR amends an earlier one via bidirectional links. The authoritative registry — including the Refined / superseded by relationships — is the decisions index; this list is a convenience mirror.
Index¶
| ADR | Title | Status |
|---|---|---|
| ADR-0001 | Content-addressed document storage with PostgreSQL as source of truth | accepted |
| ADR-0002 | Single instance with scope hierarchy and copy-on-derive | accepted |
| ADR-0003 | Fixed life cycle states with configurable review processes | accepted |
| ADR-0004 | Global catalogs for roles and document types with per-scope activation | accepted |
| ADR-0005 | Modular monolith | accepted |
| ADR-0006 | Three-layer search with in-database index and pluggable LLM | accepted |
| ADR-0007 | Angular frontend with server-side-only enforcement | accepted |
| ADR-0008 | Flyway migrations and jOOQ codegen from the migrated schema | accepted |
| ADR-0009 | RLS enforcement via a session-scoped authorized scope set | accepted |
| ADR-0010 | Self-managed QMS via an internal mandator | accepted |
| ADR-0011 | Authentication via OIDC SSO with server-side (BFF) sessions | accepted |
| ADR-0012 | Permission-based authorization and system bootstrap | accepted |
| ADR-0013 | User identity lifecycle | accepted |
| ADR-0014 | Electronic signatures (Part 11 / Annex 11-capable) | accepted |
| ADR-0015 | Document effectivity, periodic review & validity | accepted |
| ADR-0016 | Data retention, disposal & privacy (FADP / GDPR) | accepted |
| ADR-0017 | Multi-part version content | accepted |
| ADR-0018 | Non-functional targets (capacity, availability, security) | accepted |
| ADR-0019 | Notifications & tasks | accepted |
| ADR-0020 | Internationalization (content language, time zones, formatting) | accepted |
| ADR-0021 | General typed document relationships | accepted |
| ADR-0022 | Read-acknowledgement & questionnaires | accepted |
| ADR-0023 | Migration / import — current released state | accepted |
| ADR-0024 | Draft / in-review cancellation | accepted |
| ADR-0025 | Disposal & erasure mechanics (storage, index, backups) | accepted |
| ADR-0026 | RLS runtime safety — connection-pool scope isolation | accepted |
| ADR-0027 | RLS boundary — content-plane vs control-plane | accepted |
| ADR-0028 | Stateless encrypted token-state cookie (no server-side session store) | accepted |
| ADR-0029 | Document content model — draft-freeze, content-table RLS, append-only audit | accepted |
| ADR-0030 | Review & life-cycle realization model | accepted |
| ADR-0031 | Released-base readability enforcement & copy-on-derive mechanics | accepted |
| ADR-0032 | Frontend realization — Quinoa, Angular Material, i18n & E2E | accepted |
| ADR-0033 | CSRF control for the BFF session (SameSite + JSON content-type) | accepted |
| ADR-0034 | Notification realization — in-process domain events + system-context fan-out | accepted |
| ADR-0035 | Fan-out contract as shipped — gate semantics, count disclosure, audit protocol | accepted |
| ADR-0036 | Repository layer — services carry rules, repositories carry jOOQ | accepted |
| ADR-0037 | Task scheduler — in-process tick, advisory-lock election, SQL sweep | accepted |
| ADR-0038 | Periodic-review realization — derived due date, policy-gated confirmation | accepted |
| ADR-0039 | Sweep & periodic-review contract as shipped — grants, guards, addressing | accepted |
| ADR-0040 | Disposal-due surfacing — closure-anchored retention, scope-level legal hold | accepted |
| ADR-0041 | Email channel & notification i18n — notification-as-outbox, structured messages | accepted |
| ADR-0042 | Disposal execution — document tombstoning, single-actor hard-gated destruction | accepted |
| ADR-0043 | Disposal blob deletion is post-commit, GC-backstopped | accepted |
| ADR-0044 | Search FTS baseline — event-indexed, RLS-scoped search_index | accepted |
| ADR-0045 | Image attachments as content parts — draft working area, freeze-to-manifest | accepted |
| ADR-0046 | Four-eyes — submitter excluded from approval | superseded by ADR-0047 |
| ADR-0047 | Four-eyes — submitter-only exclusion | accepted |
| ADR-0048 | Document organization — folder tree per scope | accepted |
| ADR-0049 | Content model round 2 — categories, templates, trace relations, completeness | accepted |
| ADR-0050 | Live frontend — SSE push, CodeMirror 6, presence | accepted |
| ADR-0051 | In-text document links — doc: scheme | accepted |
| ADR-0052 | Addressees & read-acknowledgement | accepted |
| ADR-0053 | Addressees per document + process owner | accepted |
| ADR-0054 | Process owner is a role | accepted |
| ADR-0055 | WYSIWYG editing mode over markdown (fidelity gate + entry guard; §3/§4 retired by ADR-0057) | accepted |
| ADR-0056 | Visual-editor chrome via Milkdown Crepe (§3 canon fencing retired by ADR-0057) | accepted |
| ADR-0057 | Content record is the ProseMirror tree; markdown a derived projection (§5 amended by ADR-0058) | accepted |
| ADR-0058 | PM_DOC is the only content format — pre-production cut | accepted |
| ADR-0059 | Editor engine switches to TipTap (supersedes Milkdown/Crepe) | accepted |
| ADR-0060 | Backup & restore: scripted ordered backup + automated restore rehearsal | accepted |
| ADR-0061 | CSV: generated validation package per release | accepted |
| ADR-0062 | SBOM: CycloneDX over production layers, artifact-verified | accepted |
| ADR-0063 | User identity binds on subject only (issuer = provenance) | accepted |
| ADR-0064 | Per-document workflow role bindings, enforced | accepted |
| ADR-0065 | Diagrams: Excalidraw manual layer + Mermaid for UML | superseded by ADR-0072 |
| ADR-0066 | WITHDRAW: author side reclaims a version from review | accepted |
| ADR-0067 | Review comments: anchored, immutable, resolvable | accepted |
| ADR-0068 | Confluence/Jira QMS import: staged, read-only, repeatable | accepted |
| ADR-0069 | Arbitrary file attachments: download-only confinement | accepted |
| ADR-0070 | Training questionnaires: versioned, auto-scored, release-gated | accepted |
| ADR-0071 | Training plans: document obligations + external certifications | accepted |
| ADR-0072 | Diagrams: self-hosted draw.io replaces Excalidraw | accepted |
| ADR-0073 | Per-scope QMS configuration surface | accepted |
| ADR-0074 | PDF export of released documents — server-side via Apache FOP over the canonical projection | accepted |
| ADR-0075 | Source history import: continuity without forged audit | accepted |
| ADR-0076 | Periodic-review status visibility (due date + history), derived not stored | accepted |
| ADR-0077 | Requirement items: keyed blocks inside controlled documents | accepted |
| ADR-0078 | Generalized trace items: one typed item, type-agnostic engines (traceability model stage A) | accepted |
| ADR-0079 | Risk & risk-control items: ISO 14971 thread, scores recorded (scheme is content) — traceability model stage B | accepted |
| ADR-0080 | Traceability workspace: overview hub + role-gated sub-pages; QMB training oversight moves out of My training | accepted |
| ADR-0081 | Draft-only trace-structure changes, server-enforced (409); importer/seed resequenced create→link→release | accepted |
| ADR-0082 | Table-shaped risk authoring: a grid surface over :::risk blocks — the tree stays the record |
accepted |
| ADR-0083 | TD dossier: versioned cross-scope regulatory-structured deliverable — doc-version pins, DRAFT→ISSUED, merged PDF + hash-manifest zip | accepted |
| ADR-0084 | Validation & regulatory axes (traceability Stage C: schema-driven frontend, four new types, conformity + validation views) | accepted |
| ADR-0085 | Relation-layer consolidation: one trace vocabulary over document relations + item links | accepted |
| ADR-0086 | Requirement trace chains: end-to-end path view + flat CSV path export; rides the coverage-engine edge-type-set consolidation | accepted |
| ADR-0087 | Scope traceability report: one derived overview artifact (gap digest + sectioned chains), hub download + audit-pack entry — never a controlled document | accepted |
| ADR-0088 | Verification evidence classes: planned vs verified derived at the read layer via a per-type evidence flag; evidence dates surfaced | accepted |
| ADR-0089 | Recurring obligations: periodic QMS tasks with released-record completion evidence, derived due/overdue, inbox fan-out | accepted |
| ADR-0090 | Release & versioning policy: semver from v0.8.0 (1.0 = audit-readiness), deliberate communicated releases (tag → report-at-tag → GitHub release), pre-1.0 wire-breaking allowance | accepted |
| ADR-0091 | Release modes: single-person DIRECT release for RECORD-category types with a structural four-eyes floor (PRESCRIPTIVE/TEMPLATE never relaxable) | accepted |
| ADR-0092 | Orphan-blob garbage collection: quarantine bin (30-day soft delete, registry not file move), explicit reference-column inventory, never-delete-referenced invariant | accepted |
| ADR-0093 | One scope context: the toolbar switcher is the only scope selector; per-view selectors removed; required views show a neutral picker on ALL that writes the shared state | accepted |
| ADR-0094 | Calendar-anchored obligation schedules: INTERVAL/MONTHLY/YEARLY with day-clamping (never too late), occurrence-after-anchor next-due, sweep-derivation parity pinned | accepted |
| ADR-0095 | Key-rule governance: free until the first key is minted, then structurally frozen; deliberate audited exception act (reason required, distinct event) | accepted |
| ADR-0096 | Transition permissions: all transitions always visible, deny-unless-granted ("Empty == nobody"); tiered save validation (blocking revoke gap on prescribing types); REJECT folds into the reviewer right | accepted |
| ADR-0097 | Governed provisioning surface: mandator/scope creation, type/role activation, key rules under ONE global MANAGE_SCOPES; GLOBAL-anchored audit; seed keeps SQL (+ Amendment: the scope-settings PUT) | accepted |
| ADR-0098 | Org chart × role system married at the evidence level: de-personalized chart document, derived authority/staffing view + quarterly snapshot record, one accountable (primary) holder per scope×role with zero authorization semantics | accepted |
| ADR-0099 | The user manual is an in-app topic reference: one-question topics, generated screenshots, checked tutorial↔manual bidirectionality, contextual entry points | accepted |
| ADR-0100 | LQMS = the Living Quality Management System — the name states the derived-evidence thesis ("ein gelebtes QMS"); Lovable liability discharged | accepted |
| ADR-0101 | Executable validation journey: one canonical PQ walk on the real stack emitting an auditor-readable evidence bundle; on-demand + pre-release; release gate from v0.9.0 | accepted |
| ADR-0102 | Typed template variables (text/date/number, absent=text): server-validated + normalized at instantiation; deliberately no enum; trace-item attributes untouched | accepted |
| ADR-0103 | npm audit joins the verification gate as a second standing input (release-blocking, triage-documented) |
accepted |
| ADR-0104 | Issued dossier is a persisted artifact of record: render+store once at issue (stable hash = its identity), download serves stored bytes, issued = a record (governed disposal not delete); clone-to-draft realizes 3b re-issue-as-new-version | accepted |
| ADR-0105 | Navigation IA — group by moment-of-use (Work flat / Evidence grouped / Admin, gear separate); per-entry role visibility, empty groups hide; dissolves the toolbar overflow, co-locates audit-pack + dossier, inbox action badge, clickable logo | accepted |
| ADR-0106 | Client reflection follows assignments, not only content reach (the fresh-install dead-end) | accepted |
| ADR-0107 | Audit append gate follows assignment reach, not content reach — "write where you are staffed, read what you may view" | accepted |
| ADR-0108 | Mandator membership — invite-accept for everyone, pre-assigned responsibilities, redemption-time evaluation | accepted |
| ADR-0109 | Organization founding is its own process — operator-only workflow, narrow empty-org dissolve, MANAGE_MANDATORS splits from MANAGE_SCOPES | accepted |
| ADR-0110 | The audit trail is not a read model — written by every act, read only to be shown or counted; promoted facts answer business questions | accepted |
| ADR-0111 | Imported persons are external — unbound, uninvitable, provenance-marked; never auto-merge on email; source trail travels as evidence artifact | accepted |
| ADR-0112 | The element-kind catalog — kinds carry semantics + dependencies in the model; scopes activate kinds; per-scope labels/prefixes frozen at first use | accepted |
| ADR-0113 | Project arrival import — released documents arrive released with their true bodies, source dates and EXTERNAL authors; nothing backdated | accepted |
| ADR-0114 | The operating model arrives with the corpus — roles as functions with a mapping preview, one-transaction policy arrival, per-document source bindings | accepted |
| ADR-0115 | Collaborative draft editing — recovery buffer, session lock, honest credit; reconnect + SSE meta-sync, no feature flag | accepted |
| ADR-0116 | The system maintenance surface — the app backs itself up (async audited pg_dump + blob archive), backup evidence + job heartbeats on Admin → System; restore stays deploy-level |
accepted |
| ADR-0117 | Role documentation — described_by link on the role activation, ROLE_WITHOUT_DESCRIPTION setup finding, per-source recognizers; substitution rejected as acting-as — its named time-boxed-staffing trigger fired 2026-09-01 (ADR-0130/0131) |
accepted |
| ADR-0118 | Offline QMS copy — the repo carries the scope audit pack as loose PDFs + manifest (no second renderer); drift split into offline-consistency (every PR) and freshness (release/nightly); eligibility opt-in (open decision 10, ruled 2026-08-24) | accepted |
| ADR-0119 | Module-internal packages — capabilities first, layers second (closed at service/repository), vocabulary at the capability root as the published surface, one rule with teeth (no sibling repository imports), Konsist-enforced | accepted |
| ADR-0120 | The shared kernel — the domain-exception vocabulary leaves scope for a dependency-free common module; scope becomes purely authorization + tenancy, RlsScopeContext/TransactionPostCommit stay |
accepted |
| ADR-0121 | Project Administrator — seeded per-scope admin role WITH read access (scoped deviation from ADR-0012, gated by the project's own activation+staffing act); classification guard over the permission vocabulary | accepted |
| ADR-0122 | Organization-level administration — the V005 cascade documented and made visible ("via organization" roster rows), warning not restriction on org-activating authoring-class roles, CREATE_PROJECT split out of MANAGE_SCOPES and checked at the org anchor | accepted |
| ADR-0123 | Draft save-points — every explicit save restorable, kept for the life of the version incl. post-release, deleted by disposal, restore as a new audited save; in scope for v1.0 | accepted |
| ADR-0124 | The birth path completes without borrowing content clothes — an empty scope's folder skeleton is a setting-up act (bright empty-scope line, V106 pattern); the staffing bridge accepts hand-added roles; wizard re-entry via the governable union; filtered offers say so | accepted |
| ADR-0125 | The export twin — export emits an ARRIVAL BUNDLE (one exporter, the battle-proven consumer); released corpus travels, acts stay; audited EXPORTED act; cross-scope refs re-bind by identity; Am. 1: id adoption + REVOKED travels; Am. 2: the base-library landing (declared id namespace) | accepted |
| ADR-0126 | Project dissolution — mayBeDissolved pre-resolved; gate = CREATE_PROJECT at the org anchor; drafts dissolve with counted consent, a native release blocks; V113 destroyer + pg_constraint delete set; any-status sysadm kill switch rejected | accepted |
| ADR-0127 | Base-QMS layers — base library = the one GLOBAL scope read in place (BASE_READ, released face), templates = derive masters, records stay; migrate-then-revoke-then-supersede for tool-operational docs (his J1); Base Reader auto-assigned; Am. 1: the four visibility faces | accepted |
| ADR-0128 | The GLOBAL cascade carries administration, never content — only ADMINISTRATIVE-class permissions cascade from a GLOBAL assignment; the classification becomes a structural column with a derived cascade bit | accepted |
| ADR-0129 | Role identity from a GLOBAL assignment acts in GLOBAL alone — workflow authority stops cascading from GLOBAL; the MANDATOR-anchor cascade deliberately kept; one shared inlined predicate across the resolver family | accepted |
| ADR-0130 | Break-glass — PA keeps no standing content rights (the boundary is a witness, not a capability limit); refusals become doors with inline audited self-staffing; permission closure at import + setup-status; timed staffing expiring in the predicate; header chip | accepted |
| ADR-0131 | Role substitution as declared adoption edges — own-name timed staffing per the matrix (never impersonation, ADR-0117 upheld); matrix = per-scope operating-model content, travels in the bundle, declaration = qualification statement; content-class targets only (ADR-0128 structural); emergency ≠ planned absence; in 1.0, resequencing ADR-0130 §6 | accepted |
| ADR-0132 | Dual-hat reviewer satisfaction is a declared policy choice — a per-(scope, type) review-policy flag (one person may satisfy multiple role requirements), default OFF so the strict cross-role distinct-person floor stays everywhere unless a scope's governance opts in as a witnessed act that travels in the arrival bundle; within-role distinctness, four-eyes and one-approval-one-declared-role invariant; "Approve as <Role>" ships unconditionally; the global change rejected as undeclared semantic weakening; post-1.0 | accepted |