Skip to content

fix(dispatch-gates): a package-root vitest config or manifest edit derives check-console-intercept-disarm - #29556

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-s7o6g0
Nov 2, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-s7o6g0

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Warren · project thread

Fixes #29353

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:check-console-intercept-disarm 在自己源码里声明它真正读的文件(各工作区包根的 package.json 与 vitest.config.*),dispatch-gates 据此把它派给改这些文件的卡;残差台账里它的两行随之删除。
  • 为什么改:改包根 vitest 配置的卡,本地派生清单里没有这道门,只能等 CI 变红才知道(PR fix(server): budget counters hold their limit per key, count in a registered cache, and keep the in-process fallback bounded #29345 为此多跑了一轮)。
  • 风险与回滚:只动工具脚本,不发布任何包;回滚即 revert 本 PR,派生回到原先的"无路径"状态。
  • 先看:scripts/check-console-intercept-disarm.mjs(新声明与漂移检查)、scripts/pm/dispatch-gates.self-test.mjs(新增的实树钉子)、scripts/pm/dispatch-gates.data.mjs(删掉的两行)。
  • 两分钟验证:node scripts/pm/dispatch-gates.mjs --commands --paths packages/services/service-sms/vitest.config.ts,预期输出里出现这道门的两条命令(主扫描与 --self-test)。
  • 需要你做的:无,席位落地。

What changes

  • Landing is the gate's own file, not only dispatch-gates.data.mjs. The order expected the data table; measured, the derivation reads a gate's population only from the gate's own source (the ROOT_DIR_WATCH_HINTS idiom), and dispatch-gates.data.mjs's header says it is "not a watch surface". So the producer-side fix is a declaration in scripts/check-console-intercept-disarm.mjs:
    • ROOT_DIR_WATCH_HINTS: one pair per workspace glob the gate parses, DIR/*/package.json and DIR/*/vitest.config.*. A glob in a non-final segment makes the derivation judge the hint as a pattern, one path segment per star, so a test file or a config below the package root is not covered.
    • A drift check: main() exits 1 when the declaration and the parsed pnpm-workspace.yaml globs disagree in either direction (watchHintDrift), and the gate's self-test holds the real tree to it plus both drift directions on fixtures. A literal array, as check:watch-hint-literal requires.
    • workspaceGlobs() is split out of workspacePackageDirs() with no change in behaviour; the three gates that import workspacePackageDirs are untouched.
  • scripts/pm/dispatch-gates.data.mjs: the two ROOT_WALK_RESIDUE_LEDGER rows for the gate and its --self-test are retired, since both families have left the population. A note records why, beside the two earlier departures.
  • scripts/pm/dispatch-gates.self-test.mjs: the card's acceptance, pinned against the live tree. Every package-root vitest config (80) and the manifest beside it places both invocations. A new package's config and manifest do too. No tracked test file under a package (6926) places either, and neither does a brand-new test file or a config below a package root.

Why

The rows stated the population correctly and drew the wrong conclusion: an EDITED package-root config moves the gate as surely as a new package does. Because the rows only exempted the gate from the whole-tree channel, no path derived it at all.

Tests

  • Reverse check, with the gate file restored to 399df5bb (tree only, then restored from HEAD; git diff HEAD empty, blob hash matched): 80 of 80 package-root configs place neither invocation, and the test-file probe reads undetermined. With the fix, 0 of 80 are missed, and test files read silent.
  • node scripts/check-console-intercept-disarm.mjs --self-test: self-test OK: 15 cases + real-tree population floor + watch-hint declaration. Scan: OK: 80 vitest-running package(s).

Verification

Final union on head 1b3ea66a (base 399df5bb): node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 32 commands from the 3 changed paths; all 32 exit 0. --ran reconciliation: "32 derived famil(ies) accounted for — 32 run, 0 NOT-MEASURED". check:pm-dispatch-gates: "1945 cases pass" (fast tier). The first pass on a39185ba was 31 of 32: check:watch-hint-literal refused the Object.freeze wrapper, fixed in 1b3ea66a.

32 commands and exit codes on 1b3ea66
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-console-intercept-disarm.mjs :: exit 0
node scripts/check-console-intercept-disarm.mjs --self-test :: exit 0
node scripts/check-declaration-mirrors.mjs :: exit 0
node scripts/check-declaration-mirrors.mjs --self-test :: exit 0
node scripts/check-self-test-wired.mjs :: exit 0
node scripts/check-self-test-wired.mjs --self-test :: exit 0
node scripts/check-self-test-workflow-commands.mjs :: exit 0
node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0
node scripts/check-whole-set-label-write.mjs :: exit 0
node scripts/check-whole-set-label-write.mjs --self-test :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
node scripts/pm/bare-root-worklist.mjs --self-test :: exit 0
pnpm check:agent-test-spelling :: exit 0
pnpm check:bash32-floor :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:cli-command-ids :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:entry-guard :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:parse-guard :: exit 0
pnpm check:pm-dispatch-gates :: exit 0
pnpm check:pnpm-filter-targets :: exit 0
pnpm check:pnpm-flag-spellings :: exit 0
pnpm check:ratchet-remedy-authority :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:scripts-symbol-anchors :: exit 0
pnpm check:watch-hint-literal :: exit 0

Acceptance notes

  • Not derivable, by construction: pnpm-workspace.yaml and the root vitest.preset.mts also move this gate, but neither carries a separator, so no spelling of either is a hint the derivation admits. Edits to those two files still learn of the gate from CI.
  • Out of scope, reported to the seat and not fixed here: the gate's workspace parse stops at the first comment line inside the packages: list, so the four ui members below it are outside its population. Measured: 40 ui workspace packages run vitest and none sets the disarm. The declaration follows what the gate reads today, and its drift check will ask for the ui pairs on the day the parse reads them.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (not touched)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes. (CI-owned run)
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset. (publishes nothing: scripts/** only, skip-changeset)
  • 6. packages/spec touched: n/a.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English.
  • The Before Submitting list in CONTRIBUTING.md is done.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JQwxbXV6v4rbiDvvaaabhb


Generated by Claude Code

…rives check-console-intercept-disarm

The gate declares what its scan opens (a package-root package.json and
vitest config under each workspace glob) as ROOT_DIR_WATCH_HINTS in its
own source, and its scan reds when that declaration drifts from
pnpm-workspace.yaml. Both invocations leave the root-walk residue, so their
two ROOT_WALK_RESIDUE_LEDGER rows are retired. The dispatch-gates self-test
pins the acceptance against the live tree: every package-root config and
manifest places both invocations; no test file under a package does.

Claude-Session: https://claude.ai/code/session_01JQwxbXV6v4rbiDvvaaabhb
Co-authored-by: Claude <noreply@anthropic.com>
…l array

check:watch-hint-literal refuses a declaration wrapped in Object.freeze.

Claude-Session: https://claude.ai/code/session_01JQwxbXV6v4rbiDvvaaabhb
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:devx#1 · os-warren · session_01JQwxbaBjisxkQo9hht9bFB

维护者速读

  • 改了什么:check-console-intercept-disarm 在自己源码里声明它读的文件(各工作区包根的 package.json 与 vitest.config.*),于是派发门禁对改这些文件的卡会直接点名该门;并加一道漂移检查,声明与 pnpm-workspace.yaml 不一致即红。
  • 为什么改:以前没有任何路径能推出这道门,改了包根 vitest 配置的卡只能等 CI 红了才知道(PR fix(server): budget counters hold their limit per key, count in a registered cache, and keep the in-process fallback bounded #29345 因此多跑一轮)。
  • 风险与回滚:只动 PM 工具与一个门禁脚本,不发包;回滚即 revert。
  • 先看:scripts/check-console-intercept-disarm.mjs 的 ROOT_DIR_WATCH_HINTS 与 watchHintDrift;dispatch-gates.self-test.mjs 新增的一段。
  • 两分钟验证:node scripts/pm/dispatch-gates.mjs --commands --paths packages/services/service-sms/vitest.config.ts,预期列出 check-console-intercept-disarm。
  • 需要你做的:无,CI 全绿后席位落地。
  • 席位意见:落点换到门禁自己的源码是对的,派发推导只读门禁源码里的声明;实测反向 80/80 个包根配置修前漏推、修后全中。

席位核对

ACCEPT · domain:devx#1 · head 1b3ea66abfe65296793a04616ab72e985cd188f1 · 2026-11-02T18:44Z

  1. Shape: three files (+251 / -34): scripts/check-console-intercept-disarm.mjs (a literal ROOT_DIR_WATCH_HINTS, one package.json / vitest.config.* pair per parsed workspace glob; workspaceGlobs() split out of workspacePackageDirs() with behaviour unchanged; watchHintDrift red in the scan; four self-test cases), scripts/pm/dispatch-gates.data.mjs (both ROOT_WALK_RESIDUE_LEDGER rows retired with a ⚖️ note), scripts/pm/dispatch-gates.self-test.mjs (live-tree pins). dispatch-gates.mjs untouched.
  2. Premise, measured: on main the seat's --commands --paths packages/services/service-sms/vitest.config.ts listed the gate 0 times (claim 6584022916). The dev measured the reverse leg: 80/80 tracked package-root configs unplaced before, 0 after; a new test file under an existing package and a vitest config below a package root place nothing, as the card's acceptance asks.
  3. Gates: local 32 derived / 32 run / all exit 0, 0 NOT-MEASURED (report 6585225367). The seat arms only when every check-run on this head is green or an expected skip.
  4. Accepted deviations: the landing moved from the expected dispatch-gates.data.mjs to the gate's own source, because the derivation reads a gate's population only from that gate's source; the data file keeps only the ledger retirement. Said in the report and the PR body.
  5. Out of scope: filed check-console-intercept-disarm: the workspace parse stops at a comment line, so every ui member is outside the gate and it still prints OK #29561 (class a): the gate's pnpm-workspace.yaml parse stops at the comment block inside packages:, so ui, ui/packages/*, ui/apps/*, ui/examples/* are outside its population and it prints OK over the non-ui tree. The seat re-read the parse and the workspace file on main. This PR states that limit in its declaration comment instead of widening into it. Triage rung 6585315265.
  6. Landing: not governed (scripts/** outside the register), under 3000 changed lines, Clause-②: no, no contract review owed, skip-changeset. Seat lands it: pr_ready + automerge_enable once every check on this head is green or an expected skip.

@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 41505969728 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

分类:failure —— 按下面的日志分诊。

失败的 job(日志抽取,best effort):

  • Test Core (1/8) — 失败步骤: Run this shard's tests

    @objectstack/spec:test:repo:  FAIL   repo  scripts/strictness-ledger-doc.test.ts > the ledger and the generated counts agree > is checked in current — every shard on disk equals a fresh render, and no
      ↳ 失败原因: @objectstack/spec:test:repo: AssertionError: expected Map{ …(14) } to deeply equal Map{ …(14) }
    @objectstack/spec:test:repo:  FAIL   repo  scripts/strictness-ledger-doc.test.ts > the ledger and the generated counts agree > the read-time totals equal the sums of the shards' own rows
      ↳ 失败原因: @objectstack/spec:test:repo: AssertionError: expected 'untriaged: 1298 object site(s) across…' to be 'untriaged: 1297 object site(s) across…' // Object.is equality
    

↳ 失败原因 是判读的关键:超时(Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言(AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError。 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 36 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

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

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dispatch-gates: check-console-intercept-disarm derives for no path, so a card that edits a package-root vitest.config.* learns it only in CI

1 participant