# issue24745 evidence replay

Worktree `/home/ubuntu/wt/issue-24745`; isolated home `.t3`; web port 6294; server port 14334. Current head is the fix, and the worktree is clean. The final retained dev-runner session is 50783. No implementation edits, commits, pushes, PRs, comments, merges, or watches were performed.

## Fixture rebuild

1. Stop the retained owned runner before seeding. Never seed the live home database. `seed.py` refuses writes while either isolated server port is accepting connections.
2. Run `python3 /tmp/issue-24745-evidence/seed.py` to reopen all C/Q/L projection fixtures, or pass `C,L` / `Q,L` for a subset. The source is three existing app-authored request/node/turn-item relationships in the dedicated test thread. Every helper write creates a SQLite backup. No event-log mutation is made. These are frontend projection fixtures, not evidence of provider/backend lifecycle correctness.
3. Restart the same runner with `vp run dev` in the worktree, retaining the new session and verifying the reported ports.
4. Use your own T3 Browser tab, preferably the dedicated incognito profile. Offline projection edits do not increment event cursors: call `clearIsolatedBrowserCache` from the driver in this test profile before reloading the route. Do not clear a user's ordinary browser profile.
5. Pair if required using `node apps/server/src/bin.ts pair --base-dir /home/ubuntu/wt/issue-24745/.t3`. Consume the fresh token privately in your own tab. Tokens are not evidence.

The original baseline database is retained locally as `baseline.sqlite` and is excluded from uploads. The SQLite helper backups remain under the isolated `.t3/userdata`. This existing database contains the harmless test thread, not arbitrary user history.

## Browser driver

`capture-driver.mjs` exports functions taking the live T3 MCP `tools` adapter and your own `tabId`. It has no standalone browser dependency. The driver was actually exercised for the replacement base 390-dark C run, replacement base L run, and replacement fix L run; it is not just pseudocode.

- Open the terminal drawer and hide the thread details panel using their real buttons, matching the saved layout. Call `prepareTerminal` once; it runs a harmless printf through the actual terminal input, clearing display/scrollback and leaving `hello` on line 1. The terminal selection action uses that output, not the command text.
- C: `prepareCustom`, then `capture` with 1440x900 light and 390x844 dark. `prepareCustom` types `answer-before` in the question through the UI and seeds the independent ordinary draft to `draft-before` with no contexts. Fixture-only draft setup uses the app's existing draft store; tested Add to chat never bypasses the terminal menu or calls composerRef.
- Q: after dismissing C, call `capture` at 1440x900 light with `selectBeta:true` once. Q is `allowCustomAnswer:false,multiSelect:true`, retaining the selected option during the action. Single-select questions auto-submit after 200 ms, which would erase the fixture before Add to chat.
- L: after dismissing Q, call `capture` at 1440x900 light. The seeded literal contains @test and backtick-delimited hello; `fixture.json` records the exact text. It has `initialAnswer` defined and `allowCustomAnswer:true`.
- R/N: after all questions are dismissed through the app, read state BEFORE retry: it must be exactly `draft-before`, zero contexts. Then call `capture` with no question. It must add one `hello` context/reference while preserving the sentinel. The long `afterQLRN.mp4` records these transitions continuously.
- Extra stills: while C is active, repeat the real action at 1440x900 dark and 390x844 light. Only those extra stills are needed, not a multiplied branch matrix.

The output from `capture` includes before/after state, a saved screenshot path, and a recording path. It waits one second for the naturally rendered action result; it does not change toast DOM/CSS or fake a notification.

For matched base/fix runs, switch only when no other reviewer uses the checkout. This capture task was authorized to switch after reviewers finished. Base is c79ef8c454e1268ff79da5a9dcb74572e86f7861; fix is 4e080eb004ead1a450214326e52bec98c9f52dba. Web HMR picks up the one-file change. Return to `fix/issue-24745` before handoff. Before captures show silent Q/L returns and the hidden C draft write. New toast state must expire/be dismissed before starting a base recording; the final clips were checked for carryover.

## Diff and inspection

Run `python3 diff.py` next to the images (Pillow and NumPy). It writes `diff-results.json`. Channel tolerance is 8/255. C/Q/L compare unchanged content outside the expected toast/shadow and precisely identified blinking carets: zero changed pixels remain. N is unmasked and differs by 125/1,024,000 pixels (0.012207%), below 0.02%; the age label, caret, and thin sidebar raster account for it. This tolerance was selected and disclosed during capture review; it was not in the supplied matrix.

CSS viewport sizes are 1440x900 and 390x844, devicePixelRatio 2. Desktop recordings are 2880x1800; responsive recordings are 780x1688. The Browser screenshot save path downsizes desktop stills to 1280x800; responsive stills retain 780x1688. Do not mistake PNG dimensions for CSS viewport dimensions. Still images were inspected directly; recording start/middle/end frames were extracted and inspected.

H is not runtime-verified. Holding is a transient typing guard, not a hold/defer control. Its state clears on composer blur and after 1500 ms; clicking/selecting the terminal releases it. No distinct persistent held state was reachable for this action, so no synthetic UI fixture was invented. A is source-only: the unchanged no-active-thread return precedes the toast and draft mutation.

Final isolated app state: all test questions dismissed, ordinary draft `draft-before`, zero terminal contexts, terminal output `hello`, 1440x900 light. Server remains available for inspection. No native mobile, Electron shell, relay/tunnel, or provider-specific behavior is claimed; this is the real web composer and responsive web layout.
