# Proof audit pass 1: fixture matrix

PR [#24651](https://git.cafe/pingdotgg/t3code/pull/24651) ([GitHub mirror](https://github.com/pingdotgg/t3code/pull/24651)). Audited [f7d2c45586](https://git.cafe/pingdotgg/t3code/commit/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1), against merge-base [91ea0e18eb](https://git.cafe/pingdotgg/t3code/commit/91ea0e18ebe2db81752bad7f0d4fa70a66d7562b). Local worktree `/home/ubuntu/wt/pr-24651`, branch `pr-24651`. This pass reads source only. No captures, runtime performance measurements, tests, repository edits, server launches, pushes, or comments were made. All fixture instructions below are for the subsequent capturer; they are not claims that a fixture has been run.

## Findings first

1. **Static screenshots cannot prove this change.** The production diff is confined to `navigationThreadShellsAtom`: reuse the previous entry at the same list index when `sameApartFromStreaming` holds; then retain the previous list if every entry is identical. Two tests were added as well. Existing/new tests are useful source evidence, but they do not measure mobile renders or frame delivery. The required proof is paired recordings plus notification/render transcripts, with a meaningful-change sequence proving the list is not merely frozen. [Selector and comparator](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/packages/client-runtime/src/state/threadShell.ts), [tests](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/packages/client-runtime/src/state/threadShell.test.ts).
2. **There are four reactive mobile consumers and one imperative read.** Home, sidebar, arrangement sheet, and the PR-stack hook subscribe. `AdaptiveWorkspaceLayout` only calls `appAtomRegistry.get(...navigationThreadShellsAtom)` inside `handleStartNewTask`; it is not a stream-triggered subscription. Do not claim fewer layout renders from this read. Web/desktop have no consumer of the changed selector. See the path inventory below.
3. **A normal supported iPad does not mount `ThreadNavigationSidebar`.** `useNativeWorkspaceColumnsSupported()` enables native columns when iOS + `Platform.isPad` + native module support; `AdaptiveWorkspaceLayoutContent` then returns its children without its JSX sidebar. The native primary column uses HomeRouteScreen/HomeScreen instead. A tablet recording must disclose which path it covers. To cover the other reactive consumer, use an Android tablet at generic split dimensions, an actual unsupported-native iPad binary, or an explicitly labelled fallback-sidebar harness on the same iPad. [Native gate](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/mobile/src/native/NativeWorkspaceColumns.ios.tsx), [layout](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx).
4. **The stock mobile-showcase seed cannot establish a V2 stream.** Its thread/session/message inserts target V1 projections; its own comment explicitly says V2 clients do not read those thread rows, tracked in [#15013](https://github.com/pingdotgg/t3code/issues/15013). The existing sidebar fixture generates V2 projection SQL and a browser-only protocol replay, but that replay does not generate a sustained token stream and cannot simply run in React Native (`sessionStorage`, DOM MessageEvent, browser interception). Provider replay/test ACP tools exist, but are harnesses, not a shipped selectable fake provider. [Showcase seeder](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/scripts/mobile-showcase-environment.ts), [V2 fixture guide](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/scripts/fixtures/issue-16534/README.md).
5. **The optimization is broader than status `running`.** It uses the presented runtime: active includes preparing, queued, starting, running, waiting; `activityRunStatus` can override shell status, and background work that holds completion can park runtime at idle. Counts only permit suppression when each independently stays on the same side of zero. All other schema fields, environment identity, and aggregated child-request kind must stay equal. Once inactive, even timestamp/message-only updates must propagate. A live `threadShellAtom(ref)` must continue advancing during suppression. [Runtime presentation](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/packages/client-runtime/src/state/models.ts).
6. **Count the right work.** Working/Waiting duration text has its own second clock; sidebar/Home have minute/snooze clocks, search queries, outbox changes, and PR queries. Their legitimate commits may continue. Zero navigation notifications for an isolated pure-tick sequence is a deterministic atom-level assertion; zero total app commits over wall-clock time is not. [Rows and duration](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/mobile/src/features/threads/thread-list-v2-items.tsx).

## Source-derived render-path inventory

No component JSX changed. This inventory follows every reference to the changed selector and the hook wrapping it, then follows the rendered list/card branches. Unrelated component internals are not multiplied into an exhaustive app QA matrix.

| Path / all immediate callers | Shape and conditional branches that matter to rendering | Interactions and fixture mapping |
| --- | --- | --- |
| HomeRouteScreen ← WorkspaceRootContent, WorkspaceRootScreen fallback, WorkspaceRootScreen.android compact/split routes | `useAtomValueWhileVisible`: focused/appearing Home subscribes, covered Home holds its last list and unsubscribes. Native primary column vs standalone; generic split nonprimary renders WorkspaceEmptyDetail instead of the list. iOS native header/toolbar vs Android header/FAB; native metrics/header width/device type. Workspace selected tab threads vs PR/usage/settings/no tabs decides whether Home mounts. Preferences pending can keep the layout unmounted; failure uses default grouping. | Open Home; open stream A, back, open idle B, back; cold deep link; switch workspace tabs; new task, project/branch draft. A01/A02/A13, C01–C08. [Home](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/mobile/src/features/home/HomeRouteScreen.tsx), [visibility](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/mobile/src/features/home/home-route-visibility.ts), [workspace](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/mobile/src/features/workspace/WorkspaceRootScreen.tsx), [Android workspace](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/mobile/src/features/workspace/WorkspaceRootScreen.android.tsx). |
| HomeScreen ← HomeRouteScreen | Empty catalog: loading; no connections; unavailable/offline/error vs unsupported before snapshot; connecting before snapshot; no projects after snapshot; no threads. Populated global list vs selected environment/project; search resting/pending/no match/title match/content match. Primary-column/sidebar vs full screen padding, selected row, safe area, native scroll/glass vs Android rounded sheet; only Android has separate empty-list rendering. One vs multiple environment labels, project/repository/scratch grouping, sort order. List item kinds: pending draft/outbox, card thread, slim snoozed/settled, Working/Snoozed/Settled shelf headers, Show more footer. Shelves absent at zero; collapse/expand, selected items retained, settled initial limit 10 and next page +25. | Scroll/recycle; tap/jump shortcut; filter environment/project, search/clear; content match jumps to message; select/discard pending task; shelf toggle/show more; row actions listed below. A01/A02/A05–A11, C01–C08. [Home list](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/mobile/src/features/home/HomeScreen.tsx). |
| ThreadNavigationSidebar ← AdaptiveWorkspaceLayoutContent only; wrapper → NativeSidebarContainer → ThreadNavigationSidebarPane on iOS, direct pane on non-iOS | Mounted only on generic split layout; list width nullable; visible vs hidden/suppressed pane; selected key/null, active draft, query, workspace page overlay. iOS nativeChrome uses SidebarNavigationShell/header search; non-iOS uses custom toolbar, Android empty centered message, gutter/FAB/hinge behavior; custom non-Android fallback JSX exists but supported iOS always takes nativeChrome. Same list/shelves/row shapes as Home; sidebar adds Show more item. Empty message priority: loading; Android no connections; searching/no match; selected project empty; no threads. Theme controls drawer/row color only. | Reveal/hide sidebar; settings/environment header actions; focus search hardware command (non-Android only); search/filter; jump; scroll closes swipe; thread tap closes swipe; PR tap; every list/row action. A03 and shared A05–A11; C01–C08. [Sidebar](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx), [entities hook](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/mobile/src/state/entities.ts). |
| Shared ThreadListV2Row and pending row ← HomeScreen and sidebar | Card vs slim; selected/nonselected; working/waiting recede; status priority Approval, Input, blocked-on-thread Waiting, Working (including Goal), idle Waiting, Failed/Limited with error text, ready + unseen Done/Woke/plain timestamp. Pin/queued indicators; branch/environment/machine metadata present/absent; project icon missing/present; provider known/fallback; PR/stack badge state; content-match excerpt; long title wrapping. Legacy capability fallback Archive vs Settle; snoozed Wake vs slim Un-settle; snooze eligibility/capabilities; operate scope disables action wrappers. Pending draft vs queued task uses different label/action and divider. | Tap, message jump, PR badge; full/partial swipe, one row open, close on scroll; context/accessibility menus: branch task, settle/unsettle, archive/delete, snooze preset/custom/wake, pin/unpin, auto-settle on/off, arrange, move up/down, rename/regenerate, copy ID, acknowledge Woke. A05–A11 include changed data relevant to these actions. All menu availability combinations are inventoried but do not warrant separate unchanged-layout captures. [Rows](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/mobile/src/features/threads/thread-list-v2-items.tsx), [builder/status/limits](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/mobile/src/features/threads/threadListV2.ts). |
| ThreadArrangementSheet ← ThreadArrangementHost ← App; opened from row menu `arrange` | Host false unmounts; true full-screen modal. Pinned and Active headers even empty; Snoozed omitted when zero; Settled header always; Snoozed/Settled collapsed/expanded; counts. Working shelf preference changes Active sorting/reorder eligibility. Title 2 lines. Drop busy/pending order or lack of reorder capabilities disables handle; up/down and cross-section actions depend on planner/capabilities. Lifted original invisible; drag preview 1 line + action subtitle with valid target, 2 lines without; insertion gap, edge autoscroll, invalid/snoozed destination, cancel; external orderVersion change stops drag. Snooze expiry timer changes sections. | Open/close/requestClose; expand/collapse; drag same section/pin/settle, invalid drop/cancel; accessibility move up/down/cross section; settle/wake return. A04/A10; empty arrangement is C08. [Sheet](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/mobile/src/features/threads/ThreadArrangementSheet.tsx), [App host](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/mobile/src/App.tsx). |
| usePullRequestStack ← PullRequestRouteScreen → PullRequestOverview → PullRequestStackCard | Supported iff environment multiple-link support AND PR stacks capability. Filter links to destination environment; saved fallback matches host/repository/member, newest syncedAt/linkedAt wins. Query success vs pending/error/stale data selects fresh/query-data/saved. No stack/no summary → hidden; summary-only → position text; saved/fresh stack → expandable card; 0/1/many layers and missing-current position; selected layer, title/headBranch fallback, draft/open/merged/closed badge. Stack actions also require both environment and PR stackActions; saved membership cannot authorize merging. | Open PR from row badge or PR list/deep link; expand/collapse; select sibling layer; refresh; offline/saved fallback. A12. [Hook](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/mobile/src/features/pull-requests/usePullRequestStack.ts), [card](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/mobile/src/features/pull-requests/PullRequestStackCard.tsx), [saved selection](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/mobile/src/features/pull-requests/pullRequestStack.ts). |
| Imperative new-task read in AdaptiveWorkspaceLayoutContent ← Stack workspace wrapper | Finds current thread by environment+thread ID; uses project identity, or draft environment/project fallback, or default new-task destination. Native/generic split determines destination behavior. It does not read suppressed timestamps, counts, or message text. Primary/sidebar/auxiliary pane widths, keyboard, hinge, settings, inspector, and rail branches are independent of the selector change. | New-task button and hardware `newTask`; select current thread vs draft vs missing/archived thread; use correct environment/project/branch when opening a draft. A13/A14. [Read and caller](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx), [Stack](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/mobile/src/Stack.tsx). |

## Capture dimensions and shared data

- **P:** iPhone 17 Pro portrait, dark theme, default font scale. Record actual simulator OS, native binary/fingerprint and logical dimensions, not assumed display dimensions.
- **W:** one iPad/tablet landscape, dark, default font scale, enough width for two columns. Record actual dimensions and whether native columns are supported. Generic split threshold is width >=720 AND height >=600; native columns follow size class/native metrics instead. [Layout derivation](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/mobile/src/lib/layout.ts).
- **W-H:** same tablet, labelled fallback-sidebar harness with identical capture-only override on both revisions making `useNativeWorkspaceColumnsSupported()` return false. This is not the normal supported-iPad flow. Alternatively use a real Android tablet and label it W-A; it reaches the actual sidebar without a native-mode override. Do not substitute W-native for A03.
- **H:** Node atom-notification harness, no viewport/theme; **I:** identical local-only instrumentation in an actual mobile dev client. Any screenshot/recording made with synthetic state or local instrumentation is explicitly labelled harness-backed, not an unmodified end-to-end provider result.
- **B (shared populated dataset):** two projects P1/P2 in enabled E1; 40 ordinary stopped threads including idle B and one long title/branch, a pinned thread, two snoozed threads, 12 settled threads (above the 10 limit), one draft and one queued pending task. Streaming A has non-null latestRunId/activeRunId, runtime running, itemCount>=1, visibleItemCount>=1, assistant latestVisibleMessage, and stable latestUserMessageAt/run-start fields. Add a fork and hidden child/grandchild for A09. Second enabled environment E2 is only needed in A14. These can share one seed; rows select subsets and transitions, not new independent giant databases.
- One theme and two device widths suffice because there is no JSX/style change; palette, text scaling, hinge geometry and desktop/web do not alter the comparator. Platform differences are inventoried, with A03 explicitly covering the distinct sidebar path. No full viewport × theme × provider Cartesian product is justified.
- Capture both revisions with exactly the same seed, driver, preferences, instrumentation, OS and interactions. Baseline is the stated merge-base, not a moving main. Record head/dirty patch, fixture checksum, received shell event count, tick count and semantic event markers. Use an existing authorized checkout or let the lead arrange baseline execution; this pass did not modify/check out either revision.

## Affected fixture matrix

`MISSING` means no capture/transcript has been supplied in pass 1. Every row is required evidence, but multiple row IDs can be chapters of the same paired recording. `recording` is mandatory for every interaction/time-dependent row; stills may be extracted only as supplementary visual reference.

| ID / named fixture | Required data and transition | Diff effect / observable acceptance | Concrete seed and route | Minimum capture (viewport × theme × row) / type | Status |
| --- | --- | --- | --- | --- | --- |
| A01 Visible Home, background A | B; A receives >=100 pure streaming shell updates; positive counts; Home is visible. Scroll down/up, open idle B, back; keep A streaming. | Direct suppression: baseline list notifies on ticks; head retains list/row identity for pure ticks. Titles/status/order remain consistent; gestures and navigation work. Hidden Home already unsubscribes on baseline, so a hidden-only run cannot prove this patch. | Real provider route S below; Home route `/`, threads tab. Use driver H for repeatable pure-tick subphase and I for counts. | P × dark × A01; **recording + I render transcript + H notification transcript**. | MISSING |
| A02 Native tablet primary column | B; A streams while idle B/detail is selected and primary list remains visible. Select A/B; hide/reveal or narrow/restore column if available. | Same suppression reaches HomeScreen with `pane=sidebar`, selected/receded cards and live detail; native column must stay responsive and fresh. | Real provider S; native supported W, Home threads tab and `/threads/E1/B`. Confirm NativePrimaryColumnContext path. | W × dark × A02; **recording + I**. | MISSING |
| A03 Actual ThreadNavigationSidebar | B; same stream and selections as A02; pane mounted, then reveal/hide, search and scroll. | Direct suppression in ThreadNavigationSidebarPane, including native header search/list extraData. Sidebar selection must remain correct. | W-H identical local-only native-support override, or W-A real Android generic split. Confirm pane mounted via profiler/testID `thread-navigation-sidebar`. | W-H (or W-A) × dark × A03; **recording + I**, clearly labelled harness if override used. | MISSING |
| A04 Arrangement while A streams | B; sheet open, A active; Pinned 1, Active many, Snoozed 2, Settled 12; long title. Expand parked sections, drag active row within section, drag to pin/settle, cancel invalid drop. | Streaming ticks stop rebuilding sheet sections; valid mutation must change counts/order promptly; stream alone must not cancel drag via changed orderVersion. Busy/pending disable then re-enable. | Row context menu → Arrange threads. S for streaming; ordinary RPC/UI mutations; H-state for deterministic busy/permission variants. | P × dark × A04; **recording + I**; synthetic busy-state phases labelled harness. | MISSING |
| A05 Rename and metadata during stream | B; while A ticks, rename A from short to long title, update branch/worktree or model/provider metadata; no stop. | Navigation emits for each semantic change, row/title/branch/provider update immediately; following pure ticks quiet again; idle B object stays identical in H. | UI Rename or `thread.metadata.update`; branch task only uses real scratch workspace. Any synthetic provider-model swap is H. Home visible; repeat title phase in A03. | P × dark × A05 + title chapter in W-H A03; **recording + H**. | MISSING |
| A06 Runtime state edges | A: preparing → starting → running; queued and waiting; blockedOnThreadWait true/false; active Goal on/off; background-holding roster parks runtime idle. | Every meaningful status/goal/roster change emits, appropriate Working/Waiting/Goal label appears; pure ticks quiet only with presented active runtime. `activityRunStatus` override must be covered. | Deterministic decoded-shell driver D/H. Natural S may cover some transitions but cannot force all separately. | P × dark × A06; **labelled harness recording + H**. | MISSING |
| A07 Approval and clear | A running; pendingRuntimeRequest null → command approval → null while streaming; include auth_refresh control. | Approval takes priority promptly, clears promptly; snooze becomes unavailable for pending runtime request. auth_refresh does not become Approval. | Real provider approval if supported; deterministic D/H otherwise; mock ACP permission tool can supply request. Open Home, then thread to answer, back. | P × dark × A07; **recording + H**; D phases labelled harness. | MISSING |
| A08 User input and clear | A running; null → user_input → null; keep positive counts. | Input label appears and clears promptly; not swallowed by streaming equivalence; answering and returning updates list. | D/H exact shell request plus real UI flow if available; mock ACP `T3_ACP_EMIT_XAI_ASK_USER_QUESTION=1` or Elicitation fixture is a starting point, not a proven ready-made capture. | P × dark × A08; **labelled harness recording + H**. | MISSING |
| A09 Hidden subagent request aggregation | A parent; child and grandchild subagents hidden; fork visible. Child stream only, then user_input, then approval (approval priority), clear/finish; provider-native and auth_refresh child controls. | Hidden pure child ticks do not notify; ancestor Input/Approval changes do, including return to none when child run ends; fork remains visible and can update. | D/H construct same lineage and creationSource shapes as existing child-request tests. UI Home then open parent. No need to run actual delegated agents for selector proof. | P × dark × A09; **labelled harness recording + H**. | MISSING |
| A10 Shelves, pin/order, snooze | B; working shelf off then on collapsed/expanded; A ticks. Pin/unpin; manual active/pin reorder where enabled; settle/unsettle; snooze idle B and wake manually and by expiry; preserve queued indicator/draft rows. | Semantic fields always propagate; shelf counts and selected retention update; pure positive ticks do not reorder list. Time-based wake continues independently, including after failure; working duration still advances. | UI/actions or public RPC. Set mobile preference atoms through existing updater in local harness if no visible toggle. For expiry use a short future ISO via `thread.snooze`; H uses controlled now and explicit milestones, no sleeps in tests. | P × dark × A10 and shelf chapter in A03; **recording + H**. | MISSING |
| A11 Search/filter and stream | B; A title match, idle B content-only message match, no-match, query pending; selected P1/E1 then all, clear query; 12 settled with Show more. | Pure ticks don't rebuild navigation; changed title/search result and selected filters remain current; content-only tap jumps to right message; Show more reveals beyond 10. Search has its own subscription, so count phases separately. | Home filter/search UI. Seed transcript content through real messages or V2 projection fixture/D. For pending/error use query gate in labelled harness. Repeat search chapter in actual sidebar. | P × dark × A11 + W-H A03 search chapter; **recording**, D/query-gated phases labelled harness. | MISSING |
| A12 PR stack consumer | A running with saved 3-layer stack, another link with newer snapshot, unrelated E2 same IDs; PR query pending/error → saved fallback → fresh success; expand and navigate sibling; change link/stack while A streams. | Pure ticks do not recompute subscribed navigation; semantic PR link/snapshot changes reach saved stack. Show position/current/draft/open/merged correctly. Saved membership never supplies fresh merge heads; no merges performed. | PR route `/pull-requests/E1/P1/<encoded repository>/<number>` with explicit host in params. Synthetic PR detail/query result gate D required unless an existing safe stack is available; use fake payloads, never mutate audited PR. | P × dark × A12; **labelled harness recording + I/H**. | MISSING |
| A13 New-task destination, imperative read | A streaming in P1/E1; idle B in P2; current thread/draft/missing-thread cases. Tap compose then cancel; invoke hardware newTask if available. Branch action from row creates draft on that branch. | Correct current project/environment and draft branch persist through stale streaming fields. No performance/render-count claim for AdaptiveWorkspaceLayout itself. | Real app route `/threads/E1/A`, new-task button/context branch action; draft route `/new/draft` or `/new`. For missing-thread use A14 deletion phase. | P × dark × A13; **recording**; W A02 can include the split destination chapter. | MISSING |
| A14 List membership and environment boundaries | E1/P1 root A + fork + archived root + hidden child, E2 with same thread ID A. Stream then insert/delete/reorder preceding shell, archive/unarchive, disable/re-enable E2. | New/removed/order-changed entries emit; no wrong row reused after index shift; no cross-environment alias despite same ID. Archived and child rows absent, fork present. Mixed update replaces changed row while eligible other rows remain stable. | Public create/archive/delete/settings where supported; D/H for exact order/E2 collision. Fresh enabled catalog entries and per-environment snapshots in H. | P × dark × A14 excerpt for archive/remove/restore; **labelled harness recording + H** for order/catalog controls. | MISSING |
| A15 Independent zero crossings | Single active A: (itemCount,visibleItemCount) (0,0)→(1,0)→(1,1)→(2,1)→(2,2), then independently each >0→0; no other fields change. | Every individual zero crossing emits; positive-to-positive only quiet. First arrival cannot be hidden. Existing test changes both counts together only in positive→zero direction, so it is not complete boundary proof. | H uses decoded v2ThreadShell, unique sequences, independent count changes; optional D recording shows first visible content in live thread. | H × n/a × A15; **recording: ordered harness event/notification transcript**, no still or additional device capture. Attach the complete boundary timeline/output. | MISSING |
| A16 Stop and terminal freshness | A pure ticks then completed, interrupted, failed, rolled_back, runtime-null and parked-idle cases; after each inactive state, update timestamp/count/message independently. Failure includes normal and usage_limit, new completion unseen vs visited; snoozed failure wakes. | Stop emits latest fields; inactive timestamp/count/message updates continue emitting; Failed/Limited/Done/Woke or ready presentation and times are fresh. Positive counts alone must not retain terminal shell. | D/H terminal sequences; S real completion/interrupt for at least one real-app chapter; UI visit acknowledges completion. | P × dark × A16; **recording + H**. | MISSING |

## Unchanged branch fixtures: inventory and proportional exclusions

These enumerate visually distinct empty/loading/error shapes and minor layout branches. They are not evidence of the streaming optimization and are **not additional minimum captures**. If a later capture hits one unexpectedly, label it and recover to an affected row; do not submit it as A01.

| ID / fixture | Data / route / concrete way to seed | Type if captured | Diff affected? / pass-1 status |
| --- | --- | --- | --- |
| C01 Loading environments / disabled shelves | Home `/`; cold boot saved connection registry before ready; local query/catalog gate for deterministic hold. Sidebar message Loading threads. Preferences loading can leave whole layout blank before Home mounts. | harness recording (load/resolve) | No changed retained row yet; INVENTORIED, excluded. |
| C02 No environments | Fresh isolated mobile storage with no saved connections; Home `/`, Add environment action; Android sidebar distinct centered message. Do not clear user's real device state. | recording (add interaction) | No; INVENTORIED, excluded. |
| C03 Unavailable / offline / error | Save isolated server address then stop only captured spawned server PID before first snapshot; error detail or offline message; no loaded shell. Cached-shell offline is distinct and remains populated. | recording (disconnect/reconnect); error-text branch may use harness | No direct suppression; INVENTORIED, excluded. |
| C04 Unsupported client | D catalog state unsupported, no loaded snapshot; Home Client not supported. | labelled harness still | No; INVENTORIED, excluded. |
| C05 Connecting | Gate initial shell on saved connection, no error/snapshot; Home Connecting to environment. | labelled harness recording | No; INVENTORIED, excluded. |
| C06 No projects | Fresh isolated V2 snapshot `projects:[], threads:[]`, connected/loaded; Home No projects found. | harness still | No; INVENTORIED, excluded. |
| C07 No threads | Connected snapshot with one project and `threads:[]`; no drafts/pending messages; Home No threads yet. Filtered emptiness/search emptiness is A11, not this case. | harness still | No; INVENTORIED, excluded. |
| C08 Other unchanged branches | Generic split nonprimary Home shows WorkspaceEmptyDetail; empty arrangement has Pinned(0), Active(0), Settled(0) headers and no Snoozed header. Unsupported capabilities/read-only scope remove actions. Native/chrome, dark/light, font scale, Android hinge/keyboard/rail/inspector, missing favicon/provider/PR badge, long text, draft-vs-pending divider, no-tab/settings/usage pages are controlled by unchanged props and helpers. Seed through respective snapshot/config/preferences/context and layout width; label component-only variants harness. | recording for toggle/navigation/drag; still only for a truly static variant | No independent change branch. Base B and A01–A14 exercise representative row variants; INVENTORIED, excluded from further Cartesian captures. |

## Seeding and streaming recipes

### S — real provider run on the Linux VM, without first-party Claude/ChatGPT OAuth

Read-only host inspection found `opencode`, `pi`, and `grok` executables on PATH, and **presence only** of `CURSOR_API_KEY` (no key value read or printed). Credential validity, paid-plan entitlement, model availability and hub reachability were not probed. An installed CLI is not proof it can run a model.

Prefer the existing Cursor SDK path when the isolated server's provider probe reports it ready. The repository explicitly accepts inherited `CURSOR_API_KEY`; it does not require a `cursor-agent` binary or Claude/ChatGPT OAuth. Select an actual model from the isolated app's provider catalog (`server.getConfig`), not an invented slug. If using OpenCode/Pi/Grok, select an already configured instance that reports authenticated/ready and the model its catalog actually lists; a hub-backed CLI requires that CLI's existing model/base-URL setup. Do not infer hub readiness from installation or rewrite global hub configuration for this capture. [Cursor driver](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/packages/provider-cursor/src/server/driver.ts), [probe](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/packages/provider-cursor/src/server/status.ts).

On an isolated dev server/home, create project P1 at a scratch git workspace through the app. Create A and idle B; send to A: “Write 150 numbered short paragraphs about a fictional garden. Output the paragraphs directly. Do not use tools or edit files.” Wait for observed assistant streaming, then return to Home or select B while A remains running. If it finishes too quickly, use a longer prompt; report actual observed shell update count, not the requested output length. The prompt is a way to request a stream, not a deterministic performance workload. Seed extra stopped rows through normal thread creation or the V2-only fixtures. Pair mobile to this isolated server over the actual reachable network URL; no baked-in web origins, no live T3 home.

A typed RPC driver can do the same: `orchestration.dispatchCommand` with `thread.create` (commandId, threadId, projectId, title, selected modelSelection, runtimeMode, interactionMode, branch:null, worktreePath:null), then `message.dispatch` (unique commandId/messageId, threadId, text, attachments:[], dispatchMode:{type:'start_immediately'}). Use the repository's authenticated Effect RPC client/framing, not raw guessed WebSocket JSON. Rename with `thread.metadata.update`, stop with `run.interrupt` using the observed runId; pin/settle/snooze/archive have explicit public command variants. [Public schemas](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/packages/contracts/src/orchestrationV2.ts), [RPC transport](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/packages/contracts/src/rpc.ts).

**Do not claim RPC can synthesize arbitrary token/approval/runtime state.** Public dispatch starts a real provider. Internal provider events are not exposed as arbitrary client commands. No stock `fake` provider switch was found in production registry. Use the labelled harness below for exact boundaries or unavailable provider auth.

### D — deterministic fixture in the real mobile client, explicitly a harness

For a later capture, an identical local-only patch on both revisions can add a dev-only fixture overlay at `apps/mobile/src/state/shell.ts`. Its current `environmentSnapshotAtom` is derived/read-only: do not `registry.set` it. Rename the existing result to `liveSnapshotAtom`, add a writable family, then export the derived overlay under the original name:

```ts
const liveSnapshotAtom = createEnvironmentSnapshotAtom(environmentShell.stateAtom);
export const captureSnapshotAtom = Atom.family((_id: EnvironmentId) =>
  Atom.make<OrchestrationV2ShellSnapshot | null>(null),
);
export const environmentSnapshotAtom = Atom.family((id: EnvironmentId) =>
  Atom.make((get) => get(captureSnapshotAtom(id)) ?? get(liveSnapshotAtom(id))),
);
```

Import the contract snapshot type, and drive `appAtomRegistry.set(captureSnapshotAtom(E1), decodedSnapshot)` from a local-only dev control. `apps/mobile/src/state/threads.ts` already passes this snapshot through `createThreadEnvironmentAtoms` and then into `createEnvironmentThreadShellAtoms`, so the changed selector still executes. Keep an actual enabled E1 catalog/connection and its real project IDs. For pure synthetic rows use fixture thread IDs and intercept/drive their actions in the harness; do not claim a synthetic action reached the server. To exercise real RPC mutations, use actually created thread IDs and mirror the accepted change into the overlay or clear it; an overlay cannot be expected to reflect backend mutations automatically. The read-only derived atom and optimistic lifecycle wrapper were checked in source. This is a new local capture harness, **not an existing turnkey script**; retain the exact patch with evidence and remove it after capture. A dedicated harness screen is an alternative if it supplies RegistryContext, navigation, catalog, config and query contexts.

Supply stable run identity/status and positive counts; send sequence-numbered immutable snapshots/shell `thread.updated` events with only updatedAt/counts/latestVisibleMessage changing. Gate each semantic change from the matrix explicitly. Do not fake transcript progression by changing shell latestVisibleMessage alone: showing a live feed requires corresponding thread-projection messages/turnItems and thread stream events, or S. For A01 use at least one real provider chapter where possible; if unavailable, label the complete recording synthetic and leave provider-backed proof explicitly missing.

Existing source to reuse rather than inventing a fake production protocol:

- [Atom test fixtures](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/packages/client-runtime/src/state/orchestrationV2TestFixtures.ts), [shell reducer](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/packages/client-runtime/src/state/shellReducer.ts).
- [ProviderReplayHarness](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/server/src/orchestration-v2/testkit/ProviderReplayHarness.ts) exposes `layerProviderReplay`/`layerWithRegistry`; [replayGate](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/packages/provider-testing/src/replayGate.ts) can release labelled chunks. This is an event-sourced backend test harness; serving its state to a mobile app requires explicit local server wiring, so do not report a test replay as a real dev-server run.
- [ACP mock](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/server/scripts/acp-mock-agent.ts) sends assistant chunks and supports permission/ask-question/failure/hold flows. [writeFakeCli](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/packages/provider-testing/src/fakeCli.ts) and [GrokProvider tests](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/apps/server/src/provider/GrokProvider.test.ts) show the exact fake `--version`/`models`/`agent` wrapper and binaryPath setting. For a no-auth end-to-end **harness** stream, create a scratch copy of the mock with its relative imports corrected, replace its final single assistant update with a gate-controlled series of `agent.client.sessionUpdate({sessionId,update:{sessionUpdate:'agent_message_chunk',messageId:'capture-message',content:{type:'text',text:chunk}}})` calls, then finishPrompt only on release. Point an isolated Grok instance's binaryPath to the scratch wrapper. The stock final chunk is one-shot, and `CONTENT_THEN_HANG` alone does not provide sustained ticks. This wiring must be verified before calling it evidence; no production source edit or real OAuth is needed.
- [V2 SQL generator](https://git.cafe/pingdotgg/t3code/blob/f7d2c45586f8d0457b2c4f91a4b395ecb06bb2b1/scripts/fixtures/issue-16534/seed.py) can populate stopped roots/lineage/settled shapes in an isolated stopped server. Its fixed clock and IDs/project paths/model are from another fixture; adapt in scratch and refresh clock, do not blindly reuse stale snooze times or an unavailable Codex model. Projection-only seeding is a harness, not backend command proof. Never run active provider commands against fabricated run records.

### H/I — render and notification counts, with no committed instrumentation

**H (required deterministic transcript).** Reuse the `makeHarness` pattern in `threadShell.test.ts`: AtomRegistry.make; writable snapshot Atom.family; enabled PrimaryConnectionTarget catalog; createEnvironmentThreadShellAtoms; mount/subscribe navigationThreadShellsAtom and live threadShellAtom(A). Warm initial reads then reset counters. Apply N=100 distinct decoded immutable snapshots, one tick at a time, reading/flushing after each so batching cannot hide notifications. Keep run metadata/latestUserMessageAt/activity fields constant and change each allowed field alone, then together. Count actual subscription callback invocations, list identity replacements, A/idle row replacements, and live-shell latest fields. Also run A05–A16 semantic sequences with explicit markers.

Expected head: 0 navigation notifications/list replacements for N pure ticks, 0 A/idle row replacements, live shell advances for all distinct ticks; each semantic change notifies and final navigation source has the correct field; inactive follow-up ticks notify. Baseline should rebuild on each distinct pure shell tick in this controlled harness. If notifications batch, report that and count identities after each explicit flush/read instead of asserting an invented number. Registry scheduling must be drained synchronously through its actual API, not wall-clock sleeps. This proves selector behavior, **not React frame rate**. Store script, stdout, assertions and exact hashes alongside capture artifacts; no viewport image is useful for H.

**I (actual mobile renders).** Prefer React Native DevTools Profiler, or identical local-only counters/Profiler wrappers at HomeRouteScreen, HomeScreen, ThreadNavigationSidebarPane, ThreadArrangementSheet and a probe around usePullRequestStack/PullRequestRouteScreen. A Profiler reports commits; an inline counter reports render executions, including possibly abandoned renders—name the metric correctly. Keep logs buffered until phase end, avoiding per-token console I/O. Record mount/warm-up separately; use fixed received-event counts and semantic markers, and keep the same dev/StrictMode/React-compiler settings on both revisions. If a Profiler wrapper misses a compiled component-body execution, record that limitation rather than treating it as a measured render.

Use Home-visible and Home-hidden phases separately. Log duration-child ticks, minute/snooze ticks, query/outbox updates and navigation commits; they are expected independent work. Do not freeze device timers to make numbers attractive. Compare stream-correlated parent commits for equal events. Record scroll/navigation while streaming for actual visible responsiveness. If claiming less jank/FPS, attach profiler/frame timing evidence too; render counts alone support reduced work, not a quantified FPS improvement. Label every instrumented mobile artifact `instrumentation harness`; unmodified provider-backed recording can be a separate chapter.

## Minimum deliverables and current coverage

1. Paired P dark recording with chapters A01, A04–A14 and A16; chapters may reuse the same seeded list and transition driver. Clearly label real provider versus synthetic protocol/state phases.
2. Paired W dark recording for native primary column A02 (and split new-task destination); paired W-H dark sidebar chapter A03, unless W-A real sidebar replaces the latter. A03 includes title/search/shelf semantic checkpoints from A05/A10/A11. A supported native-iPad-only clip leaves A03 missing.
3. One paired H event recording/transcript spanning A01 and A05–A16, especially A15 independent count crossings and A16 inactive freshness; I commit/render counts for each reactive consumer in A01–A04/A12. Count results must cite the fixture/event count, not merely show a console counter.
4. At least one visible semantic update arrives before stopping the stream (rename, Approval or Input), and the live A feed visibly progresses when selected. An endlessly frozen navigation list can otherwise impersonate a successful optimization.
5. Controls C01–C08 are source-inventoried exclusions; no second theme, standalone web/desktop capture, provider matrix, or screenshot-only proof is required for this selector-only change.

All affected rows A01–A16 remain MISSING. This is an evidence sufficiency verdict, not a declaration that the implementation is incorrect. No daemon/device/browser/process was started by this audit, and the worktree was left unchanged.

FRICTION: none

VERDICT: INSUFFICIENT A01, A02, A03, A04, A05, A06, A07, A08, A09, A10, A11, A12, A13, A14, A15, A16
