# Proof audit pass 1 — [#33347](https://git.cafe/pingdotgg/t3code/pull/33347)

Audited head [697288fc8e](https://git.cafe/pingdotgg/t3code/commit/697288fc8edfaa6cda2a731a0b4c080359635c34) against merge-base [d1aeed2492](https://git.cafe/pingdotgg/t3code/commit/d1aeed249288585390dbac4fde5b54c35bd5fc06). Read-only audit; no repository edits, pushes or comments. All 76 directly linked image/video assets were downloaded successfully with curl and inspected as original stills and timestamped video frames; the supplementary P12 bottom, P18 ordinary and P15 iOS clip were followed through the linked inventories. Drivers and protocols were also read. No device, browser tab or server was started.

## Findings first

1. The core fallback is shown on web and native. Existing evidence is useful functional proof; do not discard it or repeat every payload on every platform. The current pairs are not controlled visual comparisons: separate turns change elapsed labels/narration/timestamps/hover and can change group composition. F01 hides the actual change inside a closed fold. F02/F03/F06 and a combined empty/mixed/failed fixture need only one deterministic replacement set per distinct viewport/theme. D1/D2 below specify how to get it without further model variation.
2. Native before captures are **not automatically required** for P09–P14. The old mobile implementation returned an app entry before constructing its activity for every valid app reference, independent of output shape or failure status. The Android F08 before/live recording, source diff and passing mobile regression tests establish that omission; actual native after evidence proves platform rendering. P10, P11, P12 and P14 have sufficient after evidence for their distinct shapes. P12's marker is visible in the linked inventory's bottom screenshot, not in the top screenshot. P09 lacks a successful Android TSV row reopen. P13 lacks native controlled detail-state proof, not a native before screenshot.
3. P13's web harness genuinely shows loading → error → success; it must stay labeled a renderer harness. It cannot establish native output state presentation, a live network retry, or the app-card lifecycle. Null/unavailable detail is a separate branch not shown.
4. P18 shows the app's new fallback and ordinary text after. It does not isolate the ordinary result-normalization change on identical raw input. P19 before has no row at all, so it cannot show the alleged former truncated-wrapper detail. The latest small-image preservation branch is tested in code but not demonstrated by an actual detail-RPC transcript; an app that draws only its structured table is not evidence that it received image bytes.
5. The new two-entry mobile cache path, native settings cycle, true native relaunch and inherited/older app history lack direct proof. These can be combined into one short native scenario with fixed data, plus cache assertions. They do not need payload × platform × theme multiplication.
6. Focused verification run during this audit: five changed suites, **146 tests passed** in 6.94s. The new mobile app test does not pass a feed cache, so it cannot substantiate the incremental-restart claim. Working tree remained clean.

## Render-path inventory derived from the diff

- [session-logic.ts](https://git.cafe/pingdotgg/t3code/blob/697288fc8edfaa6cda2a731a0b4c080359635c34/apps/web/src/session-logic.ts): changed derivation is used by the hook in ChatView (quiet/anchoring/history consumers), ThreadTimeline (live MessagesTimeline) and GuestPage (guest source). Valid matching app references now emit work then card; completed html_render still wins; ordinary/malformed/mismatched app calls emit work only. Unique card ID changes reconciliation. A prior app entry blocks the single-entry in-place reuse path; full projection, append, replacement and app removal matter.
- [threadActivity.ts](https://git.cafe/pingdotgg/t3code/blob/697288fc8edfaa6cda2a731a0b4c080359635c34/apps/mobile/src/lib/threadActivity.ts): use-thread-composer-state builds the native feed for ThreadDetailScreen → ThreadFeed. Each projected row/cache entry now contributes activity then card. Attempt IDs, row identity, cached prefix/restart and activity-group flushing are relevant. ThreadFeed sends sourceThreadId/sourceItemId/revision/width to ThreadMcpApp; row details use the separate native work-row renderer. Loading/No output/fetch error/unavailable, single-tool versus integration group, run fold, failed status, visibility preference and touch scrolling are distinct native paths.
- [itemDetail.ts](https://git.cafe/pingdotgg/t3code/blob/697288fc8edfaa6cda2a731a0b4c080359635c34/packages/client-runtime/src/work-log/itemDetail.ts): turnItemOutputText is used by web V2ItemInspector (from TimelineWorkEntryRow), including guest source overrides, and by native thread-work-log. App envelope + result is unwrapped; non-envelope output keeps old behavior; outputOmitted stays null until fetched. content text/TSV/resource strings, JSON/structured/scalar values, empty values, image markers, bounded array results and metadata are input classes. Syntax highlighting, word wrapping and bounded output scrolling make text/JSON and short/long visually distinct; empty and failed presentation need their own fixtures. No new CSS or theme branch was added.
- [toolOutput.ts](https://git.cafe/pingdotgg/t3code/blob/697288fc8edfaa6cda2a731a0b4c080359635c34/packages/shared/src/toolOutput.ts): nonempty structuredContent wins, image-bearing content is retained alongside structured data, empty object/array falls back to content, absent/null follows the same fallback. Used by app detail formatting and Codex/Claude/ACP ingestion; provider names do not create distinct client layouts. Existing providers without app capture use the ordinary path; do not run paid providers to multiply equivalent UI evidence.
- [WireProjection.ts](https://git.cafe/pingdotgg/t3code/blob/697288fc8edfaa6cda2a731a0b4c080359635c34/apps/server/src/orchestration-v2/WireProjection.ts): projectTurnItemForDetail runs for normal ThreadManagementService.getTurnItem and GuestShareService.item. Valid app envelope keeps ref + result unchanged if the result fits 256 KiB; oversize goes through image stripping/bounding. Invalid/absent reference takes generic bounding; missing result takes generic output branch. Image markers are retained after truncation. Small preserved images, large stripped images and invalid refs are transport assertions, not separate native screenshots.
- Unchanged descendants include app-frame network errors/loading, close/reopen, fullscreen, custom heights/CSP/permissions. Keep one retained card as a control and exercise identity changes in P21; this diff does not justify re-testing every pre-existing app-host action. Local/remote/relay share typed detail requests; no connection-mode code changed. Desktop wraps web. The SwiftUI client is not changed by this diff.

## Deterministic seeding recipes (apply the row's data below)

**S (completed persisted real-client fixture):** create an isolated project and thread with existing `projects.mutate` / `orchestration.dispatchCommand` (`thread.create`). Seed one immutable synthetic completed run with a user message, fixed tool item IDs and timestamps (e.g. start 2026-11-10T08:00:00Z, finish +5s), fixed assistant reply, and matching `t3McpApp` reference `{attachmentId:"fixture-app",server:"repro",tool:"aggregate_rum_events",resourceUri:"ui://repro/dataviz"}`. Store a local synthetic app HTML attachment under that isolated home. Use the actual server service/event path (`EventSinkV2.write` or `ProviderEventIngestorV2.ingestNormalized`) in a scratch driver, or create once with the already-uploaded synthetic MCP server then copy **the same stopped isolated database and its attachments** to both sides. Open `/<environmentId>/<threadId>` or the named thread in native. Full records must retain raw wrapped result; let each checkout generate its wire/detail output. Do not independently prompt a model on each side. Do not copy live userdata. Direct projection SQL is acceptable for labeled ephemeral visual fixtures only and does not prove ingest/event behavior.

- **S-TSV:** input `{query:"@type:view"}`, result `{content:[{type:"text",text:"Notice\nview\tcount\n/home\t42"}]}`; app intentionally ignores TSV and stays blank.
- **S-JSON:** result `{structuredContent:{rows:[{view:"/home",count:1204},{view:"/checkout",count:311}]},content:[{type:"text",text:"same JSON"}]}`; app draws table. JSON text only and raw scalar/array/object outputs are formatting equivalence cases, not an additional screenshot cross-product.
- **S-LONG:** 48 fixed rows named `/synthetic/customer-journey/checkout/step-01` through `step-48`; long query and tool/integration label. No random data.
- **S-EMPTY:** input `{}`, result `{content:[]}`, one app and no ordinary command. Null/undefined/whitespace/empty arrays/objects collapse to this visual output class.
- **S-MIXED:** structured rows plus text, fixed 1px PNG image, resource_link, and `_meta`; marker must be visible after scrolling. With structured+image normalization the JSON and marker are expected, not all discarded redundant text/link content.
- **S-FAIL:** failed item and `{isError:true,content:[{type:"text",text:"Deferred: review required; no write performed."}]}`.
- **S-VIS:** same fixed run containing one S-JSON success and one S-FAIL plus ordinary command. Change actual client setting, not CSS/DOM hiding.
- **S-CONTROL:** ordinary tool, absent/malformed/mismatched app ref, completed html_render. Use projection/parser assertions to keep control proof cheap.

**L (live sequence):** use a scratch service driver with real normalized/event publication and explicit release gates. Emit running item without output; release completed wrapped result; end run; then mutate that same source item to update result/remove/replace app and append a command. Keep one sibling app unchanged. Capture status transition and open/close actions as a recording. Compare cached projection output with a fresh rebuild for each step. A client-only injected fixture must be labeled a harness. The shipped `Replay (dev)` scenarios (`showcase`, etc.) do **not** contain this MCP-app fixture: a custom transcript/driver is required. Do not advertise `showcase` as automatically covering app references.

**H-DETAIL/H-GUEST:** controlled real-component source with a held response, error, null, then the fixed full item; record the state changes, identify the item/revision in driver logs. GuestPage already has an uploaded fixed-clock stub stream and guest-detail fixture. These are labeled harnesses unless their real transport is exercised.

**N/B (normalization/boundary):** re-feed the exact raw MCP result through each checkout's normalization/ingest seam; copying an already-normalized ordinary item from the after DB would erase the normalization comparison. For B, fetch `orchestration.getTurnItem` for small/large/invalid-reference app results and print only byte counts/presence, normalized text and reference validity. An image-aware app may display `received-image-bytes: N`; never print base64. Compare the latest bound fix with its parent (and retain main-to-head fallback proof), so a missing prechange row does not mask a bound regression.

**R:** same S fixture persisted once, actual reload/relaunch/history/fork navigation. For inherited history use actual `thread.fork`; for edge attempt/runless metadata use a labeled projection harness. Do not turn repeated provider runs into different fixtures accidentally.

For new paired stills: same data, font scale, viewport, theme, DPR, fold/group/row state, pointer, scroll position and app HTML. Completed start/end times remove elapsed differences without clock emulation. Fix clock or normalize only OS chrome if needed; do not mask content/narration to hide uncontrolled input. Provide pair plus Diff0/DiffT. Preserve existing evidence as functional clips. Interaction/time rows require recordings; stills cannot replace them.

## Fixture matrix

`Already-covered functionally` means the stated behavior is visible, not that a noisy pixel diff is isolated. `Missing` control-only captures explicitly marked nonblocking are not requests for more images. Row IDs retain the body aliases; F05 is deliberately not invented because no body fixture defines it.

| ID | Named fixture and visually distinct data | Diff affected? | How to seed | Minimum viewport × theme × row / evidence type | Existing coverage and action |
|---|---|---|---|---|---|
| F01 | Settled TSV app, outer fold closed; blank 320px app remains outside fold. One app + ordinary command. | Affected row hidden; card is control. | S-TSV, closed. | Web 1440×1000 dark still pair; existing 1440 light and 390×844 dark are bonus. | Already-covered by [dark before](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/fa9078d6d371cdde/F01-web1440-dark-before.png) / [dark after](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/4d810cd774b46bc5/F01-web1440-dark-after-final.png), [light before](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/7801178802536553/F01-web1440-light-before.png) / [light after](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/132711b6704ba61e/F01-web1440-light-after.png), [narrow before](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/43e87c3f82949089/F01-web390-dark-before.png) / [narrow after](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/3541502015e71814/F01-web390-dark-after.png). Thresholded differences are primarily the elapsed label (Diff0 also has composer raster noise); these do not prove the hidden row. |
| F02 | Settled TSV app; open outer fold, integration group, tool row; close and reopen row/group/fold. | Affected. | S-TSV; add one ordinary command to create the integration group. | Web 1440×1000 dark RECORDING pair; one settled open still pair. | Already-covered functionally by [before recording](https://git.cafe/api/repos/pingdotgg/t3code/attachments/attach_01m722n7hnendba5fpby1xjhgn) / [after recording](https://git.cafe/api/repos/pingdotgg/t3code/attachments/attach_01m722n8r8endba5frbj4vzf6r) (after 36s open, 40s closed, 42s open, 44s outer closed, 47s open). Insufficient for an isolated visual comparison: narration, timestamps and elapsed labels vary. Include this in D1. |
| F03 | Nonempty structuredContent table; expanded JSON output; successful app still draws same 2-row table. JSON-text-only and generic/scalar JSON results collapse to this visual class. | Affected output plus app control. | S-JSON; variants content JSON text only, structured-only, ordinary JSON scalar/object. | Web 1440×1000 light still pair; disclosure is RECORDING, shared mechanics already covered by F02. | Already-covered functionally by [before still](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/df98582738b1d0f5/F03-web1440-light-before-take2.png) / [after still](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/d518f561c5b5032e/F03-web1440-light-after-final.png) and [before clip](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/19a94666ca71126e/F03-web1440-light-before-take2.mp4) / [after clip](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/9aec43ba72ed7b18/F03-web1440-light-after-final.mp4). Clip does not show claimed close/reopen; F02 supplies that common interaction. Insufficient isolated still pair (elapsed/hover differs); include in D1. |
| F04 | App absent, invalid reference or server/tool mismatch; ordinary MCP and completed html_render precedence. No app card for invalid/ordinary call; HTML remains standalone. | Control, except empty structured ordinary result belongs to P18. | S-CONTROL: plain tool; invalid t3McpApp; mismatched tool; completed html_render. | No additional visual capture required: parser/projection assertions are the appropriate proof. | Missing dedicated captures, not a blocking visual gap. Existing ordinary-command paths are visible in F02/F08; unchanged HTML/reference gates precede the changed code. Do not expand the app-host feature matrix. |
| F06 | Long TSV (48 numbered rows), long query and long tool/integration name; output height exceeds 320px; inner output scroll followed by outer feed scroll past retained app. | Affected. | S-LONG: 48 fixed TSV rows plus 160-character query; same data on both sides. | Web 390×844 dark RECORDING pair plus open still pair. | Already-covered functionally by [before still](https://git.cafe/api/repos/pingdotgg/t3code/attachments/attach_01m722na24endba5g37zfrhp3z) / [after still](https://git.cafe/api/repos/pingdotgg/t3code/attachments/attach_01m722nb39enbbxq8kdfgwk6vj) and [before scroll](https://git.cafe/api/repos/pingdotgg/t3code/attachments/attach_01m722ne0xenbbxq8tcwmkh2v0) / [after scroll](https://git.cafe/api/repos/pingdotgg/t3code/attachments/attach_01m722nf6tendba5gv986v8k9w) (after 6–8s includes row 48 and feed beyond app). Insufficient isolated pair because narration, timestamps, 13s/15s elapsed differ; the current tool label is not a long-label stress case. Include the long-label variant in D1. |
| F07 | Pending/running call without app reference → completed reference → settled fold; one row and one card, no duplicated or stale row. Also failed completion in P14. | Affected time path. | L-SEQ; explicit release gates rather than model-generated timing. App reference is data-gated, not intrinsically completed-only. | Web 1440×1000 dark RECORDING pair. | Already-covered functionally by [before lifecycle](https://git.cafe/api/repos/pingdotgg/t3code/attachments/attach_01m722n7hnendba5fpby1xjhgn) / [after lifecycle](https://git.cafe/api/repos/pingdotgg/t3code/attachments/attach_01m722n8r8endba5frbj4vzf6r). Running and completed phases are distinguishable, but separate turn timing/narration prevent deterministic frame comparison. D1 can include this; do not retake solely to match seconds. |
| F08 | Two app calls interleaved with ordinary commands: TSV blank app then structured table app; native live and settled order. | Affected count/group/cache boundary. | L-SEQ: command → TSV app → JSON app → command, fixed reply. | Android 411×923 dp dark RECORDING pair; settled pair is supporting only. | Already-covered functionally by [native live before](https://git.cafe/api/repos/pingdotgg/t3code/attachments/attach_01m723ht4pendba5r2vqq248t1) / [native live after](https://git.cafe/api/repos/pingdotgg/t3code/attachments/attach_01m722wyfhendba5hzh2frhyzw); [closed-fold before](https://git.cafe/api/repos/pingdotgg/t3code/attachments/attach_01m723bactenbbxq96aqv4zf4p) / [closed-fold after](https://git.cafe/api/repos/pingdotgg/t3code/attachments/attach_01m723bbqdendba5q2xt4as78k) cannot show hidden rows. No extra native baseline per payload is necessary. Cached update/remove path remains P21. |
| P09 | Android: open settled fold/group; TSV row open → inner scroll to final row → close → reopen; JSON row to closing brace; card retained. | Affected native interaction. | S-LONG + S-JSON in same thread; target item-specific row, not ambiguous duplicate accessible label. | Android 411×923 dp dark RECORDING after; existing F08 is adequate native baseline. | Insufficient: [TSV clip](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/d1a6258186d587e5/P09-android-dark-after-take2.mp4) shows TSV rows through /view-32 and outer-fold reversal, but TSV close/reopen fails (also admitted by protocol). [JSON supplement](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/3fadf485dfe3eb1c/P09-android-dark-after-json.mp4) covers complete JSON. Add only a successful TSV row close/reopen after recording. Native before not required: old unconditional app early-return and F08 baseline prove absence independently of payload. |
| P10 | iOS: open fold/group; TSV inner scroll to final row; tool row and outer fold close/reopen; retained app. | Affected native interaction. | Same S-LONG state used for Android, opened on iOS. | iOS 402×874 pt dark RECORDING after. | Already-covered by [iOS TSV](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/9122a612ff92d14a/P10-ios-dark-after-take2.mp4) and [reversals](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/7fe4060921aa49c4/P10-ios-dark-after-reversals.mp4) (final TSV row, row collapse/reopen, outer fold closed at ~62s and reopened by ~66s). Native before not required: changed feed/output code has no platform branch; F08 baseline + mobile projection test establish old omission, this clip proves iOS touch/render path. No separate iOS JSON pair required. |
| P11 | Single app call with input {}, content []; no other tool; direct row, No output, blank app retained. | Affected count=1/empty/ungrouped path. | S-EMPTY: input {}, result {content:[]}, fixed completed run. | Web 1440×1000 dark still pair; Android dark after still. | Already-covered functionally by [web before](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/09bddb08d745f0c9/P11-web1440-dark-before.png) / [web after](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/f74d78ac589fd72c/P11-web1440-dark-after.png) and [Android after](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/501e675c510bbaae/P11-android-dark-after.png). Native before not required: same early-return baseline, payload null result formatting is shared, native after shows direct-row No output. Include web shape in deterministic combined D2; no new native empty capture needed. |
| P12 | Mixed result: nonempty structuredContent + text/image/resource-link + metadata; JSON then [image], no wrapper/base64, app table retained. | Affected output and small-result preservation boundary. | S-MIXED: fixed 1px PNG, text, resource_link, _meta, structured rows. | Web 1440×1000 dark still pair; Android 411×923 dp light after still at output bottom. | Already-covered functionally by [web before](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/5c50c2abb6266fc2/P12-web1440-dark-before.png) / [web after](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/50bfb09649d95f0b/P12-web1440-dark-after.png), [Android top](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/4366f84dbaadf6a9/P12-android-light-after.png) and [Android bottom (linked inventory)](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/4bd306bbf88d0076/P12-android-light-after-bottom.png). Bottom shows [image] and table. Native before not required: same structural omission, shared formatter test, actual native light output captured. Include web shape in D2. Does not prove app receives image bytes: P20. |
| P13 | On-demand detail states: omitted wire result and summarized input; pending, fetch error, missing item, success. Output fallback differs between web and native. | Newly reachable row; fetch-state renderers unchanged. | H-DETAIL: actual inspector or native work row, controllable Deferred response → failure/null/success with same item/revision; never use arbitrary real-network timing. | Web 1440×1000 dark RECORDING harness; Android dark RECORDING harness/integrated controlled source for distinct native renderer. | Web loading/error/success already-covered by clearly labeled [actual V2ItemInspector harness](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/37951e9c54129b60/P13-web1440-dark-states-after.mp4) (~18s pending, ~19–21s error, ~24s success). Before not required for unchanged loader renderer. Missing null/unavailable branch and native controlled detail recording; no native before required. Do not claim this harness proves live transport/app-card lifecycle. |
| P14 | Failed app-bearing tool with deferred-write text; failure indication and readable output above still-retained blank app. | Affected failed-status path. | S-FAIL: status failed, result isError:true, fixed error text. | Web 1440×1000 dark still pair; Android dark after still. | Already-covered functionally by [web before](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/33ebe42c027ac4a9/P14-web1440-dark-before.png) / [web after](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/1f7f623053d20a37/P14-web1440-dark-after.png) and [Android after](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/4a0710dfc525ea3a/P14-android-dark-after.png). Native before not required: old early return is independent of status; both mobile failed-app and generic-row tests plus actual after show failure tone/output. Include web shape in D2, no fresh native failed pair needed. |
| P15 | Reload/reopen persisted thread; exactly one activity + one card per item; open fold and detail after history loads. | Affected reconstruction. | R-PERSIST: completed S-EMPTY copied once; browser reload; native terminate/relaunch same app, reopen thread. | Web 1440×1000 dark RECORDING pair; one native dark cold-relaunch after recording. | Web already-covered by [before reload](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/83736ecc6b7e6c65/P15-web-dark-before.mp4) / [after reload](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/d5b59b7e49813838/P15-web-dark-after.mp4) (after ~68s shows No output). [iOS persisted-thread clip (linked inventory)](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/50085564a0f0779c/P15-ios-dark-after.mp4) starts on thread list and later opens row; protocol explicitly says relaunch failed. Missing actual native cold-relaunch proof, before unnecessary. Can combine with P21/P24 native after sequence. |
| P16 | Show all → Hide successful → Hide all → Zen → Show all, successful and failed app calls; app cards retained while appropriate rows disappear/reappear. | Affected visibility/settings entry point. | S-VIS: one completed and one failed app call; web Settings → Chat → Tool calls in chat. | Web 1440×1000 dark RECORDING after, prechange baseline already establishes missing rows. | Already-covered functionally by [after settings cycle](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/b7fdc6479a1a3c03/P16-web-dark-after.mp4) (~40s both rows; ~60s failed only; ~80s none; ~100s Zen; ~130s both restored), with [before default only](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/b4789065bdce9cd4/P16-web-dark-before.mp4) as limited baseline. A full before cycle would add no changed behavior. Include fixed data in D2 if producing isolated comparisons. |
| P17 | Guest route uses same timeline but guest detail source; expanded TSV available, no normal environment-detail fallback. | Affected separate caller/transport seam. | H-GUEST: existing actual GuestPage with stubbed guest SSE and guest detail response, fixed clock and IDs. | Web 1440×1000 dark still pair; RECORDING only if claiming guest interaction flow rather than resulting renderer state. | Already-covered as a labeled renderer/guest-detail seam harness by [before](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/2947ce8cbfe21194/P17-web1440-dark-before.png) / [after](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/17d37e0163f3d6f3/P17-web1440-dark-after.png) and uploaded driver (asserts one guest detail request). Not a live Connect share or app lifecycle proof; those unchanged services do not need a new visual run. No recapture needed. |
| P18 | Empty structuredContent {} or [] with text, app and ordinary MCP; text must survive normalization. | Affected formatter and ingest helper; not just restored row. | N-EMPTY: same raw result {structuredContent:{},content:[text]} and [] variant replayed at ingest on both sides; ordinary and wrapped app siblings. | Web 1440×1000 dark open detail pair; text-vs-No output assertion/runtime transcript for ordinary sibling. | App output already-covered functionally by [before](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/5b244a0a55263a5d/P18-web1440-dark-before.png) / [after](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/29ea7039ae0cd4aa/P18-web1440-dark-after.png). [ordinary after (linked inventory)](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/ed551e0a048dda64/P18-web1440-dark-ordinary-after.png) proves after text only; no matching ordinary before is shown, and pre-normalized DB copying can mask this ingest change. Insufficient isolated normalization comparison; add deterministic ordinary before/after or service transcript for same raw result, including [] equivalence. Fold into D2. |
| P19 | Oversized app result (>256 KiB) with image; preserve valid app reference, omit image bytes, keep readable text/[image], bound remainder. | Affected server detail transport. | B-LARGE: 307678-byte deterministic PNG or same fixed base64 payload; text Rendered chart; valid app ref. | Web 1440×1000 dark still pair if isolating visual change; short detail-RPC transcript for bound and metadata. | After behavior visible in [after](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/1c0ada30ba3c4d9e/P19-web1440-dark-after.png); [before](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/716d5e882862c56c/P19-web1440-dark-before.png) has no tool row, so cannot demonstrate the claimed earlier truncated-wrapper behavior or isolate the later bounding fix. Insufficient boundary proof without matching raw request/result transcript. WireProjection test checks large image omission; add D3 transcript, not another paid turn. |
| P20 | Small app result ≤256 KiB keeps image bytes for app; oversized result omits them; invalid reference falls back to generic bound; nonimage oversized object remains bounded. | Affected new server branches; app retention is externally observable. | B-SMALL/B-LARGE/B-INVALID: same tool result, two payload sizes, valid and invalid app ref. Fetch through actual orchestration.getTurnItem. App fixture displays received byte count for small case, if doing UI proof. | Short before/after runtime transcript; optional one web dark app-status still. No repeated native visual matrix. | Missing runtime evidence for latest small-image preservation fix. Existing WireProjection test passed and checks small preserved / large stripped / invalid bounded; P12 table app ignores image bytes, so its picture cannot prove preservation. Add D3 transcript of byte presence, length and valid reference; no need to expose base64. |
| P21 | App-bearing item changes in-place (app result update, app removed, app replaced); then append ordinary item; repeated mobile cached rebuild and unchanged-row reuse. | Affected identity/cache logic. | L-UPDATE: same sourceItemId; send successive normalized turn-item events with fixed times/revisions, then append command; keep a second unchanged app item. | One web dark and one Android dark RECORDING after plus projection assertions; no before native required. | Web app-removal regression test already-covered and passed; no recording. Missing mobile cached mutation/append proof: new mobile test calls buildThreadFeed without cache, so it does not exercise changed cache/restart boundary. Supply focused cached-input equality transcript or one native live sequence; no need to replay all payloads. |
| P22 | Older-page/inherited fork history and superseded/runless attempts; sourceThreadId/itemId used for details/card; one row + card retained. | Affected source identity, same rendering. | R-HISTORY: one app call in parent, thread.fork via orchestration.dispatchCommand; load inherited history / older page, expand its row. Runless/superseded variants may be small projection harness assertions. | One web or native dark RECORDING for older/inherited navigation; other pure projection variants need assertions, not screenshots. | Missing app-specific inherited/older-page proof; body explicitly excludes it. Existing generic history tests do not exercise the new two-entry app path. No full theme/device cross-product; can combine one inherited app with native persistence sequence. |
| P24 | Native settings independently filter newly restored activities; cards are separate entries. | Affected separate native visibility renderer. | S-VIS; native Settings → Appearance → Tool calls in chat; cycle four modes and restore. | Android dark RECORDING after; reuse P09/P15/P21 device session and fixture. | Missing native setting-cycle evidence; web P16 cannot establish deriveThreadFeedPresentation behavior. No before needed because filtering code is unchanged and old app calls have no activity row. One native surface suffices; no separate iOS cycle. |
| C-HOST | Card loading/error/retry/close/reopen/fullscreen; iframe/WebView theme/height/platform/remote asset behavior. | Unchanged descendants; identity can affect lifecycle. | Reuse F03/F08/P21 retained app fixture; do not enumerate all app-host permissions or tool actions. | No new full host matrix. P21 only needs evidence stable sibling card is retained and changed app identity updates. | Existing table/blank-card web/native captures cover retained-host rendering. No desktop shell changes, new RPC schema or origin handling in this diff; web proof applies to Electron renderer. Live tunnel, all providers and all fullscreen modes are not separate blockers for this visual fix. |

## Smallest additional proof set for capturer/lead

- **D1:** one immutable completed TSV+structured+ordinary-command fixture, both checkouts, three views only: open wide dark (F02), open structured light (F03), long/narrow dark with long tool/integration label (F06). One controlled web recording may cover F07 transitions as well. Existing before/after recordings remain functional proof; do not replace them merely to align elapsed seconds.
- **D2:** one fixed completed empty/mixed/failed/empty-structured thread, open relevant row in each checkout at web 1440×1000 dark. Include ordinary empty-structured before/after text or matching raw normalization transcript. This handles P11/P12/P14/P18; no new native before payload set. Existing P16 setting cycle is already sufficient functionally.
- **D3:** short actual detail-RPC before/after transcript for small image kept, large image omitted, invalid ref bounded; fixed IDs/timestamps and identical payloads. This fills P19/P20 and validates the latest small-image patch. The passing tests remain the primary invariant proof; screenshots of an app table do not substitute for bytes reaching the app.
- **D4:** one Android after session: successful TSV close/reopen (P09), controlled detail missing/loading/error/success (P13, labeled harness if needed), true cold relaunch (P15), cached same-item mutation/append (P21, or focused cached-vs-fresh assertions), native visibility cycle (P24), and one inherited/older app call (P22, can use either real client). Capture only these missing branches. iOS P10 and Android P11/P12/P14 after evidence are already sufficient and should not be repeated.

No screenshots for every provider, every theme, every remote transport or Electron shell are required. No newly paid turns are required to resolve these proof gaps.

## Focused test transcript

Command (read-only on tracked source):

```text
vp test run apps/web/src/session-logic.test.ts apps/mobile/src/lib/threadActivity.test.ts packages/client-runtime/src/work-log/itemDetail.test.ts packages/shared/src/toolOutput.test.ts apps/server/src/orchestration-v2/WireProjection.test.ts
```

```text
note: You are running `vp test` as a Vite+ built-in command. If you meant to run the test npm script, use `vpr test` instead.

 RUN  v5.0.5 /home/user/wt/pr-33347


 Test Files  5 passed (5)
      Tests  146 passed (146)
   Start at  12:06:15
   Duration  6.94s (transform 71%, setup 19%, import 10%)

```

## Audit friction and scope

FRICTION:
tool | Python urllib received HTTP 403 for public ghdrop assets; curl fetched the same immutable URLs successfully. Pillow was unavailable; ImageMagick and ffmpeg supplied local review sheets. | Use curl and the installed media tools for this environment.

Repo remained clean. 107 GB remained available. No servers/devices/leases/worktrees were created. Downloaded originals were already publicly uploaded; local review sheets are private audit derivatives. This report preserves the observations, source URLs and complete focused-test log before scratch cleanup. No private runtime model identifiers are included in this upload.

VERDICT: INSUFFICIENT deterministic comparisons F02/F03/F06/P11/P12/P14/P18; incomplete or missing P09/P13/P15-native/P19/P20/P21-mobile/P22/P24
