Uh oh!
There was an error while loading. Please reload this page.
fix(scripts): refuse a logless check-test-completeness run instead of answering exit 1 - #13152
Merged
Merged
Conversation
… reporting exit 1 A gate that cannot measure must not answer in the vocabulary of a verdict. Run with no turbo-test-log, check-test-completeness exited 1 and printed its usage banner -- indistinguishable at a glance from a finding, and the bare form is exactly what the gate family `scripts/pm/dispatch-gates.mjs` derives prints. Six devs on six unrelated cards each re-derived that it was an invocation error. A missing or unreadable log now answers with the repo's own PREREQUISITE NOT MET / exit 3 refusal convention (check-dual-build-cjs-loads, check-half-states), naming what it needs and saying that nothing was measured. A malformed command line keeps exit 1: a typo in the caller's own line is not an absent prerequisite, and nothing emits one. CI behaviour is unchanged: both call sites in ci.yml pass the argument and guard the file's existence first, and neither branches on the exit code. Verified by running both CI shapes before and after -- byte-identical output, same codes. The argv classification is now a pure exported function, so the exit-code contract is pinned by the self-test that already runs on every invocation rather than asserted in a comment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw
os-elon
marked this pull request as ready for review
August 29, 2026 06:56
os-elon
enabled auto-merge
August 29, 2026 06:56
This was referenced Aug 29, 2026
Contributor
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 33242331510 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 29, 2026
This was referenced Aug 29, 2026
This was referenced Aug 29, 2026
Merged
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 freeto 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#13110
A gate that cannot measure must not answer in the vocabulary of a verdict.
Run with no turbo-test-log,
scripts/check-test-completeness.mjsexited 1 andprinted its usage banner. Exit 1 plus text on a gate script reads as a finding — and
the bare form is not a mistake anyone chose: the gate family
scripts/pm/dispatch-gates.mjsderives NAMES this script with no argument, so thetooling hands out the invocation that cannot succeed. Six devs on six unrelated cards
each re-derived that it was an invocation error. All six got it right; that is the
point. The cost was six correct readers paying the same re-derivation, with a seventh,
less careful, one red PR away.
What changed
A missing or unreadable log now answers with the repo's own refusal convention —
PREREQUISITE NOT MET, exit 3, plus a line naming what the gate needs and statingthat nothing was measured. Copied from the established shape, not minted:
check-dual-build-cjs-loads.mjs(exit 3, "This is NOT a pass: nothing was measured"),scripts/pm/check-half-states.mjs(EXIT_PREREQUISITE_NOT_MET = 3) andscripts/import-prerequisite.mjs's frame.The refusal text names the derivation explicitly, so the next reader who arrives from
the derived family is told on the spot that the local reading is NOT MEASURED.
The argv classification became a pure exported function (
invocationVerdict), so theexit-code contract is pinned by the self-test that already runs on every invocation
rather than asserted in a comment.
The deliberate boundary
Exit 3 is for a missing or unreadable log — an input the world did not supply. A
malformed command line (an unknown flag, half of the scheduled/package-list flag
pair) stays exit 1: that is a typo in the caller's own line, it names itself as one,
and nothing emits it — the derived family prints the bare form, never a misspelled
flag. Pinned in the self-test so it stays a decision and not a leftover.
Precondition verified, not assumed
Every reference to this script in the tree, and what it does with the exit code:
.github/workflows/ci.ymlTest Core (:666)run:, any non-zero fails the step.github/workflows/ci.ymlDogfood (:1166)scripts/check-self-test-wired.mjsselfTest({ quiet: true })in its code, which is preservedcheck-agent-test-spelling.mjs,check-entry-guard.mjs,dispatch-gates.mjs,run-with-stall-guard.mjspackages/spec/src/shared/strict-object.test.tsNothing keys on exit 1. Both CI call sites also guard the log's existence with a
[ ! -f ... ]test before invoking, so neither new branch is reachable there.CI behaviour did not move — measured in both directions
Both CI invocation shapes were run against the unmodified script (restored from the
HEAD blob) and the modified one, and the output compared byte for byte:
And the changed direction, reproducing the control the card asked for — the
unmodified script and the changed one, both invoked bare:
The pin, ablated
The self-test case is the durable half, so it was proven able to fail. With the
implementation committed first, the refusal code was mutated back to the finding code;
the mutation was confirmed on disk by anchored counts (removed text 1 to 0, injected
text 0 to 1) and by a blob-hash change against the HEAD blob, never by an editor exit
code. The pin then red:
on both
--self-testand an ordinary bare invocation, since the self-test runs on everyinvocation. The restore leg was proven the same way: anchored counts back, worktree blob
hash equal to the HEAD blob,
git diff HEADempty. No build leg applies — node executesthis
.mjsdirectly and nothing resolves it through a package's exports or dist.Gates
Derived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(not stale: derived from this branch's tree at the commit below). All run at
589e6644, each read from the gate's own printed verdict line: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-ci-filter-parity·check-cross-package-test-inputs·check-self-test-wired·check-shard-attestation— all green.
Convention pair for a gate-script edit, both green:
scripts/pm/bare-root-worklist.mjs --self-test(40 recorded verdicts, none stale,missing or contradicted) and
check:pm-dispatch-gates(872 cases).Plus
check:nul-bytes,check:published-files, and the repo-wideeslint . --no-inline-config— 5414 files, 0 errors, 0 warnings, run under theshared verify lock (VERDICT command-exit 0).
The thirteenth derived family member is this script itself, invoked bare. It now reads
exit 3, PREREQUISITE NOT MET — which is the fix, observed through the very tool that
produced the complaint.
No changeset — derived, not asserted
Across all 80 tracked manifests, 69 publishable: zero
filesentries name anyscripts/path and zero escape their own package (control: 69 entries namedist).This path ships in nothing, so the PR releases nothing.
skip-changesetapplied.Out of scope, deliberately
Remedy B — the derivation no longer emitting an invocation that cannot succeed — is
not in this PR.
scripts/pm/dispatch-gates.mjsis held by #13129. Noted forwhoever picks it up: with A landed, the gate refuses legibly however it is invoked, but
the derived family still prints a command line nobody should run. A is worth having
either way; B remains a separate call once the file is free.
Generated by Claude Code