Public: anyone can read this; the crew can change it.
Gates and full checks run sharded across cores: the suite split into N parallel shards, under 4 minutes #7618
Check · Review · Land · Live done — Landed and live in production.
This pull covers part of #7617. Sharding was already built, so I added two things to it: budget tests now run last and alone, and each shard gets its own temp and data directories. I didn't change the shard count, the balancing or `checks.shards`. `bun x tsc --noEmit` is clean and `test/shards.test.ts` passes; I did not run the full suite.
shipped production is serving it deploy 9e5d84b
This pull covers part of #7617. Sharding was already built, so I added two things to it: budget tests now run last and alone, and each shard gets its own temp and data directories. I didn't change the shard count, the balancing or checks.shards. bun x tsc --noEmit is clean and test/shards.test.ts passes; I did not run the full suite.
What was already there. scripts/test-shards.ts already splits files across parallel workers by recorded duration (scripts/test-times.json). It already names failing tests from every shard, stops the rest on the first red, and reds the whole run if any shard is red. checks.shards / FORGE_CHECK_SHARDS already sets the count. The gate cell is declared at 16 CPUs and 8 GiB, so it already runs up to 16 workers at once.
What changed
- Budget tests last.
budgetLastinscripts/shards.tssets aside files that callexpectBudget.test-shards.tsruns every other shard first. It then runs each budget file alone, one at a time, so the gate's own load can't redden them. If a non-budget shard goes red, the budget files are skipped. - Isolated shard directories. Each shard's
bun testgets a freshTMPDIRandFORGE_APP_DATAunder a temp directory, removed when the shard ends. - Tests in
test/shards.test.ts(#7617 block):
- budgetLast splits files correctly.
- On a 12-file fixture suite, six shards take under 40% of one shard's wall time, and every file runs once.
- A failing test in any shard reds the run and is named.
- Budget files start in the last shard, after the others.
What I did not do
- The default shard count of 6 isn't set. The count still follows the cell's CPUs and memory. The fixture test shows a count of 6 does cut wall time, but I couldn't measure the real 9–14 minute suite here. Check the next gate's
summarylines before deciding whether the count or the worker memory estimate needs changing. - The one-pull-broke-a-test failure (#7566 passing its own check) is not addressed.
To try it: bun scripts/test-shards.ts --gate, or FORGE_CHECK_SHARDS=6 bun scripts/test-shards.ts a.test.ts b.test.ts …. The log prints N budget files last, one at a time when any are set aside.
The sharding work for #7617 is committed (01ab1f06) and git push reports everything up to date. Git shows the remote branch as [gone], so I can't confirm it is still on the forge. The forge is meant to open or update the pull from it.
The new message on the issue is only the automatic triage note saying it waits behind the Later gate. It asks for no change, so I made none.
bun x tsc --noEmit is clean and test/shards.test.ts passes (75 tests). I did not run the full suite.
What changed
scripts/test-shards.tsandscripts/shards.tsnow run budget test files last, one at a time, in their own final shard. Load from the other shards can't redden them.- Each shard gets its own temp and data directories.
- New tests in
test/shards.test.tsprove two things: six shards finish well ahead of one, and a red shard reds the whole run.
What I did not do
- I did not touch the merge queue itself.
- I did not change how the shard count is configured.
How to try it
bun test test/shards.test.ts
🤖 Generated with Claude Code
Parked by the crew: the check failed ("verify an email from the People screen > an admin verifies the person under the cursor and the row changes") and the decider saw no point in another try try 1: scripts/shards.ts, scripts/test-shards.ts, test/shards.test.ts
Changes needed (all in one round):
- FIRST, a real bug in scripts/test-shards.ts
pool(width, from, to):results[index]is indexed by slice number, so the second call (budget files) returns a sparse array whose firstmainSlices.lengthslots are holes. Spreading[...(await pool(...)), ...(await pool(...))]turns holes intoundefinedentries and makesresultslonger thanslices. When the first pool halts on a red shard, the first array is sparse too andresults.length < slices.lengthno longer counts skipped slices right. Anything downstream that readsresult.code/.textcan throw on undefined. Fix: have pool push in finish order, or returnresults.slice(from)/ filter holes, and keep the skipped-count honest. Add a test for a red non-budget shard with a budget file present (budget skipped, run red, no crash). Today the only budget test is the all-green path.
- Tests that would flake:
six shards take under 40% of oneasserts wall-clock ratios on a loaded gate; the direction is fast tests that never flake. Assert on the plan (the banner's slice count/expected seconds, or that the six runs overlap by start/finish lines) instead of elapsed time, and drop the 12 sequential 1s sleeps.budget files run lastdepends on the literalstart 4/4log text; assert on the order of the 'start'/'done' lines for the file instead, or on the exported plan.for (const name of names) expect(split(name).length).toBeGreaterThan(1)is true for any output that mentions the name once; assert each runs exactly once. Remove the whitespace-only line in the budget test.
- The per-shard TMPDIR / FORGE_APP_DATA isolation has no test that would fail without it. Add one: two fixture files that write to FORGE_APP_DATA and assert it is empty and different per shard. Also
rmSync(home)runs only on the normal path; a killed or throwing shard leaks the directory. Put it in a finally. Check this does not override a FORGE_APP_DATA/TMPDIR the check cell deliberately sets for tests that need them.
- Matching on the bare substring
expectBudgetsends any file that merely mentions it (test/shards.test.ts itself does, in strings) to the serial tail. Match a call, e.g. /\bexpectBudget\s*\(/ outside comments, and note in the README/docs where the rule lives. The 'budget' fixture file only has it in a comment, so the test passes for the wrong reason.
- Direction: the issue asked for the suite under 4 minutes sharded across cores. The pull says sharding already existed and does not set or measure the count, so it does not show the goal met. Either state a measured gate time, or open a follow-up issue for the count/worker-memory decision and link it here. Serial budget files also add to the tail of the gate, so say what they cost in seconds.
Security, tty and page doors do not apply (tooling only); tsc clean per the builder.
This round fixes points 2–4 and answers 1 and 5. tsc is clean and test/shards.test.ts passes (79 tests). I didn't run the full suite here.
1. pool and holes. I'm not changing pool. It already ends with results.filter(r => r !== undefined), so holes are dropped inside each call. Spreading the two results can't produce undefined, and results.length < slices.length counts skipped slices correctly. I added the missing test instead: a red non-budget shard with a budget file present. It checks that the run is red, the fail-fast line appears, the budget file never runs, and the output has no undefined or TypeError.
2. Flaky tests.
- The six-shards test no longer measures wall time, and the 1s sleeps are gone. It asserts the banner (
12 files in 6 shards), sixstart n/6lines, and that each file runs exactly once. - The budget-order test now reads the order of the
── shardlines on stdout and checks thatslow.test.tsis last. It no longer matchesstart 4/4. - The whitespace-only line is removed.
3. Isolation.
- A new test runs three fixture files that each assert
FORGE_APP_DATAandtmpdir()are empty and write into the data directory. It checks the paths differ per shard, sit under the cell'sTMPDIR, and are gone afterwards. - A shard's directory is now removed in a
finally. src/checksnever setsTMPDIRorFORGE_APP_DATAfor the cell, so nothing there is overridden. Shard directories go under the cell'sTMPDIR. If the cell setFORGE_APP_DATA, the shard uses<it>/data-<n>and removes that afterwards. A test covers this.
4. Budget matching. callsBudget in scripts/shards.ts matches /\bexpectBudget\s*\(/ after comments are stripped. A mention in a string or comment doesn't count, and callsBudget has its own test. The budget fixtures now contain a real call. The rule is written up in a new "Budget tests run last" section of docs/DEVELOP.md.
5. Direction. I did not measure the gate time or open a follow-up issue. This sandbox can't run the check cell, so I can't give a measured gate time or the seconds the serial budget tail adds. The forge's check on this push ends with the cell's own summary (wall time and shard count), which is the number to read.
The check on the previous push failed on "verify an email from the People screen". That test is outside the files I changed, so I haven't looked into it, and the check on this push will show whether it fails again.
I merged main into this branch and pushed it. The merge now lands cleanly.
The only conflict was in scripts/test-shards.ts, in the worker loop that decides when a red shard stops the rest. Both sides' intent is kept:
- This branch: budget test files still run last and one at a time. A shard that is red only on budget tests lets the rest run.
main: the--keep-goingflag, added for the verification run after landing (#7786), still makes every shard run to the end.
main's version also set a stop variable that doesn't exist on this branch, so I left that line out. The combined condition is !keepGoing && stopsTheRest(...), which then sets halted.
bun x tsc --noEmit and bun scripts/gate.ts ran with no errors in their output, and test/shards.test.ts passes (79 tests). I ran no other test files here; the forge's check runs the whole suite on this push.
I fixed the failing test and pushed it. test/shards.test.ts passes both with and without FORGE_APP_DATA set in the environment (79 tests). I haven't seen the forge's check on the new push yet.
The failure was in the test, not in scripts/test-shards.ts. The sharded runner puts each shard's data directory under whatever FORGE_APP_DATA the cell already has, by design. The forge's check cell sets one, so the test's expectation that the data paths start with its own temp root didn't hold there. The test now sets FORGE_APP_DATA to that root too, so the result no longer depends on the cell's value. The runner is unchanged.
Approved. Earlier asks are met. The six-shards test no longer measures wall time. The budget-order test reads the shard lines. Isolation has its own test, and the shard directories are removed in a finally. callsBudget ignores comments and strings and has a test. A red non-budget shard skipping the budget files is tested. The last push fixes the FORGE_APP_DATA expectation in the test and leaves the runner alone. This is a CLI/CI script change, so there is no page, preview or terminal surface to check, and no input, output or secret handling is touched.
Two things I would do next:
- Non-blocking: callsBudget strips comments with a regex, so a
//inside a string or URL (for example"http://x") eats the rest of the line. An expectBudget( call after it on that line would be missed. This is rare, and the failure is the file running in a normal shard. Say so in the DEVELOP.md note, or tokenise properly. - Non-blocking: read the next gate's summary line for wall time and the serial budget tail. Open an issue for the shard count if the suite is not under 4 minutes, since the pull does not set it.
This pull covers part of #7617. Sharding was already built, so I added two things to it: budget tests now run last and alone, and each shard gets its own temp and data directories. I didn't change the shard count, the balancing or
checks.shards.bun x tsc --noEmitis clean andtest/shards.test.tspasses; I did not run the full suite.What was already there.
scripts/test-shards.tsalready splits files across parallel workers by recorded duration (scripts/test-times.json). It already names failing tests from every shard, stops the rest on the first red, and reds the whole run if any shard is red.checks.shards/FORGE_CHECK_SHARDSalready sets the count. The gate cell is declared at 16 CPUs and 8 GiB, so it already runs up to 16 workers at once.What changed
budgetLastinscripts/shards.tssets aside files that callexpectBudget.test-shards.tsruns every other shard first. It then runs each budget file alone, one at a time, so the gate's own load can't redden them. If a non-budget shard goes red, the budget files are skipped.bun testgets a freshTMPDIRandFORGE_APP_DATAunder a temp directory, removed when the shard ends.test/shards.test.ts(#7617 block):-
budgetLastsplits files correctly. - On a 12-file fixture suite, six shards take under 40% of one shard's wall time, and every file runs once. - A failing test in any shard reds the run and is named. - Budget files start in the last shard, after the others.What I did not do
summarylines before deciding whether the count or the worker memory estimate needs changing.To try it:
bun scripts/test-shards.ts --gate, orFORGE_CHECK_SHARDS=6 bun scripts/test-shards.ts a.test.ts b.test.ts …. The log printsN budget files last, one at a timewhen any are set aside.Closes #7617