fix(ios): release snapshot bridge exit watchers after each round trip

Merged
KD
3d ago
+316-7

Summary

Each snapshot round trip added its own process.wait.then(...) to detect a bridge exit. wait stays pending while the bridge runs, and a reaction on a pending promise cannot be removed. So each round trip's frame decoder, request frame, and envelope stayed reachable until the bridge exited.

transport.ts now adds one exit reaction per bridge process. Each round trip subscribes a watcher at start and unsubscribes it in finish.

3 files, with a new test and a changeset. No scope expansion. No linked issue. #4182 (at aeb439d482) still calls roundTripSnapshotBridge for local Simulators.

Validation

Tested head 07c722b61 on base 1846ef96d.

  • pnpm check:affected --run --head 07c722b61 passed on Node 24.16.0 (exactCommit: true). Node 24.11.0 fails 2 unrelated host-kit tests, on origin/main too.
  • transport.test.ts has 5 tests. The first (21 round trips leave at most 1 reaction) fails on base with 21 !== 1. The other 4 pin existing behavior.
  • Local run, iPhone 17 Pro Simulator, 2,000 snapshots, forced GC: live memory (heapUsed + arrayBuffers) grows to 182.3 MiB on base and levels off near 33 MiB on head. Mean snapshot time: 46.3 ms on base, 46.0 ms on head. The harness is not in the repo.
  • Left to CI: coverage, replay-macos. Not verified: a real bridge exit (tests use a fake process), Node 22.

AI assistance and human review

AI was used to prepare these changes. Human-reviewed by @kdy1.

  • Harness: Claude Code 2.1.309 (T3 Code)
  • Model: claude-sonnet-5-5
  • Reasoning Effort: max

Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.