ADR bodies are never rewritten on amendment; there is no precedence rule between ADRs and downstream docs
Источник:
dev/fera-research/docs/oc1/018-amendment-hygiene-no-precedence-rule.md(read-only; сгенерировано scripts/sync-docs.mjs)
ADR bodies are never rewritten on amendment; there is no precedence rule between ADRs and downstream docs
Section titled “ADR bodies are never rewritten on amendment; there is no precedence rule between ADRs and downstream docs”- kind: problem
- area: modules / ui
- evidence:
- The corpus defines the right mechanism and then doesn’t use it.
ADR.md:3— “Status legend: Accepted | Proposed | Superseded”. Verified: all 31**Status:**lines sayAccepted; the stringsProposed/Supersededappear once in the whole file (the legend itself). So the legend’s two working states are unused — an amended ADR has no way to be marked as partially superseded. - Amendments live only in the Status line; bodies keep the old claim.
ADR.md:227-231(ADR-009 status line) — “amended by ADR-023: a fourth axis, Native… amended by ADR-029: Style is declarative data… the axis split is redefined”. But ADR-009’s Decision body (ADR.md:239-249) still opens “UI is split into three independently hot-swappable module axes” and describes Style as an axis with per-OS aesthetics — and ADR-023 explicitly forbids this:ADR.md:948-949— “Four swap axes must not silently collapse into three: Engine / Style / Feature / Native are separate modules and separate swap decisions.” The corpus writes the rule and then breaks it in the same file.ADR.md:242— the Engine axis is stillrenderer-gpui.so, killed by ADR-028 (ADR.md:996: “the undocumented-GPUI risk is dropped”).ADR.md:252— “The ‘official triads’ per OS (one Engine + one Style + one Feature set)” is stated as current policy, whileOQ.md:320-322(2026-10-02, author-confirmed) says “‘triad’ is obsolete… The official per-OS combination is a quad: Engine + Native + Feature + Style-data”.ADR.md:633-635(ADR-016) still says the metadata/content classification is “flagged as an open question” — closed by ADR-031 two weeks earlier.ADR.md:19-21(ADR-001) still listsprotocol,UI styleandpredictoras hot-swappable subsystems, contradicted by TECHSPEC §0/§1/§5 and by the repo (my finding 015).
- The good pattern exists but is inconsistent. ADR-020 carries two
dated in-place addenda (“Naming addendum (2026-09-30)”,
ADR.md:801; “Link-time dispatch addendum (2026-09-30)”,ADR.md:807) and OQ-021 has a FINAL block (OQ.md:649-657). The amended ADRs get none of this. - Same disease inside TECHSPEC itself, twice in one document:
TECHSPEC.md:34— “/style-macos/ /style-win/ /style-linux/— Style axis, nylon-ring cdylib” vsTECHSPEC.md:271— “Style: declarative data, not a dylib (ADR-029)”. §0 contradicts §6.TECHSPEC.md:33—/renderer-gpui/vsTECHSPEC.md:266—renderer-vello.
- No document states which wins when an ADR, an ADR addendum, an OQ
resolution block, and a downstream spec section disagree.
README.md:41-45defines the protocol for resolutions (“An OQ resolved becomes an ADR (or an ADR addendum) and this OQ entry is marked Resolved → see ADR-XXX”) and it is followed diligently insideOQ.md(12 markers) — but there is no equivalent convention for amendments to existing ADRs, which is the actual failure mode here. - Downstream docs have no marker mechanism at all, which is why my findings 001, 002, 003, 008, 011 and 014 are all the same bug at different sites.
- The corpus defines the right mechanism and then doesn’t use it.
- severity: mid
Суть / Substance
Section titled “Суть / Substance”This is the structural cause under seven of my other findings. The corpus has a good instinct — dated addenda, resolution markers, a status legend — and applies each one locally, while the practice that actually matters (keeping a living ADR set consistent with its own downstream docs) has no convention at all.
The cost is not tidiness. It is that ADR.md stops being readable as a
decision set. An agent asked “how many UI axes are there?” greps, finds
“three” (ADR-009 Decision), “four” (ADR-023 title), “three dylibs + Style
as data” (ADR-009 status line), “quad” (OQ-012), and picks whichever it
read first. Worse, the bodies are what get quoted in code comments and
implementation plans — my finding 015 shows the repo built eleven in-process
crates against a section (TECHSPEC.md:30-39) that ADR-029/028 had already
invalidated.
The unused Superseded/Proposed legend values are the tell: the corpus
anticipated exactly this and never built the habit. There is also no
concept of a partially superseded ADR, which is the common case here
(ADR-009’s axis-count decision was superseded; its “axes must be
independent” principle was not).
Варианты решения (tradeoffs)
Section titled “Варианты решения (tradeoffs)”- (A) Amend bodies in place, ADR by ADR. Most correct, most churn, loses the visible history of what was decided when.
- (B) Keep bodies frozen; require a prominent
**Amended:**block listing the superseding ADRs plus the specific bullet each one overrides, and add the missing legend values in use. Preserves history; still needs the discipline. - (C) Write one “corpus state” document that restates the current answer for every live question, with pointers. Cheap to maintain, duplicates content, and goes stale the moment someone edits an ADR.
- (D) Mechanically lint it. A script that, for every ADR, extracts asserted quantities (axis counts, module names, statuses like “open question”, “three”) and flags them if a later ADR contradicts them or if the string appears in a doc without a resolution marker. Powerful, brittle on prose.
Предлагаемое решение
Section titled “Предлагаемое решение”(B) as the convention + (A) for the four ADRs that actively mislead + (D) restricted to a few mechanical patterns.
- Amendment convention (add to
README.md:39-46, where the resolution protocol already lives):- An ADR that changes a previous ADR gets (i) an in-body
**Amended by ADR-0NN**block directly under its Status line, naming the specific decision text overridden; (ii) the superseded sentences get an inline strikethrough-style note(superseded by ADR-030)— ADR-020’s dated addenda are the model to copy. Supersededis for whole-ADR retirement; for partial change keepAccepted (amended).
- An ADR that changes a previous ADR gets (i) an in-body
- Amend the four misleading bodies now: ADR-009 (three→four axes;
Style as data;
renderer-vello; “triads”→“quads” with the OQ-012 pointer), ADR-008 (GPUI residue, per my finding 001 — and name the new home of local UI state), ADR-016 (encryption-tier question is closed by ADR-031), ADR-001 (the swappable-subsystem list, per finding 015). - Fix the two self-contradictions inside TECHSPEC (
§0style dylibs andrenderer-gpui), and add to TECHSPEC’s header a line naming ADR.md as authoritative where they differ — that single sentence is what an implementer needs. - Downstream marker convention: extend OQ.md’s
Resolved → see ADR-XXXpattern toTBD/open question → resolved by ADR-0NNin RFC/TECHSPEC/QA, so the stale pointers in findings 003 and 011 become self-announcing. - A deliberately small lint (the subset that is unambiguous and
already bit us): (a) every ADR referenced as
Resolved → see ADR-NNNmust exist; (b) every ADR title/number appears in at least one downstream doc (this is QA’s own failed gate, finding 014); (c) any line matchingTBD|open question|to be decidedmust be followed within N lines by an ADR/OQ pointer. Skip the semantic checks — (a)-(c) are ~40 lines and catch the recurring class.