fix(devx): teach check:adr-anchors that a tombstone is not a decision (#7329) - #7838
Merged
Merged
Conversation
…on (#7329) `docs/adr/0107-withdrawn-hook-body-write-set-static-gap.md` (#6676) is the corpus's first tombstone — a file whose entire content is "this number is withdrawn, do not reuse". It was written so the historical `ADR-0107` citations resolve, and it does that. But the gate assembled its record set from filenames alone, so the same file also satisfied the anchor loop's `records.has(...)` guard, which was never meant to accept it. Measured on `main` @ 69fde55: a shard in `scripts/adr-anchors/` citing `ADR-0107` made the gate print OK and exit 0 — live content anchored to a number whose own record says nothing in it is in force. The only thing standing in the way was a reader noticing that the required `invariant` field cannot be written truthfully for a number that decided nothing. The two audits want opposite answers about the same file, so one set was not enough. A tombstone number is now in `records` (citations resolve — the job it exists for) and in `nonDecisions` (anchors refused). No live record's treatment changes. The marker is the filename, `NNNN-withdrawn-<slug>.md`, for the reason the `.vN` rule and the cross-repo qualifier are also spellings rather than lists: a structural signal reaches every future record without editing the gate. A `status:` field in YAML front-matter was the more obvious design and lost on three measurements — zero of the 119 records carry front-matter today; the records' status lines are free prose and already outside this gate's rules; and #6741 routes any `docs/adr/**` diff to the maintainer's own merge, coupling a script-only fix to a governance approval while failing open the same way. An explicit list was rejected as the one option needing a gate edit per tombstone. The anchor loop's ADR-id judgement is extracted into `anchorIdProblem` so `--self-test` drives the real function: 15 new assertions cover both directions over the same file, the `.vN` case, the prefix-not-substring boundary, the contested-number case (a tombstone sharing a number with a live record is a collision, and the live record keeps its anchors), and an ablation on the real tree replaying the exact probe that came back green on `main`. Refs #6676, #6634, #5992.
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
This PR touches `scripts/` only and releases nothing, so it takes the `skip-changeset` route the gate names rather than declaring a release it does not make. An empty-frontmatter changeset is a real input to changesets/action and an all-empty set stalls the release silently and greenly (#4898); the pre-existing empty ones on the base commit are grandfathered and this gate judges only what a PR newly introduces (#5471). The reasoning that changeset carried is in the commit message and the PR body, which is where it is readable without a release cycle.
huangyiirene
marked this pull request as ready for review
August 11, 2026 20:39
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Aug 11, 2026
Contributor
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 31534161719 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #7329
The premise, re-measured before writing code
The issue's comment history disagreed with itself, so the first job was to settle it empirically rather than pick a comment.
Measured on a clean
origin/main@69fde55. Dropped one probe shard intoscripts/adr-anchors/anchoring a real tracked file toADR-0107, the tombstone number:Fact 2 is still open. #7316 landed the tombstone and documented this hole inline at the guard (
check-adr-anchors.mjs:600-608, verbatim: "Anchoring code to one is not caught mechanically") — it did not close it. The 03:10Z triage conflated three things #7316 genuinely did fix (citations resolve, number re-use collides loudly inauditAdrDirectory, the allowlist shrank) with the one it did not: the anchor loop still accepts the tombstone. So this PR takes the route the card and the PM ruling describe.The same probe against this branch:
What changed
recordswas doing two jobs that want opposite answers about the same file. It now returns a second set:recordsnonDecisionsNo live record's treatment changes. A number counts as a non-decision only when every stem claiming it is a tombstone: a tombstone sharing a number with a live record is a collision, the existing audit already reports that loudly and about the right fact, and the live record keeps its anchors until it is resolved.
The anchor loop's ADR-id judgement (parses / names a record / is a decision) moved into a pure
anchorIdProblem, so--self-testdrives the real function instead of an imitation — the file's own standard, "a gate whose red path was never exercised is not a gate".The contract choice, and why not front-matter
The card named three options. Filename convention (
NNNN-withdrawn-<slug>.md) — same shape as the.vNversion rule and the cross-repo qualifier, both structural spellings rather than lists, and available to every future record without editing the gate.This diverges from the PM's stated preference for a front-matter
status:field. Three measurements decided it:Statuslines are free prose and already deliberately outside this gate's rules — the header says so, and the survey backs it:Proposed,Draft (2026-05-24),Accepted in part,Superseded by v4, and multi-sentence audit paragraphs.docs/adr/**PR #6741 routes any diff touchingdocs/adr/**to the maintainer's own merge —check-adr-merge-approval.mjsmatches on thedocs/adr/path prefix with no size exemption. Marking the tombstone would couple a script-only fix to a governance approval. (Confirmed on this PR:ADR maintainer approvalis green precisely because nothing underdocs/adr/is touched.)And front-matter fails open in exactly the way the filename does — a future tombstone can forget either one — so it buys no robustness for that cost. The third option, an explicit
NON_DECISION_RECORDSlist in the script, is the only one needing a gate edit per future tombstone, which is the property the other two are chosen for.The one real hazard of the filename route is handled: the marker is a slug prefix, not a substring, so a live decision about withdrawal (
0123-withdrawn-plugin-cleanup-policy.md) keeps its anchors. Pinned as an assertion.Out of scope, with the reasoning recorded
No blanket "flag every gap in the ADR numbering" check. The card's own census is the rejection: four holes, three causes.
0001UNRESOLVED_ADR_CITATIONS0075main00830107A gate that alarms on three innocent skips to re-cover one risk already covered trades noise for nothing.
The ADR-0001 tombstone upgrade (03:10Z triage's re-scope) is untouched. It was the deliverable only if Fact 2 had turned out closed; it did not. It needs a new
docs/adr/0001-*.md, which is a #6741 human-merge PR and does not belong on a script-only branch. Still open, unclaimed by this card.Verification
pnpm check:adr-anchors— 70 assertions (55 → 70), gate green: 48 anchored files, 119 decision numbers, 22,444 citations across 3,709 files resolve..vNcase, the prefix-not-substring boundary, the contested-number case, the threeanchorIdProblemverdicts, and a live-tree ablation replaying the exact probe that came back green onmain.pnpm check:adr-links✅ ·pnpm check:doc-authoring✅ ·eslintclean.Changeset: route 2 (
skip-changeset)This PR touches
scripts/only and releases nothing. It first carried an empty-frontmatter changeset, following the three existingadr-anchors-*ones — but those are grandfathered base-commit files, andCheck Changesetcorrectly refused a newly introduced one (#4898: an empty changeset is a real input to changesets/action, and an all-empty set stalls the release silently and greenly; #5471: the gate judges only what a PR adds). Taken to route 2 as the failure prescribes: changeset deleted,skip-changesetapplied. The reasoning it carried is in the commit message and this body.⛔ No ADR's decision content is touched; no file under
docs/adr/is touched at all.