ci(coverage): keep a red coverage shard's failing test names readable - #9243
ci(coverage): keep a red coverage shard's failing test names readable#9243baozhoutao wants to merge 1 commit into
Conversation
`ci.yml`'s `test-coverage` legs ran `pnpm test:coverage --reporter=blob --shard=N/4`. A CLI `--reporter` REPLACES the reporter set rather than adding to it, and the set it replaced was exactly `default` plus — under `GITHUB_ACTIONS=true` — `github-actions`. A red shard's log therefore ended at `blob report written to …` with no failing test name, no assertion text and no timeout message, and the shard job's only annotation was the generic `Process completed with exit code 1.` The failures survived solely inside the `coverage-blob-N` artifact, which is download-only. The shard legs now pass `--reporter=blob --reporter=default --reporter=github-actions`. Additive on purpose: the blob stays first and the threshold overrides are untouched, because the merge job is what enforces the thresholds and it has nothing to read without the blob. Pinned in both directions by `scripts/__tests__/coverage-shard-reporter-readability.test.ts`, and the pipeline page's `test-coverage` row is updated to the command it now runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
✅ ACCEPT — reviewed against the DIFF. Probe below, ⭐ with expected values I COUNTED, posted BEFORE the merge.
§0 — what I checked myself1. The functional change is one line, and the overriding constraint holds. ⭐ The blob stays, and stays first. I resolved the folded scalar at the head and read what the runner will actually execute — 2. 3. The pin is read through the YAML parser, and that is the right call — not a stylistic one. The step is a folded block scalar ( 4. Both directions are pinned, which is what this card needs: dropping the readable reporters restores objectui#9177; dropping the blob trades an unreadable failure for an unevaluated floor. ⛔ Neither is caught by CI going green. 5. ⭐ The docs row lost a claim this card refutes. The old prose said the artifact "is what makes a failing coverage run diagnosable at all" — the exact sentence objectui#9177 disproves. It is gone, replaced by what the flags actually do. ⛔ That was not asked for and it is the right call: leaving it would have shipped a fix beside prose asserting the fix was unnecessary.
⛔ Not weakened: the diff only adds. No threshold moved, no ratchet raised, no floor lowered, no test skipped. The probe — ⛔ written and posted BEFORE the merge, run scoped to
|
| string | file | before → after |
|---|---|---|
--reporter=default |
ci.yml |
0 → 2 |
--reporter=github-actions |
ci.yml |
0 → 2 |
coverage-shard-reporter-readability |
ci.yml |
0 → 1 |
L3 — ⛔ what must STAY, as INVARIANCE rather than a fixed count (the merge parent may not be 91facaef6f, so a literal N could move for innocent reasons):
--reporter=blobinci.yml— unchanged across the merge (it was 2 at review: the run step and the comment). ⭐ A drop to 1 or 0 is the "same defect wearing the other hat" and must fail this leg.coverage.thresholds.lines=0— unchanged (1 at review). Same forfunctions/branches/statements.test-coverage:job key — unchanged (1 at review).- ⭐ The resolved run line: print the folded scalar from the merge commit and show
--reporter=bloboccurs in it and that all four--coverage.thresholds.*=0are still on it. A count over the whole file cannot distinguish "in the step" from "in the comment"; this leg must read the step.
L4 — the decide steps are untouched, two-sided with a control that HITS. In ci.yml, markdown-test-inputs and the paths-ignore/decide-step regions: unchanged across the merge. Run tests with coverage is present on both sides — so an "unchanged" reading is about those regions, ⛔ not a grep that matched nothing.
L5 — the pin is new and non-vacuous. scripts/__tests__/coverage-shard-reporter-readability.test.ts — ABSENT on the base, present on the merge (verified: git cat-file -e fails on 91facaef6f, succeeds on the head). It must contain both --reporter=blob and both readable reporters as constants, and the two anti-vacuity tests.
L6 — the docs row. In content/docs/guide/ci-cd-pipeline.md: is what makes a failing coverage run diagnosable at all 1 → 0, and coverage-shard-reporter-readability.test.ts 0 → 1. ⭐ Two-sided: the false claim leaves and the replacement arrives.
L7 — blobs for all four paths at the merge commit.
L8 — changeset frontmatter EMPTY ⇒ no package released.
mergeable_state=behind — main advanced while the PR was opened, and nothing in that range touches the test-coverage job. The merge queue builds on top of main, so this resolves at enqueue; ⛔ if the queue refuses, the fix is to bring the base in, ⛔ never to force.
Generated by Claude Code
Fixes #9177
The defect
ci.yml'stest-coveragelegs ranpnpm test:coverage --reporter=blob --shard=N/4. A CLI--reporterreplaces the reporter set rather than adding to it, so a red shard's log ended atblob report written to …— no failing test name, no assertion text, no timeout message — and the shard job's only annotation was the genericProcess completed with exit code 1.The failures survived solely inside the download-onlycoverage-blob-Nartifact. objectui#8545 priced that: two episodes in which one test file heldmain's coverage gate unevaluated for 84 and 87 consecutive pushes, both found by a person reading a job log by hand, days later.The measurement triage asked for FIRST
The card's shape 1 carried an explicitly unmeasured premise — whether vitest 4.1.10 accepts both reporters and whether the annotations survive the sharding. Measured before any line was written.
Version, read from the tree, not from the card.
pnpm-lock.yamlcarriesvitest@4.1.10:,node_modules/vitest/package.jsonreports4.1.10, and the CLI banner printsvitest/4.1.10 linux-x64 node-v22.22.2. The card was right.Why the set was empty — the enforcing line, quoted. In
node_modules/vitest/dist/chunks/coverage.DM_a_rWm.js:vitest.config.mtssets noreporters(grep: zero matched lines), so the CLI flag was the whole set and the branch that would have addeddefault+github-actionsnever ran. The CLI side is a de-duplicated list, which is why several--reporterflags coexist.Coexistence, measured. One throwaway test with a failing assertion and a deliberate 50 ms timeout, run under
--shard=N/4with all three reporters,GITHUB_ACTIONS=true. Shard 4/4 held it, exit 1, and its log carried:⇒ (a) is satisfied and the annotation leg is a bonus the card did not expect:
github-actionsnames the failing test in a check-run annotation, which is the only form of this an API reader gets without downloading an artifact. Shards 1..3 stayed green and each still wrote its ownblob-N-4.json.(b) the blob, the merge and the thresholds — untouched and re-verified
The four blobs that the new three-reporter invocation wrote were merged with the CI merge command, twice, same blob set:
lines: 40)ERROR: Coverage for lines (36.12%) does not meet global threshold (40%)--coverage.thresholds.*=0Lines : 36.12% ( 233/645 )coverage/coverage-final.json(175 KB) and the text table were produced in both. The control that must HIT does:does not meet global thresholdmatches twice in leg A and zero times in leg B, over the same merged report. ⇒ the merged report is still produced and the thresholds still evaluate over it.The change
Additive to the shard leg's reporters, and nothing else:
Read back through the YAML parser rather than off the source (the step is a folded scalar), the runner now executes exactly one line with the four threshold overrides and the shard expression intact.
⛔ The decide steps, the path filters and the shard trigger are untouched — objectui#9142, objectui#9241 and PR objectui#9141 live in other regions of this file and none of their lines move here.
The pin
scripts/__tests__/coverage-shard-reporter-readability.test.tsreads the step through the YAML parser and fails in both directions, because the defect has two shapes. Ablated from the committed state, each leg proved on disk before it ran and restored by blob hash:--reporter=default/--reporter=github-actions× passes a reporter that prints failing test names alongside the blob--reporter=blob× keeps the blob reporter the merge job depends onBoth restored to the exact HEAD blob
5dab3a560b…withgit diff HEADempty. The second direction is the overriding constraint made mechanical: buying log readability by dropping the blob would trade an unreadable failure for an unevaluated coverage floor.Does the docs command-parity rule apply?
At command granularity, no — at prose granularity, yes, and the row is updated.
ci-cd-pipeline-doc.test.tscompares the two sides with:whose capture stops at the first space, so
pnpm test:coverageis all it ever sees and every flag on that line could be deleted with that file still green. That is precisely why the new pin exists. The page'stest-coveragerow nevertheless quotes the command verbatim, and its old sentence claimed the artifact "is what makes a failing coverage run diagnosable at all" — the claim this card refutes. Both are corrected in the same PR.Verification
vitest run --project unit scripts/__tests__— 149 passed | 2 skipped (151), 4416 tests passed, exit 0, at7d61508deb.check:control-bytesexit 0 (scanned 7429 tracked text file(s)),check:new-line-citationsexit 0 (0 new citation(s)),check:changeset-claimsexit 0,check:action-ref-conventionexit 0 (116 action reference(s) in 37 workflow file(s) … control actions/checkout present: true),check:action-forward-parityexit 0,changeset:checkexit 0,check-changeset-presence.mjsexit 0.check-governed-queue-guard.mjs --testover all four paths:NOT GOVERNED — 4 path(s) checked against 5 governed surface(s); none matched.--project unitand a positional filter to keep them cheap. The CI leg runs the whole suite; reporter resolution does not read the project filter, but this PR does not claim a full-suite run was performed locally.🤖 Generated with Claude Code
https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
Generated by Claude Code