# Proof audit, pass 1

PR [#33194](https://git.cafe/pingdotgg/t3code/pull/33194), mirrored as [#33194](https://github.com/pingdotgg/t3code/pull/33194). Reviewed the requested head [6b4dbf3](https://github.com/pingdotgg/t3code/commit/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213), branch `fix/issue-33186`, in `/home/user/wt/pr-33194`; full diff `origin/main...6b4dbf3a1f` (four files, two production adapters and two test files). GitCafe's refreshed metadata agrees with this head. Existing recordings are from [4e06d31](https://github.com/pingdotgg/t3code/commit/4e06d3193cb7a8ae8a8e4cd3e197af59a8e764a4), with before recordings from main [4c36119](https://github.com/pingdotgg/t3code/commit/4c361199f47f6ea32a2c14835df392640d0f7c63).

## Findings

1. **The existing evidence is insufficient for the current head, but remains reusable in part.** The original waiting state (R1), tool-only settlement (R3), answer wake (R4), answer persistence/reload (R5), and a question held in an already active continuation (R6) follow render/data paths unaffected by the later fixes for these fixture inputs. Preserve those recordings with their actual revision label. Their Task-card portions are stale: they show **Tool**, missing original input, and a reset tool start time. Current raw-output updates should preserve **Task**, its input, and the original start time. R2 needs replacement, R7 needs a new two-continuation fixture, and R5's acceptance is limited to the answer; it does not establish the new Task-card state after reload. The PR body's claims that title loss remains the expected current behavior and that only the foreground-status fix followed the recordings are out of date.
2. **Contradicted row R8: a content-only completion still loses the title.** A read-only Node invocation of the actual `parseSessionUpdateEvent` and `mergeToolCallState` at this head confirmed that a sparse `rawOutput` completion retains `title: "Task"`, whereas the equivalent `content: [{type: "content", content: {type: "text", text: "done"}}]` completion produces incoming and merged `title: "Tool"`. The original input survives, but the merged `rawOutput` also remains the old `Task launched in background` value. This follows the [parser's presentation fallback](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/packages/provider-acp/src/server/runtimeModel.ts#L875), the [title merge](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/packages/provider-acp/src/server/runtimeModel.ts#L932), and the [output selection](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/packages/provider-acp/src/server/adapter.ts#L4106). No app recording of this variant exists. This is a lower-layer runtime result, not a claim to have observed the current UI. The smallest correction is to preserve whether the update actually supplied a title, and ensure its new content can be displayed instead of a carried launch output; extend the existing Task regression with this data shape.
3. **Contradicted row R11: the new request admission fix covers late questions, not ordinary late approvals.** [awaitRequestTurn](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/packages/provider-acp/src/server/adapter.ts#L4972) now admits a post-settle Droid question. But [the ordinary permission handler](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/packages/provider-acp/src/server/adapter.ts#L6572) calls it only when `followsAgentWorkState` is true. Droid v1 does not set that flag. The handler then evaluates [activeContext](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/packages/provider-acp/src/server/adapter.ts#L5618), which fails with `ACP agent requested input without an active turn`. A permission request that is converted to a *question* is a different branch and does get admission. This is a deterministic source trace; no late-approval app run was performed. A successful ordinary late-approval capture cannot be obtained at this head with that sequence. Apply the post-settle admission condition to the ordinary ask branch before requiring an active turn, and let automatic allow/deny decisions use the saved policy without an active context; extend the request-admission regression with `session/request_permission`. The same unconditional active-context read also prevents a late auto-approved read from receiving its immediate decision (C12).
4. **R6 is not proof of a request opening its own continuation.** Its transcript has a late Task at 20.065 s, run 2 starting, then `elicitation/create` at 20.568 s. It proves the ~29.4 s hold and UI answer during an existing run. It does not reach the newly changed no-active-turn branch. R9 must send a question as the first event after settlement. Approval hold/resolution without subsequent output (R10), foreground-tool hold (R12), and Grok's stored-status/persistent-background cases (G1–G3) have no linked app captures. Tests cover several of these branches but are not recordings.
5. **No account or client obstacle prevents the missing app fixtures.** All required positive rows can use a synthetic stdio ACP executable through the real app, including the real Grok adapter via its configurable `binaryPath`. A `factory-droid` registry fixture does not exercise Grok's background-task extractors or extensions. Label captures precisely. The brief states the live Grok provider is reachable through the hub; this audit did not invoke it or verify account availability. A live run is unnecessary for deterministic protocol proof.

## Required matrix

Minimum presentation for every required row: **web, 1280 × 900 CSS px, dark, `toolCallVisibility: showAll`**, same scaling/zoom before and after; existing recordings use DPR 2. All rows below involve autonomous timing or an interaction and are **recording** rows. Stills cannot replace them. Rows distinguish changed data shapes and lifecycle behavior; they do not require separate videos. “Shared Grok” means the same code path applies, not that a Droid recording verifies the Grok adapter. D/Q/P/F/G/T refer to the seeding recipes below.

| Row | Named fixture and required data | Observable state / interaction | Diff impact and Grok applicability | Minimum capture / current status |
| --- | --- | --- | --- | --- |
| R1 | Waiting baseline: one Task launch with title/input/launch output, `waiting`, `end_turn`; later frames withheld then released | Initial run settles; no final answer yet. Before/main still has no answer after release | Registry continuation opt-in; initial control unchanged. Shared late-output shape applies to Grok | **recording; valid-existing** — B before and A faithful initial segment. These are revision-labelled baseline evidence |
| R2 | Sparse Task completion: same ID, `completed`, new `rawOutput`, no title/kind/input, after initial settlement | Continuation notice and temporary Stop; collapsed label stays **Task**; expand original input plus new result; original `startedAt` retained; collapse/reopen and reload after settlement | Newly changed prior-run merge/start-time lookup. Shared Grok | **recording D; needs-capture** — old A faithful/gated are **stale for card state**, although their continuation admission remains valid. Include persisted `startedAt` comparison; pixels alone cannot prove it |
| R3 | Tool-only continuation, answer held beyond the 3,000 ms quiet window | Continuation settles without answer; Stop disappears before the next wake | Quiet finalizer; this input has no blockers and no newly changed stale foreground path. Shared Grok | **recording; valid-existing** — A gated, corroborated by faithful and terminal timestamps. Task-card assertions belong to R2 |
| R4 | Usage then nonblank assistant answer after R3; public marker and short wrapping text | Further continuation notice; ordinary assistant text streams then completes | Late message admission/stream projection unchanged by later fixes for this fixture. Shared Grok | **recording; valid-existing** — A faithful/gated plus runtime transcript; no new outbound prompt |
| R5 | R4 completes; reload same actual thread route | Exactly one complete answer remains; no Stop after settlement/reload | Existing persistence and answer renderer unchanged for this short, unqueued history. Shared Grok | **recording; valid-existing** — A faithful/gated. Does **not** prove corrected Task metadata after reload; R2 does that |
| R6 | Question during an already active continuation; `elicitation/create`, one choice plus Other; answer followed by output | Pending question stays answerable beyond 3 s, UI selection resolves it, answer arrives, run settles and survives reload | Open-request blocker already present in recorded revision; admission now returns immediately because run exists. Shared Grok user-input path, including its question extension | **recording; valid-existing** — R6 head; before/main R6 remains valid negative evidence. Does not cover R9 or R10 |
| R7 | Two Tasks launched in run 1; task-1 completes in run 2; run 2 settles without ever updating task-2; task-2 completes sparsely in run 3. Also put an assistant-only continuation between launch and a later Task completion to cover an empty tool map | Every Task retains its own title/input/start time across intervening runs; no duplicate card; expand task-2 and reload | Accumulated finalized tool maps in later fix. Shared Grok | **recording D; needs-capture** — old fixtures launch only one Task. Existing updated unit regression covers two Tasks, not the whole app |
| R8 | Content-only variant of R2: terminal update has `content` text `done`, no `rawOutput` and no title | Expected Task label/input/new result; actual lower-layer merge has **Tool** and old launch `rawOutput` | Same shared merge path; applies to Grok too | **recording D variant; not-capturable+why** — successful expected card state is contradicted by parser/merge at this head. Failure can be seeded/recorded now; capture success after correction |
| R9 | Request-only wake: normal initial `end_turn`; **no** preceding Task completion/message/other wake; later send only an `elicitation/create` question. Hold >3 s; select an option; send **no further agent output** | Request itself admits continuation; visible question remains usable; response alone rearms settlement; no stuck Stop | Newly changed `awaitRequestTurn` plus request cleanup/rearm. Shared Grok (standard elicitation or its native question extension) | **recording Q; needs-capture** — R6 does not reach this branch. Record before at the parent of the request fix and after/head, plus response/terminal transcript |
| R10 | Approval in an already active continuation: wake with assistant text, then `session/request_permission` in approval-required mode; keep tool out of open-foreground blockers; hold >3 s; Approve; no later output | Approval card/actions remain usable, then resolve; cleanup alone allows quiet settlement | New runtime-request blocker/rearm path; shared Droid/Grok. Approval is distinct UI from question | **recording P-active; needs-capture** — tests have an approval row; no linked approval recording. Record offered action(s), resolved state, no Stop after quiet window |
| R11 | Approval-only wake: normal `end_turn`, then ordinary `session/request_permission` with no prior wake, `kind: execute`, benign command, allow/reject options, approval-required mode | Intended: request opens continuation and approval UI; source currently requires missing active turn | `awaitRequestTurn` caller omission; applies to Droid v1 and Grok's non-work-state path | **recording P-late; not-capturable+why** — desired successful state is unreachable by this source path at current head. Record actual failure if useful; repair then capture success. Do not substitute R10 |
| R12 | Continuation with foreground `Execute` tool, no background ID/subagent metadata; vary `pending` → `in_progress`; hold each beyond quiet window; terminal `completed` or `failed` with output | Tool and run remain active through the hold; terminal tool releases run. Show expanded command/output; failed variant shows error styling | Newly added foreground blocker: pending/running count here; terminal statuses do not. Internal waiting guard is C11, not a v1 wire status. Shared Grok | **recording F; needs-capture** — no linked tool-blocker video. One clip may cover both open statuses and a success/failed terminal pair in successive fixtures |
| G1 | Actual Grok flavor: recognized nonpersistent background shell task(s), plus a `Read file` foreground tool still `in_progress` at `end_turn`; background completion with exit 0 | At `end_turn` stale foreground tool is completed; background work still holds root; after background completion/hydration root settles without Stop | Newly changed stored status and end-turn terminalization. **Grok-specific seed required**; Droid has no background extractor for this branch | **recording G-success; needs-capture** — old Droid captures cannot reach it. Include stored tool status and terminal event in transcript |
| G2 | G1 with early background completion exit 1, sparse updates/replayed start ACK, then remaining background task completes | Failed background output/status stays failed through sparse traffic; stale foreground no longer holds root; root can still settle | Shared finalizer change; failure is visually distinct. Grok test table has both exit outcomes | **recording G-failure; needs-capture** — can be a second thread/sequence in G recording |
| G3 | Grok persistent Monitor: `rawInput.variant: monitor`, `persistent: true`, task identity; prompt ends and monitor remains running; also issue/release one ordinary foreground blocker during a continuation | Persistent background card may remain running while root settles; ordinary foreground tool still blocks until terminal | New foreground predicate deliberately excludes tracked background tools; persistent exemption must survive. Grok only; generic Droid Task is not a Monitor | **recording G-persistent; needs-capture** — seedable through Grok binary override. No linked capture |

### Tool-shape preservation matrix

The cache is shared by **every `emitTool` projection**, not just the Task example. These are the finite card-layout classes selected by preserved kind/input/metadata. Add them to one gallery run T, using short synthetic values and expanding each affected card before and after its late sparse completion. Do not multiply every row by every theme/status/count; the shared status variants are R12/G1/G2 and the count/carryover variant is R7.

| Row | Named fixture / initial data; late terminal update omits those fields | Distinct projection / interaction | Diff, seed, and status |
| --- | --- | --- | --- |
| T1 | Command: `kind: execute`, `rawInput: {command: "echo fixture"}`, a text result and nonzero exit-code variant | `command_execution`, retained command, streamed/fetched command output, exit indicator | Shared Droid/Grok prior-state lookup; **recording T; needs-capture**. Can combine failed variant with F/G2, but include a cross-run sparse update |
| T2 | File change: `kind: edit`, location `fixture.txt`, a small diff/snippet; second item with structured changes including rename and old path | `file_change`, retained path/diff/change list, expand and Open diff | Shared Droid/Grok; **recording T; needs-capture**. `delete`/`move` and structured change lists converge here; one small multi-change example covers count >1 |
| T3 | Read: `kind: read`, `rawInput: {path: "fixture.txt"}`, result text | Dynamic Read label/path and input/output rather than generic Tool | Shared Droid/Grok; **recording T; needs-capture**. Same details component as Task, separate kind-dependent label path |
| T4 | File search: `kind: search`, non-web input/path/pattern and a matching preview | `file_search`, retained pattern/path and result preview | Shared Droid/Grok; **recording T; needs-capture** |
| T5 | Web search/fetch: search with `rawInput.variant: websearch`, raw output action containing query and one synthetic `https://example.com/` source; companion `kind: fetch` item with URL location | `web_search`, retained query/URL, result link and snippet | Shared Droid/Grok; **recording T; needs-capture**. Empty versus one result can be shown on these two items; opening external destination is unnecessary |
| T6 | MCP dynamic tool: `_meta.is_mcp_tool_call: true`, `rawInput: {server: "fixture", tool: "lookup", arguments: {query: "fixture"}}`, synthetic result | MCP identity/presentation and arguments survive, rather than falling back to generic Tool; expand | Shared Droid/Grok; **recording T; needs-capture**. This is simulated ACP-reported MCP activity, not an actual external tool execution |
| T7 | Compaction special tool ID `acp-compaction:fixture` and summary; generic unknown kind with absent title/input | Existing compaction/fallback branches; blank versus present details | **recording if investigated; needs-capture (optional, excluded from minimum)**. ID alone selects compaction before the cached-kind branch; generic fallback is R1/R2's old shape. Original time is already checked in R2/T1. No compaction behavior is changed |

## Existing capture disposition

These five uploaded videos were downloaded and decoded into contact sheets; sheets were visually inspected. The accompanying fixture scripts, evidence sheet, and runtime transcript were read. This pass did not create new app footage.

| Existing artifact | What remains valid at the reviewed head | Stale / unproven portion |
| --- | --- | --- |
| [B-before-main.mp4](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/af071c0bd25595b1/B-before-main.mp4) | R1 negative baseline: waiting/launch output remains after late frames, no answer | A before recording, never positive proof of head. Frozen browser clock makes ages/durations unsuitable for timing assertions |
| [A-after-faithful.mp4](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/87e6eb93ac2cc1eb/A-after-faithful.mp4) | R1/R3/R4/R5: waiting, two wakes, completed answer, reload once; transcript preserves 3.805 s Task-to-answer gap | Expanded **Tool** and missing input are old behavior; old start-time/duration is not evidence for R2. Do not label whole clip “current head” |
| [A-after-gated.mp4](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/c1985bbca65e4458/A-after-gated.mp4) | R3's deliberately extended answer gate; R4/R5 answer and persistence; tool-only run visibly settles | R2 card metadata stale. The extended gate is a fixture variant, not exact reporter timing. No two-Task or empty-intervening-tool-map row |
| [R6-before-main-35s.mp4](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/c8f36467f77e418e/R6-before-main-35s.mp4) | Negative R6 baseline; protocol transcript records question failure and no continuation/answer | Not a request-only sequence; a Task completion precedes the question |
| [R6-after-head.mp4](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/a88068ec3e7996fb/R6-after-head.mp4) | R6 hold/selection/answer/completion/reload, using native browser Date | Name means the historical capture head. Request arrives after run 2 is already running, so not R9. Old Task update stale; no approval or no-output resolution proof |

The [runtime transcript](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/5454e38eab7b37f9/runtime-transcript.txt) remains evidence about its labelled old runs: one `session/prompt` each, continuation ownership and terminal events, answer persistence, pending/resolved input. Its statement that title/input loss was confirmed on “current head” is historical and must not be reused as a claim about the reviewed head. The [evidence sheet](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/7a1d5746a918b4d1/evidence-sheet.md) has superseded faithful-video timestamp narration; the PR body corrects it. The validity assessment here relies on sequence/persisted events, not the older sheet's exact video times.

## Changed components, callers, and all affected render paths

There are **no changed React components, styles, client layout props, or wire schemas** in this PR. The rendering change comes from which provider events exist and the values on projected items. Later merged-main edits to prompt suggestions, queue/steering logic, and projection-window selection do not change these short, unqueued fixture paths. That is why limited reuse of the older footage is justified.

Production entry points:

- [makeAcpRegistryAdapterV2](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/packages/provider-acp-registry/src/server/adapter.ts#L179) is called by its `AcpRegistryAdapterV2Driver.create` at line 341; [registry driver creation](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/packages/provider-acp-registry/src/server/driver.ts#L525) supplies the continuation service. Only `source: registry`, `agentId: factory-droid` gets the two new flags. A local executable source or other registry ID does not.
- [makeAcpAdapterV2](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/packages/provider-acp/src/server/adapter.ts#L1681) has five production factory callers: [registry](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/packages/provider-acp-registry/src/server/adapter.ts#L297), [Grok](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/packages/provider-grok/src/server/adapter.ts#L347), [Factory FX](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/packages/provider-fx/src/server/adapter.ts#L198), [Kiro](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/packages/provider-kiro/src/server/adapter.ts#L582), and [Antigravity](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.ts#L299). Grok already sets both continuation and deferred-finalize flags. FX, Kiro, Antigravity, and other registry agents do not acquire those flags here; the generic request cleanup and terminalization helpers are still shared. Claude ACP v2 already uses its work-state admission branch, without this post-settle tool cache. Codex, native Claude, Cursor and OpenCode do not call this ACP factory.
- Late output → wake buffer / offer → [ProviderContinuationService](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/apps/server/src/orchestration-v2/ProviderContinuationService.ts#L188) → `message.dispatch`, provider creation source, `queue_after_active` → continuation attach/drain, without another `session/prompt` → adapter message/tool/request/terminal events → ingestor/projection → subscribed clients. A request-only wake uses `awaitRequestTurn` before request projection; ordinary approval has the caller gap described above.
- Cache branches: save only completed turns when post-settle is enabled; merge all earlier untouched entries; consume saved cache on turn start; use it only for a continuation on the same native session; current-run item overrides saved item; absent prior item uses existing fallback; new user turns, different session, failed/cancelled/interrupted runs must not inherit completed-continuation metadata. These are data/ownership boundaries, not distinct new layouts; R7/T plus controls below cover them.
- Finalizer branches: defer flag off; prompt not settled/finalized/interrupted; ordinary nonpersistent background or subagent work; persistent background exemption; open native request; projected runtime request; foreground pending/running/waiting; completed/failed tool; 3 s generation invalidated by progress; request close/remove with no following output. Stale foreground terminalization at `end_turn` is a different branch from a foreground tool arriving **during** a continuation. G1 and R12 cannot substitute for each other.

Client consumers and callers:

- [ThreadRouteView](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/apps/web/src/components/ThreadRouteView.tsx#L149) calls `ChatView` for draft, promoted draft, and server thread (lines 149/173/197); [SideChatPane](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/apps/web/src/components/chat/SideChatPane.tsx#L172) is the other caller. Provider replay occurs in the server-thread branch. `ChatView` calls [ThreadTimeline](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/apps/web/src/components/ChatView.tsx#L3370), which calls [MessagesTimeline](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/apps/web/src/components/chat/ThreadTimeline.tsx#L57). GuestPage is the other [MessagesTimeline caller](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/apps/web/src/components/guest/GuestPage.tsx#L482), through its existing shared projection/access policy.
- `MessagesTimeline` renders assistant text/streaming metadata, continuation lifecycle rows, work groups/folds, working/awaiting status and approval fallback rows. Work-group children call [SimpleWorkEntryRow / WorkEntryLogRow](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/apps/web/src/components/chat/TimelineWorkEntryRow.tsx#L152) from group rendering at lines 102 and 215 of [TimelineWorkGroupRows](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/apps/web/src/components/chat/TimelineWorkGroupRows.tsx#L102). The expanded branch calls `V2ItemInspector` or `FetchedToolOutput`; details can be inline, lazily fetched, pending, absent, failed, or command-streamed. The work path includes compaction/fallback rows; `V2ItemInspector` selects command, file change, file search, web search, and dynamic/MCP detail bodies. Preservation of kind, title, command, raw input/output, diff/location/MCP metadata and original time affects these choices (R2/R7/R8/T1–T6).
- [TimelinePendingApprovalRow](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/apps/web/src/components/chat/TimelinePendingApprovalRow.tsx#L36) is reached from individual work entries, work-group headers, or unmatched pending approvals in `MessagesTimeline`. Its actions are shared with `ChildThreadApprovals`; variants include live versus gone process, can-operate, responding disabled, primary approve/decline, and optional More actions. `ChatComposer` uses the separate [ComposerPendingUserInputPanel](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/apps/web/src/components/chat/ChatComposer.tsx#L3265) plus approval/Stop/send controls. Request presence changes composer, timeline and sidebar awaiting badges. Question choices, custom answer, next/previous, Submit/dismiss are separate existing interaction states.
- [ThreadScreenHost](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/apps/mobile/src/features/threads/ThreadScreenHost.tsx#L41) is the `ThreadDetailScreen` caller. [ThreadDetailScreen](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/apps/mobile/src/features/threads/ThreadDetailScreen.tsx#L1466) calls `ThreadFeed`, and separately shows `PendingApprovalCard` / `PendingUserInputCard`. Feed work rows use [thread-work-log](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/apps/mobile/src/features/threads/thread-work-log.tsx#L945) for typed command/dynamic/search/file details. Both iOS and Android receive the same changed server projections. Native rendering is separate and untouched; old web footage makes no claim of native verification.
- Desktop runs the same web renderers; no Electron IPC/shell behavior changes. Local, LAN, relay/tunnel and linked environments use the same typed projection. Read-only guest or insufficient-permission clients can see permitted content without gaining new response rights. Settings/palette/keybindings choose providers/navigate or send normal mutations; no new action is added. Agent/MCP/scheduled dispatches converge on the service/adapter path; there is no new handler-only implementation.

Test-only direct factory call sites (not additional product entry points):

- [apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts#L529): lines 529, 690, 782, 868, 995, 1079, 1173, 1329, 1416, 1506, 1573, 1647, 1965, 2042, 2159, 2336, 2423, 2559, 2677, 2815, 3142, 3342, 3422, 3505, 3700, 3769, 3847, 3941, 4050, 4311, 4393, 4575, 4679, 4820, 4971, 5095, 5304, 5737, 5836, 5974, 6083, 6178, 6524, 6589, 6688, 6779, 6869, 7031, 7224, 7467, 7758, 7914, 7999, 8129, 8257, 8468, 8676, 9028, 9195, 9400, 9486, 9593, 9683.
- [apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts#L261): lines 261, 545, 1024, 1115, 1170, 1309, 3085.
- [apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.testkit.ts](https://github.com/pingdotgg/t3code/blob/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.testkit.ts#L59): lines 59.

### Layout and interaction equivalence classes

The changed data axes are message absence/presence and streaming/completed; zero/one/multiple continuation notices; zero/one/two independently identified tools; original versus missing title/input/start; generic versus typed tool bodies; no output versus result/failed result; pending/live/resolved/cancelled request; no blocker versus question/approval/foreground/background/subagent blocker; active versus settled run; disclosure folded/expanded; persistence after reload. The required rows isolate each changed axis; they can share recordings.

Other renderer axes were inventoried and are unchanged: short/long wrapped text, markdown blocks, input absent/scalar/object, output plain/JSON/image/HTML, empty/one/many search/change entries, bounded output scrolling; inline versus fetched details/loading/error/empty; history fold/load/error and full timeline empty/loading/disconnected; `showAll`/`hideSuccessful`/`hideAll`/Zen (Find temporarily maps Zen to hideAll); light/dark/system, typography scale, narrow/wide web, side pane, guest, desktop and iOS/Android. They do not add server branches or justify a full viewport/theme Cartesian product. One modestly wrapping answer/input, a two-Task history, and the typed-card gallery cover the changed data dependencies; stable renderer variants are controls below.

Interactions inventoried: send initial prompt; autonomous wake; time past quiet window; disclosure expand/collapse; follow/scroll; reload; question choice/custom answer/next/previous/Submit/dismiss; approval approve/decline/cancel/session option; Stop; competing user prompt; Open diff; result link and output scroll; provider selection and tool-visibility setting. All captures involving any such interaction require recording, even where their underlying renderer is unchanged.

## Control and boundary matrix (not additional mandatory app recordings)

These rows document excluded combinations, not claimed verification. A status of `needs-capture (optional)` means no suitable recording exists and it is outside the minimum because the changed equivalence class is already represented above. Use focused runtime/harness proof for pure ownership guards rather than fabricating UI.

| Row | Fixture / data / seed | Expected class and affected branch | Status / capture decision |
| --- | --- | --- | --- |
| C1 | Ordinary foreground answer, no Task/late output; normal `end_turn`; error/cancel variants in D script | Ordinary Droid turn still finalizes immediately; failure/cancel UI uses existing renderer | **needs-capture (optional)**; recording if filmed. Focused runtime transcript is sufficient as the control; R1 already has ordinary settlement |
| C2 | Sparse update to unseen ID; current-run update has explicit new title/input; new user turn reuses ID; different native session; completed versus failed/interrupted predecessor | Cache fallback/current override/same-session and continuation-only boundaries; no accidental stale label or command | **needs-capture (optional)**; adapter harness can seed all guards, while D covers visible preserve path. Harness transcript must say it is not app footage |
| C3 | Change tool visibility on a successful and a failed item; Zen and Find; collapse/expand history and work | Existing filtering/grouping: successful tools hidden or shown, failed differs, Zen changes live/final presentation | **needs-capture (optional)**; recording for toggles. No filter code changed; showAll gallery is mandatory |
| C4 | Long multiline input/output, empty output, scalar/object input, result counts zero/one/many; detail pending/error; history loading/error | Existing wrap/scroll/fetch/error layouts; title/input retention still uses same cache lookup | **needs-capture (optional)**; real app for ordinary values; labelled client harness for deterministic detail-fetch loading/error injection. Not a substitute for adapter replay |
| C5 | Stop during held question/tool continuation, then release residual output; question dismiss or approval decline/cancel with no further output | Existing interrupted/cancelled UI and quarantine; cleanup must not reopen stopped run | **needs-capture (optional)**; D/Q/P/F variants are seedable. Record if validating; no live Droid cancel behavior is claimed |
| C6 | User sends another prompt while wake offered/active; queued continuation; session switch | Existing queue/ownership guards; no duplicate answer and no cache leakage to new user turn | **needs-capture (optional)**; app/RPC gates for race, adapter harness for exact ordering. Existing generic tests are not app footage |
| C7 | Grok native subagent spawn ACK and later child terminal/hydration; parent has no other foreground blocker | Existing background/subagent hold, excluded from ordinary foreground predicate; child view/card differs | **needs-capture (optional)**; G fake can emit supported Grok spawn/result envelopes and child frames; no Droid equivalent. Background hold predicate itself is unchanged; G1/G3 exercise the new predicate boundary |
| C8 | Local registry source, other registry agent, no continuation service, empty/whitespace/usage-only/wrong-session late frames, duplicate offer | Existing no-wake/drop classes; not a positive setup for this fix | **needs-capture (optional)**; app variants where supported, direct adapter harness without service. No new renderer shape beyond R1 |
| C9 | Archived/deleted thread or disabled instance; stale generation; stopped quarantine; no last route, closed session | Service/lifecycle admission refusal, no newly renderable successful continuation | **not-capturable+why** as a successful new UI state; labelled service/adapter harness transcript is the appropriate evidence. Deliberate refusal can be observed in real app but is outside minimum |
| C10 | Theme/width/platform/side chat/guest/remote variants on same projection; response capability gone or no permission | Existing independent presentation/access branches, no new CSS/platform/transport logic | **needs-capture (optional)**; real routes/settings/client for integration; labelled client harness only for intentionally injected transient states. No second theme/viewport/native capture required by this server-only diff |
| C11 | Internal foreground tool status `requiresAction` → projected waiting | New blocker checks waiting, but ACP v1 wire ToolCallStatus only accepts pending/in_progress/completed/failed and the normalizer does not produce requiresAction | **not-capturable+why** through the ordinary Droid v1 wire. A labelled adapter harness with a flavor normalizer injecting requiresAction can exercise this guard; no mandatory fabricated UI row |
| C12 | Late permission with policy allow/deny, no active turn, benign read or denied action | Immediate native decision should need no new run/card. At pinned head activeContext is evaluated before disposition, so it fails too | **not-capturable+why** as a successful decision at pinned head; adapter/runtime transcript is appropriate because success has no approval UI. Include with the R11 correction; no extra app video needed |

## Reproducible real-app seeding

The lead, not this auditor, can reuse/start its isolated development app. Do not start a server in the audit or against live userdata. Record actual routes `/$environmentId/$threadId` from IDs returned by the app. All filenames/markers/workspaces/models in these fixtures must be public synthetic labels.

**Common driver setup.** The linked [rpc-driver.mjs](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/6d9b85b846582cbd/rpc-driver.mjs) already uses normal app HTTP/RPC to configure a provider, create project/thread, dispatch a message, subscribe and inspect projection. Its root and that of [fake-droid.cjs](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/8ce6d8ae2bf4a185/fake-droid.cjs) are hardcoded to an earlier scratch directory: make a new copy under the lead's designated capture scratch and adjust that root; do not run the downloaded script unmodified. The [browser driver](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/0a7b23659a2f00ac/browser-driver.js) expands real disclosures but also freezes Date; omit the Date replacement for new captures so displayed durations/ages are meaningful. No fabricated DOM/projection. The [export script](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/2c6a80c665d168c3/export-runtime.py) can derive the sanitized event transcript from a read-only dev database snapshot.

- **D (R2/R7/R8):** Configure enabled `driver: acpRegistry`, `config: {source: "registry", agentId: "factory-droid", commandPath: "/absolute/capture-scratch/fake-droid.cjs", customModels: ["fixture-safe"]}` via Settings or `server.updateSettings` provider upsert. Keep `source: registry`; local source clears the registry ID. The fake implements ACP v1 initialize/session/config/model/probe calls, protocol-only stdout, stays alive after end_turn, logs input/output, and accepts registry distribution argv. Launch two Task IDs with different prompts in the same initial turn. Gate completion-1 until initial terminal, gate completion-2 until run 2 terminal. For the empty-map boundary, insert a separately settled assistant-only continuation before another Task update. Preserve reporter-relative Task-to-answer gap >3 s in a faithful variant or explicitly label the extra gates. Emit sparse terminal updates; raw-output and content-only are separate variants. Expand cards before late update, immediately after, then after reload. Include exact persisted original/new `startedAt` values and item identities.
- **Q (R9):** ACP v1 prompt returns end_turn without any later tool/message wake. After initial terminal send the existing `elicitation/create` form as the **first** later frame. Wait for the persisted pending request, then hold visibly past quiet window, select/submit in the UI, send no later provider output. Await terminal event. This tests admission and cleanup-driven rearm together. For comparison use the same fixture on the parent of [6b4dbf3](https://github.com/pingdotgg/t3code/commit/6b4dbf3a1f09d8a2e128911213dfca17bfa1b213), then head. Existing main R6 is only a broader negative comparison.
- **P (R10/R11):** Use `runtimeMode: approval-required` so permission is actually asked. Request `session/request_permission` with root session, `toolCall: {toolCallId: "approve-fixture", title: "Run fixture", kind: "execute", status: "pending", rawInput: {command: "echo fixture"}}` and allow-once/reject-once options. For R10 first wake/attach an active continuation using harmless assistant text; do not separately emit a running tool that would mask cleanup rearm. For R11 request only after terminal with no other wake. Approve in UI; fake returns no additional updates. Response/terminal events disambiguate the 3 s quiet window. R11 currently fails before UI admission; do not claim success from R10 or from auto-approval.
- **F (R12):** Wake an active continuation, then emit a foreground `tool_call`, title Execute, kind execute, `rawInput.command: "fixture-command"`, status pending then in_progress (the supported nonterminal ACP v1 wire statuses). Do not emit requires_action as a v1 tool status; it is not accepted by that schema. Withhold completion across >3 s quiet windows, then terminal update and short output. Use success and failed variants, or make a failed second item in T. These are protocol simulations; no actual slow command is needed.
- **G (G1–G3 and shared rows):** Configure an enabled **Grok** instance with `config.binaryPath` pointing to a synthetic executable, not a registry instance named Grok. Its wrapper handles status/version/model probing and `agent stdio`/permission argv, accepts initialization/config/auth negotiation without real account requirements, and uses a public fixture model. Standard session/prompt response completes the prompt; Grok's wrapper also accepts its native completion extension. For background shell, emit completed Execute start ACK `rawOutput: {type: "BackgroundTaskStarted", task_id: "fixture-bg"}` and matching `x.ai/task_backgrounded` / `x.ai/task_completed` payload `{sessionId, update: {sessionUpdate: "task_completed", task_id, task_snapshot: {exit_code: 0|1, output: "FIXTURE_DONE"}}}`. Keep a second task live until after end_turn, include the stale Read tool, then finish/hydrate all nonpersistent work. Use explicit fences/events so remaining hydration or injected reports do not masquerade as the stale-tool bug. The existing early-Grok-completion test is the sequence reference. For persistent Monitor, use `rawInput: {variant: "monitor", persistent: true, task_id: "fixture-monitor", command: "fixture"}` and corresponding Monitor identity output; leave it running while checking parent terminal. Actual Grok subagent envelopes can be added for C7. The shared question/approval/typed-card rows can be run through this same configured adapter. **All are seedable** without spending a real turn; direct `makeGrokAcpAdapterFlavor` harness output is a labelled fallback, not an app recording.
- **T (T1–T6):** Same D executable, unique tool ID per card. In the initial run seed full kind/input/location/MCP metadata; in a later continuation update only ID/status/result. Expand each card and exercise Open diff on a tiny synthetic file change. A single gallery recording can cover these classes. Results/changes use zero/one/two small entries; no external websites or real tool side effects are required.

**Minimum new capture bundles:** D (Task preservation, accumulated/empty intervening maps, raw-output and content-only variants, post-reload metadata), Q (request-only question and no-output settlement), P (active approval, plus late approval after correction), F (foreground-tool holds/terminal statuses), G (Grok success/failure/persistent), T (typed-card gallery). These are six fixture bundles, which may share one recording session; they are not six mandatory full faithful-timing repeats. For each changed/contradicted row capture a like-for-like before/after sequence, or use an explicitly labelled earlier recording only for the same data shape. The existing B/A faithful/A gated/R6 pair can be reused for the valid rows above. Current successful recordings for R8/R11 require corrections first.

Record event-gated transitions without editing out the quiet intervals. Accompany captures with head/base, selected actual adapter, protocol version, source/agent ID, native tool ID and projected item ID, start-time equality, outbound `session/prompt` count (one per initial fixture), continuation admissions/terminal events, request pending/resolved/no-active failures, and reload answer/card counts. Distinguish ACP frames from persisted events and observed UI. Neither tests nor a backend transcript alone fill an app-recording row.

If the app cannot launch an override, a `makeAcpRegistryAdapterV2` or `makeGrokAdapterV2` harness is possible, labelled **adapter harness / simulated ACP**. It can prove state and timing but cannot replace the required real-client projection/render/reload proof. No task requires a live Factory account. Do not expose credentials, pairing links, production/customer data, or private operational model identifiers in any evidence. Attribution where needed: **AI-assisted; verified with the tests and tools listed above.**

## Audit scope and friction

Read-only code/forge/evidence inspection, plus one read-only runtime parser/merge probe from the owning package. No source edits by this auditor, pushes, comments, test-suite runs, servers, devices, provider calls, or other worktrees. The checkout acquired uncommitted adapter/test edits while the audit was running; the approval finding was rechecked with `git show 6b4dbf3a1f:...` and remains explicitly about the requested pinned commit. Those concurrent edits were left untouched and are outside this verdict. All downloaded/generated audit files are under `/tmp/lead-33194-r2/proof-audit-p1/`; nothing was deleted. This is pass 1 fixture derivation and evidence applicability, not a full correctness/merge verdict. The uploaded artifact contains no runtime model identifier.

FRICTION:
- tool | Python urllib received HTTP 403 for the public evidence downloads; curl fetched all of them | Use the available curl path for this host
- tool | Pillow was not installed for contact-sheet assembly; ffmpeg assembled and decoded sheets successfully | Check installed image tools first; ffmpeg was sufficient
- other | The review checkout acquired concurrent uncommitted edits after its clean initial inspection; pinned source was rechecked with git show | Keep the audit bound to its requested SHA while implementation continues
- forge | Initial PR view briefly reported the earlier head; refreshed view and status reported the requested head | Audit a pinned local SHA and refresh metadata before attributing current-head evidence

VERDICT: INSUFFICIENT R2/R7 current Task-card recordings; R8 content-only title/output contradicted; R9 request-only question; R10 approval hold and no-output resolution; R11 late ordinary approval contradicted; R12 foreground-tool hold; G1–G3 actual-Grok settlement/persistent cases; T1–T6 typed sparse-update card captures
