feat: opt-in parallel test execution (N=2) — experimental#6
Draft
kevinccbsg wants to merge 8 commits into
Draft
Conversation
Proves three POC criteria against the holafly/web-checkout app: - Service-worker isolation works across browser.createBrowserContext(); 60/60 tests pass at N=2 with overlapping mocks and zero contamination. - Per-worker __coverage__ dumps to .nyc_output/out-<i>.json merge cleanly via nyc report (85.91% statements, identical to serial baseline). - Full suite completes with a 1.83× wallclock speedup (63.7s → 34.8s). Includes anti-throttle Chromium flags, which reduced flakiness at moderate N values. Documents findings and the random-test-ID discovery that forced a self-filter-by-index pivot during implementation. Throwaway script; production feature will be implemented under src/ in a follow-up commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Specifies the opt-in \`parallel: true\` config flag and the supporting architecture: new src/runParallel.js module, src/mergeMocks.js utility, and a thin branch in src/index.js. Serial path remains byte-identical when the flag is absent or false. N=2 is hardcoded in this release; deterministic test IDs, unified reporting, and configurable worker counts are documented follow-ups. The plan decomposes the work into six TDD-style tasks (config default, mergeMocks utility, runParallel core, contract handling, index.js branch, manual smoke + README). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a \`parallel\` boolean field to the config schema with a default of false. No behavior change — subsequent commits wire the flag into a new runParallel module. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Worker-index-prefixed keys prevent silent collisions if two browser contexts happen to generate the same twd-js random testId. Each merged mock carries its workerIndex field for downstream testName resolution. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduces src/runParallel.js. Launches one Puppeteer browser with anti-throttle flags, creates two isolated browser contexts, navigates each to the configured URL, runs a test chunk via runByIds (self-filtered by idx % N === workerIndex inside page.evaluate), dumps per-worker window.__coverage__ to .nyc_output/out-<i>.json, and aggregates pass/ fail/skip counts. Contract mock collection and merging land in the next commit. Not yet wired into src/index.js. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each worker exposes its own __twdCollectMock via page.exposeFunction, writing into a per-worker Map. After both workers complete, mergeMocks combines them with worker-indexed keys. Each mock carries workerIndex so buildTestPath can pick the correct handler tree for testName resolution. Validation and markdown reporting reuse the existing serial pipeline unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an early-return branch at the top of runTests(): if config.parallel is truthy, load contract validators and hand off to runParallel. Serial code path below is textually unchanged and runs when parallel is absent or false. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the `parallel` boolean to the Configuration Options table and a new section covering how the feature works, the 1.8× measured speedup, and current limitations (N=2 fixed, per-worker reporting, CI tuning). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TWD Contract Validation
23 passed · 41 failed · 3 warnings · 1 skipped Failed validations./contracts/users-3.0.json
./contracts/posts-3.1.json
./contracts/products-3.0.json
./contracts/events-3.1.json
|
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.
Summary
Adds an opt-in
parallel: trueflag totwd.config.jsonthat runs the TWD suite across two isolated Puppeteer browser contexts in parallel. Measured ~1.8× wallclock speedup on a 60-test suite (63.7 s → 34.8 s) on a developer laptop. Zero behavior change for users who don't set the flag.This ships as experimental / beta — worker count is fixed at 2 for now; higher counts are gated on a future twd-js change that makes test IDs deterministic.
How to use
{ "url": "http://localhost:5173", "parallel": true, // ← opt-in, default false "retryCount": 2 // existing; honored per worker }All existing config (
coverage,contracts,timeout,puppeteerArgs,headless,retryCount) works unchanged.What changes for existing users
Nothing. Without
parallel: true:runTests()skips the new branch and runs the existing serial body unchanged.runTests.test.jstests pass unchanged; a new test explicitly asserts the serial path is selected when the flag is absent and that anti-throttle flags are NOT in the launch args.What's inside
New:
src/runParallel.js— orchestrates the parallel flow (~200 LoC): launch → 2 browser contexts →Promise.allofrunByIdsself-filtered byidx % N === workerIndex→ per-worker coverage dump → merged mock validation → per-worker result trees + combined summary.src/mergeMocks.js— pure utility that combines per-worker mock maps with worker-index-prefixed keys (defense-in-depth against twd-js random-ID collisions).tests/runParallel.test.js— 15 unit tests covering launch args, anti-throttle flags, navigation, retry pass-through, pass/fail aggregation, coverage writes (including on failures),.nyc_outputcleanup, contract mock exposure, merged-mock validation, and contract error propagation.tests/mergeMocks.test.js— 5 unit tests for the merge utility.Modified:
src/config.js—parallel: falseadded toDEFAULT_CONFIG.src/index.js— early-return branch:if (config.parallel) return runParallel(...). Serial path below unchanged.tests/runTests.test.js— 2 new tests asserting branch selection.README.md— documents the new field with expected speedup and current limitations.Key design decisions
Self-filter by index, not probe + distribute. Initial design probed one context, split IDs in Node, and passed chunks to each worker. That failed because twd-js generates test IDs via
Math.random()— IDs from one context don't exist in another, sorunByIdssilently matched zero tests in workers ≥1. Each worker now enumerates its own__TWD_STATE__.handlersand takes the slots whereidx % N === workerIndex. Registration order is stable across contexts; IDs are not.Anti-throttle flags appended automatically.
--disable-background-timer-throttling,--disable-renderer-backgrounding,--disable-backgrounding-occluded-windowsare added topuppeteerArgsunless the user already set them. Measured to materially reducewaitFortimeouts at N≥2.Coverage always dumps, even on failures. The serial path skips coverage on test failures; parallel always dumps so one worker's flake doesn't blind the other's data. Standard nyc
reportmerges the files automatically.Per-worker reports (not unified). Each worker's results render with
reportResultsindependently, followed by a combined summary. Unified reporting needs canonical test identity across contexts, which is blocked on deterministic IDs in twd-js — tracked as a follow-up.POC evidence
The approach was validated in a throwaway POC at
poc/parallel/:.nyc_output/out-0.json+out-1.jsonmerge cleanly vianyc reportat 85.91% statements — identical to serial baseline.See
poc/parallel/README.mdfor the full findings, including the discovery of the random-ID issue and the concurrency ceiling at N≥3.Known limitations / follow-ups
workers: Nis gated on deterministic test IDs in twd-js.waitFordefault gets tight under CPU contention. Retries (which are fully supported) absorb most of this; a per-test timeout override is a future improvement.test-example-appis not instrumented with istanbul, so the manual smoke logsno __coverage__ on window— the coverage code path is fully unit-tested. Real coverage behavior was verified against an externally instrumented app in the POC.Test plan
npx vitest run)test-example-appwithparallel: true: 71/71 pass, 67 mocks validated, contract report written to.twd/contract-report.mdtest-example-appwithoutparallel: identical output to previous release — no behavior changenpx nyc reporton merged.nyc_output/out-<i>.jsonfiles works on CI runner1.2.0)Related docs (in-tree)
docs/superpowers/specs/2026-04-21-parallel-test-execution-production-design.mddocs/superpowers/plans/2026-04-22-parallel-test-execution-production.mddocs/superpowers/specs/2026-04-21-parallel-test-execution-poc-design.md,docs/superpowers/plans/2026-04-21-parallel-test-execution-poc.md,poc/parallel/README.md🤖 Generated with Claude Code