Two backlog trees are complete: `docs/project/backlog/` (75 files, every WP done) and `docs/project/refactor-backlog-setup/` (the arc before it). Move both under `docs/project/archive/` with `git mv`, so history stays intact through `git log --follow`. `SHOWCASE-ROADMAP.md` moves with them, because it points at the now-archived backlog README. Add `docs/project/archive/README.md`. It states that these trees are historical and names the two directories that are still live. Repoint every inbound reference named in RD-30's Files table: CLAUDE.md, the root README, both backend READMEs, `LetterHtml.cs`, `a11y.mdx`, the `document-feature` and `new-ssp` skills, and the readable-codebase PLAN, README, and RD-19 ticket. Fix two upward-relative links inside the moved WP files (WP-68, WP-69) that gained a directory level and would otherwise break. Repoint `.prettierignore`'s two agent-prompt exclusions to their new path, so prettier keeps leaving those files' exact wording alone. Mark RD-30 done and check off its acceptance criteria; flip its README row to done. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
5.5 KiB
ADR-C-009 — state the runtime-editable-config exception as a test, not a list
Status: implemented · 2026-08-27 · Source finding: 06-adr-conformance.md §ADR-C-009
· Gated on: RB-07 (satisfied — batch 2)
What was wrong
ADR-0004 said "never runtime-editable" and then named one exception in the singular,
justified narrowly ("specific to one sub-organization's identity"). WP-47 added a second
runtime-editable SQLite surface, FeatureFlagStore, whose own doc-comment states the
equivalence the ADR did not: "SQLite-backed like OrgTemplateStore, same single-gate
idiom."
The code is right; the ADR's text was wrong. A closed list of one leaves the next operational-config surface with no principle to test itself against.
What changed
| File | Change |
|---|---|
docs/reference/architecture/0004-...md |
§"The deliberate exception: org-templates" → §"The deliberate exception: operational configuration" — a four-part test plus a table of the two passing surfaces |
| same file, §Context + the table | src/locale/*.xlf → apps/<app>/src/locale/*.xlf (two apps since WP-67) |
CLAUDE.md §4 |
the singular "Org-templates are the deliberate exception" replaced with the four-part test |
No code changed — the finding says so outright, and verification confirmed it.
Why the RB-07 gate was real, verified clause by clause
Clause (4) of the test is "writes are admin-capability-gated and audited". Signing this
ADR before RB-07 would have ratified a control the code did not implement. RB-07 has landed,
so the clause is now true. Read at backend/src/BigRegister.Api/Program.cs:863-923: each of
the five gates now computes var ok = …, calls AuditAuthz(ctx, capability, resource, ok, principal) with the real boolean, and only then branches. FlagsAdmin's own comment
names this ticket: "this is the surface CQ-004/ADR-C-009 hinge on."
All four clauses were checked against both surfaces rather than assumed:
| Clause | OrgTemplateStore |
FeatureFlagStore |
|---|---|---|
| (1) catalog in code | the OrgTemplateDto shape + OrgTemplateRules validate before save (OrgTemplateStore.cs:49) |
FeatureFlags.Catalog (Domain/Features/FeatureFlags.cs:15) |
| (2) fails closed | unknown subOrgId → null → endpoint 404s (:44-45,:55-56,:72-73,:94-96) |
Set returns false for an unlisted key (:54); IsEnabled returns false (:42-43) |
| (3) operational | one sub-organisation's letterhead | an on/off rollout switch |
| (4) gated + audited | OrgAdmin → orgtemplate:edit (Program.cs:863) |
FlagsAdmin → flags:manage (Program.cs:914) |
FeatureFlagStore's own comment states clause (1) and (2) explicitly: "The CATALOG … is
code … this store only holds the admin's on/off overrides. An unknown key is never
writable/enabled — the code catalog is the authority."
Judgement calls
- Clause (2) is about the write/enable path, not every read.
OrgTemplateStorehas a deliberate read-path fallback for briefs from before WP-23 (:110-114, its ownponytail:comment): an emptySubOrgIdfalls back to the first seeded sub-org rather than failing a whole screen. That is a preview convenience on a read; the four write entry points all returnnullfor an unknown sub-org. The clause is worded "cannot invent a setting, enable a feature, or be written" so this read fallback is not caught by it. Recorded because a reader checking clause (2) againstOrgTemplateStore.cswill meet that fallback first. - Org-templates' publish/rollback versioning is mentioned but excluded from the test. It is stronger than the test requires, and making it a fifth clause would block a legitimate flag-style surface that has nothing to version.
- The stale
src/locale/*.xlfpaths were fixed in the same diff, though ADR-C-009 did not flag them. They are two occurrences of the same WP-67 drift ADR-C-001 and ADR-C-007 exist to correct, in the section being edited, and leaving a known-false path in a document while amending it is the exact failure mode those two findings describe. Scope creep is two words wide here; the alternative is filing a third ticket for it.
Gate released
ADR-C-009 blocked "any ticket proposing a third runtime-editable config surface". Such a ticket can now be judged against a written test rather than by analogy to org-templates.