Found while testing whether the shard partitioner's emptiness guard actually fires, as part of #10046. Filed unassigned. This is a latent gap, not a live bug — the mitigations below are real and were clearly designed in; recording it because the residual hole is the exact shape (#4690) the surrounding machinery exists to prevent, and nothing currently names it.
What was measured
scripts/partition-test-shards.mjs's shape assertion works as its header advertises. Fed deliberately broken payloads it exits 1 and names the cause:
items key removed -> exit=1
items is an object -> exit=1
But an empty document and a populated one are indistinguishable to it, by design — both exit 0:
EMPTY (count 0, items []) -> exit=0, stderr "shard 1/3: 0/0 packages, weight 0", 0 stdout lines
NON-EMPTY (count 2, items 2) -> exit=0, stderr "shard 1/3: 1/2 packages, weight 414", 1 stdout line
Zero stdout lines then short-circuits the test step in ci.yml:
if [ ! -s "$RUNNER_TEMP/shard-packages.txt" ]; thenecho "No packages on this shard — nothing to test."exit 0fi
— a green Test Core that ran nothing.
Why the partitioner is the wrong place to fix it
Zero is frequently the correct answer here, unlike #4690's scanner:
- a PR touching only
.github/, content/docs/ or scripts/ (with no declared cross-package glob hit) genuinely affects no package; - with 3 shards, any run with fewer than 3 packages legitimately leaves a shard empty.
So a blanket "red on empty" at the partitioner would be wrong. The header already states the contract correctly — zero lines means "nothing to run", NOT "no filter".
The mitigations that already exist
Worth stating so nobody re-derives them: every failing producer falls back to the full package list, never to an empty one. ci.yml has three such branches — unresolvable merge-base, a failed git diff, and a failed --union-into — each falling back to turbo ls --output=json over the whole workspace. Steps run under bash -e, so a non-zero turbo ls --affected fails the step outright. A crashing selection therefore cannot yield an empty shard.
The residual hole
What none of that covers is a producer that exits 0 with a wrong, plausible, empty answer. The concrete unchecked signal:
if ! git diff --name-only "$SCM_BASE" HEAD > "$RUNNER_TEMP/changed-files.txt" \|| ! node scripts/check-cross-package-test-inputs.mjs ... ; then
Only the exit status of that git diff is checked. Its emptiness is not. A PR whose merge-base resolved to something wrong-but-valid produces an empty changed-file list, which yields zero affected packages, which yields a green shard that tested nothing — and every log line along the way reads as a normal quiet PR.
An empty changed-files.txt on a pull_request event is not a legitimate state: a PR always differs from its merge base. That makes it a decidable signal, unlike shard emptiness itself, and it is the one place where "no packages selected" and "selection failed" can actually be told apart.
Suggested shape (deliberately not implemented here): treat an empty changed-files.txt on a pull_request event as a reason to take the existing full-package-list fallback, with a ::warning:: naming it — same posture as the merge-base fallback next to it (#6195), same reasoning: doubt costs minutes, not coverage.
⚠️ Note the shard attestation (#6082) does not cover this: it attests "shard N of 3 ran and every step passed", which is exactly what a shard that tested nothing does.
Refs: #10046 (where this was measured), #4690 ("zero is a broken scan, not a clean repo"), #6195 (the merge-base fallback whose posture this would follow), #6082 (shard attestation), #7802.
Generated by Claude Code
Found while testing whether the shard partitioner's emptiness guard actually fires, as part of #10046. Filed unassigned. This is a latent gap, not a live bug — the mitigations below are real and were clearly designed in; recording it because the residual hole is the exact shape (#4690) the surrounding machinery exists to prevent, and nothing currently names it.
What was measured
scripts/partition-test-shards.mjs's shape assertion works as its header advertises. Fed deliberately broken payloads it exits 1 and names the cause:But an empty document and a populated one are indistinguishable to it, by design — both exit 0:
Zero stdout lines then short-circuits the test step in
ci.yml:— a green
Test Corethat ran nothing.Why the partitioner is the wrong place to fix it
Zero is frequently the correct answer here, unlike #4690's scanner:
.github/,content/docs/orscripts/(with no declared cross-package glob hit) genuinely affects no package;So a blanket "red on empty" at the partitioner would be wrong. The header already states the contract correctly — zero lines means "nothing to run", NOT "no filter".
The mitigations that already exist
Worth stating so nobody re-derives them: every failing producer falls back to the full package list, never to an empty one.
ci.ymlhas three such branches — unresolvable merge-base, a failedgit diff, and a failed--union-into— each falling back toturbo ls --output=jsonover the whole workspace. Steps run underbash -e, so a non-zeroturbo ls --affectedfails the step outright. A crashing selection therefore cannot yield an empty shard.The residual hole
What none of that covers is a producer that exits 0 with a wrong, plausible, empty answer. The concrete unchecked signal:
Only the exit status of that
git diffis checked. Its emptiness is not. A PR whose merge-base resolved to something wrong-but-valid produces an empty changed-file list, which yields zero affected packages, which yields a green shard that tested nothing — and every log line along the way reads as a normal quiet PR.An empty
changed-files.txton apull_requestevent is not a legitimate state: a PR always differs from its merge base. That makes it a decidable signal, unlike shard emptiness itself, and it is the one place where "no packages selected" and "selection failed" can actually be told apart.Suggested shape (deliberately not implemented here): treat an empty
changed-files.txton apull_requestevent as a reason to take the existing full-package-list fallback, with a::warning::naming it — same posture as the merge-base fallback next to it (#6195), same reasoning: doubt costs minutes, not coverage.Refs: #10046 (where this was measured), #4690 ("zero is a broken scan, not a clean repo"), #6195 (the merge-base fallback whose posture this would follow), #6082 (shard attestation), #7802.
Generated by Claude Code