Development record
2026-08-02 — iss-39 (review pass): four correctness repairs to `record_schema`, each found by mutating the rule rather than reading it. (1) The "no lifecycle state escapes" invariant was implemented for bucketed stores only — a FLAT store (the ADRs) declares no buckets, so the walk read its files and never looked at its directories, and a whole `adrs/archive/` of records with mismatched ids and phantom successors was invisible to every check in the rule. A flat store declares zero buckets, so any subdirectory of it is undeclared by definition, and now says so. (2) The retirement escape hatch was self-attesting: `retired` was built from every record's `supersedes` before the resolution loop ran, so one record naming `adr-9999` there made that id resolve in every other record's `related_adrs` — reopening the exact phantom-reference class the rule exists to close. It is now bounded by the store's allocation high-water mark (an id above it was never issued, so nothing can have pruned it) and the declaration itself is reported; `supersedes` also leaves `recordRefFields`, where it could never fail its own check and read as coverage it did not provide. (3) The shared frontmatter scanner is a same-line scanner, so a block sequence (`supersedes:` then indented `- adr-12`) read as empty — and an empty read here did not merely under-report, it made the bidirectional check assert that ANOTHER file omitted a link that file plainly carried. Other rules degrade to "field missing on this file"; this one degraded to a confident false claim about a different file, which is worse, so the block spelling is folded in at the one place the rule parses handles. Local to `record_schema` rather than in the shared scanner: the canonical scanner has many consumers whose keys are scalars, and widening it for one rule's list fields would change what every other rule reads. (4) A dot-directory (`.obsidian`, `.vscode`) tripped a blocker as an undeclared bucket; tooling state is not a lifecycle the record authored. Also disclosed rather than fixed: widening `superseded_by` to `^(itd|adr)-` makes `intent_lifecycle` looser ALONE — it reads only the intent tree and cannot resolve an `adr-` target — so the CHANGELOG and the pattern's own comment now say the two rules are armed together and `record_schema` is what resolves a cross-store target. The surviving adr-6 prose citations are captured as iss-179 rather than papered over: adr-29 states in its own body that adr-6's decision stands and that adr-29 does not supersede it, so the supersession vocabulary cannot record it.
2026-08-02 — iss-39 (review pass): four correctness repairs to record_schema, each found by mutating the rule rather than reading it. (1) The "no lifecycle state escapes" invariant was implemented for bucketed stores only — a FLAT store (the ADRs) declares no buckets, so the walk read its files and never looked at its directories, and a whole adrs/archive/ of records with mismatched ids and phantom successors was invisible to every check in the rule. A flat store declares zero buckets, so any subdirectory of it is undeclared by definition, and now says so. (2) The retirement escape hatch was self-attesting: retired was built from every record's supersedes before the resolution loop ran, so one record naming adr-9999 there made that id resolve in every other record's related_adrs — reopening the exact phantom-reference class the rule exists to close. It is now bounded by the store's allocation high-water mark (an id above it was never issued, so nothing can have pruned it) and the declaration itself is reported; supersedes also leaves recordRefFields, where it could never fail its own check and read as coverage it did not provide. (3) The shared frontmatter scanner is a same-line scanner, so a block sequence (supersedes: then indented - adr-12) read as empty — and an empty read here did not merely under-report, it made the bidirectional check assert that ANOTHER file omitted a link that file plainly carried. Other rules degrade to "field missing on this file"; this one degraded to a confident false claim about a different file, which is worse, so the block spelling is folded in at the one place the rule parses handles. Local to record_schema rather than in the shared scanner: the canonical scanner has many consumers whose keys are scalars, and widening it for one rule's list fields would change what every other rule reads. (4) A dot-directory (.obsidian, .vscode) tripped a blocker as an undeclared bucket; tooling state is not a lifecycle the record authored. Also disclosed rather than fixed: widening superseded_by to ^(itd|adr)- makes intent_lifecycle looser ALONE — it reads only the intent tree and cannot resolve an adr- target — so the CHANGELOG and the pattern's own comment now say the two rules are armed together and record_schema is what resolves a cross-store target. The surviving adr-6 prose citations are captured as iss-179 rather than papered over: adr-29 states in its own body that adr-6's decision stands and that adr-29 does not supersede it, so the supersession vocabulary cannot record it.
Frontmatter
| id | dcn-2608020000000005 |
|---|---|
| type | decision |
| date | 2026-08-02 |
.abcd/work/decisions/dcn-2608020000000005-iss-39-review-pass-four-correctness.md
open on GitHub ↗ · commit history ↗
mentions4
- adr-12`.work/issues.md` stays the live operational ledger; the structured per-issue store is deferred until the native spec layer schedules the migration (historical)
- adr-29A native local redacted transcript corpus
- iss-39record schema validation: adr-6 is cited as a live design input but does not exist and no successor records superseding it; adr-12 is not marked superseded by adr-32; itd-47 and itd-49 sit in superseded/ without supersession frontmatter; the superseded/ lint exemption is broad enough to hide content-rule violations. Detector: a mechanical record-schema check — every prose handle (adr-N, itd-N, iss-N) resolves to a file; supersession is bidirectional (superseded-by and supersedes both present); filenames match their patterns; every lifecycle directory is covered by a catch-all so no state escapes linting; the superseded/ exemption narrows to content rules only. Acceptance corpus: the four instances above.
- iss-179adr-29 and adr-25 still narrate adr-6 in prose, but adr-6 is in no store and is absent from all git history (the ADR set landed in one import commit; 0004/0006/0008/0014-0018 were never migrated, and every one but 0006 is accounted for by a successor's supersedes). adr-29 states in its own body that adr-6's decision stands and that adr-29 does not supersede it, so the supersession vocabulary cannot record it and neither ADR body should be rewritten to paper over it. iss-39 closed the machine-readable half (the two related_adrs entries, and the two brief pages that cited it). The surviving prose sites are adr-29 lines 24, 39, 43, 56, 60, 63, 71 and adr-25 lines 51, 80; adr-29's line 43 in particular still says it "links to ADR-6" now that the frontmatter link is gone, which is the residual made legible rather than hidden.