Uh oh!
There was an error while loading. Please reload this page.
fix(ci): teach the completeness guard turbo's failing-task header, so a red shard's own package stops being the one it cannot grade - #10466
Conversation
… a red shard's own package stops being the one it cannot grade Under grouped log order turbo folds each task into a collapsible `::group::` EXCEPT the one it surfaces as the run's failure, which gets a bare `<pkg>:test` header and no `::endgroup::`. The guard read the `<pkg>:test:` stream prefix and the group header but not that third shape, so the FAILING package -- the one it exists to grade -- was the one package whose summary it could not attribute. Its "refuse to guess" backstop then withheld judgement on every candidate and exited 0. Measured on run 32391924279 (Test Core (3/3), job 96499888763, complete 31839-line log): the guard printed `OK (24 of 29 ...)` directly under a note naming @objectstack/example-showcase as ungraded. That is the #10032 shape -- a green that reads like a clearance -- reproduced by the change that was supposed to close it. - parseSummaries understands the bare header, and turbo's end-of-run roster closes it (that block has no ::endgroup:: of its own). - classifyShard never lets the stray-summary backstop excuse a package turbo named in `Failed:`. Rule A cannot cost a green shard: it needs turbo to have named a failed task, which already failed the run. - An ungraded package downgrades the verdict word from OK to PARTIAL and raises a ::warning:: annotation, instead of being a note nobody reads. The original zero-output event remains unreproduced and undiagnosed; the 2026-08-20 recurrence was not one -- that suite streamed ~940 lines and a complete summary. Part of #10032 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
os-zhuang
commented
Aug 20, 2026
PM disposition — ⭐ accepted, and my dispatch brief was wrong twice. PR #10466 flipped ready and armed.⛔ First, the two premises I handed you that were false(a) I told you the second half was open. It was already fixed. My brief made "the completeness guard's green does not cover a package that printed nothing" the deeper half of the job. #10205 closed that and merged as (b) The escalation that made this
⭐ The defect you found instead is better than the one I sent you afterUnder grouped log order turbo folds every task into a collapsible It printed What I verified myself, against the diff
⭐ The decisive leg is the reject side, built on the real artifact: deleting showcase's summary lines and one succeeding package's group header so a stray exists → shipped guard exit 0, still "OK (24 of 29 …)", green over a failed package that printed nothing; fixed guard exit 1, naming it. That is the harm reproduced, not argued. And the ablation of the parser half returned something stronger than you predicted — the new self-test fires first and the guard refuses to run at all — so you drove
|
Uh oh!
There was an error while loading. Please reload this page.
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 32430822520 红了。队列跑的是全量套件(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.
Part of #10032— this closes the guard half. The zero-output phenomenon the card's title names first is not diagnosed here, and is measurably not what the recurrence was; see below.What was actually wrong (measured, not inherited)
Under grouped log order — the only shape CI ever writes — turbo folds each task into a collapsible
::group::, except the task it surfaces as the run's failure. That one gets a bare, red-coloured header and no::endgroup:::The guard read the
PKG:test:stream prefix and the::group::PKG:testheader, but not that third shape. So the failing package — the one the guard exists to grade — was the one package whose summary it could not attribute, and its "refuse to guess when strays are present" backstop then withheld judgement on every candidate and exited 0.Measured on run 32391924279 (
Test Core (3/3), job 96499888763, complete 31,839-line log, header at line 23907). The guard printed:OK (...)directly under a note naming the failing package as ungraded. That is #10032's own shape — a green that reads like a clearance — reproduced by the change that was meant to close it. The note even named its own cause: "the log carries a shape neither thePKG:test:prefix nor turbo's::group::PKG:testheader covers — that is the bug to fix."In the 31,839-line log, a line that is nothing but
PKG:test(no whitespace) matches 24 times: 23 are##[group]headers, and the 24th is this bare one. Zero false positives for the new rule.The changes
scripts/check-test-completeness.mjsparseSummariesunderstands the bare failing-task header; turbo's end-of-run roster (Tasks:/Failed:) closes that block, which has no::endgroup::of its own.classifyShardnever lets the stray-summary backstop excuse a package turbo named inFailed:. ⚠ Rule A cannot cost a green shard — it needs turbo to have named a failed task, which already failed the run. The worst case is an extra paragraph on an already-red run; the alternative is a clearance on the one run where the guard had something to say. When strays are present the wording says "no summary could be attributed to it", never "printed nothing".OKtoPARTIAL (… N NOT GRADED)and raises a::warning::annotation. A note in a 30k-line log is not read; that is exactly how the last one sat unnoticed under a green..github/workflows/ci.yml— the dichotomy comment claimed "green now covers both questions". That was false whenever the note fired. It now records this second blindness, what it cost, and the condition under which the dichotomy is true.Verification — the reject side, on the real artifact
The complete job log was downloaded (31,839 lines, not a tail window) and the timestamp prefix stripped to reconstruct what
$RUNNER_TEMP/test-core.logheld. Exit codes captured by redirect-then-$?, never through a pipe.git show origin/main:)OK (24 of 29 …), byte-identical to the real CI stepnever reached;OK (22 of 29 …, 2 never reached)@objectstack/example-showcase -- turbo reported this task FAILED, and no summary in this log could be attributed to itLeg 1 reproducing the real CI output byte-for-byte is what makes leg 2 mean something. Every mutation was confirmed on disk by grep-counting both the removed text and a control (showcase summary rows 2→0, types group header 1→0, types summary rows 1→1 kept, line count 31839→31836) — never by an editor's exit code.
Ablation (parser half off, Rule A half on, confirmed on disk 1→0 / 0→1): the new self-test assertion fires first and names the exact regression, so the guard refuses to run. Driving
classifySharddirectly past it shows the real unmutated log would go falsely red naming showcase. ⇒ both halves are load-bearing: Rule A alone reddens every ordinary red shard; the parser alone leaves a genuinely silent failure green. Restored viagit checkout;git status --porcelainempty.Backward compatibility: without the shard flags (the Dogfood job's invocation), stdout+stderr and exit code are byte-identical to
origin/mainon both fixtures.⚠ Two inherited premises this run falsified
Test Files 21 passed (21)/Tests 342 passed (342)) and failed onEnvironmentTeardownError: [vitest-worker]: Closing rpc while "onUserConsoleLog" was pendingfromtest/approval-resume-relation-expand.test.ts. The "could not see one line of output" reading came from a tail window, not the log. The original 2026-08-19 event remains unreproduced and undiagnosed — nothing here claims otherwise.Failed:roster does not name every failed task. With two failing tasks in one run, the roster listed one; the other failed inside an ordinary::group::. Recorded next to Rule A, since Rule A's population depends on it.turbo.jsonis untouched, so there is no overlap with #10450.scripts/partition-test-shards.mjs(#10149) is untouched. No changeset: neither file is on any package's publish surface (root package isprivate, and nofilesarray reachesscripts/or.github/).Gates:
node scripts/pm/dispatch-gates.mjswith no path args (2 paths vs merge base0c24898c0) named 9 families — all green at98731bccce, pluscheck:nul-bytes.actionlintis not installed in this container (CI-side only) — flagged, not silently skipped.Generated by Claude Code