Found while implementing #6195 (PR against .github/workflows/ci.yml). Out of that issue's scope, so filed separately and unassigned per Prime Directive #10.
The defect
scripts/check-shard-attestation.mjs, the static drift guard, decides whether a job is an aggregate gate with a two-substring test over the job's joined run: text:
const text = runTextOf(job);
if (!text.includes(SCRIPT_BASENAME) || !text.includes('--verify')) continue;
SCRIPT_BASENAME is check-shard-attestation.mjs. The problem is that every attesting shard job already contains that string — it is the job's own --emit step at the bottom (node scripts/check-shard-attestation.mjs --emit ...). So for a shard job the sole discriminator is the bare substring for the script's own verify flag, appearing anywhere in any run: step of that job — including inside a shell comment, since comments are part of run:.
How it fires (measured, not hypothesised)
Adding an ordinary git rev-parse strict-existence check to the Compute this shard's package set step of ci.yml — the idiomatic spelling for "does this ref resolve" — flipped job test from attesting shard job to aggregate gate, and the guard went red with two complaints that name nothing to do with the edit:
✗ check-shard-attestation — 2 problem(s) in .github/workflows/ci.yml
• job 'test' is an aggregate gate but its job-level if: is "${{ !cancelled() && needs.filter.outputs.core != 'false' }}", not always() — a gate that skips publishes no required context (#3622).
• gate 'test' never downloads the shard attestations it claims to count.
Baseline check, both directions, same tree: origin/main's ci.yml exits 0; the same file plus that one flag exits 1. Removing only the flag restores green.
The diagnostic is actively misleading. Neither message mentions the classifier, the flag, or the step that was actually edited, and both describe the shard job as something it is not. The natural response to "job 'test' is an aggregate gate but its if: is not always()" is to go change the shard job's if: — which would be wrong, and would break #3622/#4928.
There is a second-order trap on top: documenting the collision in a comment re-triggers it, because the comment lives in run:. That cost a second lap here — the same shape as #4890, where a raw NUL landed in SKILL.md while writing the no-raw-NUL rule.
Why this is worth fixing rather than routing around
PR for #6195 routes around it (git cat-file -e, with the reason written next to it), so nothing is on fire. But the trap stays armed for the whole --verify-shaped family: git rev-parse --verify, git show-ref --verify, gpg --verify, npm audit --verify, or any prose mentioning the flag. The next person to write one in a shard job gets a red naming the wrong job and the wrong property, on a workflow file where the gate's whole purpose is to be trustworthy.
Suggested direction (not a ruling — triage's call)
Require adjacency instead of co-occurrence, so the classifier keys on the actual invocation rather than on two independently-occurring substrings, e.g. matching check-shard-attestation.mjs followed by the verify flag in the same command (a regex over SCRIPT_BASENAME\s+.*--verify on a per-step basis, or per-line). The --emit side already works this way in spirit — emit.run.includes(SCRIPT_BASENAME) && emit.run.includes('--emit') is scoped to a single step's run, not to the job's joined text, which is exactly why the emit side does not have this bug.
Whatever shape is chosen, the self-test should gain a fixture with a shard job whose steps contain the verify flag for an unrelated reason, asserting it is still classified as an attester. Without that fixture the fix can regress silently.
Scope note
Not fixed in the #6195 PR: that PR's dispatch ruling scopes it to the Compute this shard's package set step of ci.yml, and this is a defect in scripts/check-shard-attestation.mjs.
Found while implementing #6195 (PR against
.github/workflows/ci.yml). Out of that issue's scope, so filed separately and unassigned per Prime Directive #10.The defect
scripts/check-shard-attestation.mjs, the static drift guard, decides whether a job is an aggregate gate with a two-substring test over the job's joinedrun:text:SCRIPT_BASENAMEischeck-shard-attestation.mjs. The problem is that every attesting shard job already contains that string — it is the job's own--emitstep at the bottom (node scripts/check-shard-attestation.mjs --emit ...). So for a shard job the sole discriminator is the bare substring for the script's own verify flag, appearing anywhere in anyrun:step of that job — including inside a shell comment, since comments are part ofrun:.How it fires (measured, not hypothesised)
Adding an ordinary
git rev-parsestrict-existence check to theCompute this shard's package setstep ofci.yml— the idiomatic spelling for "does this ref resolve" — flipped jobtestfrom attesting shard job to aggregate gate, and the guard went red with two complaints that name nothing to do with the edit:Baseline check, both directions, same tree:
origin/main'sci.ymlexits 0; the same file plus that one flag exits 1. Removing only the flag restores green.The diagnostic is actively misleading. Neither message mentions the classifier, the flag, or the step that was actually edited, and both describe the shard job as something it is not. The natural response to "job 'test' is an aggregate gate but its
if:is not always()" is to go change the shard job'sif:— which would be wrong, and would break #3622/#4928.There is a second-order trap on top: documenting the collision in a comment re-triggers it, because the comment lives in
run:. That cost a second lap here — the same shape as #4890, where a raw NUL landed inSKILL.mdwhile writing the no-raw-NUL rule.Why this is worth fixing rather than routing around
PR for #6195 routes around it (
git cat-file -e, with the reason written next to it), so nothing is on fire. But the trap stays armed for the whole--verify-shaped family:git rev-parse --verify,git show-ref --verify,gpg --verify,npm audit --verify, or any prose mentioning the flag. The next person to write one in a shard job gets a red naming the wrong job and the wrong property, on a workflow file where the gate's whole purpose is to be trustworthy.Suggested direction (not a ruling — triage's call)
Require adjacency instead of co-occurrence, so the classifier keys on the actual invocation rather than on two independently-occurring substrings, e.g. matching
check-shard-attestation.mjsfollowed by the verify flag in the same command (a regex overSCRIPT_BASENAME\s+.*--verifyon a per-step basis, or per-line). The--emitside already works this way in spirit —emit.run.includes(SCRIPT_BASENAME) && emit.run.includes('--emit')is scoped to a single step'srun, not to the job's joined text, which is exactly why the emit side does not have this bug.Whatever shape is chosen, the self-test should gain a fixture with a shard job whose steps contain the verify flag for an unrelated reason, asserting it is still classified as an attester. Without that fixture the fix can regress silently.
Scope note
Not fixed in the #6195 PR: that PR's dispatch ruling scopes it to the
Compute this shard's package setstep ofci.yml, and this is a defect inscripts/check-shard-attestation.mjs.