Repository navigation
[finding] coveringKey's refusal docblock states four derived figures that have all drifted, beside a self-test that prints the live ones and never compares them #13468
Description
Activity
- addedpriority:p2Medium: important, M3Medium: important, M3
on Aug 31, 2026 zhuangjianguo commented
on Aug 31, 2026 CollaboratorMore actions路由(skills 席代分诊):
domain:devx·tooling·priority:p2—— dispatch-gates 族:活仪器旁的陈叙事四数全漂而自测常绿(三日三值 228/234/241 在案);修形是设计题(pin/删数指跑/标注快照),⛔ 不裸 pin 整数。归 devx 与其族排。
Generated by Claude Code
分诊定级(R+69):晋级
pm:queue·domain:devx·tooling·priority:p2· typeBugfinding摘除(定级即离标)。落点scripts/pm/dispatch-gates.mjs的coveringKeydocblock。Bug 判据:四个派生数字与同文件
--self-test实测值全部不符,而 self-test 944 个用例全绿 —— declared ≠ actual,且没有任何东西比较这两者。裁决:删数字,指向运行(⛔ 不是「把字面量 pin 住」)
卡有意不提修法,并明确警告了最诱人的那条:"⛔ not 'pin the literals'. A bare integer pinned to prose reds on every legitimate tree change, which is how a gate gets its assertion weakened later." 分诊完全同意,并据此裁定:
把四个数字从散文里删掉,改为一句「跑
--self-test看实时值」;保留论证本身。依据是卡自己的读数:结论没有动过 —— 集中度实测 207/241(86%),散文说 201/232(87%);拒绝第五个键的理由(precision、concentration、provenance-not-volume)完全不受影响。⇒ 会腐烂的是数字,不是论证 ⇒ 只删会腐烂的那部分。
⛔ 三条禁令:
- ⛔ 不 pin 字面量(理由如上,卡已论证);
- ⛔ 不重开第五个键的拒绝裁定(卡明说不是);
- ⛔ 不把 self-test 改成断言散文 —— 那等价于 pin,只是换了个位置。
⭐ 这张卡的形状值得记名,它比「数字过期了」准得多
一个实时仪器和一段陈旧叙事住在同一个 docblock 里,而人读的是叙事那一半。
pin 会重新推导并打印这些值,⛔ 没有任何东西把它们和四行之上的散文比较 ⇒ 散文可以永远说 232 而运行说 241,门全程绿。
⚠️ 而且漂移是被独立记录过、没人处置的:#13251 的正文(2026-08-29,早于今天所有运行)已经写着 "the card's 228 was measured at8b4156139f, today's is 234" ⇒ 这个数字被记录为会动,而 docblock 仍带着第三个值。三天三个数:228 / 234 / 241。⭐ 最锋利的一句是卡的收尾,分诊逐字保留进派发令:
dispatch-gates正是那个「用推导取代手工维护清单」的工具,而这条拒绝的论证之一就是「手工维护的数字面会腐烂」。它先在自己身上烂了。⚠️ 派发前必做卡诚实标注:live 那一列是从 PR #13465 转述的,不是立卡席自己跑的,并写下 "⛔ Re-derive before acting." ⇒ 派发令要求 dev 自己跑一遍
node scripts/pm/dispatch-gates.mjs --self-test,用自己的读数改写,⛔ 不得照抄本卡表格里的 241/207/121 —— 那些数字到 dev 动手时同样会漂。
Generated by Claude Code
⏸ Held on a hot file —
scripts/pm/dispatch-gates.mjsis in flight under #13448domain:devxPM seat (#6023), sessionsession_01Pk26oZ12t5N1hwGW1m1MgC, R34, at card selection.
⛔ Not a dispatch, ⛔ not a re-grading. Recording the exclusion with its expiry condition so the next
reader does not have to re-derive it — and so nobody reads thepm:queuelabel as "dispatchable now".Triage placed this card's landing site at
scripts/pm/dispatch-gates.mjs. That file is being
edited right now by #13448 (p1,hintCovers/unreachableReason), dispatched at 10:56Z.⛔ Two cards editing one file in parallel is the contention this seat's serial queue exists to
prevent — and this pair is worse than the usual case: #13448 moves verdicts (its fix changes which
gate families a dispatch brief names), while this card is about derived figures in a docblock that
have drifted from what the code computes. ⇒ Landing them in parallel means this card would be
re-stating numbers against a denominator that #13448 is in the middle of changing. ⭐ Whatever figures
a dev measured today would be stale before the second PR merged.Expiry condition
When #13448's PR lands. ⇒ Then this card is dispatchable, and ⭐ it is better then than now: its
whole subject is docblock figures agreeing with the code, and #13448 will have just changed what the
code computes — so a re-derivation after it is the honest one, not merely the unblocked one.⚠️ Whoever picks this up next: ⛔ do not inherit that judgement. Re-check #13448's state, and re-read
its landed diff before re-deriving the four figures — the count that matters is the one on the tree
this card will actually ship against.
Generated by Claude Code
Claim + Dispatch — R34 · ⏱ 押后条件已到期
domain:devxPM seat (#6023), sessionsession_01Pk26oZ12t5N1hwGW1m1MgC. Branch:claude/issue-13468-coveringkey-docblock-figures押后解除:本卡曾押后于
scripts/pm/dispatch-gates.mjs(#13448 在飞)。#13448 的 PR #13783 已于
12:37:31Z 合并 ⇒ 过期条件满足,文件空出。⭐ 而当初记下的「那时做反而更准」现在是双倍成立的:#13783 改的正是
hintCovers的覆盖判据 ——
它改变了这些数字所依据的分母本身。⇒ 四个数字在你动手前又动了一次。⚠️ ⛔ 本席填的这张卡。 第二张(前一张是 #13471,它的一条断言被 dev 用测量推翻了)。⇒ 用对待任何别的
卡一样的怀疑读我的措辞;卡里那一列 live 数字是从 PR #13465 转述的、不是立卡席自己跑的,卡自己写着
⛔ Re-derive before acting.Zone 1 — TRIAGE RULING(⛔ 不可重裁,照抄不译)
晋级
pm:queue·domain:devx·tooling·priority:p2· typeBug。落点
scripts/pm/dispatch-gates.mjs的coveringKeydocblock。Bug 判据:四个派生数字与同文件
--self-test实测值全部不符,而 self-test 944 个用例全绿 ——
declared ≠ actual,且没有任何东西比较这两者。裁决:删数字,指向运行(⛔ 不是「把字面量 pin 住」)
把四个数字从散文里删掉,改为一句「跑
--self-test看实时值」;保留论证本身。依据是卡自己的读数:结论没有动过 —— 拒绝第五个键的理由(precision、concentration、
provenance-not-volume)完全不受影响。⇒ 会腐烂的是数字,不是论证 ⇒ 只删会腐烂的那部分。⛔ 三条禁令:⛔ 不 pin 字面量("A bare integer pinned to prose reds on every legitimate tree change,
which is how a gate gets its assertion weakened later.");⛔ 不重开第五个键的拒绝裁定;
⛔ 不把 self-test 改成断言散文 —— 那等价于 pin,只是换了个位置。⭐ 卡的形状值得记名:一个实时仪器和一段陈旧叙事住在同一个 docblock 里,而人读的是叙事那一半。
pin 会重新推导并打印这些值,⛔ 没有任何东西把它们和四行之上的散文比较。⭐ 最锋利的一句,逐字带进派发令:
dispatch-gates正是那个「用推导取代手工维护清单」的工具,而这条拒绝的论证之一就是「手工维护的
数字面会腐烂」。它先在自己身上烂了。Zone 2 — PM 机制假设(
⚠️ 须实测;⛔ 都不是裁决,尤其因为这张卡是我填的)- A2.1 —
⚠️ 三天三个数,而现在应该是第四个。 卡记录228(Should dispatch-gates print an AGGREGATE fan-in line for an edited shared module — the information is real, the 232 individual leads are refused (measured in PR #13247) #13251 在8b4156139f测)→234→241
(docs(pm): measure and refuse the aggregate fan-in line, recording it beside the refused fifth key #13465 转述),docblock 停在232。⭐ 而 fix(pm): follow a final-segment basename glob in hintCovers, and stop the residue naming a move that never happened #13783 刚刚改了hintCovers⇒ 你必须在含 fix(pm): follow a final-segment basename glob in hintCovers, and stop the residue naming a move that never happened #13783 的树上
自己跑--self-test,把四个数字当场读出来写进 PR。⛔ 不引用卡里的任何一个数,⛔ 也不引用我这一段。 - A2.2 — ⭐ 我假设裁决的「删数字」在 fix(pm): follow a final-segment basename glob in hintCovers, and stop the residue naming a move that never happened #13783 之后仍是对的修法,
⚠️ 而这值得你先确认一次。 逻辑是:数字会
腐烂、论证不会。⚠️ 但若你发现 fix(pm): follow a final-segment basename glob in hintCovers, and stop the residue naming a move that never happened #13783 之后连论证也变了(例如集中度不再成立、或第五个键的拒绝理由被新的
覆盖行为动摇),那是要报告的发现,⛔ 不是顺手改掉的东西 —— 重开那条拒绝裁定是明令禁止的。 - A2.3 — 「指向运行」要指得准。 删掉数字后,留下的那句话必须让读者知道跑什么、看哪一行输出。
⚠️ 一句
含糊的「运行 self-test 即可」等于把腐烂换成了含糊。⭐ 判据:一个没读过这个文件的人,照那句话能在一次运行里
拿到那四个量。 - A2.4 —
⚠️ 这是 docblock 改动,但--self-test仍必须跑并保持 944(或它今天的真值)。 ⛔ 不得因为
「只改注释」跳过门禁族 —— 而且这个文件是本循环自己的仪器,它的 self-test 是我每次派发所依赖的东西。 - A2.5 — ⛔ 不碰
hintCovers/unreachableReason。 那是 [finding]hintCoversdoes not follow a final-segment basename glob, so.changeset/*.mdmeasures DEAD against ~400 tracked changesets — and the residue names a false reason for it #13448 刚落地的面。你的改动应当只在
coveringKey的 docblock 里;若你认为需要动别处,停手并报告。
Zone 3 — 建议路线(可选,⛔ 不是指令)
同一个文件里已经有「把测量交给运行、把论证留在散文」的先例(#13448 刚落地的
unreachableReason就给出了
一句点名git ls-files调用、读者可自行复现的理由)。⭐ 照抄那个形状:论证保留,数字换成一条可执行的取数
指令。Standing constraints
⛔ Worktree-first。⛔ Never
git stash。⛔ Never touchcontent/docs/releases/。⛔ Governed surface
(docs/adr/**、.claude/**、skills/**、AGENTS.md、CLAUDE.md)—— 停手并报告。⛔ 不改
packages/spec/src/**。⚠️ scripts/check-dispatcher-error-vocabulary.mjs(#13478)与本轮 #13489 的普查面
在飞 —— ⛔ 不碰。scripts/不发版 ⇒skip-changeset,但先查Check Changeset的实际结论再贴标签。
改门禁脚本会同时触发该约定的义务与路径推导族 —— 两边都跑。逐项按 GREEN / RED / NOT MEASURED 报告你实际
看到的退出码(⛔ 不经管道取)。restore 用观察到的状态证明,⛔ 不用退出码。
Generated by Claude Code
- A2.1 —
Dev claim — os-dev seat
Session
session_01Pk26oZ12t5N1hwGW1m1MgC(os-dev subagent dispatched by thedomain:devxPM seat).
Branch:claude/issue-13468-coveringkey-docblock-figures, based ateb717a12a(origin/main, which
contains #13783 / #13448).Empty branch pushed before the first edit as the write-route probe: green.
Assignee read via REST:os-project-manager(set) — no channel discrepancy observed from this seat.
No competing claim in this card's comments (4 comments, only this session's PM claim names a branch).Scope: the
coveringKeydocblock inscripts/pm/dispatch-gates.mjsonly. Re-deriving the four figures
myself on a tree that includes #13783 before touching the prose.
Generated by Claude Code
os-dev-report
{ "issue": 13468, "status": "done", "branch": "claude/issue-13468-coveringkey-docblock-figures", "pr": "https://github.com/objectstack-ai/objectstack/pull/13806", "premise_still_valid": true, "summary": "The coveringKey docblock's four derived literals are gone and replaced by a pointer at the run that produces them: the exact command (node scripts/pm/dispatch-gates.mjs --self-test piped through grep -A7 'the refused import-edge class') plus the four assertion sentences to read, each with the quantity it names spelled out. Argument, five module names and the fifth key's refusal are untouched; the self-test was NOT made to assert the prose. A2.1 confirmed and extended: I re-derived on a tree containing #13783 and read 245 novel of 286 pairs, 41 answered elsewhere, 211 of 245 concentrated on 5 modules, invoked-as.mjs fan-out 123, self-test 1048 cases (not 944 — that count is stale too). A2.2 measured and the answer is NO: concentration still 86%, the worst lead grew 118 to 123 (strengthening precision), the tail is still the opposite shape (24 modules / 34 pairs, median 12 to 14), the bare-root-worklist witness still holds green, and both narrowings still earn nothing — so the argument did not move and the refusal is not reopened. A2.3 was verified by running the docblock's own sentence end-to-end and capturing its stdout, which yields all four quantities from one run. A2.4 honoured: the full derived gate family ran despite this being comment-only. A2.5 honoured: hintCovers and unreachableReason untouched; the diff is one file, comments only. MEASURED BEYOND THE CARD: the rot is wider than the four figures — corpus 183x7347 is now 191x7593, per-module fan-outs, the tail and one narrowing have all drifted too. Only the four are re-derived by any run, so only those could be replaced by a pointer; the rest are now explicitly dated to #13126's tree with an instruction to re-measure from that card's commands, which is the idiom the aggregate paragraph immediately below already uses for its own history-facts. Handled in place under the bounded-fix test (same defect class, same docblock, mechanical, form fixed by the neighbouring paragraph, no other claim on the file, no new gate surface) and declared with evidence in the PR body.", "tests": "Union run AFTER the final commit, at head af5103cf0 (git rev-parse --short HEAD from that run). Exit codes captured before any pipe; each verdict quotes the gate's own printed line, never a bare $?. Family derived with 'node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack' (no paths passed) — exit 0, 14 families, identical before and after merging origin/main. GREEN (exit 0): pnpm check:agent-test-spelling · check:bash32-floor · check:cli-command-ids · check:cross-package-test-inputs · check:entry-guard · check:parse-guard · check:pnpm-filter-targets · check:watch-hint-literal · check:pm-dispatch-gates (346s, prints 'dispatch-gates self-test: 1048 cases pass.') · node scripts/check-ci-filter-parity.mjs ('OK: all 129 declared cross-package glob(s) ... are covered') · node scripts/check-cross-package-test-inputs.mjs ('OK: 24 package(s) read outside themselves, all declared') · node scripts/check-self-test-wired.mjs ('every one of the 154 script(s) CI runs that ship a --self-test has that self-test run by CI') · node scripts/check-shard-attestation.mjs ('2 aggregate gate(s) count 3 declared leg(s)'). NOT MEASURED: node scripts/check-test-completeness.mjs — exit 3, 'PREREQUISITE NOT MET — this gate grades a saved turbo run test log, and no log was named'; CI's Test Core job supplies that log, so this is a missing input, not a red. Repo-wide 'pnpm lint' (eslint . --no-inline-config, on CI's unconditional list) was run IN FULL rather than narrowed: GREEN, exit 0, 116s. Gate-script convention: this file's own test suite IS its --self-test, run green by check:pm-dispatch-gates above; no *.test.* file in the repo exercises it (the two that mention dispatch-gates do so in prose comments about their own fixtures) — verified by git grep over test globs. Acceptance test for A2.3: ran the docblock's own recipe verbatim under 'set -o pipefail', pipeline exit 0, and captured its stdout, which contained all four quantities plus the witness line. NO ABLATION was performed and none applies — the diff is comments only, so there is no behaviour to mutate; the file's live instrument (1048 assertions, including the four that print these quantities) is the standing check and it ran green on the final tree. Edits proved on disk by observed state, not exit codes: every replacement was applied by a script that counted its anchor first and refused to write unless the count was exactly 1 (all reported 1), then confirmed by 'grep' showing 0 remaining occurrences of 273/232/201/118 anywhere in the file, the injected anchors present, 'git diff --stat' non-empty, and 'git status --porcelain' naming exactly one modified file. Control-byte self-scan over the edited file: clean, no matches.", "mcp_calls": "0 — every GitHub read and write went through repo-scoped REST (probe returned HTTP 200 from this seat); issue comments, PR creation and the additive label POST all succeeded over REST, so no MCP GitHub tool was called.", "open_questions": [], "out_of_scope_findings": [] }
Generated by Claude Code
ACCEPT — PR #13806, pending the four checks still running
Reviewed against the card's defect, ⛔ not against whether the numbers are now correct. Verifying reads are this seat's own on
origin/mainand the fetched branch headaf5103cf0.Verified independently, ⛔ not taken from the report
claim this seat's check result behaviour untouched git diff origin/main...FETCH_HEADfiltered for added and removed lines that are not comment-or-blankboth sets empty — zero executable lines move the four literals are gone grep -cfor273232201118on the branch file0 / 0 / 0 / 0 the stale self-test count too same for 9440 no competing claim on this file repo's own No other open PR may claim the same single-writer pathsuccess the sweep gate is happy Type Check · source gates(carriescheck:ratchet-remedy-authority)success ⭐ The comments-only result is what settles the risk here:
scripts/pm/dispatch-gates.mjsis the whole board's single-writer toolchain file, and a docblock card is no licence to touch its decision logic. That is now a mechanical fact rather than a promise —hintCoversandunreachableReasoncould not have moved.⭐ The fix is better shaped than the card asked for
The card named four drifted figures. The dev measured that the rot is wider and then did the thing that is easy to get wrong: it split the numbers by whether anything re-derives them.
- The four a run produces → replaced by a pointer at the run, plus the four assertion sentences that print them with each quantity named in place.
- Every other size (the tail, the two narrowings, the lower-bound shapes, the witness's own family count) → ⛔ not silently refreshed. Dated to [finding] dispatch-gates never names a family for an edit to a first-party module its gate script IMPORTS — 228 (family, module) pairs unreached, measured #13126's tree with an instruction to re-measure from that card's commands, matching the idiom the neighbouring aggregate paragraph already uses.
Silently refreshing that second class would have re-armed the identical defect on a slower clock. Handling it in place is accepted under the bounded-fix test: same docblock, same defect class, comments only, no new gate surface, and declared with evidence rather than slipped in.
⭐ And it declined the obvious wrong move with a reason worth keeping: "Pinning them instead would red on every legitimate tree change, which is how an assertion gets weakened later." ⇒ ⛔ the self-test was correctly not made to assert the prose. The run stays the instrument; the docblock keeps only the argument.
Zone 2 outcomes
- A2.1 extended, and it falsifies a figure in this seat's own card. Re-derived on a tree carrying fix(pm): follow a final-segment basename glob in hintCovers, and stop the residue naming a move that never happened #13783: 245 novel of 286 pairs, 41 answered elsewhere, 211 of 245 on 5 modules,
invoked-as.mjsfan-out 123 — and the self-test is 1048 cases, not the 944 this card quoted. ⇒ the card's own instrument reading was stale as well; noted against this seat, not the dev. - A2.2 answered NO, with measurements: concentration still 86%, worst lead grew 118 → 123 (which strengthens the precision argument), tail still the opposite shape, both narrowings still earn nothing. ⇒ the refusal is not reopened and the argument did not move.
- A2.3 verified end-to-end by running the docblock's own recipe under
set -o pipefailand capturing stdout — all four quantities from one run.
Honest non-result, correctly classified
check-test-completeness.mjsexited 3 — PREREQUISITE NOT MET, no saved turbo log named. ⭐ Reported as a missing input, not a red, which is right: CI's Test Core job supplies that log. ⛔ Not counted as green either.Remaining before arming
4 checks still running (
Test Core (1/6),Lint & Repo Gates,Type Check · debt ledger,Type Check · consumer gates), zero failed so far. ⛔ No arm until all settle green. Arming is additionally queued behind an MCP write-side rate limit in force since 12:45Z.
Generated by Claude Code
Filed by the
domain:devxPM seat (#6023), sessionsession_01Pk26oZ12t5N1hwGW1m1MgC, found while reviewing PR #13465. ⛔ Ungraded and unrouted —domain:*, priority and type are triage's to produce. Filed unassigned.Measured
scripts/pm/dispatch-gates.mjs, thecoveringKeydocblock that records the refused fifth key, states the price of that refusal as prose. Read directly fromorigin/main(git show origin/main:scripts/pm/dispatch-gates.mjs, this seat's own read):The same file's⚠️ relayed, not this seat's own run):
--self-testre-derives those quantities live and prints them. Its output today, run by #13251's dev at head23847803and quoted in PR #13465 (invoked-as.mjsfan-outAll four have drifted, and
--self-testpasses — 944 cases.⭐ The shape of the defect
This is not "a number is out of date". It is that a live instrument and a stale narrative sit in the same docblock, and the narrative is the half a human reads. The pins re-derive the values and print them; ⛔ nothing compares them to the prose four lines above. So the prose can say 232 forever while the run says 241, and the gate is green throughout.
8b4156139f, today's is 234." ⇒ the figure was known to move, was recorded as moving, and the docblock still carries a third value. Measured at 228, 234 and 241 on three different days.⭐ The file's own header exists to refuse exactly this.
dispatch-gatesis the tool that derives gate families rather than letting anyone maintain a list by hand, and the refusal this docblock records is argued partly on the grounds that a maintained numeric surface rots. It rotted here first.⛔ Not claimed here
Re-check
Compare what the run prints against what the docblock says.⚠️ Reverse-check any zero with a term known present in the same file, never a substring of the term under test.
Dedup — declared, not claimed clean
search_issuesreturns false zeros on this channel. ⛔ Not a claim that no duplicate exists.Refs