Implements Option C step 3 per .planning/debug/empty-archive-port-race.md:
"Continuous end-to-end vitest covering 600 s of port lifecycle
(2 reconnects + simulated REQUEST_BUFFER round-trips). Becomes the
new pinning contract for the port lifecycle."
The UAT Test 3 BLOCKER surfaced because no test exercised the full
operator timeline — 5+ minute recording with port-replacement windows
crossing real SAVE_ARCHIVE round-trips. This file pins that contract
end-to-end at the unit-test level.
What's exercised:
- Both SW (src/background/index.ts) and offscreen recorder
(src/offscreen/recorder.ts) loaded into the SAME chrome stub, with
paired port-pair factory (one connect() yields offPort + swPort
that talk to each other through captured listeners).
- 12 ping/pong cycles (~300 s simulated wall-clock).
- 3 SAVE_ARCHIVE round-trips (one before reconnect, two after each
of the two forced reconnects).
- 2 EXTERNAL port disconnects (port._disconnected=true) — simulates
the SW eviction / port glitch path that the H1.b test pins.
- JSZip mocked at file scope (vi.mock) because Node 22+ JSZip can't
read native Blobs — preserves integration shape (size accounting)
without depending on JSZip's Node compatibility.
Final assertions:
1. All 3 saveArchive calls return success:true.
2. EVERY BUFFER message that crossed the wire carried segments (no
silent-loss path was reachable).
3. PONGs round-tripped (proves health-probe loop closes).
Suite: 53 GREEN / 53 tests. tsc --noEmit exit 0; type-safety grep clean;
npm run build exit 0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>