Repository navigation
No gate requires a breaking-behavior changeset to carry the breaking annotation at all — the ADR-0087 disposition gate only fires once one already does #8607
Description
Activity
Triage:
finding+domain:spec-tooling(held); native type Task. Triage seat Routine, 2026-08-14 ~02:15Z.Rationale: the gap is real and measured once (the #8411 changeset), but the one known hit was caught and repaired by the existing human loop, and the card itself argues the strongest point against acting now — a false-negative-prone semantic detector would give false confidence, which is worse than the honestly-absent gate. Putting an appetite question in the decision box with no forcing event would be inbox noise; held as a finding instead.
Routing: the gate family is
scripts/check-adr-0087-registration.mjsand siblings — the ADR-0087 changeset-discipline toolchain, same lane as in-flight #8299. If #8299's PR restructures the disposition categories, fold this question into that review rather than racing it.Re-grade triggers (any one fires ⇒ back to triage for promotion or the decision box):
- A second breaking-behavior changeset lands unmarked — a repeat of the #8296's changeset ships a breaking engine-API change as
minorwith no breaking annotation — the release digest cannot classify it #8411 incident. Promotes toneeds-user-decisionwith option B (narrow known-seam lint + mandatory human confirmation) as the concrete proposal. - The ADR-0087 gate family is reworked for any other reason (e.g. out of ADR-0087's changeset disposition has no category for published runtime TS interfaces with no metadata surface — the #8277 exemption argument is correct, unverifiable, and will be re-litigated #8299) — the "should this have declared itself breaking" question rides that redesign.
- A cheap high-precision "diff touches a known engine/ingress seam" signal materializes (e.g. from dispatch-gates' path→gate map) — option B stops being speculative.
Generated by Claude Code
- A second breaking-behavior changeset lands unmarked — a repeat of the #8296's changeset ships a breaking engine-API change as
State-hygiene completion: swapped
finding→pm:on-holdso the label matches the grade already recorded in the 2026-08-14 02:18Z triage comment above (held, with three named re-grade triggers). No new decision here — this is the label half of that same grading action, completed per the grade-means-leaving-the-label discipline (2026-08-13 ruling). Hold provenance: seat grading 2026-08-14; reason and the three restart conditions live in the 02:18Z comment.本评论来自分诊座位 Routine。
Generated by Claude Code
Hold-condition rewrite (triage seat, session
session_014qTKTqjme5Fp5BH9iRmy6t, on-hold weak-hit audit): the written rework trigger has fired THREE times without waking this card —scripts/check-adr-0087-registration.mjswas touched by PR #8456 (which closed #8299, the exact example event, two hours after the hold was written), PR #8670, and PR #9560 — all unrelated maintenance that did not revisit this card's question. Verified on origin/main:breakingDeclaration()still reads only self-declared signals (major bump, BREAKING marker,!), so the gap is real and unchanged, and the "ride #8299's redesign" premise is spent (merged and closed).Letter fired, intent never did — same treatment as #8360's void leg: the over-broad "family is reworked" trigger is retired and replaced with the condition keyed to what this card is actually about. The hold itself stands (there is still no independent breaking-signal to enforce with).
Restart-when: a second breaking-behavior changeset lands unmarked (a repeat of #8411), or a PR changes the signal set inside
breakingDeclaration()inscripts/check-adr-0087-registration.mjs, or dispatch-gates.mjs gains a path→seam classification usable as option B's known-seam signal
Generated by Claude Code
Measurement input from the triage seat's contract-review round of 2026-08-31. ⛔ The hold stands; nothing is unparked here. Recording live evidence this card has been waiting for, plus one reading that bears directly on which of its three options is right.
First, the trigger question — answered on the literal, which is NO
This card's current condition reads:
Restart-when: a second breaking-behavior changeset lands unmarked (a repeat of #8411), or a PR changes the signal set inside
breakingDeclaration()…, or dispatch-gates.mjs gains a path→seam classification…Two changesets this round misgraded a breaking behaviour change — but neither landed. Both were caught in draft, before merge, by contract review. ⇒ The first leg has not fired, and the other two have not either. The card stays parked.
⚠️ Recording that distinction explicitly because this thread has the mirror precedent: the 2026-08-19 rewrite retired a trigger whose letter fired three times while its intent never did. This is the opposite — intent firing, letter silent — and it would be just as wrong to act on the letter here as it was to keep waiting on it there.The measurement — 4 of 5 changesets misstated their own diff, and every gate was green
Six PRs carried
needs:contract-reviewtoday; five have verdicts. Their changesets:PR what the changeset says what the diff says #13829 "@objectstack/spec": patcha required member added to the published IObjectQLEngine. The PR body meanwhile states it "ships@objectstack/specasminor" and argues minor-vs-major at length — describing a file that does not exist as written#13864 '@objectstack/objectql': patchan accept-set narrowing its own text calls "a security fix". The same package's own precedent, .changeset/hook-input-symbol-key-refusal.md, argues verbatim: "Bump level, argued:minor, notpatch"#13834 "new exports ( HookBodyExtractionError,HookBodyRefusalKind,BodyExtractionWarning)"all three are module-private; the package exportsmap exposes only.and./console, andsrc/index.tsre-exports none of them#13910 "the last surviving GRANTS-LOST disguise at the package door" ended the shipped single-kernel wiring still answers 403 — its provider absorbs one layer earlier (#13904), verified at head #13857 minor+ BREAKING banner✅ matches Check Changesetwas green on all five. So werecheck-empty-changeset,check-changeset-no-majorandcheck-adr-0087-registration— exactly as this card predicts, because each judges only a changeset that already self-declares.⭐ The reading that bears on the option choice
This card's three options end with a warning against defaulting to a detector. That warning is now stronger, and one option has evidence it did not have in August:
- Option A (rely on review) is not hypothetical any more — it fired 4 times out of 5, in one afternoon, and caught things a detector could not. Two of the four are not bump-level errors at all: feat(cli): make the silent hook-body downgrade loud —
os lintrefuses an accidental scope leak (ask 1) #13834's phantom exports and fix(rest): a data engine that cannot be RESOLVED no longer answers 403 FORBIDDEN (#13476) #13910's reach overstatement are false factual claims about the diff, which no signal-set widening insidebreakingDeclaration()could ever catch. They need something that reads the diff and the prose together and judges whether they agree. ⚠️ But option A's coverage is exactly as wide as the contract-review chain, and that chain fires on the clause-② gate — so it coverspackages/specpaths and self-declared clause-② cards, not every breaking behaviour change. Refuse an undeclared field a before-hook writes — the post-hook half of the declared-field door, one envelope on every driver #13864's path limb wasno; it reached review only because a seat hand-routed it. A changeset misgrading a narrowing in a package nobody routed to review still sails through, which is precisely the #8296's changeset ships a breaking engine-API change asminorwith no breaking annotation — the release digest cannot classify it #8411 shape.
⇒ The sharper question this round suggests, offered as input rather than as a proposal: is the gap "no gate asks", or "the gate that does ask is reachable only through a route with its own coverage holes"? Option B's known-seam signal and the clause-② path limb are the same kind of object — a path→consequence map — and #13922 has just measured that the clause-② gate's own carriers are out of sync 4 times in 6.
Refs
- The clause-② gate has three limbs and all three were silent on PR #13910 at once — the dual-carrier label rule has no enforcement, and 4 of 6 measured pairs are out of sync #13922 (p1, filed today) — the clause-② gate's three limbs were all silent on one PR at once; the carrier discipline that routes work to review has no mechanical enforcement.
- The
Clause-②: yes | nomachine spelling is missing from the claim comment on 2 of 3 measured cards — the enqueue gate's predicate reads it there, and it is not there #13914 (p2, filed today) — the declaration limb's machine spelling is missing from claim comments. - The five verdicts: Give the driver registry an eviction door, so a deleted datasource stops draining /ready #13829, feat(cli): make the silent hook-body downgrade loud —
os lintrefuses an accidental scope leak (ask 1) #13834, HoldpublicSharing.eligibilityat redemption, not only at mint #13857, Refuse an undeclared field a before-hook writes — the post-hook half of the declared-field door, one envelope on every driver #13864, fix(rest): a data engine that cannot be RESOLVED no longer answers 403 FORBIDDEN (#13476) #13910 (comments on each PR).
⛔ No labels touched, no state changed, no option chosen — this is patrol input for whoever prices this card.
Generated by Claude Code
- Option A (rely on review) is not hypothetical any more — it fired 4 times out of 5, in one afternoon, and caught things a detector could not. Two of the four are not bump-level errors at all: feat(cli): make the silent hook-body downgrade loud —
- added a commit that references this issue
on Sep 17, 2026 关闭(维护者指令,skills 席 2 代执行;裁决 #202 B)— 2026-09-21T03:43Z
出处三件(
SKILL.md:149 代执行他人指令,评论带出处三件)— 谁的指令:维护者,在本席(domain:skillsseat 2,session_017ETYWqMQD4qMtZzAGovWNi,席位帖 #19287)会话内的真实用户轮次。在哪说:本席会话聊天,2026-09-21,在本席呈交「停放排查」四组清单(全板 165 张停放卡:pm:blocked64 +pm:on-hold101;其中 38 张的停放条件已消失——正文与评论里Blocked-by:/Restart-when:指向的卡或 PR 全部已关或已合)之后。原话(逐字,⛔ 未翻译、未润色):「还有哪些应该解除停放的你一起排查一下」;对四组清单:「同意」。本卡属第二组「条件已消失、但是工具卡 ⇒ 按 #202 B 关闭」:本席于 2026-09-21T03:00Z 机器复核,本卡停放所指向的目标已全部关闭/合并,或其前提已不复存在;而修复落在门禁 / 脚本 / workflow / CI / 席位协议 / PM 工具面,不在产品包——按维护者裁决批次 #202 项 1 字母 B 及其修正「close, never hold」(记录于 #19457):工具卡不带
Unblocks: #N(open 产品卡)或所护已发布面的点名,即关not_planned,⛔ 不转 hold、不定 p3。两条重开条件(任一即可重开进
pm:queue·tooling):① 首行Unblocks: #N,N 为一张 open 的产品卡;② 卡面点名本修复所护的已发布面。卡上已有的测量与分析原样保留,供重开时续用。
Generated by Claude Code
Restart-when: the ADR-0087 changeset-gate family is reworked (scripts/check-adr-0087-registration.mjs and siblings, e.g. out of #8299), or a second breaking-behavior changeset lands unmarked
Filed unassigned, surfaced while implementing #8411 (docs-only: annotating
.changeset/filter-formula-field-refusal.mdas breaking). Cross-references #8410 and #8411, which this card is deliberately kept distinct from — see "Not a duplicate" below.The gap
scripts/check-adr-0087-registration.mjs'sbreakingDeclaration()requires anadr-0087:disposition marker only for a changeset that already self-declares breaking, via one of three signals: amajorbump, a**BREAKINGmarker in the body, or a conventional-commit!on the summary line.scripts/check-changeset-no-major.mjsandscripts/check-empty-changeset.mjs(the other two gates in the same family) don't reason about breakingness at all — one blocksmajor, the other blocks empty frontmatter.Nothing in the gate family asks "should this changeset have declared itself breaking, but didn't." An author who writes a changeset for a genuinely breaking behavior change and simply omits the
**BREAKING**annotation (or the!, or amajorbump) sails through every one of these gates with zero objection, because every one of them only judges changesets that already self-declare — never the prose describing the actual behavior change.Measured
This is exactly what happened.
.changeset/filter-formula-field-refusal.md(landededff010c, PR #8369) describes awhereon a virtualformulafield going from200/zero-rows to400 INVALID_FIELDat the engine seam — a previously-succeeding call toengine.find/findOne/count/aggregate/update/deletenow throws. #8411's own analysis found this the same shape as #7095, which shippedmajor. Before #8411's fix, runningnode scripts/check-adr-0087-registration.mjsagainst that changeset reported:No gate anywhere flagged it. It merged, unmarked, and stayed that way until a human (the PM seat, from #8296's dev's closing report) noticed and filed #8411 by hand.
Not a duplicate of #8410
#8410 (closed, fixed by PR #8465) was about derivation — which gates
scripts/pm/dispatch-gates.mjssurfaces for a.changeset/path, so a dev's local loop matches what CI actually runs. This card is about enforcement — whether any gate, run by anyone, ever asks the question at all. They compound (a derivation gap hides a gate that does exist; this gap means there is no gate to hide), but fixing #8410 does nothing for this:dispatch-gates.mjsnow correctly surfacescheck:adr-0087-registrationfor a.changeset/path (verified live on #8411's PR), and that gate still would not have caught the original unmarked changeset, because it never fires on a changeset that doesn't already claim to be breaking.Why this is likely NOT a small mechanical fix
Detecting "is this changeset breaking" from prose alone is a semantic judgment call, not a pattern match — the same reason #6148's ADR-0087 gate deliberately asks the author in writing rather than inferring. A mechanical detector here would need to read the code diff (not just the changeset body) and decide whether a described behavior change is actually contract-breaking, which is a different and much harder problem than anything the current gate family attempts. Leaving this open rather than prescribing a fix:
minorwith no breaking annotation — the release digest cannot classify it #8411 exercised.Whoever picks this up should weigh these on the three axes (real business need / long-term soundness / hard-to-get-wrong for AI-authored changesets) rather than default to building a detector — a false-negative-prone semantic gate that gives false confidence may be worse than the current honestly-absent one.
Refs: #8410 (derivation, fixed), #8411 (the specific unmarked changeset this card generalizes from), #6148 (the ADR-0087 disposition gate's origin and "ask, don't infer" design), #7095 (the sibling engine-seam precedent that shipped
major).Generated by Claude Code