Skip to content

fix(#934): make the required E2E aggregate consult its own shards - #935

Merged
TortoiseWolfe merged 2 commits into
mainfrom
fix/934-aggregate-consults-shards
Aug 22, 2026
Merged

fix(#934): make the required E2E aggregate consult its own shards#935
TortoiseWolfe merged 2 commits into
mainfrom
fix/934-aggregate-consults-shards

Conversation

@TortoiseWolfe

Copy link
Copy Markdown
Owner

Closes #934. The finding, the evidence and the blast radius live in that issue body.

What merged that should not have

E2E (local) result — a required check — concluded success on
run 32560171118,
whose shard matrix concluded failure. Three shards had each executed zero tests. PR #928
merged on that green tick and 95e4ff4e is on main.

The aggregate reconstructed a verdict from uploaded artifacts and never asked the shards.
Its own log:

shards reporting: 24/24
  passed   2035   baseline 1807
  failed      0
Gate passed: 24/24 shards, 0 failures, above the floor.

Upload results is if: always(), so a failing shard still uploads a valid results.json
describing nothing; a shard with no tests has no failing tests; and Playwright redistributed
the work, so the total rose to 2035 and cleared the 1700 floor. Every condition passed
because of the failure, not despite it.

Two commits, and the order matters

da2e725c makes color-contrast.spec.ts shardable, so no shard runs zero tests
d322064b makes the aggregate read needs.e2e-local.result

They are in one PR because the gate fix cannot land on a main whose shards are already
red
main is in exactly that state right now, running zero tests in gen 2/6 on all
three browsers. Split across two PRs, the second would correctly block itself on the first.

#930 is open against the same main and will hit the same starved shard; it should pick
this up after merge.

Verified by mutation, not by reading

Reading a guard does not find this class — both green-through-deletion cases in this repo
were invisible on review and obvious in one mutation run.

step deleted         pass 4  fail 3
exit 1 -> echo       pass 6  fail 1
!= success inverted  pass 6  fail 1
step ungated         pass 6  fail 1
restored             pass 7  fail 0

The step's shell driven directly: success → rc=0; failure, cancelled, skipped → rc=1.

test:scripts 546 passed · type-check clean · lint clean · pre-push gate green.

What is deliberately unchanged

The part worth keeping after this merges

Every anti-vacuity guard in this lane runs if: always() after the tests, so none of their
verdicts reach the artifacts — including check-zero-assertions.mjs (#861), whose entire
subject is tests that pass while measuring nothing. Those show as expected in
results.json, so the totals count them as passed. The guards built to stop vacuous green
were invisible to the check that decides whether green means anything.

🤖 Generated with Claude Code

TurtleWolfe and others added 2 commits August 22, 2026 04:50
CI reported chromium/firefox/webkit `gen 2/6` failing on this branch, with the
zero-assertion gate saying "this shard executed 0 tests (0 skipped) — a green
tick here would be meaningless" (#861). The gate was right and it was my change.

CI sets `fullyParallel: false` (playwright.config.ts:78) so specs sharing the
PRIMARY/TERTIARY fixture users run serially. Under that setting Playwright shards
by FILE — a file is indivisible — and color-contrast.spec.ts is 142 of
chromium-gen's 668 tests. Adding the third house theme pushed it past a balancing
threshold and left shard 2 with nothing.

Measured against the parent commit, same command:

  shard   baseline   with forge   with this fix
  1/6        184        232           113
  2/6         39          0           112
  3/6         89        108           116
  4/6        105        112           112

The sweep authenticates nothing and shares no fixture user, so its cases are safe
to schedule individually. `test.describe.configure({ mode: 'parallel' })` makes
them distributable across shards without touching `workers`, which stays 1 in CI
for the specs that genuinely need it. Identical distribution on all three
browsers, and better balanced than the baseline it replaces — 39 in one shard was
already a symptom.

The first diagnosis was wrong and worth recording: three shards failing across
three browsers looked like a real forge contrast violation, which is what the PR
body warns could happen. It was not. The annotation named the cause exactly, and
reading it beat reasoning about it.

type-check, lint clean; theme guards 14 passed.
`E2E (local) result` concluded `success` on run 32560171118, whose shard matrix
concluded `failure`. Branch protection reads the named context rather than the
run, so PR #928 merged with chromium/firefox/webkit `gen 2/6` red, and 95e4ff4
is on main.

The aggregate reconstructed a verdict from uploaded artifacts and never asked the
shards. Its own log shows why every condition still passed:

    shards reporting: 24/24
      passed   2035   baseline 1807
      failed      0
    Gate passed: 24/24 shards, 0 failures, above the floor.

The three shards had each executed ZERO tests. `Upload results` is `if: always()`,
so a failing shard still uploads a valid results.json describing nothing; a shard
with no tests has no failing tests; and Playwright redistributed the work, so the
total rose to 2035 and cleared the 1700 floor. The zero-test detector was right and
was the only thing that noticed.

The blast radius is wider than that detector. Every anti-vacuity guard in this lane
runs `if: always()` after the tests, so no verdict of theirs reaches the artifacts —
including check-zero-assertions.mjs (#861), whose subject is tests that pass while
measuring nothing. Those are `expected` in results.json, so the totals count them as
passed. The guards built to stop vacuous green were invisible to the check that
decides whether green means anything.

Fix: one step reading `needs.e2e-local.result`. A STEP, not a job-level `if:`, for
the reason every other condition here is a step — branch protection is never
satisfied by a `skipped` job, so it must run and pass on a docs-only PR, and it is
gated on `changes` so it does. Placed last, so the totals print first and a future
reader sees the numbers that looked fine before the verdict that did not. The
totals stay; they answer a different question and they caught #732.

Verified by mutation rather than by reading, which is the only thing that finds
this class:

    step deleted        pass 4  fail 3
    exit 1 -> echo      pass 6  fail 1
    != success inverted pass 6  fail 1
    step ungated        pass 6  fail 1
    restored            pass 7  fail 0

and the step's shell driven directly: success -> rc=0; failure, cancelled and
skipped -> rc=1.

The guard strips comments before matching. The workflow now carries a comment block
naming these very symbols, and a raw-text match would pass with the code deleted —
which has happened here four times, twice to guards written to catch this class.

test:scripts 546 passed; type-check and lint clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@TortoiseWolfe
TortoiseWolfe merged commit 49a03bd into main Aug 22, 2026
49 of 50 checks passed
@TortoiseWolfe
TortoiseWolfe deleted the fix/934-aggregate-consults-shards branch August 22, 2026 11:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

E2E (local) result reports pass while its own shards fail — the required gate cannot see them

2 participants