Skip to content

[finding] 67 of 125 ADR numbers declare no machine-readable decision letters — 37 live ADR-NNNN Dk citations into 6 of them can never be verified #12786

Description

@claude

Found while implementing #9592 (the ADR-NNNN Dk decision-letter check in scripts/check-adr-anchors.mjs, PR #12785). Filed as a finding, not fixed there: the remedy is an edit to docs/adr/**, which is governed surface routed to the maintainer's own merge per #6741, and the gate deliberately reports rather than forces it.

Measured on origin/main @ 1246b4cf2

docs/adr/ holds 131 files under 125 distinct ADR numbers. Grouping every number by whether any of its records declares a machine-readable decision letter, by the four grammars the corpus actually uses (### Dk heading, **Dk —** bold lead, | Dk | decision-table cell, and a Dk heading's own top-level list declaring Dk.1 / Dka):

numbers with a decision-letter index : 58
numbers with NO decision letters     : 67

The 67 number their decisions topically (### Schema, ### Hostname routing — ADR-0006, ADR-0014, ADR-0024, ADR-0081) or ordinally (### 1. A turn is a commit, ### 2. Commits are atomic — ADR-0067 and ~20 others), never as Dk.

Why it matters now

The new check has to report a third verdict for them — "cannot verify" — because there is no index to resolve against. On the anchored surface alone that is 37 citations across 12 letter/file sites and 6 ADR numbers:

ADR-0006 D4     1 site   packages/spec/src/data/object.zod.ts
ADR-0014 D2     1 site   packages/objectql/src/engine.ts
ADR-0024 D4     2 sites  packages/plugins/plugin-auth/src/{auth-manager,auth-plugin}.ts
ADR-0024 D5.2   2 sites  packages/platform-objects/src/identity/sys-member.object.ts, packages/plugins/plugin-auth/src/invitation-role-cap.ts
ADR-0067 D2     4 sites  packages/metadata-protocol/src/protocol.ts, packages/objectql/src/engine.ts, packages/spec/src/contracts/objectql-engine.ts, scripts/adr-anchors/packages__spec__src__contracts__objectql-engine.ts.json
ADR-0081 D1     2 sites  packages/platform-objects/src/identity/sys-member.object.ts, packages/plugins/plugin-auth/src/auth-plugin.ts

Each of those citations is a promise a reader cannot collect: ADR-0067 D2 is meaningful (that ADR's own amendment calls its second decision "the D2 join"), but the record never writes D2, so following the citation means counting headings and hoping.

The gap is not the citation's fault and not the gate's: it is that the corpus has two decision-numbering conventions and the citation convention only fits one.

Options, for triage

  1. Give the cited records decision letters. Narrowest possible edit: only the 6 records above are actually cited by letter today. Governed-surface PR, maintainer-merged. Converts 37 unverifiable citations into checked ones and shrinks the note toward empty.
  2. Respell the citations to name what the record does say (a section title, a link), so no letter is claimed. Touches code, not ADRs.
  3. Leave it. The note is honest and counted; it just never shrinks.

An ordinal-to-letter inference in the gate (D2 = the 2nd ### N. heading) was considered while building #9592 and rejected: the record never writes D2, so the mapping would be the gate's invention rather than the document's statement, and a gate that manufactures the index it checks against cannot fail honestly.

Re-check

node scripts/check-adr-anchors.mjs   # the note prints under the OK line

Refs: #9592 (the check) · PR #12785 (where the note ships) · #6741 (governed-surface routing for docs/adr/**)


Generated by Claude Code

Activity

  1. huangyiirene commented on Aug 27, 2026

    @huangyiirene
    Collaborator

    Triage: routed domain:skills, ⛔ left ungraded (finding stays) — skills-lane findings are self-triaged by #7623 under the standing carve-out.

    ⚠️ The routing needs its reasoning stated, because the ADR-citation family is deliberately split across two lanes this round and a reader will otherwise think one of them is a mistake:

    card subject lane
    #9592 (closed) / #12787 (closed this round) the gate — scripts/check-adr-anchors.mjs, its precision and its coverage domain:devx
    this card, and #12748 the corpus — what the ADR records should declare, and how citations into them should be spelled domain:skills

    The line is the gate's SUBJECT versus the document's content. A checker's own precision is code quality (devx); "should these 6 records grow decision letters, or should the 37 citations be respelled" is a question about the governed record itself, and option 1 lands in docs/adr/** — governed surface, maintainer-merged per #6741. ⇒ Same lane as #12748, which asks the sibling question about citation spelling.

    Two notes for the skills seat, so they are not re-derived:

    • ⭐ Option 1 is far narrower than the headline suggests. The headline is "67 of 125 numbers", but only 6 records are actually cited by letter today — ADR-0006, 0014, 0024, 0067, 0081 (and the sixth in that table). ⇒ The narrowest fix converts 37 unverifiable citations into checked ones by touching six documents, and it shrinks the note toward empty rather than capping it. That asymmetry (small edit, large verification gain) is the strongest argument on the card.
    • ⛔ The rejected fourth option should stay rejected: an ordinal-to-letter inference in the gate (D2 = the 2nd ### N. heading) was considered while building tooling: check-adr-anchors should verify that a cited ADR-NNNN Dk decision letter is a real heading of the cited ADR #9592 and rejected because "the record never writes D2, so the mapping would be the gate's invention rather than the document's statement, and a gate that manufactures the index it checks against cannot fail honestly." If anyone revisits this card, that is the option to not re-open.

    Cross-link worth carrying: this card is axis ③'s objection in #12787, which closed this round partly because this population is large. ⇒ If this card resolves and the unverifiable set shrinks substantially, that is recorded there as reopen criterion 3 for the repo-wide coverage question. The two are sequenced, not duplicated.

    ⚠️ Note the corpus has two decision-numbering conventions (topical ### Schema, ordinal ### 1. …) and the citation convention fits only one. Whatever is decided, that is the underlying fact — ⛔ not a per-record oversight to patch six times without saying so.

    Measured on origin/main @ 1246b4cf2; re-verify at grading, since PR #12785 was landing the note as this was filed.


    Generated by Claude Code

  2. hotlong commented on Aug 28, 2026

    @hotlong
    Contributor

    Measured escalation on one row of this card's table: ADR-0006 D4 is no longer a "cannot verify" note — it is now a hard failing assertion, and check:adr-anchors is RED on main because of it.

    Found while implementing #12867 (the ADR-0006 D1 retirement addendum, PR #12908); filed here rather than as a new card because this card already names the exact site.

    What changed since this card was measured: it recorded ADR-0006 as one of the 67 numbers declaring no decision letters, so ADR-0006 D4 scored the soft third verdict. PR #12736 then landed the ADR-0006 D1/D2/D3 addendum, which gives that record a real machine-readable letter index. The citation now resolves against an index that exists and is absent from it, which the gate treats as a failure rather than a note:

    ✗ check-adr-anchors --self-test — 1 failure(s) of 106 assertion(s)
      • ADR-0006 D4 is cited by 1 file(s), but ADR-0006 declares no D4 —
            it decides: D1, D1.1, D1.2, D1.3, D2, D3
            packages/spec/src/data/object.zod.ts
    

    Both node scripts/check-adr-anchors.mjs --self-test and the plain scan exit 1. The citing line is packages/spec/src/data/object.zod.ts:1467:

    '`namespace` was retired in ADR-0006 D4 — the object `name` IS the canonical id '
    

    Measured on origin/main @ 0c2334f6c, with a positive control: I ran the scan against my branch and against origin/main's version of the ADR restored in place, and the two outputs are byte-identical — so this is main's state, not an artifact of the addendum work.

    Triage note on this card's option 1 ("give the cited records decision letters"): for ADR-0006 that half-happened as a side effect. #12736 added the letters the record actually decides, and D4 was not among them because ADR-0006 does not decide namespace retirement anywhere. The remaining routes for this one site look like option 2 (respell the citation to name what the record does say) or locating the record that really did retire namespace and citing that. #12867's addendum deliberately did not mint a D4 to green the gate: that card retires D1, and manufacturing a decision letter to satisfy a checker is the defect this gate exists to name.


    Generated by Claude Code

  3. huangyiirene commented on Aug 28, 2026

    @huangyiirene
    Collaborator

    <!-- os-decision-facets -->

    定级:Task · priority:p1 · domain:skills · needs-user-decision

    分诊席位,session session_01Aujz2zykf5LXt3T98gRsGe。

    needs-user-decision —— 选项 1 是编辑 docs/adr/** ⇒ ADR 类,人工地板,永不代裁(且按 #6741 路由到维护者自己的合并)。
    priority:p1(卡未自定级) —— 理由见下:这张卡在 2026-08-27 19:23Z 填写,次日 07:49Z 它列表的第一行就引爆了。


    ⭐ 本卡不是"37 条无法验证的引用",是 37 根武装着的绊线

    昨天的预言,今天的事故

    卡的引用表第一行是:

    ADR-0006 D4     1 site   packages/spec/src/data/object.zod.ts
    

    ⇒ 正是今天 07:49Z 让整条合并队列停摆的那一处(#12913 / #12917)。

    机制是本卡没有点破的那一层。 门有三态(我从 scripts/check-adr-anchors.mjs:790-793 读的):

    被引记录 判定
    声明零字母 unverifiable —— 只报告,不判错
    声明了字母、但引用的不在其中 error

    ⇒ 本卡列的 6 个号当时全在第一桶(绿)。#12736 给 ADR-0006 铸了 D1–D3,它跨进第二类,D4 同一秒掉进 error 桶。

    ⇒ ⭐ 本卡的清单不是"待整理项",是"任何一个记录一旦声明任意一个决议字母,其引用立刻变成阻塞合并队列的红"的名单。

    实测:还有 4 个号仍然武装着

    ADR-0006 : 已声明字母 3   ⬅ 已引爆
    ADR-0014 : 0    ADR-0024 : 0    ADR-0067 : 0    ADR-0081 : 0    ⬅ ⚠️ 仍武装
    正对照 ADR-0028 → 6 个字母标题   ✅ 仪器能返回非零,这些零是真读数
    

    仍然暴露的引用(除已处理的 ADR-0006):

    引用 站点数
    ADR-0014 D2 1
    ADR-0024 D4 2
    ADR-0024 D5.2 2
    ADR-0067 D2 4
    ADR-0081 D1 2
    合计 11 处 / 4 个号

    ⚠️⚠️ 最重要的一条:选项 1 就是引爆动作本身

    卡的选项 1 是「Give the cited records decision letters」。⛔ 照字面执行会立刻制造今天那场事故的复刻。

    ⭐ 今天的成因逐字就是这个:#12736 给 ADR-0006 加了 D1/D2/D3,而 D4 正被引用着,且没有同批修那处引用 ⇒ main 全红。

    ⇒ 围栏(本卡最重要的产出):

    给一个被按字母引用的记录添加决议字母时,必须在同一个 PR 里,要么把被引用的那个字母也真正声明出来,要么把引用改成不引字母的陈述。⛔ 二者必居其一,不得只加字母就走。

    ⚠️ 而这条对 ADR-0024 D5.2 尤其棘手:那是子字母形态,加 D5 不够,必须真的有 D5.2。


    四棱

    ① 项目长远合理性

    ⭐ 卡把根因说得很准:「the corpus has two decision-numbering conventions and the citation convention only fits one」 —— 67 个号按主题(### Schema)或序数(### 1. A turn is a commit)编号,58 个按 Dk。⇒ 长远正解是语料统一到一种可机读的编号,⛔ 但那是 131 个文件的工程,不是本卡。

    ② 实际业务拉动

    间接但已经兑现过一次:今天队列停摆一个多小时,多个 PR 被 CI_FAILURE 出队(#12895、pr-12897/12901/12905)。⇒ ⭐ 这一棱不再是假设,它有一次实测的成本。

    ③ 防 AI 犯错

    ⭐ 最重。引用一个不存在的字母,是 agent 找不到出处时的默认失败模式 —— #4522 就是这么产生 ADR-0006 D4 的(#12918 已确立该退休从未被写下)。⚠️ 而门在 2026-08-27 之前根本查不出这一类,所以这些引用积累了数月无人知晓。
    ⭐ 卡拒绝"序数推断"的理由完全正确,值得原样保留:「the record never writes D2, so the mapping would be the gate's invention rather than the document's statement, and a gate that manufactures the index it checks against cannot fail honestly.」

    ④ 创业阶段不扩散

    ⚠️ ⛔ 不要为此启动 131 文件的编号统一工程。⭐ 卡自己给出了最窄的范围并且是对的:今天真正被按字母引用的只有 6 个记录(现为 4 个),⇒ 只需处理这 4 个。


    选项 × 成本(按今天的读数重估)

    做法 成本 风险
    1 给 4 个被引记录加字母 治理面 PR ×4 ⛔ 必须同批修引用,否则就是今天事故的复刻
    2 ⭐ 把 11 处引用改成不声称字母的陈述(指向节标题/链接) 改代码,⛔ 不动 ADR ⭐ 不可能引爆,且这正是今天 #12917 用的解法
    3 留着 0 ⚠️ 4 根绊线继续武装;任何人给这 4 个记录加一个字母就红

    建议:2

    理由是不对称的:

    退路

    若维护者认为这些决议确实应当有字母(它们是真决策,值得可机读的编号)⇒ 取 1,⛔ 但必须逐个记录执行上面那条围栏,并且一个记录一个 PR,⚠️ 不要一次改 4 个。


    ⚠️ 置信缺口

    1. 我只测了"是否声明字母",⛔ 没有测那 11 处引用是否仍在树上。 卡的读数取自 1246b4cf2,此后 main 已推进很多。⇒ 取卡者第一步应重测那 11 处站点。
    2. 我的字母检测只覆盖三种语法(### Dk / **Dk / | Dk),⛔ 未覆盖卡提到的第四种(Dk 标题下的顶层列表声明 Dk.1 / Dka)。⚠️ ⇒ 某个号可能已通过第四种语法声明了字母而我没看见 —— 那会让它已经是红的而我报成"仍武装"。
    3. 58 + 67 = 125、131 文件这组普查数字我没有复现,只复现了与决策相关的那 5 个号。

    裁后执行段

    • 取 2(建议):改 11 处引用 ⇒ 落 packages/** 与 scripts/adr-anchors/*.json ⇒ 跨车道(engine / services / spec / cli 都有),⚠️ 建议按域拆卡,⛔ 不要一个 PR 横扫。
    • 取 1:⛔ 每个记录一个 PR,且必须同批处理其被引用的字母。治理面 ⇒ draft + 人工合并;⚠️ 手合前 base 必须更新到当前 main 并重跑门(今天的教训)。
    • ⛔ 无论取哪个:不得在门里加序数推断。卡已论证,我复述:一道自己制造索引再拿来检查的门,无法诚实地失败。

    相关

    #9592 / PR #12785(门与本卡的出处)· #6741(docs/adr/** 的治理路由)· ⭐ #12913 / #12917(本卡第一行今天引爆并被修复)· ⭐ #12918(该退休从未被写下 —— 与本卡是同一个洞的两半)· #12736(铸出 D1–D3 的那次治理直合)


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions