Follow-up to #34. The current `MultiNodeSpec` runs every role in the same process / event loop. This was the deliberate v1 trade-off: deterministic, fast test starts, clean stack traces. Cost: races that need true parallelism (e.g. two journals writing simultaneously, scheduler-thread interleaving) can't surface in tests.
Strategy: add a worker-thread variant on top of `MessageChannelTransport` (which we already ship for worker-mesh deployments). Same public API as `MultiNodeSpec` so tests can opt into the parallel variant by changing one line:
```ts
const spec = new ParallelMultiNodeSpec({ roles: ['a', 'b', 'c'] });
```
Each role boots its own `ActorSystem` in a `worker_threads` Worker; the harness brokers cluster traffic via `MessageChannel` pairs.
Components:
| File |
Task |
| `src/testkit/ParallelMultiNodeSpec.ts` (new) |
Public API mirroring `MultiNodeSpec` but worker-backed. |
| `src/testkit/internal/MultiNodeWorkerRunner.ts` (new) |
Boots an ActorSystem inside a Worker, listens for harness commands. |
| `src/testkit/internal/MultiNodeBroker.ts` (new) |
Routes envelopes between worker MessageChannel pairs; also handles partition + crash directives from the test side. |
| `tests/unit/testkit/ParallelMultiNodeSpec.test.ts` (new) |
Self-tests + smoke test of one of the existing multi-node scenarios. |
Estimate: 4-5 days.
Verification:
- Self-tests cover spawn-N / partition / heal / crash / leave (the same surface the in-process variant has).
- One existing multi-node test (e.g. `tests/multi-node/sharding-failover.test.ts`) ported to the parallel variant — must stay green.
Out of scope:
- Replacing the in-process variant. Both ship; pick the one you want per test. In-process stays the default because it's faster and easier to debug.
Follow-up to #34. The current `MultiNodeSpec` runs every role in the same process / event loop. This was the deliberate v1 trade-off: deterministic, fast test starts, clean stack traces. Cost: races that need true parallelism (e.g. two journals writing simultaneously, scheduler-thread interleaving) can't surface in tests.
Strategy: add a worker-thread variant on top of `MessageChannelTransport` (which we already ship for worker-mesh deployments). Same public API as `MultiNodeSpec` so tests can opt into the parallel variant by changing one line:
```ts
const spec = new ParallelMultiNodeSpec({ roles: ['a', 'b', 'c'] });
```
Each role boots its own `ActorSystem` in a `worker_threads` Worker; the harness brokers cluster traffic via `MessageChannel` pairs.
Components:
Estimate: 4-5 days.
Verification:
Out of scope: