Merged
Conversation
Classes: .e2e = drives a real browser (Playwright/CDP), .int = boots real processes / dev servers / createTestThings / TestProject, .unit = pure in-process logic. The former .fast/.slow pairs map to .int/.e2e. The suffix becomes the single source of truth for test dispatch — the slow-tests.ts registry dies in the follow-up.
The release commit bumped every package.json to 0.2.0 but the lock kept the 0.1.12 workspace entries; any bun install regenerates this diff.
Comments, dev docs, and the (soon to die) scripts registry now point at the .unit/.int/.e2e names. CHANGELOG.md deliberately untouched — history stays history.
The class suffix (.unit/.int/.e2e) is now the whole registry: test.ts discovers files, ENFORCES the suffix against each file's real imports (browser import ⇒ .e2e, real machinery ⇒ not .unit), plans the two lanes (parallel groups + solo = every e2e + the SOLO_INT heavies), and runs everything through one guarded runner — wall-clock timeout, tree-kill + browser reap, fresh-process retry, honest failure extraction (real markers only, tail fallback), and passed-but-would-not-exit counted as a warned pass instead of a 10-minute red. Replaces scripts/slow-tests.ts + scripts/test-parallel.ts. ci-decide now only decides policy (oses/publish) and forwards the plan; --skip-tests is gone — every tag tests, prerelease or stable. scripts/*.unit.test.ts now actually run (the old runner never scanned scripts/). New aliases: testf = fast (unit+int sans solo), testa = all, tests = solo lane, test:unit/int/e2e/plan.
build runs on every run and its dist artifact feeds check, test AND publish — check no longer does its own duplicate build. Test matrices come straight from the plan (scripts/test.ts via ci-decide): test-fast = one runner per group with no Playwright at all (the fast lane carries no browser files by construction), test-solo = one runner per heavy-int/e2e file through the same guarded runner (the bash retry loop dies — retries live in test.ts). publish now requires the whole pipeline green with no skipped-stage special case (--skip-tests is gone). New test-one.yml: dispatch a single file on a chosen OS with a repeat count — reproduce a flaky release-matrix leg without burning the full matrix.
…erve, retry chromium launch rsc.e2e.test.tsx carried four browser launches; one bad describe cascaded into the rest and the file concentrated 4× the launch-flake exposure on loaded CI runners. Now each project (bun dev / bun build / vite dev / vite build) is its own rsc-*.e2e file = its own solo runner with exactly one launch; the fixture pages and every shared assertion body live in tests/utils/rsc-e2e.tsx. Salvaged from the ci-flakes investigation (the quarantines were NOT taken): chromium.launch retries 3× with a 40s per-attempt timeout (a pipe-terminated launch fails fast instead of burning the 180s default), and the build files boot + warm the prod server in beforeAll (warmProdServe polls /rsc with bounded attempts) — the boot is off the per-test 60s budget, which was bimodal on loaded runners (~730ms warm vs a full 60s hang).
…dgets
Root cause of the 'flaky' HMR pair: since the ssr-batch, vite Fast Refresh
remounts the point-rendered page on a file edit DETERMINISTICALLY (state resets,
'Hay 0'), so the state-continuation waits ('Hay 1'/'Hay 11') could never appear
on vite — that's a documented product limitation (vite-fast-refresh-point-state-
loss.md, architectural fix open), not a timing flake. Both tests now fork on
vite right after the first edit: the client test asserts propagation only (its
vite branch just starts earlier), the server test asserts the server half on a
FRESH page — state 0 + one click must return the edited inc: 10.
Load-hardening on top: waitStarted is 90s on vite projects (cold deps optimizer
— a slow start is not a failed start), the HMR pair's waitContent calls carry
15s ceilings (poll returns the moment content lands), and the client test
retries on a fresh project (retry 2) like its server sibling.
Cards: ci-flakes.md maps the whole v0.2.0 flake cluster to its fixes;
test-non-exit.md tracks the bun-on-Windows green-but-won't-exit hang the runner
now reports as a warned pass.
…sy-spin Root-caused live on a Windows machine (bridge session): with the .unref() in place, the fired deadline timer in this exact context (async subtree through normalize + settle inside the superstore ALS) busy-spins the bun 1.3.14 event loop at 100% forever — the green test file's process never exits, which is what red-flagged every v0.2.0 Windows fast lane. A bare setTimeout().unref() does not reproduce, and skipping the post-fire clearTimeout does not help — the .unref() itself is the trigger. Verified 5/5 clean exits on Windows and the full suite on macOS with the fix. The settle-time clearTimeout already covers the normal path; the one semantic change is bounded — an exit with a genuinely un-settled hole waits at most holeTimeoutMs for the deadline to fire. Full story: dev/backlog/test-non-exit.md --run-tests
… transitive launches build.e2e/assets.e2e call tp.gotoServer(), which lazily inits a real PlaywrightBrowser — no direct playwright import, so the class check missed them and the solo runner skipped the Playwright install (the one red job of the first full-matrix run: 'Executable doesn't exist'). verifyClasses now treats gotoServer/gotoClient/initBrowser/setBrowser as browser markers, so a transitive launch can't hide in .int again; both files leave SOLO_INT (e2e is solo by construction). --run-tests
A defer() whose subtree never settles + the fired unref'd deadline timer is enough (needs @point0/core); four dependency-free pure-JS variants all exit clean, so the trigger lives in the promise/timer topology of register(). Bun issue is the remaining step. --skip-ci
--skip-ci in a PR's tip commit used to skip the whole matrix while the gate stayed green — an untested change could merge. decide now resolves the pull_request branch before the flag check, so the only thing that skips a PR's matrix is a provably docs-only diff; --skip-ci/--run-tests remain effective on the maintainer's own branch pushes. Pinned by a new invariant test.
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.
What
The full CI/test reorganization that came out of the v0.2.0 release-run flake cluster (instead of the quarantine chase on the archived
ci-flakesbranch).Test classes in the filename
Every test file now carries its class —
.unit(pure logic, 45) /.int(real processes/servers, no browser, 47) /.e2e(real browser, 18). The suffix IS the dispatch registry:scripts/slow-tests.tsandscripts/test-parallel.tsare gone, and the suffix is enforced against each file's contents (browser use — direct import or transitivegotoServer/gotoClient— ⇒.e2e; real machinery ⇒ not.unit).One runner/planner:
scripts/test.tsDiscovers, verifies, plans and runs. Two lanes: parallel groups (unit / int-N / pinned heavies) + solo (every e2e + 4 heavy int). Wall-clock guard per file (tree-kill + browser reap), fresh-process retry, honest failure extraction, and "all green but the process won't exit" = pass with a loud ⚠, not a 10-minute red.
scripts/*.unit.test.tsnow actually run in CI (the old runner never scanned scripts/). Aliases:testf/testa/tests+test:unit|int|e2e|plan.Linear pipeline
decide → build → check → test → gate/publish.checkconsumes the build artifact (no second build). Matrices self-size from the plan. The fast lane installs no Playwright at all (no browser files there by construction).--skip-testsis gone — every tag tests. Newtest-one.yml: dispatch one file on a chosen OS with a repeat count.The three v0.2.0 flakes — fixed, zero quarantines
.unref()'d hole-deadline timer after it fires.fix(rsc): drop the.unref()— verified 5/5 clean exits on Windows; minimal repro recorded indev/backlog/test-non-exit.md(bun issue pending).waitStarted90s on vite, 15s waitContent ceilings.rsc-*.e2efiles (one launch per solo runner),chromium.launchretried 3×40s, prod server booted+warmed inbeforeAlloff the test budget.Test plan
testa— 111/111 files, 0 fail, 0 warn, 15m42s; types + lint green.--run-tests) — all ~59 jobs green on ubuntu+windows, no quarantines; Windows core-rsc exits clean in 431ms (was the 10-min timeout).After merge
ci-flakesbranch per the archive-tag convention.