# Proof fixture matrix for [#32861](https://git.cafe/pingdotgg/t3code/pull/32861)

Pass 1: code-derived proof inventory, before any new runtime capture. Read-only audit of `/home/user/wt/pr-32861`. No repository files, forge state, or comments changed; no tests or application captures run in this pass.

## Findings

1. **The full desktop agent flow is unproved (P12).** The body explicitly excludes it. The driver calls `host.attach` directly, bypassing renderer registration, manager control-session recovery, the backend stream, server attachment deadlines, and the actual MCP tool handlers. Its `mainEval`, locator count, and wait do not demonstrate locator actions or snapshots. A real desktop run on the integrated head is the smallest proof that connects the demonstrated race fix to the claimed user outcome.
2. **The branch evidence is incomplete (P04, P05, P06, P07, P10).** The three new tests exercise `dom-ready`, main-frame ERR_ABORTED, backend subscription while pending, TestClock expiry, and early detach. They never emit `did-stop-loading`, a non-ERR_ABORTED main-frame failure, a subframe failure, or change/clear `shown` during the wait. The five-second fallback has virtual-clock proof but no wall-clock runtime transcript. These cases can share one small scripted sequence against the real host; they do not require permanent wrapper/UI tests or separate desktop runs.
3. **The old harness evidence is useful and reusable within its scope.** Recounting all embedded JSONL rows confirms idle 11/30 → 0/30, loaded 16/30 → 0/30, data URL 0/30 → 0/15, main-with-delayed-attach control 0/30, and replacement navigation 0/20 after. The after runs used [5202328a4c](https://git.cafe/pingdotgg/t3code/commit/5202328a4c). Comparing that revision to the requested [17ea24e7f9](https://git.cafe/pingdotgg/t3code/commit/17ea24e7f93be05ca7b43e6691513b95d1234328) shows **only a comment change in DesktopBrowserHost.ts**, plus an added test. The brief calls this a constant rename; the actual change only names `DESKTOP_ATTACH_TIMEOUT` in the comment. A wholesale repeat of the old before runs is unnecessary. However, the merge includes server attachment/reannouncement and visibility changes, so legacy direct-host evidence cannot certify the integrated app.
4. **Replacement timestamps need better labels, not a claim that the fix failed.** The driver's `wc.on("did-navigate")` overwrites `firstCommit` on every navigation; `domReady` is recorded once. In all 20 replacement rounds, `attachMs` precedes the logged `firstCommitMs`. Those fields can describe different navigations. The successful waits remain evidence of success in those rounds, but the transcript does not establish that an ERR_ABORTED event occurred or that announcement waited for the replacement document. Record every navigation's URL/event/code/time in any replacement follow-up.

## Revision and scope

- Requested checkout HEAD: [17ea24e7f9](https://git.cafe/pingdotgg/t3code/commit/17ea24e7f93be05ca7b43e6691513b95d1234328), a local merge of the PR tip and main. Its merge-base with `origin/main` is [fa2f4ff2a2](https://git.cafe/pingdotgg/t3code/commit/fa2f4ff2a2bac5ac92e07749f6b83b0003d642a0).
- GitCafe metadata still reports PR tip [1ea1de4384](https://git.cafe/pingdotgg/t3code/commit/1ea1de4384361008cc726453ae0c6b5db803e45f). The local merge is not the published PR head as of this read. This report evaluates the requested local head; evidence should identify the tested OID explicitly when the lead publishes it.
- Complete PR diff: [DesktopBrowserHost.ts](https://git.cafe/pingdotgg/t3code/blob/1ea1de4384361008cc726453ae0c6b5db803e45f/apps/desktop/src/preview/DesktopBrowserHost.ts) and [DesktopBrowserHost.test.ts](https://git.cafe/pingdotgg/t3code/blob/1ea1de4384361008cc726453ae0c6b5db803e45f/apps/desktop/src/preview/DesktopBrowserHost.test.ts), 118 additions / 5 deletions against the local merge-base.
- Desktop main process only. Web's Electron renderer is an actual caller and must be included in P12. Standalone web/server Chromium, native mobile, provider adapters, contracts, remote viewers, and relay/tunnel transports receive no code change here. A separate provider/platform/transport matrix is unnecessary for this fix. No visible styling or animation changes: screenshots and videos would not prove the affected behavior.

## Caller inventory

All production callers of the changed host methods, including internal callers:

| Method | Callers and entry paths | Fixtures |
|---|---|---|
| `attach` | `Manager.restoreControlSession` after `registerWebviewUnlocked`; registration follows `HostedBrowserWebview` initial registration, `did-attach`, and `dom-ready`. Also restored by `recoverDetachedControlSession` after unexpected debugger detach and by `openDevTools`'s `devtools-closed` listener. | P01–P08, P12 |
| `detach` | Internally at the start of `attach` for a different guest under the same key; internally in the CDP relay's debugger-detached callback. Externally `Manager.releaseControlSession` for `closedServerTab` and all matching webContents owners. Session release comes from close/destroy, guest replacement, destruction, debugger recovery, and opening DevTools. | P08, P11, P12 |
| `setShown` | `Manager.setShown` through desktop IPC from `HostedBrowserWebview` after registration and its visibility effect/cleanup; `hideDestroyedGuest(false)` on guest destruction; `closeTabUnlocked(undefined)` to forget a closed tab. | P02, P08, P10, P11, P12 |
| `announceAll` | Only `host.events` subscription, after subscribing to the outbox. `DesktopBackendManager` supplies this stream to the server subprocess for initial startup/restart; a new subscription resets relays and replays announced tabs. Manager's pointer/painting subscriptions do not call it. | P01, P09, P11, P12 |

The user flow is `preview_open` → server session announcement → Electron renderer guest creation/registration → manager control session → host `attached`/`shown` → DesktopBrowserChannel → Playwright connection → `preview_wait_for` / locator `preview_act` / `preview_snapshot`. The server allows 10 seconds for desktop attachment; the host fallback is 5 seconds from host attachment, not from the user's tool call.

## Existing test names

These are all in `apps/desktop/src/preview/DesktopBrowserHost.test.ts`:

- **T1:** `announces a guest still loading its first page only once that page is up`
- **T2:** `keeps a loading guest unannounced across a replaced navigation and a new backend`
- **T3:** `announces a first page that never comes up, but never a tab closed first`
- **T4:** `announces tabs already attached to a backend that starts later`
- **T5:** `re-sends shown to a backend that starts later and when a tab attaches again`
- **T6:** `replays an explicit hidden report before announcing another tab`

The body reports 38/38 host tests and 115/115 host/manager/backend tests passing before the local merge. Those claims were read, not independently rerun. The named tests and their assertions were inspected at the requested head.

## Fixture matrix

“Minimum” means needed to establish a distinct behavior/control, not a request for a separate test file or runtime session per row. Rows can share a driver and transcript. P03 and additional variants marked optional do not gate the verdict.

| ID / fixture | Diff impact; minimum | Data and expected behavior | Proof needed / existing proof | Status and freshness | How to produce |
|---|---|---|---|---|---|
| **P01 Ready guest** | Preserved fast path; **yes**, control | `isLoadingMainFrame() = false`; unknown, true, or false shown state. Immediate `attached`, followed by known `shown`, without the 5 s wait. | Unit T4/T5/T6; old main delayed-dom-ready harness control is corroborating, not an assertion about the loading flag. | **Covered at unit layer.** Rerun existing host suite on integrated head; no dedicated new runtime run required. | Run `vp test run apps/desktop/src/preview/DesktopBrowserHost.test.ts`. Existing makeDebuggee defaults to not loading. |
| **P02 Loading → dom-ready** | Changed readiness boundary, once-only announcement; **yes** | Fresh about:blank guest, loading=true; listener already subscribed; shown=true before attach. No early events. At first `dom-ready`: one `attached`, then `shown`; later stop/ready events and fallback must not duplicate them. Idle and CPU-loaded populations. | T1 proves deferred event order. Real Electron/Playwright JSONL proves idle 11/30 → 0/30 and loaded 16/30 → 0/30, including attach/commit/dom-ready/connect timestamps. Old driver does not count duplicate host announcements. | **Core race covered by reusable legacy runtime + T1.** Include duplicate-event counts in the shared head follow-up (P04/P10); no need to recreate old before failures. | Adapt embedded driver imports to this checkout, resolve installed dependencies rather than copying `.pnpm` paths, record HEAD and loading flag. If repeating: 30 idle rounds and 30 with tracked CPU burners; count all attached/shown events through stop and fallback. |
| **P03 Explicit first URL** | Same loading gate, URL variant; **no**, optional expansion | Fresh guest with embedded `data:` content, rather than blank. Successful locator/wait and attach after ready. Real app URL input should use a local HTTP fixture because tool URL validation excludes data URLs. | Embedded before 0/30, after 0/15. | **Covered as a direct-host control.** No fresh standalone run needed; real local HTTP URL/pending URL can be included in P12. | Existing harness `SRC=data:...`; alternatively local HTTP page through the actual app. |
| **P04 Loading → stop-only / duplicate signals** | New alternative readiness event and shared guard; **yes** | Loading guest; `did-stop-loading` is the first accepted signal, no prior dom-ready/failure. One attachment. Emit further accepted signals and let the deadline pass; still one. | No named test emits stop-loading, and embedded logs do not identify the winning ready event or count announcements. | **Missing.** Fresh head scripted transcript required; can be combined with failure/visibility fixtures. | Scratch host sequence with controlled EventEmitter webContents, using real host and event subscription; log the signal order and all emitted events, with a sentinel/drain boundary. For a real guest, label every readiness event and isolate this branch deliberately; do not infer it from a successful normal load. |
| **P05 Main-frame failure accepted** | New failure readiness path; **yes** | Loading=true; `did-fail-load`, isMainFrame=true, non--3 code (e.g. -105). Prompt one `attached`; a later stop event produces no duplicate. | T2 only sends -3. No existing runtime failure row. | **Missing.** Fresh head branch transcript or focused table extension required. | Same shared scratch sequence, or a deliberately failing local request in Electron. Log frame flag, error code and attach ordering. No external website/DNS dependency is needed. |
| **P06 Failure ignored** | New guards; **yes** | (a) Main frame code -3, then replacement readiness. (b) Subframe error with non--3 code, then actual main readiness. Neither rejected event announces; final accepted event announces once. | T2 covers (a) plus resubscription. Replacement harness has 0/20 failures but no error-code/frame/URL event log and overwrites “firstCommit.” No proof of (b). | **Partial.** ERR_ABORTED logic has unit proof; fresh subframe sequence missing. Replacement runtime is success evidence, not guard proof. | Reuse T2's shape with a subframe event in shared scratch transcript or extend that existing test table. If redoing Electron replacement rounds, log each navigation URL and ERR_ABORTED/frame events; a loadURL call alone does not prove the guard was reached. |
| **P07 No signal → bounded fallback** | New timeout and cancellation; **yes** | Loading guest emits no readiness event. Attachment at roughly 5 s from host attach, not immediately; one event only, with a functioning connection inside the server's 10 s attachment budget when exercised end to end. | T3 advances TestClock 5 seconds and receives attached. No real timing/deadline runtime row. | **Partial.** Unit behavior covered; required timing transcript absent. | Shared scratch real-host run with a loading guest that suppresses ready signals; per-round monotonic attach-start/attached/connect timestamps and counts. At least one real deadline round; use multiple if making a reliability claim. P12 supplies actual channel proof; do not equate 5 s from host.attach with 5 s from preview_open. |
| **P08 Detach while pending / replacement ownership** | Changed suppression and wait cleanup; **yes** for close; replacement variant useful | Pending tab detached before readiness emits neither attached nor detached; stale ready/error/timeout cannot revive it. A different guest under the same key replaces/cancels the old wait; old guest events must not announce the replacement. Already-announced detach remains P11. | T3 covers close-before-timeout and silent cancellation. No old-guest readiness/replacement variant in added tests. | **Covered for minimum early-close case; replacement cleanup partly unproved.** Add stale-event/replacement sequence to shared head driver at little cost; no separate app session needed. | Host attach→detach, then signal old webContents and pass deadline; sentinel event verifies no stale event. Repeat with a new guest under the key, wait for its own ready. Same guest repeated attach is a preserved no-op control, optionally checked here. |
| **P09 Backend subscribes while pending** | Changed announceAll skip; **yes** | Attach loading guest before stream subscription; ERR_ABORTED; subscribe backend; unrelated ready tab announces first; pending one only after readiness. Subscription after announcement must replay it normally (P11). | T2 exactly covers pending subscribe + ERR_ABORTED ordering. T4/T5 cover ready replay. | **Covered at unit layer.** Reusable source proof; rerun existing host suite on integrated head. No fresh dedicated backend runtime capture necessary. | Existing T2. P12 may also restart/reconnect a backend if needed, but this is not a separate minimum full-app fixture. |
| **P10 Shown before/during announcement** | Changed setShown gate + delayed state lookup; **yes** | Known shown before attach; while pending toggle true→false and optionally clear to undefined. No shown while pending; latest defined state is emitted immediately after attached; clearing emits no stale shown. After ready, changed shown emits immediately; identical values remain no-ops. | T1 covers true set before attach. T6 covers explicit false on a ready guest. Neither changes nor clears visibility during a wait. | **Partial.** Fresh pending-update/clear proof missing. | Shared scratch script, table variants: true→false, false→true, true→undefined. Subscribe before mutation and keep all event counts through a sentinel; then change shown after readiness. Fold once-only ready signals from P02/P04 into this sequence. |
| **P11 Announced lifecycle replay** | Preserved reverse path/control; **yes** with existing tests | Ready tab detaches and reattaches, shown persists; backend reconnect replays attached then shown. A waiting tab's silence must not remove normal detached events. | T5 explicitly expects attached/shown/detached/attached/shown; T4/T6 cover replay and hidden state. | **Covered at unit layer.** Rerun focused suite; DevTools close/reopen and guest crash recovery use the same methods, so separate runtime permutations are optional. | Existing tests. Optional real-app DevTools or renderer-reload smoke can share P12; do not duplicate tests at every wrapper. |
| **P12 Real desktop agent tools** | User-visible outcome through changed host; **yes** | Fresh tab via `preview_open` with no URL and `reuseExistingTab:false`; returned tab must be desktop-owned. Before navigating away, prove `preview_wait_for` on body, snapshot, and locator action using a button injected by evaluate without replacing the document. Verify action side effect and later snapshot. Repeat blank in Incognito via a reported profile; include a local HTTP initial/pending URL variant in the same run. | Body explicitly says not checked. Harness does not call any of these tools, actual renderer IPC, server channel or manager registration. Also main merge changed server attachment reannouncement and visibility. | **Missing; fresh integrated-head app transcript required.** This is the essential new runtime capture. Linux is sufficient minimum; macOS/Windows are optional platform expansion. | Lead runs real Electron app under `headless-desktop`, isolated state, `T3CODE_DEV_SEED=0 vp run dev:desktop --home-dir <scratch-run-dir>/t3-home` from the checkout (wrap command with headless-desktop). Create only synthetic project/thread fixtures. Invoke the actual session MCP preview tools; keep tab IDs explicit. Log app HEAD, runtime versions, each round/tool start/end/result, desktop host attachment time, and counts. Do not use production tool context or accidentally prove server-owned fallback Chromium. |

## Minimum next proof package

1. Rerun the existing focused host suite against the integrated head; preserve its output and OID. The legacy failing-on-main claims are adequate context and need not be recreated by editing the audit worktree.
2. One shared scratch host transcript covers P04/P05/P06(subframe)/P07/P10, plus duplicate-ready/stale-event checks from P02/P08. Controlled event sequences are sufficient for event-branch contracts; P07 needs real per-round timing. No new permanent UI/wrapper tests are needed just to mirror callback wiring.
3. One real desktop integrated session covers P12. Preserve the original race transcript as the before proof. Record fresh after results from the integrated checkout, including body wait and snapshot before any corrective navigation; otherwise navigation might hide the initial utility-world failure.

## Production prerequisites and limits

The embedded driver cannot run verbatim here: it hardcodes `/home/ubuntu/wt/issue-32848`, `.pnpm` versions/paths, and an old scratch output path; its shell block also combines build/run fragments and lacks a package entry point for running the directory. Extract it into this audit/lead scratch tree, adapt all imports/output paths, use an explicit `harness.cjs` entry or a scratch package.json, and write each run under a new timestamped directory. Record the resolved Electron and Playwright versions. The requested checkout has Playwright 1.60.0 and Electron package metadata for 44.10.1, but its Electron executable and `.bin/esbuild` are absent. Normal installation/runtime provisioning is needed before running it (desktop has `ensure:electron`); this pass did not download/install anything or modify the shared checkout.

Use the lead's approved scratch root for logs, HTML fixtures, drivers, and runtime state. The lead, not this read-only audit sub-agent, starts any dev server. Start no server against live T3 home. The `headless-desktop` wrapper is available and creates Xvfb/D-Bus itself; it has no help flag (read its script usage instead). Keep every child's PID/session and stop only those children. Disk availability at audit start was 109 GB, above the 5 GB stop threshold.

No image/recording requirement applies: there is no visual surface, and ordered tool results plus per-round timestamps establish the timing more precisely. No runtime captures or tests were performed during this matrix-only pass.

AI-assisted; verified with the source inspection and embedded transcript recount listed above.
