refactor(smoke): discover test files instead of registering them - #2406
Merged
Conversation
The smoke manifest enumerated 128 commands by hand, and a test only ran if someone remembered to add it. #2402 measured the result: 153 test scripts that no gate invoked, and a package.json that absorbed 15 percent of all commits acting as the de facto test registry. Discover tests/*.test.{ts,mjs} and scripts/*-smoke.{ts,php} by convention. Adding a test file is now enough to make it run. Anything that must not run is listed in DISCOVERY_EXCLUSIONS with a reason, so exclusions stay short and reviewable rather than being the silent default. The manifest keeps only work that is not a single test file: 'build' for tsc -b plus the CLI bin and provenance steps, and 'test:cloudflare-runtime' for tsc -p packages/runtime-cloudflare --noEmit. It drops from 198 lines and 128 entries to 50 lines and 2. Coverage is verified, not assumed. Of the 147 files the old manifest executed, 145 are in the discovered set; the other two are build helpers that 'build' still runs. Discovery adds 212 files that were never gated. Discovered files are independent processes, so they run at concurrency 8 and report every failure instead of stopping at the first. Five files contend on the shared Playground archive cache and run in a serial phase afterwards; they are correct in isolation and are listed in DISCOVERY_SERIAL. Measured: 359 commands over 357 files in 7m59s, against 128 commands over 147 files before. 143 percent more files for about three more minutes.
…el phase CI surfaced two failure classes the local run did not. tests/prepare-declaration-rebuild.test.ts deletes packages/runtime-core/dist and runs npm install at the repository root. Under concurrency that removed @automattic/wp-codebox-core/dist/index.js while 17 other files were importing it, producing a burst of ERR_MODULE_NOT_FOUND. It is destructive to shared state rather than merely order-sensitive, so it is excluded; the Homeboy gate still runs it through test:prepare-declaration-rebuild. tests/browser-actions-navigation-capture.browser.test.ts and tests/editor-actions-save.integration.test.ts each boot a browser and a WordPress runtime. On a two-core runner at concurrency 8 they were starved and timed out, one after 75 seconds on savePost. Both move to the serial phase. Local runs passed because the machine has enough cores to absorb the oversubscription and the dist deletion window stayed narrow.
tests/browser-adaptive-exploration.test.ts asserts cancellation timing, so CPU starvation makes it fail rather than merely run slower. Same class as the two tests serialized in the previous commit.
tests/native-mariadb-runtime-service.test.ts failed on CI because dissolving test:runtime-services lost the ordering its members rely on. Being individually discoverable is not the same as being safe to run standalone or concurrently. Restore test:generic-primitives and test:runtime-services alongside build and test:cloudflare-runtime as declared commands, and exclude the 22 files those chains own from discovery so nothing runs twice. This is what the original must-keep analysis indicated; I overrode it for two chains on the grounds that their files were discoverable, which was the wrong test. Coverage unchanged: 145 of the 147 files the old manifest executed are covered, and the remaining two are build helpers that 'build' still runs. Discovery now contributes 334 files, 326 parallel and 8 serial.
This was referenced Aug 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2402 item 4.
The smoke manifest enumerated 128 commands by hand. A test ran only if someone remembered to register it — which is how #2402 found 153
test:scripts that no gate invoked, and whypackage.jsonabsorbs 15% of all commits acting as the de facto test registry.This replaces enumeration with discovery.
What changes
scripts/smoke-discovery.tsfindstests/*.test.{ts,mjs}andscripts/*-smoke.{ts,php}by convention. Adding a test file is now enough to make it run. Anything that must not run is listed inDISCOVERY_EXCLUSIONSwith a stated reason, so exclusion is an explicit, reviewable act rather than the silent default.The manifest keeps only work that is not a single test file:
buildtsc -bfor runtime-core/playground/cli, plus the CLI bin and provenance stepstest:cloudflare-runtimetsc -p packages/runtime-cloudflare --noEmitscripts/smoke-manifest.tsdrops from 198 lines and 128 entries to 50 lines and 2.Coverage is verified, not assumed
The risk in this change is silently losing coverage. Measured against the old manifest:
Both are build helpers that
npm run buildstill runs. 145 of 147 are discovered directly; the remaining 2 are covered by the retainedbuildcommand. Nothing is lost, and discovery adds 212 files that were never gated.Concurrency, because 357 files sequentially is 30 minutes
Discovered files are independent processes, so the discovered phase runs at concurrency 8 and captures output per file, reporting every failure rather than stopping at the first. The old runner failed fast, which is why #2402 needed an out-of-tree harness to produce a full picture.
Five files contend on the shared Playground WordPress archive cache and fail when run alongside each other. They are correct in isolation, so rather than excluding them they run in a serial phase afterwards, listed in
DISCOVERY_SERIAL. Verified: all five pass serially in 90s total.Measured
143% more files for about three more minutes.
Exclusions
14 total, each with a reason in the source. Three categories:
agent-task-contractsworkflow or the Homeboy gate.scripts/run-smoke.tsmatches*-smoke.tsand would recurse.Three of those ten fail for one shared root cause worth recording:
packages/runtime-playground/src/query-recorder.ts:80emits PHP inside a JS template literal containing\0and\x0B, which JavaScript interprets as actual control characters. The generated PHP therefore carries a raw NUL byte, and Node refuses to pass it as an argv argument. The string value PHP ends up with is the same either way, so this is a portability defect rather than a behavior bug, but it blocks any harness that shells out withphp -r.I have deliberately not fixed those ten here. This PR should be evaluated on whether discovery is correct, not bundled with ten unrelated repairs. They are #2402 item 5.
Update: four CI rounds to converge
Local runs passed on the first try. CI did not, and the gap was instructive — my machine has enough cores to hide the problems. Each round found a distinct class:
prepare-declaration-rebuilddeletesruntime-core/distand runsnpm installmid-run, breaking every concurrent import of@automattic/wp-codebox-corenative-mariadb-runtime-servicelost the orderingtest:runtime-servicesgave itRound 4 corrected a design mistake. My original analysis flagged four commands as must-keep; I overrode two of them because their member files were individually discoverable. Being discoverable is not the same as being safe standalone or concurrently.
test:generic-primitivesandtest:runtime-servicesare back as declared commands, and the 22 files they own are excluded from discovery so nothing runs twice.Round 1 was worth the trip on its own: a test that deletes shared build output while other tests import from it is a real defect, and it was invisible while the suite ran sequentially.
Final CI:
smoke aggregategreen in 11m16s.Final shape
Coverage verified against
origin/main: 145 of the 147 files the old manifest executed are still covered, and the other two are build helpersbuildstill runs.Exclusions total 15, each with a reason, plus 22 chain-owned files executed by their declared chain.
Follow-up
The 218
test:scripts inpackage.jsonare now largely redundant with discovery, but removing them is a separate diff with its own review surface — docs and operator habits reference them. Item 5 covers that alongside the ten excluded files.AI assistance disclosure: authored by Claude (Sonnet 4.5) running in OpenCode, directed by @chubes4. The model probed all 226 previously-ungated files individually to establish their status, measured sequential vs concurrent execution to size the runtime, identified the five archive-cache contenders empirically and confirmed they pass serially, and proved the 145-of-147 coverage overlap before collapsing the manifest. All figures are measured. Reviewed by a human before opening.