# Proof audit pass 1 — async answer visibility

Issue [#15873](https://git.cafe/pingdotgg/t3code/issues/15873). Read-only derivation, 2026-10-22.
Before: [b5a226e49c52](https://git.cafe/pingdotgg/t3code/commit/b5a226e49c52ecab4ddc2d678a7012d97e651a6a).
After: [7d869fd13b74](https://git.cafe/pingdotgg/t3code/commit/7d869fd13b74f7cc040ad23f375ab01f36bc9afe).
Full SHAs: `b5a226e49c52ecab4ddc2d678a7012d97e651a6a` and `7d869fd13b74f7cc040ad23f375ab01f36bc9afe`.
Checkout examined: `/home/ubuntu/wt/issue-15873`; HEAD and supplied merge base verified. No PR exists in this brief.

## Findings first

1. **Proof remains incomplete.** This pass derives requirements; it does not capture or certify rendering, cache behavior, navigation, native builds, or pixel comparisons. The lead reports both round-2 reviews found no code bugs and all 105 focused tests passed; those executions were not repeated here. No new code defect is asserted by this audit.
2. **Native Find requirement conflicts with the actual source.** Web in-thread Find calls the changed `ThreadFind` service. Native mobile has global thread search in Home, the navigation sidebar, and the command palette, backed by the separate, unchanged `ThreadSearch` service. No native consumer of `threadFind`/`threadFindProgressive`, in-thread Find bar, or answer-level match navigation was found. A native global-search result can open the thread; it does not prove server in-thread Find or automatic navigation to the answer. Record native search → open → scroll as a control and label any added native Find demonstration as a harness. Do not silently replace the requested native Find proof with web at 390px.
3. **Static native builds alone cannot prove cache transitions.** Fresh mount and same-mounted-instance prefix→answer delivery must both exercise the production `buildThreadFeed` caller. A fixture that renders the complete array once proves only the fresh path. A separate control must change the answered-question map on an already mounted instance; merely appending a message to an already answered prefix does not exercise map invalidation.
4. **The lead's real repro is stronger than a fabricated stand-alone bubble.** The supplied export contains three runs, the answered request in run 1, queued user/assistant messages in run 2, and `async-answer:<requestId>` plus acknowledgment in run 3. Its answer has `inputIntent: turn_start`, `creationSource: server`, no attachments, and 111 characters. The added web test uses `steer`; both shapes need coverage. The existing screenshot is useful context, not full proof: [lead screenshot](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/ca2619b37f70ca54/browser-screenshot-localhost-mviqtsv3-f960605b.png).
5. **Scope can stay small without hiding paths.** Real async-message dispatch supplies nonempty question/answer text, `createdBy: user`, `creationSource: server`, and `attachments: []`. Generic media, automation, agent attribution, context chips, and attachment failures do not require separate affected captures. Long free-text answers can reach wrapping, markdown width, collapse/reveal, and copy paths, so one representative long-answer fixture remains necessary.

## Source-derived model and caller inventory

Only six diff files: web `session-logic.ts` and its test, mobile `threadActivity.ts` and its test, server `ThreadFind.ts` and its test. No renderer, CSS, schema, provider adapter, transport, or settings implementation changes.

Source links below point at the reviewed after commit:

- [Web timeline derivation](https://git.cafe/pingdotgg/t3code/blob/7d869fd13b74f7cc040ad23f375ab01f36bc9afe/apps/web/src/session-logic.ts), [incremental hook](https://git.cafe/pingdotgg/t3code/blob/7d869fd13b74f7cc040ad23f375ab01f36bc9afe/apps/web/src/hooks/useIncrementalTimelineEntries.ts), [ChatView](https://git.cafe/pingdotgg/t3code/blob/7d869fd13b74f7cc040ad23f375ab01f36bc9afe/apps/web/src/components/ChatView.tsx), [MessagesTimeline](https://git.cafe/pingdotgg/t3code/blob/7d869fd13b74f7cc040ad23f375ab01f36bc9afe/apps/web/src/components/chat/MessagesTimeline.tsx), [user row](https://git.cafe/pingdotgg/t3code/blob/7d869fd13b74f7cc040ad23f375ab01f36bc9afe/apps/web/src/components/chat/TimelineUserMessageRow.tsx).
- [Mobile feed derivation](https://git.cafe/pingdotgg/t3code/blob/7d869fd13b74f7cc040ad23f375ab01f36bc9afe/apps/mobile/src/lib/threadActivity.ts), [composer caller](https://git.cafe/pingdotgg/t3code/blob/7d869fd13b74f7cc040ad23f375ab01f36bc9afe/apps/mobile/src/state/use-thread-composer-state.ts), [route](https://git.cafe/pingdotgg/t3code/blob/7d869fd13b74f7cc040ad23f375ab01f36bc9afe/apps/mobile/src/features/threads/ThreadRouteScreen.tsx), [detail](https://git.cafe/pingdotgg/t3code/blob/7d869fd13b74f7cc040ad23f375ab01f36bc9afe/apps/mobile/src/features/threads/ThreadDetailScreen.tsx), [native feed renderer](https://git.cafe/pingdotgg/t3code/blob/7d869fd13b74f7cc040ad23f375ab01f36bc9afe/apps/mobile/src/features/threads/ThreadFeed.tsx).
- [ThreadFind](https://git.cafe/pingdotgg/t3code/blob/7d869fd13b74f7cc040ad23f375ab01f36bc9afe/apps/server/src/orchestration-v2/ThreadFind.ts), [web Find hook](https://git.cafe/pingdotgg/t3code/blob/7d869fd13b74f7cc040ad23f375ab01f36bc9afe/apps/web/src/components/chat/useThreadFind.ts), [Find bar](https://git.cafe/pingdotgg/t3code/blob/7d869fd13b74f7cc040ad23f375ab01f36bc9afe/apps/web/src/components/chat/ThreadFindBar.tsx), [separate global ThreadSearch](https://git.cafe/pingdotgg/t3code/blob/7d869fd13b74f7cc040ad23f375ab01f36bc9afe/apps/server/src/orchestration-v2/ThreadSearch.ts), [async dispatch](https://git.cafe/pingdotgg/t3code/blob/7d869fd13b74f7cc040ad23f375ab01f36bc9afe/apps/server/src/orchestration-v2/Orchestrator.ts).

For each answered `user_input_request`, construct `async-answer:${questionAnswer.requestId} → question.runId`. Suppress a user message only when its `messageId` resolves to the same run ID as that message. `null === null` folds; a null/non-null mismatch does not. Missing map entry is `undefined`, while valid run IDs are string or null. Before suppresses every matching message ID, regardless of run. Iteration preserves projected item order; it does not sort by timestamps or move the answer beside the question.

| Path/caller | Inputs and props that carry the change | Observable consequences and capture mapping |
|---|---|---|
| Web `deriveTimelineEntriesFromVisibleTurnItems` → `derive…WithState` → `useIncrementalTimelineEntries` → `ChatView` | `visibleTurnItems` with item identity/type/run/message/request IDs, position/source visibility, ordinal and timestamps; `attempts`, `nodes`, `plans`; `optimisticMessages`, `anchoredMessages`, attachment URL map | Matching later-run user item now becomes a `kind: message` entry with run/attempt metadata and `projectedItem`; user/assistant order and surrounding work groups can change. G/A/S/L. Full and incremental projection must agree. |
| Web `ChatView` → `MessagesTimeline` → user row/body | `timelineEntries`, derived `timelineMessages`, displayed/remembered thread key, latest/active run, checkpoints/revert mapping, row context, theme/fonts, width, Find context | Bubble, intent marker, timestamp, copy/actions, checkpoint/revert ownership; normal markdown body and long-answer fold. Added bubble shifts later rows and scroll extent. G/A/S/L, C as control, I for interaction. |
| Web timeline-derived counts | `entries.length === 0` plus sync phase; `hasTimelineEntries`; user message count `>1`; remembered timeline/paint-only switch; timeline cache | Existing history already makes affected real threads nonempty. Added answer may cross user-count threshold in a minimal projection; setup follow-up/docked hero state must not be incorrectly retained. Q and B controls; no invented empty cross-run normal thread. |
| Web incremental fast path | thread key; references/shallow equality of all inputs; appended answer/request forces full projection; streaming assistant replacement can reuse rows | Test hot append, unanswered→answered update, duplicate/replayed delivery, route away/back and full reload. G/C recordings; streaming tail S. |
| Mobile `buildThreadFeed` → `useThreadComposerState` → `ThreadRouteScreen` → `ThreadDetailScreen` → `ThreadFeed` | projected rows; anchored pending-creation messages; attempts/nodes; checkpoint summaries; stable `createThreadFeedCache`; then acknowledged messages and queued outbox | Added `type: message` user bubble is a group boundary; answered request remains standalone activity/history, later work grouping/folds and terminal assistant metadata must stay coherent. G/A/S/L/C. |
| Mobile derivation caches | cached rows/feed/restart indices; map equality includes keys AND run values; delegated child sets; attempts/nodes references; checkpoint summary equality; anchored array length | Fresh mount; hot answered-prefix→append; hot unanswered→answered map update; same request ID/run-value reassociation as harness control. G/C/K recordings. Anchored inputs disable prefix reuse. |
| Mobile feed presentation/list | `feed`, latest run, expanded turn/work groups, active work start, runless work, pending messages, `anchorMessageId`, layout variant, window/content width, font scale, insets | Settled turn fold/work disclosure, insertion height, live follow, history position, setup anchor, empty/filled list key, copy/status/steer badge, bottom space. G/S/L/I recordings. |
| Mobile detail/route secondary use | user count `>1` / `<=1` for worktree setup; `.some(user)` before send, compactable-user predicate, acknowledgment flag, submitted-ID membership, haptics on feed | Valid nonempty repro already has user history, so compaction/has-user/empty branches generally unchanged. Setup count and submitted anchor can change for sparse/history windows. B control; G/S check spinner/queue/anchor and avoid duplicate acknowledged bubble. |
| Server memory projection `findProjectedThreadItems` | projected items, query/skills, start entry + occurrence, index/offset; run/entry IDs and source identity | Cross-run answer searchable; same-run remains excluded. Result total/active index/selected run and entry/occurrence/navigation reflect the new document. SQL and memory covered by focused tests per lead; F is visible proof. |
| Server SQL `makeThreadFind` → projection → management/orchestrator → WS → runtime query/subscription → web Find | SQL now loads JSON `runId`; decoded question map; snapshot sequence; query cache; progressive vs final frames; source/fork rows | Loading/counting→selected answer→final count; correct run reveal even when offscreen/folded; next/previous and close restore usable chat. F recording. SQL/memory are not distinct visual fixtures. |
| Mobile Home/sidebar/palette global search | runtime `threadSearch`/`searchThreads`; snippet, matched thread key; selected thread navigation | Unchanged service searches persisted messages, including the previously hidden answer. Result snippet may exist before and after; opened thread now shows bubble after. N control recording. No in-thread answer target inferred. |

## Data and conditional equivalence classes

“Affected” means the diff can change output for this input; “control” means an invariant to check. This inventory bounds the capture set instead of enumerating unrelated generic component states.

| Input/condition | Class | Expected before → after / representation |
|---|---|---|
| Answered question Q, matching synthetic message ID, Q.run != answer.run | Affected | Missing user bubble → exactly one chronological user bubble; F gains its answer document. G/A/S are separate observable run histories. |
| Answered question, matching ID, same run (including both null) | Control | Same answered card/history; no separate bubble or duplicate server Find document. C. Null case is data/harness-only, visually equivalent to ordinary same-run. |
| Question unanswered / absent / different request ID; message is user | Control | No fold match; ordinary message remains visible/searchable. Q/K; orphan export projection only if needed to force partial-history state. |
| Assistant message/plan or other item with coincident ID | Control | Fold predicate only affects user messages; ordinary assistant/plan/work rendering unchanged. Include assistant acknowledgment and work next to G/S; no extra screenshots for unrelated item kinds. |
| Nonempty text, no attachments, user/server attribution, turn_start | Affected real shape | G/A short ordinary user bubble; not an automation prompt merely because creation source is server. |
| Nonempty text, no attachments, `steer` (or promoted queued-to-steer) | Affected shape | S must visibly show steer marker/badge; active-run and later settled states. Promoted variant shares marker renderer; label variant can be checked in same labelled harness if actual async dispatch cannot produce it. |
| Multiline/multi-question/free-text markdown answer | Affected shape | L joins question/answer blocks with blank lines. Include >600 visible characters AND >8 lines, wrapped paragraphs plus one wide markdown block; web fold/reveal and native block sizing. Exact 600/8 boundaries are unchanged generic collapse logic, no separate capture required. |
| Empty/whitespace message, attachment-only answer | Control/unreachable real async-message path | Dispatch requires at least one nonempty answer and constructs question text plus answer; attachments are `[]`. Mobile additionally removes truly empty messages. No affected media/error matrix; an attachment-only answered question card is a same-run/history control if fixtures support it, not a claim that this dispatcher sends attachments. |
| Anchored/optimistic/acknowledged duplicate ID, repeated snapshot | Control with affected message present | Retained ID set still includes all answered IDs; no extra ghost/replay bubble. Hot G plus route/reload; K only if real replay cannot be driven deterministically. |
| Run/attempt/node/checkpoint present versus absent; local versus inherited source | Control with affected message present | Carry actual answer run metadata, source identity, checkpoint ownership and fold boundaries. G real data includes them; sparse/inherited projections have same bubble look, so use focused tests or labelled K replay rather than a separate visual grid. No null-as-string fixture. |
| Count 0, 1, multiple visible/user rows | Control/indirect | Empty/error/loading threads cannot be produced merely by this diff in a complete valid answered thread. G covers multiple and length change; B covers sparse count/empty/history presentation, only if reachable. |
| Find blank / 0 / 1 / multiple matches; incomplete/final; >17 document navigation window | Affected counts + control limits | F must show 0→1 for answer-exclusive text and next/previous over multiple results; blank/unrelated zero and >17 windowing unchanged. Piggyback blank/zero controls into F; >17 tested/data control, no new 18-document visual fixture for this fix. |
| Find loading/counting/error/retry | Control around affected query | Capture progression when observable; force latency/error only through labelled transport harness if needed. Error is not caused by changed fold predicate. F/Q; no claim from a static successful result. |
| Light/dark; wide/narrow; native phone/wide workspace | Affected presentation exposure | Same new row painted by existing renderer in each; no color/style changes expected outside exposed content, reflow, Find highlight and count. Mandatory geometry grid below. |
| Idle/completed/active/queued | Affected history/time | G queued gap, A adjacent new run, S active steering, C same-run fold. Pending→answered and cached insertion are recordings, not still-only rows. |

## Named fixture and minimum capture matrix

Every required timeline checkpoint is a Before | After | Diff0 | DiffT pair at identical data/viewport/theme/scroll. “Recording” below is mandatory in addition to the checkpoint pair. Stills alone fail those rows. A single continuous recording may satisfy several rows if its timestamp index names every checkpoint and transition.

| ID / fixture | Affected or control; exact content | Minimum checkpoint and expected delta | Required motion/interaction evidence | Real seeding/navigation, fallback explicitly labelled |
|---|---|---|---|---|
| G — Queued-gap late answer | Affected. Q in run N; queued prompt + reply in N+1; synthetic answer starts N+2; N+2 acknowledgment. Use lead's genuine three-run fixture, preserving turn_start intent. | Context view includes Q/history plus N+1 separator/history and N+2 answer/ack; if screen cannot contain all, aligned top and bottom checkpoints with shared anchor IDs. Before has no synthetic bubble; after has one at N+2, not at Q. Question preview/content unchanged. | **Recording:** cold navigation/hydration; history scroll; hot mounted prefix→answer event; answer→ack; route away/back/reload; duplicate delivery must not duplicate row. Show live-follow at end, then insertion while scrolled in history without a jump to end. | Prefer replay of sanitized own export into isolated seeded real app. Web route uses supplied project/thread IDs after seed; mobile normal environment/thread route. Genuine live repro described below establishes runtime provenance. If hot replay needs a new deterministic driver, label driver but keep production routes, stores and renderer. |
| A — Immediately adjacent next-run answer | Affected. N ends with unresolved async question; no queued/intervening run; late answer is first user item of N+1 with acknowledgment. | Q/answer/ack may be adjacent in pixels; still must show new answer bubble and correct later run chronology. No accidental fold merely because adjacent. | **Recording:** finish N, answer, begin N+1, reveal card and bubble, verify no second bubble on echo. | New real thread with same request after N finishes and no queued follow-up. For deterministic capture, derive a separate coherent two-run seed, remapping all related run/node/position rows; never just change the displayed title. |
| S — Answer steers a different active run | Affected. Q belongs N; another run M is actively producing output; answer is dispatched with `steer` into M, not Q. | Active-state answer bubble with steer marker, active work/streaming reply, unchanged answered Q. Then settled state maintains chronological position. | **Recording:** Q remains available → M begins → submit Q answer → steer bubble/ack → M settles. Show queue count/composer/working control and scroll behavior. | Real Codex session that supports active steering; queue a follow-up long enough to answer while M is running. Static replay must carry actual run/attempt/session capability data, not fake a badge. EAS static mode must stage active/settled snapshots and annotate replay; it cannot prove provider steering. |
| C — Same-run fold and map transition | Control. Async Q answered while its own run remains active; same-run synthetic reply present in projection; answer history remains on card. | Before/after card preview AND expanded history identical; no separate async user bubble and no duplicate Find document. | **Recording:** mounted unanswered Q → answer/Q-map update → provider acknowledgment; expand/collapse card; cold reopen. Same request ID with reassociated run value is K, not a live-data requirement. | Real same-run answer before N finishes. Use valid nonempty question/options; do not copy tests' `questions: []` for a convincing UI. Deterministic snapshot replay may keep session stopped. |
| L — Long formatted later-run answer | Affected. A-style later run; multiple question/answer blocks, >600 visible chars, >8 lines, a wide markdown block and normal text. One unique token near end, absent from assistant ack. | Collapsed web state and revealed state; native wrapped/pinned-width body and final line. No horizontal overflow, clipped marker/timestamp/copy control or duplicated question history. | **Recording:** submit or replay; web expand/collapse and Find reveal of tail token; native scroll through body and copy; web copy; fixed-clock stable checkpoints. | Real free-text answer supported by async input; use minimal deterministic markdown. Real isolated seed for both builds. Static native fixture must use production ThreadFeed, not a recreated bubble. No fabricated attachments/context/automation attributes. |
| F — In-thread Find reaches late answer | Affected WEB; server SQL/memory semantic coverage from tests. Use G or L data with answer-exclusive token. Also query a token in answer + a normal assistant message to make multiple results. | Before answer-exclusive query has no answer match; after exact answer entry/run highlighted, counter correct. Compare selected answer, final count, and revealed long body checkpoint. Multiple-results query gains exactly the answer occurrences, excluding Q history from server documents. | **Recording:** start offscreen/folded → open Find via command/keybinding → type exclusive token → loading/counting if observed → scroll/reveal correct run → next/previous/Enter/Shift+Enter and wrap → close/Escape returns composer focus. Run progressive and nonprogressive config paths; settled pixels can be shared, transitions cannot be inferred. Include blank/unrelated zero and same-run control query. | Real web Find on seeded real server. Keep token out of Q text/card and assistant response for an unambiguous 0→1. Lead's phrase repeats in Q history, so create a safe answer-only token or document exact server-vs-DOM counts. Config flag selects progressive service. Forced error/latency needs labelled RPC fault harness, not production seeding. |
| N — Native search opens thread containing answer | Control service with affected destination. Home/sidebar/palette global search snippet can already match the persisted answer before. | Snippet before/after unchanged; after opening and scrolling, bubble exists at answer run. Before may show acknowledgment only. Native automatic answer-level Find remains unavailable. | **Recording:** enter global search → inspect excerpt → select result → real thread route → scroll to answer; then back and repeat alternate entry points where reachable. Label manual scroll. Do not caption this as in-thread Find. | Real ThreadSearch over seeded projection_messages; Home for phone, sidebar for wide native layout, command palette entry point if keyboard available. With offline static fixture lacking server search, label result stub/harness and leave real native search unverified. |
| Q — Empty/loading/error and unanswered controls | Control. Unanswered question; unrelated empty thread; detail/history loading/unavailable; Find error + retry if forced. | Same display/chrome before/after; no placeholder regression caused by insertion. Stable captures only where an actual state is reached. | **Recording:** route/connection/history transitions; retry must work if shown. | Prefer normal empty route and controlled isolated transport disconnect/reconnect. Fault injection is labelled harness. No fabricated “cross-run answer is entire empty conversation” scenario. |
| B — Sparse counts/setup/history boundaries | Indirect control. Valid initial prompt + later answer with Q outside current history window; test user-count boundary, setup anchor and submitted-message anchor. | Correct setup retirement/working control, history load and first visible bubble; no wrong remount/blank hero after answer appears. | **Recording:** load earlier activity to deliver Q, retain position; setup/submitted anchor only if reachable in a valid stopped fixture. | Real history pagination with enough earlier items. Worktree setup overlap is not established by brief; use labelled minimal data harness if impossible, or record test-only equivalence/unverified status. Do not hold a live setup/provider running only for screenshots. |
| K — Cache-map/replay boundary harness | Control, no distinct final visual beyond G/C. Reuse same mounted caller; map changes from absent→answered, same key different run value, key removal/restore; anchored duplicate then committed snapshot; ordinary unmatched message/null run cases. | At each step cold rebuild and warm cache output agree; retained question/card and one correct message. Final pixels reuse G/C if identical. | **Recording REQUIRED if shown visually:** annotate each snapshot update and mounted instance/cache identity. Supporting serialized feed IDs/test transcript are needed; a screen cannot prove cache identity itself. | Labelled production-store/projection replay harness if normal mutations cannot create these boundary states. Focused tests are the better proof for impossible reassociation/null/orphan shapes. No need to invent new user flow or native Find screen. |
| I — Existing row actions and disclosures | Control on newly exposed rows. Copy, timestamp/action visibility, long collapse, Q history disclosure; checkpoint/revert ownership (turn_start/queued_turn eligible, steer and inherited rows ineligible; unsettled runs excluded), native settled-turn/work disclosure. | Controls usable and attributed to answer run; no unintended edits to Q or prior run. | **Recording:** show copy feedback and verify copied text; web hover/focus timestamp/actions; open revert confirmation (cancel) if available, do not execute destructive revert; native long/collapsed-turn disclosure and return. | Piggyback G/L/C production rows. If late turn has no valid checkpoint, report action unavailable; do not fake a revert count. |

### Minimum set and geometry expansion

Five distinct timeline fixtures are necessary: G, A, S, C, L. G/A/S cannot be collapsed into one history: queued gap, adjacency and active steering expose different ordering/group/intent/working states. C is the reverse-state control. L adds the only materially different reachable body layout. Q/B/K/I are controls or extra checkpoints inside those same recordings, not four more full fixture grids. F reuses G/L. N reuses G. Common idle/cached snapshots share final pixel pairs only after data/output equivalence is demonstrated; never eliminate a transition recording.

- **Web required grid:** viewport **1440×1000 CSS px** and **390×844 CSS px**, each in light and dark (default palette). Browser/device emulation at 390 is still web. Freeze DPR, zoom=100%, loaded fonts, base font size, timestamps, timezone, locale, reduced-motion setting, scrollbar behavior, sidebar/panel state and scroll anchor. Record exact browser/OS build. 5 fixtures × 4 cells = **20 timeline checkpoint pairs**, plus the necessary expanded-card/long-body/active→settled context checkpoints. F has one navigation recording per grid cell, with progressive/nonprogressive substeps; selected Find pairs are additional unless already indexed in those clips. Reuse recording segments, not substitute stills for interactions.
- **Native iOS required grid:** a real EAS iPhone target at its native logical viewport, preferably **390×844 pt** (for example a compatible 390pt device, report its actual identity), light and dark; a real native wide/tablet target, preferably **iPad Pro 13 landscape 1376×1032 pt** if EAS offers it, light and dark. Report actual point bounds, framebuffer pixels, DPR, safe-area insets, RN/OS/native build, text scaling and available pane width. 5 fixtures × 4 native cells = **20 timeline checkpoint pairs**, plus transition/disclosure checkpoints. If EAS lacks the proposed devices, use available native devices and state exact substitute sizes and any missing wide-layout coverage.
- **What 1440/390 mean for native:** 1440 and 390 are the WEB CSS widths. Native uses points and actual device layouts. A native 390pt iPhone can happen to match the narrow logical width; framebuffer could be 1170px at DPR 3. A tablet is the native wide counterpart; 1376pt is not 1440 CSS px and is not an exact-width substitute. Do not resize screenshots or pass a 390px web capture off as native. If an exact 1440pt native viewport is unavailable, explicitly report it as not exercised. A phone-only native capture leaves split/wide layout unverified.
- **Fresh/cached minimum:** both SHAs and both native themes must include (1) complete G fresh mount, (2) same-mounted-instance answered prefix→late answer, and (3) unanswered→answered Q map update. Cold launch/reopen after hot updates must converge. Reuse the same fixture data on the wide target, but demonstrate it uses the production cache caller. A restart between prefix and answer invalidates the claimed cache proof.
- **Q/B/K controls:** include a stopped failed/interrupted run snapshot in the same representative native control recording to check failure-specific ungrouping and turn disclosure; keep its answer position correct. Record once per production surface on a representative geometry/theme when reachable; unchanged branch logic and identical named fixture results justify no full width/theme Cartesian product for fault/null/reassociation cases. Do not mark controls passed if not run. C remains in the full grid because same-run folding is the central invariant.
- **N:** phone Home and wide sidebar in both appearances; palette may be a substep in one recording per appearance. No answer-level native Find claimed. No need to cross-multiply global search faults because its service is unchanged.

These are lower bounds on independent evidence states, not a target number of files. Use one recording with multiple timestamped checkpoints to avoid a file explosion. If evidence is combined, the index must still enumerate all fixture × build × geometry × theme × fresh/hot branches.

## Seeding and reproducibility handoff

This pass did not edit seeders, modify a database, start a server/build/simulator, capture media, use a browser, comment, push or create a PR. Only this artifact outside the worktree is written/uploaded. Seeder implementation belongs to the authorized capture task.

### Preferred real web seed

The lead's own `/tmp/15873-runtime-fixture.json` is available locally (~90KB). It contains one project/thread; 3 runs, 3 attempts, 10 nodes, 1 runtime request, 7 messages, 14 turn items/positions, checkpoint/session/provider tables; no auth/events/outbox. This was structurally inspected without writing it. Do not upload the raw export. Minimize/sanitize content and identifiers first, including nested payloads, native provider IDs, workspace path, command output, project settings and metadata. Absence of auth/events does not itself establish public-safety.

Use one deterministic seed definition and same fixture inputs for both SHAs. Seed only disposable state while its server is stopped. Never point at or modify live `~/.t3/userdata`. Preserve/validate all relational and payload IDs (including request→synthetic message ID), run ordinals, projected positions, attempt/node links, thread latest/active pointers, request status/answer, message and turn-item text agreement, and source/fork identities. Both the turn-item projection and projection_messages must agree: timeline and global search read different projections. A seeder that writes only messages may make search pass while the UI still fails. Use migrated schema and existing showcase/fixture seeding patterns rather than inventing a second rendering pipeline.

The existing isolated state is `.t3/userdata/statev2.sqlite`; lead reported its server stopped and ports clear. Treat that as handoff information, not a current availability check. Prefer copies/minimal stopped fixtures. Starting a server against full active session/export state can resume work: sanitize runs and pending effects/requests intentionally for static captures, or use deliberate replay into an isolated fixture transport. Document any runtime-status change from the original repro. No tokens, pairing links or credentials in public artifacts.

For G genuine runtime provenance, recreate the lead flow if needed: Codex GPT-6 Astra, foreground sleep 60s, then async Blue/Red question and finish run N. Queue a follow-up during the sleep before the question occupies the composer. Finish N+1. Answer the old question; it starts N+2 and gets acknowledged. Before build persists the answer but omits its bubble. Freeze the resulting data for comparisons rather than asking the model to generate two supposedly identical histories. Do not block the audit/capture channel for the entire sleep; this audit executes none of it.

The lead's existing project/thread route is local-only:
`http://localhost:8486/fa4d5014-437f-4f1b-803e-de3a2f9bbe23/15665bac-b649-4006-98ab-7ac988064b10`.
Use remapped sanitized IDs if committed. Route/port must come from the capturer's isolated environment, not be baked into code or assets. Browser lead tab is a handoff only; this audit does not inspect or reuse it.

### Native EAS/static fixture handoff

Lead reports app-owned device access is disabled (“Agent device access is turned off for this environment.”); EAS authentication and availability were checked, no builds/sessions started, and no `ios.simulator` profile exists. These are reported prerequisites, not checks performed here. The user explicitly authorizes EAS iOS static fixture mode for the capture task. This audit does not create a profile or start EAS.

Existing `EXPO_PUBLIC_SHOWCASE=1` is a coordinator for real app routes paired to seeded servers, not an existing offline static answer-fixture renderer. `src/test-fixtures.ts` contains test shell builders, not a shipped static fixture mode. See [mobile showcase operations](https://git.cafe/pingdotgg/t3code/blob/7d869fd13b74f7cc040ad23f375ab01f36bc9afe/docs/operations/mobile-app-store-screenshots.md). Do not assume setting SHOWCASE alone supplies G/A/S/C/L data offline in EAS.

Prefer real sanitized seeded app data over a renderer-only harness. If static mode is required, the capturer must make a clearly labelled deterministic data/transport harness that mounts the production application route, stores/composer caller and `ThreadFeed`, and stages cold and hot projection updates. Native static recordings can prove native rendering, cache transitions, layout and local actions; they cannot prove real RPC Find, global search, network/provider dispatch or genuine steer delivery when those are stubbed. Keep that limitation next to each affected row, not only in a global footnote. A stub that precomputes a feed or paints a custom bubble is insufficient.

Build both SHAs with the identical minimal fixture/capture overlay and sanitizer; record the overlay commit/hash separately from product SHA, dependency lockfile and native build ID. Temporary simulator profile/new static native builds may be required. Commit the sanitized seeder/data if requested by the capture owner; never commit evidence-only assets. This read-only audit commits nothing. If fixture overlay introduces UI or changes logic under review, proof is confounded and must be corrected.

### Clock, scroll and readiness

Use one recorded UTC epoch and identical fixture timestamps on both builds; freeze presentation clocks while allowing deterministic event/animation progression for recordings. Do not freeze timers in a way that prevents transitions. Set system/native status-bar clock or declare its pixel region if it cannot be controlled. Record exact fixed epoch and playback step times. Prefer old fixed message timestamps to avoid different freshness animations in settled stills; still record actual insertion in transition clips.

Readiness means production hydration/projection milestone and loaded fonts/media, not an arbitrary sleep. Each checkpoint uses named entry ID + top-offset scroll anchor, saved panel/fold/composer/keyboard state and same source snapshots. Record scrollTop/content offset as well as anchor; added content changes total height, so blindly matching end offsets is not equivalent framing. Show both history-preserving and end-follow modes. No animation frame may be selected solely to hide a transient regression.

## Evidence contract for capture and pass 2

For every affected row/checkpoint, publish: **Before | After | Diff0 (amplified, zero threshold) | DiffT (explicit threshold)**. Caption with both full product SHAs, fixture hash, renderer/device, DPR/font scale, physical and logical dimensions, theme, frozen epoch, scroll anchor/offset, and overlay/native build identity. Upload captures and comparisons through ghdrop; no PR-only assets committed. The operational EVIDENCE.md source was not found by the lead; this follows the user's supplied standard, without claiming another runbook was read.

For recording rows, place ordered steps and actual clip timestamps **above** the corresponding still pair. List original clips as well as selected frames. Each build must have its own replay of the identical script. Include active/settled, answer submit/echo, scroll, Find reveal, disclosure, copy and retry transitions wherever the row demands them. A video of only the final still is insufficient.

Compute Diff0 on identical-size original lossless screenshots (per-pixel channel differences, zero threshold; amplify for visibility). Publish changed-pixel count and algorithm/amplification. Compute DiffT with a declared channel/color threshold and definition before assessing the result; publish threshold, count and method. Do not silently mask text, crop chrome, rescale native output, shift/register images to remove layout differences or change tolerance until they pass. If nondeterministic system chrome must be excluded, publish full unmasked Diff0 and explicit region/mask alongside, with reason. Thresholded zero is not proof of no change.

Write down **every visible difference**, intended or unintended: added bubble, shifted later content/scroll extent, work/turn grouping changes, steer marker, reveal/expanded body, Find counter/highlight/selection, composer or setup chrome, status bar, text rasterization, loading indicators. Claim “nothing else changed” only for exactly zero unexplained pixels at Diff0. Expected layout movement is still a difference, not an excuse to omit comparison. Controls should be exactly equal after deterministic setup; otherwise explain every residual.

Pass 2 must independently rebuild at least one selected capture and pixel comparison, preferably G at 390/light plus F answer navigation if time allows, from committed sanitized seed/overlay and recorded full SHAs. Download/check original input hashes, rerun comparison and verify result counts. A contact sheet without reproducible originals and seeder cannot satisfy this. The audit matrix is not itself visual proof.

## Unverified limits and disposition

- No browser/native session, capture, seed write, native build, SQL execution or focused test execution occurred in this audit. Lead's runtime repro/export structure was examined; its screenshot and test claims are not independently recertified.
- Android and Electron shell/IPC are unverified. Desktop wraps the changed web path, but no native shell claim follows from web captures. Native Android has separate layout/keyboard behavior; iOS evidence does not certify it.
- Remote/relay/tunnel modes are unverified; no transport or contract changes in this diff. Deterministic local seed validates rendering only unless a real remote route is explicitly recorded. Providers other than the reproduced Codex path are unverified; change is generic projected-item logic, not adapter code.
- Exact 1440pt native width may be unavailable; report actual native phone/tablet bounds and missing width coverage. Native answer-level in-thread Find is not present in the reviewed source. A native static harness cannot turn this into production feature proof.
- Cache reassociation/null/orphan/inherited histories can be tested/harnessed but should not be manufactured as ordinary user actions. Any skipped observable row remains named/unverified rather than silently merged into “covered by tests.”
- **Disposition:** evidence-only gap remains. Capture owner should implement the minimum seeded real-app matrix, label native static/transport harness portions honestly, and provide recording plus pixel-comparison evidence before proof audit pass 2. No code-change request is introduced by this pass.

FRICTION — tool: native device access disabled per lead; EAS alternative is capture-owner work. brief: native answer-level Find requested but no production consumer found; 1440/390 require CSS-vs-native-point distinction. runbook: source EVIDENCE.md absent per lead; used explicit user standard. infra: native simulator profile/static fixture mode and both builds still required. forge: none; no forge CLI/API calls needed. policy: READONLY/no browser/server/capture respected. model: no independent execution of the reported 105 tests. other: none.

End of pass-1 matrix — derived requirements only; no rendering pass claimed.
