Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

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 say Accepted; the strings Proposed/Superseded appear 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 still renderer-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, while OQ.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 lists protocol, UI style and predictor as 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” vs TECHSPEC.md:271 — “Style: declarative data, not a dylib (ADR-029)”. §0 contradicts §6.
      • TECHSPEC.md:33 — /renderer-gpui/ vs TECHSPEC.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-45 defines 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 inside OQ.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.
  • severity: mid

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.

(B) as the convention + (A) for the four ADRs that actively mislead + (D) restricted to a few mechanical patterns.

  1. 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.
    • Superseded is for whole-ADR retirement; for partial change keep Accepted (amended).
  2. 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).
  3. Fix the two self-contradictions inside TECHSPEC (§0 style dylibs and renderer-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.
  4. Downstream marker convention: extend OQ.md’s Resolved → see ADR-XXX pattern to TBD/open question → resolved by ADR-0NN in RFC/TECHSPEC/QA, so the stale pointers in findings 003 and 011 become self-announcing.
  5. A deliberately small lint (the subset that is unambiguous and already bit us): (a) every ADR referenced as Resolved → see ADR-NNN must 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 matching TBD|open question|to be decided must be followed within N lines by an ADR/OQ pointer. Skip the semantic checks — (a)-(c) are ~40 lines and catch the recurring class.