Files
atomic-design-poc/docs/project/refactor-backlog-setup/refactor-backlog/implementation/adr-c-009.md
T
ehoandClaude Opus 5 25a5d415a5 docs(adr): land ADR-C-001, ADR-C-003, ADR-C-007 and ADR-C-009
The architect approved the four ADR-fix tickets. All four change what the
architecture documents claim. No code changes.

ADR-0001, ADR-C-001: the worked example claimed the POC has no real backend.
It rewrites against `backend/src/BigRegister.Api`. Every path it named is
repointed. The out-of-scope list drops two discharged bullets: 33 `parse*`
boundaries exist, and `npm run gen:api` is real.

ADR-0001, ADR-C-003: a new section states that the generated client is the wire
contract. A hand-written `contracts/*.dto.ts` is the exception for two cases
only. The four survivors stay, because NSwag emits every property as optional
and flattens `RegistrationStatusDto` into five optional strings. The `parse*`
trust boundary stays mandatory, because a generated type is a compile-time
claim about the wire and not a runtime guarantee.

ADR-0003, ADR-C-007: four paths moved in WP-67 and are repointed. Point 4 kept
the principle and changed its example to `skeleton` and `spinner`. Two of its
claims were false and the amendment says so: `app-alert` wraps the vendored
`.feedback` classes, and `site-header` composes the vendored `.titlebar`.

ADR-0004, ADR-C-009: the exception section states a four-part test instead of
one named exception. `OrgTemplateStore` and `FeatureFlagStore` both pass it. RB-07
gated this ticket, because clause 4 needs an audited allow path. RB-07 landed
that, so the ADR does not ratify a control that the code lacks.

Three tickets need a matching CLAUDE.md correction in the same diff. CLAUDE.md
section 2 loses the false `alert` example. Section 4 gets the generated-client
rule and the four-part test.

Two findings were wrong. ADR-C-001 asked to keep an out-of-scope bullet that
reads "SessionStore is in-memory". The session persists to `localStorage` now,
so the bullet covers multi-tab sync only. ADR-C-007 flagged one half of point 4
and missed that the other half is equally false.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 18:29:05 +02:00

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. OrgTemplateStore has a deliberate read-path fallback for briefs from before WP-23 (:110-114, its own ponytail: comment): an empty SubOrgId falls 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 return null for 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) against OrgTemplateStore.cs will 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/*.xlf paths 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.