PR #19931 review r1 (Opus, read-only). Local head a40886cd3e; diff origin/main...HEAD only (2 files, +211/-5).

VERDICT: no blocking source defects. Hold merge until native acceptance is shown (BLOCKED on headless Linux).

Traced beyond the diff: captureScreenshot -> captureStillImage -> startFrameCapture/stopFrameCapture (shared consumer sets,
guest and host throttling), capturePageWithRetry (requireCurrentGuest before and after), control sessions
(prepareWebview always runs ensureControlSession, so the debugger fallback only applies when DevTools are open or another
debugger is attached), closeTab, guest replacement in registerWebview, the main-window "closed" cleanup, and the IPC handler.

Verified correct:
- Each screenshot gets its own Symbol consumer, and acquireUseRelease always releases it on success, failure or interruption.
  stopFrameCapture runs uninterruptibly and restores guest and host throttling only when the last consumer leaves.
  Recording and PiP ending never stop a pending screenshot, and the reverse holds too.
- No screenshot-specific behavior leaks into recording-gated paths. Every `consumers.has("recording")` check ignores the
  symbol consumer. A screenshot session uses scope=null, so it starts no PiP loop and captures no initial frame.
- The paint probe reuses our own debugger and never attaches or displaces another one. It holds the control semaphore for
  at most 1s because the outer timeout covers both waiting for the semaphore and the probe. Interrupting it releases the permit.
- Historical CodeRabbit finding (the page promise never settled): fixed. The page-side setTimeout resolves false and calls
  cancelAnimationFrame; a successful double rAF clears the timer.
- The warmup image is discarded and the final capturePageWithRetry is the only source of saved pixels. Final errors and
  stale or destroyed guests still propagate, and no artifact is written on failure.
- Fallback is explicit: it logs a warning and returns available pixels with freshness unconfirmed, as the spec allows.
- No focus, show or raise calls. Error union stays within PreviewManagerError, and IPC is unchanged.
- Rerun: Manager.test.ts + ipc/methods/preview.test.ts, 116/116 pass. Worktree clean.

Non-blocking (low):
1. Failure-path latency roughly doubles. The warmup uses capturePageWithRetry, with 3 attempts x 1s timeout + 2x120ms,
   about 3.24s. When capturePage keeps timing out or failing, the screenshot now spends about 3.24s on the warmup (the
   probe runs in parallel) plus another 3.24s on the final capture, about 6.5s before reporting the error, against about
   3.24s on main. The modified test pins 6 calls. Smallest fix: run the warmup as one capture attempt without retry, since
   the final capture already retries, and change the expected call count from 6 to 4. Only worth doing if failure latency matters.
2. Not verified: Page.createIsolatedWorld probably creates a new world on every call, which lasts until the document
   navigates. That is negligible for screenshot rates. Caching per document would add more machinery than it saves. No change.

Tests: the 5 new regressions catch real ordering and ownership failures: stale pixels before the paint or deadline,
interrupting one of two concurrent screenshots, recording or PiP ending mid-screenshot, and throttling restored after
failure. The updated capturePage call counts in existing tests mirror the implementation, but that is acceptable.
No slop to remove.

Remaining native acceptance (missing proof, not source findings), on macOS/Windows desktop:
- Mutate a marker just before capture, then decode the saved PNG. Check base against head with the preview hidden, the
  main window minimized, and the display off or locked.
- No window raises or takes focus. Run overlapping screenshots alongside recording or PiP, stop each consumer
  independently, and confirm rendering and throttling are restored once all have stopped.
- Interrupt a capture, replace or destroy the guest, and quit the app: no stale artifact is written and no consumer is
  disturbed.
- Explicit DevTools-open/foreign-debugger fallback and timeout fallback are logged as freshness unconfirmed. Record
  current-head timing.
