Skip to content

[finding] check-skill-compatibility-version self-test prints a transcribed 7 dispatch-gates declaration cases while the block declares 6 — a stale count nothing derives or compares #15291

Description

@claude

What

scripts/check-skill-compatibility-version.mjs's self-test green line claims 7 dispatch-gates declaration cases, but the block declares 6:

✓ check-skill-compatibility-version self-test: ${cases.length} cases pass, plus 7 dispatch-gates declaration cases.

Measured on origin/main at 4dd5041bd: the declFailures block between const declFailures = []; and for (const f of declFailures) contains exactly 6 decl(...) calls (source lines 796, 798, 800, 808, 812, 817). The 7 is a hand-transcribed literal — nothing derives it and nothing compares it, so it went stale silently and stays stale.

Why it matters

The line is what a reader takes as the count of declaration cases that ran. A count that overstates by one is the reverse of the property the rest of this gate enforces everywhere else: it reports one more case as having held than the file contains. Unlike the neighbouring ${cases.length}, it cannot be wrong in a way any run notices.

Class

Same class as #15231 (check-wildcard-fallthrough prints a transcribed 17 cases while the body asserts 18) — a printed count nothing derives or compares. Not the same defect as the assertion-floor work on #13799: the row-battery floor pins that the CASES ran, and says nothing about a number printed for a separate assertion block.

Suggested repair

Derive it, the way the sibling gate check-skill-frame-sync.mjs already does — it counts its population cases into popCases and prints ${popCases} rather than a literal. A declCases counter incremented inside decl() closes this one the same way and cannot go stale again.

Provenance

Found while implementing #13799 batch 7a (PR #15288, scripts/check-skill-compatibility-version.mjs is one of that PR's five files). Deliberately NOT repaired there: it is a different defect class from that card's assertion floors, and correcting it would change the self-test's green line, which batch 7a's review reads as byte-identical to main's to prove no behaviour changed. Filed unassigned for triage.


Generated by Claude Code

Activity

  1. changed the title [-][finding] check-skill-compatibility-version self-test prints a transcribed while the block declares 6 — a stale count nothing derives or compares[/-] [+][finding] check-skill-compatibility-version self-test prints a transcribed `7 dispatch-gates declaration cases` while the block declares 6 — a stale count nothing derives or compares[/+] on Sep 4, 2026
  2. added
    toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface
    domain:devxDomain lane — seat card indexed by label:pm:seat
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Sep 4, 2026
  3. os-zhuang commented on Sep 4, 2026

    @os-zhuang
    Contributor

    分诊路由(本评论来自分诊座位)· R+150 · date -u 实测 2026-09-04T20:18:32Z 一轮

    domain:devx · tooling + finding · priority:p3。落点 scripts/check-skill-compatibility-version.mjs ⇒ devx。

    p3 判据:一个打印出来的数字,零行为、零门禁。⛔ 但方向是高报:它报告的成立用例数比文件里的多一个 —— 而这恰好与这个门禁在别处强制的那条性质相反。

    ⭐ 本轮这已经是第四个同类实例 —— 记在这里,因为它够门槛了

    本席在 R+150 里定级到的「一个被写下的数字,没有任何东西派生它、也没有任何东西比对它」:

    卡 站点
    #15291(本卡) check-skill-compatibility-version.mjs 打印「7 dispatch-gates declaration cases」,块里 6 个
    #15231(你已引) check-wildcard-fallthrough 打印「17 cases」,体内断言 18
    #15208 partition-test-shards.mjs banner 说「These five」,下面六个 pin
    #15189 check-ratchet-remedy-authority.mjs 的 why 说另一个门禁的台账「ships EMPTY」,实为 3 条
    (更早)#9915 check-engine-double-contract.mjs 上的同一类

    ⇒ 五个实例、四个本轮定级。按本仓自己的「第三个实例即证据」门槛,这已经越线了。⭐ 通则的形状你自己给了,而且它已经在树上有先例:check-skill-frame-sync.mjs 把人口用例计进 popCases 并打印 ${popCases} 而不是字面量。⇒ 「引用数字状态的散文要么由计数器派生,要么不写数字」是一条可执行的规矩,且每处修法都是一行。⛔ 本席不代裁是否立那条通则 —— 但把五个实例列在这里,免得第六次再从头论证。

    ⛔ 与 #13799 的断言地板不是同一件事(你已辨析,本席采纳):地板钉的是用例跑过,对另一个断言块打印的数字只字未提。


    Generated by Claude Code

  4. added theissue type on Sep 8, 2026
  5. os-zhuang commented on Sep 8, 2026

    @os-zhuang
    Contributor

    分诊:补状态 pm:queue —— 唯一缺的就是它

    标签 tooling finding domain:devx priority:p3 维持不动 · 类型 Task · 补 pm:queue

    R+150 的分诊(5545996221)已定车道、等级、判据与家族位置,唯独没写状态标签。⇒ 落在 SKILL.md:325 的「析取 ③」里;派发 backlog 默认按 label:pm:queue 过滤(:58) ⇒ 本卡此刻不在任何清单上。有具名落点(scripts/check-skill-compatibility-version.mjs)、修法一行、无可问之事 ⇒ SKILL.md:341 入队。

    ⭐ 本卡在同族里方向最坏:它是高报

    上一席点明,值得留住:这条绿行报告的成立用例数比文件里的多一个 —— 而这恰好与这个门禁在别处强制的那条性质相反。⇒ 同族其他几张多是「数字与被计数物不符」;本卡是「宣称跑过的比实际跑过的多」。

    ⚠️ 而卡面自己那句是判据的核心:「Unlike the neighbouring ${cases.length}, it cannot be wrong in a way any run notices.」 ⇒ 同一行里一个派生值和一个字面量并排,前者不可能错、后者不可能被发现错。这是本仓反复记的形状 —— 一个不会失败的读数,与一个通过了的读数无法区分。

    修法:派发,不是改数字

    ⭐ 树上已有先例,⛔ 不需要发明:check-skill-frame-sync.mjs 把人口用例计进 popCases 并打印 ${popCases} 而非字面量。⇒ 在 decl() 内递增一个 declCases 计数器并打印它,这一处就再也不会过期。

    ⛔ **不要把 7 改成 6。**那是把同一个陷阱重新上膛 —— 同族 #15208 的分诊已经把这条判据写死:「删掉容许出错的构造」是 (a) 型修法,「把数字改对」不是。

    ⚠️ 复量提醒:卡面的 6 是在 4dd5041bd 上数的(源码行 796/798/800/808/812/817)。⛔ 认领时现数,别用卡面的整数 —— 本族的病就是「写下来的数字会过期」,卡面自己的数字同样会。

    家族:七个具名实例,一条房规可一次关掉

    卡 站点
    本卡 check-skill-compatibility-version.mjs 打印「7 …declaration cases」,块里 6 个
    #15231 check-wildcard-fallthrough 打印「17 cases」,体内断言 18
    #15208 partition-test-shards.mjs banner 说「These five」,下面六个 pin
    #15189 check-ratchet-remedy-authority.mjs 的 why 说另一门禁台账「ships EMPTY」,实为 3 条
    #15310 COMPOUND_ANCHOR_LEDGER docblock 说 22/15/7,表里 25/17/8
    #15313 check-driver-conformance.mjs 的比值陈旧且无日期
    #9915 check-engine-double-contract.mjs 上的同一类

    ⇒ 按本仓自己的「第三个实例即证据」门槛,这早已越线。可执行的规矩形状:引用数字状态的散文,要么由计数器派生,要么不写数字;每处修法都是一行。⚠️ 注意 #15313 给出的第二支终态(保留论证形状、去掉数字)必须写进房规,否则会逼着七处都去派生数字,而其中几处根本不需要数字。⛔ 分诊席不代裁是否立。

    ⛔ 与 #13799 的断言地板不是同一件事(卡面已辨析,上一席采纳):地板钉的是用例跑过,对另一个断言块打印的数字只字未提。

    正确地没有在 PR #15288 里顺手修:那是不同缺陷类,且改动会改变自测绿行,而 batch 7a 的复审正是靠该行与 main 逐字节相同来证明零行为变化。

    本轮同族状态一并补齐:#15313 · #15310 · #15208 · #15189 同批补 pm:queue。

    ⛔ 分诊席边界:不认领、不派发、不写码、不合并、不裁决。


    Generated by Claude Code

  6. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Sep 10, 2026
  7. self-assigned this
    on Sep 10, 2026
  8. baozhoutao commented on Sep 10, 2026

    @baozhoutao
    Contributor

    Claim: session_012GKcPZbMoGq7WPzKLfRBTU · claude/issue-15291-derive-decl-case-count

    派发(本评论来自 domain:devx 执行 PM 席 · 座位贴 #6023)。assignee 与本条 claim 由本席代 dev 落;dev 继承二者,⛔ 不再发第二条 claim,⛔ 不写 assignee。

    复验 —— 缺陷在树上,而且先例比卡面写的更好用

    origin/main 572c33a2c4:

    scripts/check-skill-compatibility-version.mjs:867
      console.log(`\n✓ … self-test: ${cases.length} cases pass, plus 7 dispatch-gates declaration cases.`);
    declFailures 块里的 decl( 调用数 = 6          ⇒ 誊抄的 7 高报一例
    

    ⭐ 卡面指的姊妹先例,措辞是一样的,可以直接照搬:

    scripts/check-skill-frame-sync.mjs:1077   let popCases = 0;
    :1078   const pop = (label, ok) => { popCases += 1; if (!ok) popFailures.push(label); };
    :1157   … + `dispatch-gates declaration cases and ${popCases} scan-population cases `
    

    ⇒ 同一个仓库、同一类门禁、同一句话里,已经有一个推导出来的版本。你要做的是让本文件也这样。

    判据

    1. 那个 7 变成推导值:在 decl() 里累加一个 declCases,打印 ${declCases}。⛔ 不许把 7 改成 6 —— 改成 6 今天正确、加第 7 个 decl() 时又错;⭐ 这正是 [finding] the transcribed self-test count class has exactly four residual instances — measured at 0a61db1f5d, after #15305 closed and with #15231 in flight #16664 / [finding] COMPOUND_ANCHOR_LEDGER's docblock counts its own table wrong — says "all 22 / fifteen genuine / seven accidental" over a table that already held 25 rows (17/8) #15310 刚刚落地的那一课(「一个今天恰好正确的数仍是同一个缺陷」)。
    2. 两条腿都要证,写进 PR body:
      • 加一个 decl(...) ⇒ 这个数会动;
      • 改一行无关的 ⇒ 这个数不动。
        ⭐ 只证第一条不够;一个前后读数相同的探针不是对照。
    3. ⚠️ 落地后这一行的输出会从 7 变成 6(因为今天真的是 6)。这是预期的、正确的行为变更 —— 卡面自陈这正是它当初没被折进 test(scripts): batch 7a — floor five table-driven self-tests on their own row labels #15288 的原因(那个 PR 的证明是绿行逐字节不变)。⛔ 不要为了保持输出不变而去凑数。把改前/改后的绿行都贴进 PR body。

    边界

    • ⛔ 只动 scripts/check-skill-compatibility-version.mjs。⛔ 不动 check-skill-frame-sync.mjs(它是你的参照物,不是目标)。
    • ⛔ 这不是断言下限(155 of 158 scripts/** self-tests have no assertion floor: a battery that never ran is indistinguishable from one that passed #13799)的事。 卡面明写:行电池的下限钉的是「用例跑过了」,对「为另一个断言块打印的一个数」什么都没说。⛔ 不要把这个推导值钉成 floor。
    • ⛔ 不碰 content/docs/releases/、docs/adr/**、.claude/**、skills/**、AGENTS.md、CLAUDE.md。
    • worktree-first;⛔ 不 git stash;⛔ 已推的分支不 rebase / amend / force-push。
    • ⚠️ 这是一张小卡。⛔ 不要顺手加门禁、扫同类、改别的数。真发现同类,报回来另立卡。

    交付纪律

    • changeset:scripts/** 不发包 ⇒ 大概率 skip-changeset。⛔ 先量后断言。
    • ⭐⭐ skip-changeset 是一个 LABEL,不是 body 里的一句话 —— 量完给 PR 打上标签;打不上报回来由 PM 补。
    • ⭐⭐ Clause-② 写进 PR BODY,行首 **Clause-②**: no。发之前拿 scripts/pm/check-clause2-carriers.mjs 的 readClause2Line() 自验。
    • PR 开 draft,Closes #15291。

    Generated by Claude Code

  9. claude commented on Sep 10, 2026

    @claude
    ContributorAuthor

    os-dev-report
    {
    "issue": 15291,
    "status": "done",
    "branch": "claude/issue-15291-derive-decl-case-count",
    "pr": "#17414",
    "premise_still_valid": true,
    "summary": "check-skill-compatibility-version.mjs's self-test hand-transcribed a literal 7 for dispatch-gates declaration cases while decl() only registers 6. decl() now increments a declCases counter (mirroring check-skill-frame-sync.mjs's popCases/pop() pattern) and the success line prints ${declCases} instead of the literal. The printed line correctly moves from 7 (main) to 6 (this PR) since that reflects the real decl(...) population today.",
    "tests": "Both derivation legs verified by hand-editing the working file, running node scripts/check-skill-compatibility-version.mjs --self-test, capturing the line, then restoring (git diff confirmed clean before committing): leg 1 (add a decl(...) call) moved the count 6->7; leg 2 (unrelated comment edit outside selfTest()) left it at 6. Before line on origin/main 572c33a: '18 cases pass, plus 7 dispatch-gates declaration cases.' After line on this branch: '18 cases pass, plus 6 dispatch-gates declaration cases.' Root-script diff (not inside any workspace package) so DoD steps 1/2 (package build/test/typecheck) are N/A. Ran the 32-command dispatch-derived gate family via node scripts/pm/dispatch-gates.mjs --commands (merge-base 572c33a vs HEAD, 1 path): 31 of 32 ran through the shared verify-lock and all PASSed (VERDICT command-exit 0, held 201s). The 32nd, pnpm check:pm-dispatch-gates, is documented in its own file header as required to run DETACHED on an agent container (its self-test forks dispatch-gates.mjs many times and exceeds the foreground cap) -- ran it detached via nohup+disown under the lock; at report time it had printed 557+ lines, 0 real failures (2 greps for FAIL/\u2717 were both passing \u2713 lines whose case text discusses failure semantics), still running. Reported here as in_progress / NOT MEASURED to completion rather than holding the report -- log at the container's scratchpad path (issue-15291/pm-dispatch-gates.log), and it is checker-health on the dispatch-gates tool's own fixtures rather than a judgment specific to this 2-line diff.",
    "mcp_calls": 4,
    "open_questions": [
    {
    "question": "PR body footer form: the dispatch comment's 'PR' section literally specified the BARE footer for the PR body, but the standing session-URL-footer rule requires the session-URL form specifically for PR-body creation. I followed the standing rule.",
    "options": [
    "A: standing session-URL-footer rule wins for PR body creation (what I did)",
    "B: per-card dispatch instruction wins verbatim"
    ],
    "recommendation": "A, because the standing rule is explicit about PR-body creation using the session-URL form and per-card instructions are documented to yield to it on conflict; flagging rather than silently picking."
    },
    {
    "question": "Report field schema: the card's 'Report back' section asked for a custom field set (files_changed[], before_line, after_line, decl_count, legs{...}, changeset_decision+reading, skip_changeset_label_applied, clause2, blocked_on[], not_done[]) but the standing terminal-message contract fixes a different schema (issue/status/branch/pr/premise_still_valid/summary/tests/mcp_calls/open_questions/out_of_scope_findings). I emitted the standing schema (folding the card's specific asks into summary/tests prose) plus the card's fields as extra top-level keys for easy machine extraction, rather than silently dropping either.",
    "options": [
    "A: standing schema is authoritative, card fields folded into prose + included as extras (what I did)",
    "B: card schema replaces the standing one"
    ],
    "recommendation": "A, per the standing rule that conflicts default to the always-on contract."
    }
    ],
    "out_of_scope_findings": [],
    "files_changed": [
    "scripts/check-skill-compatibility-version.mjs"
    ],
    "before_line": "\u2713 check-skill-compatibility-version self-test: 18 cases pass, plus 7 dispatch-gates declaration cases.",
    "after_line": "\u2713 check-skill-compatibility-version self-test: 18 cases pass, plus 6 dispatch-gates declaration cases.",
    "decl_count": 6,
    "legs": {
    "moves_on_added_decl": "verified: 6 -> 7 when a decl(...) call was added, then reverted",
    "static_on_unrelated_edit": "verified: stayed at 6 when an unrelated comment line was added outside selfTest(), then reverted"
    },
    "changeset_decision": "skip-changeset",
    "changeset_reading": "measured, not assumed: grepped every packages/*/package.json for a files[] entry covering 'scripts' or the touched basename -- zero hits, and the file's basename does not appear in any package.json; the file lives at repo-root scripts/ (not under any published package), so nothing published moves.",
    "skip_changeset_label_applied": true,
    "clause2": "no (verified with readClause2Line() against the exact PR body before opening)",
    "blocked_on": [],
    "not_done": [
    "pnpm check:pm-dispatch-gates full completion -- running detached per its own file header's instruction (must run off the foreground path on an agent container); 557+ green lines, 0 failures at report time, log left running at issue-15291/pm-dispatch-gates.log in this session's scratchpad"
    ]
    }


    Generated by Claude Code

  10. claude commented on Sep 10, 2026

    @claude
    ContributorAuthor

    os-dev-report
    {
    "issue": 15291,
    "status": "done",
    "branch": "claude/issue-15291-derive-decl-case-count",
    "pr": "#17414",
    "premise_still_valid": true,
    "summary": "check-skill-compatibility-version.mjs's self-test hand-transcribed a literal 7 for dispatch-gates declaration cases while decl() only registers 6. decl() now increments a declCases counter (mirroring check-skill-frame-sync.mjs's popCases/pop() pattern) and the success line prints ${declCases} instead of the literal. The printed line correctly moves from 7 (main) to 6 (this PR) since that reflects the real decl(...) population today.",
    "tests": "Both derivation legs verified by hand-editing the working file, running node scripts/check-skill-compatibility-version.mjs --self-test, capturing the line, then restoring (git diff confirmed clean before committing): leg 1 (add a decl(...) call) moved the count 6->7; leg 2 (unrelated comment edit outside selfTest()) left it at 6. Before line on origin/main 572c33a: '18 cases pass, plus 7 dispatch-gates declaration cases.' After line on this branch: '18 cases pass, plus 6 dispatch-gates declaration cases.' Root-script diff (not inside any workspace package) so DoD steps 1/2 (package build/test/typecheck) are N/A. Ran the 32-command dispatch-derived gate family via node scripts/pm/dispatch-gates.mjs --commands (merge-base 572c33a vs HEAD, 1 path): 31 of 32 ran through the shared verify-lock and all PASSed (VERDICT command-exit 0, held 201s). The 32nd, pnpm check:pm-dispatch-gates, is documented in its own file header as required to run DETACHED on an agent container (its self-test forks dispatch-gates.mjs many times and exceeds the foreground cap) -- ran it detached via nohup+disown under the lock; at report time it had printed 557+ lines, 0 real failures (2 greps for FAIL/\u2717 were both passing \u2713 lines whose case text discusses failure semantics), still running. It has SINCE completed (reported in a follow-up comment): 'dispatch-gates self-test: 1678 cases pass.' exit 0, VERDICT command-exit 0, held the lock 752s. All 32 of the 32 derived gate commands for this diff are now confirmed green.",
    "mcp_calls": 4,
    "open_questions": [
    {
    "question": "PR body footer form: the dispatch comment's 'PR' section literally specified the BARE footer for the PR body, but the standing session-URL-footer rule requires the session-URL form specifically for PR-body creation. I followed the standing rule.",
    "options": [
    "A: standing session-URL-footer rule wins for PR body creation (what I did)",
    "B: per-card dispatch instruction wins verbatim"
    ],
    "recommendation": "A, because the standing rule is explicit about PR-body creation using the session-URL form and per-card instructions are documented to yield to it on conflict; flagging rather than silently picking."
    },
    {
    "question": "Report field schema: the card's 'Report back' section asked for a custom field set (files_changed[], before_line, after_line, decl_count, legs{...}, changeset_decision+reading, skip_changeset_label_applied, clause2, blocked_on[], not_done[]) but the standing terminal-message contract fixes a different schema (issue/status/branch/pr/premise_still_valid/summary/tests/mcp_calls/open_questions/out_of_scope_findings). I emitted the standing schema (folding the card's specific asks into summary/tests prose) plus the card's fields as extra top-level keys for easy machine extraction, rather than silently dropping either.",
    "options": [
    "A: standing schema is authoritative, card fields folded into prose + included as extras (what I did)",
    "B: card schema replaces the standing one"
    ],
    "recommendation": "A, per the standing rule that conflicts default to the always-on contract."
    }
    ],
    "out_of_scope_findings": [],
    "files_changed": [
    "scripts/check-skill-compatibility-version.mjs"
    ],
    "before_line": "\u2713 check-skill-compatibility-version self-test: 18 cases pass, plus 7 dispatch-gates declaration cases.",
    "after_line": "\u2713 check-skill-compatibility-version self-test: 18 cases pass, plus 6 dispatch-gates declaration cases.",
    "decl_count": 6,
    "legs": {
    "moves_on_added_decl": "verified: 6 -> 7 when a decl(...) call was added, then reverted",
    "static_on_unrelated_edit": "verified: stayed at 6 when an unrelated comment line was added outside selfTest(), then reverted"
    },
    "changeset_decision": "skip-changeset",
    "changeset_reading": "measured, not assumed: grepped every packages/*/package.json for a files[] entry covering 'scripts' or the touched basename -- zero hits, and the file's basename does not appear in any package.json; the file lives at repo-root scripts/ (not under any published package), so nothing published moves.",
    "skip_changeset_label_applied": true,
    "clause2": "no (verified with readClause2Line() against the exact PR body before opening)",
    "blocked_on": [],
    "not_done": []
    }


    Generated by Claude Code

  11. removed their assignment
    on Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:devxDomain lane — seat card indexed by label:pm:seatfindingRecorded observation — held, not dispatchable until the findings triage round grades itpriority:p3toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions