# Proof audit, pass 1 — ACP option errors

PR: [#33118](https://git.cafe/pingdotgg/t3code/pull/33118) ([GitHub mirror #33118](https://github.com/pingdotgg/t3code/pull/33118)). Read-only audit of `/home/user/wt/pr-33118`.

Reviewed local, unpublished merge head [0b1d374b5a](https://git.cafe/pingdotgg/t3code/commit/0b1d374b5a3bd107f5484256c42045e470b7ac1c) against main [4c361199f4](https://git.cafe/pingdotgg/t3code/commit/4c361199f47f6ea32a2c14835df392640d0f7c63). The head link may not resolve before push. The forge still reports published head [a0f7256de1](https://git.cafe/pingdotgg/t3code/commit/a0f7256de1d32e7874ddfe60c2198f0a5f0abe6c); its tests/reviews do not certify the local merge head.

## Findings

1. **Proof incomplete: the short runtime transcript is missing.** The repository requires a test plus a short runtime transcript for a server-only fix with no visible surface. The PR Verification section describes synthetic replay test results only. No screenshot is needed to satisfy this requirement.
2. **The new test covers the central protocol sequence, but not every affected entry point.** It scripts model activation revealing `thinking`, a first `thinking=high` response of `-32603`, a failed `startTurn`, a second write of the same option, successful completion, and no write for `unavailable=x`. The replay completeness check rejects unexpected/missing requests. It checks only the failure tag, not the preserved request error code/message; add those observations to the transcript rather than assuming the failure text. No option-refusal (`-32602`) or option-error open/resume fixture was found in the relevant adapter tests. Existing model-error and synthetic-mode-error tests are different paths.
3. **No changed renderer or layout contract.** The production change is one catch branch in a shared server adapter; the other changed file is its test. Viewport/theme/platform permutations would not distinguish this branch. The existing failed-run UI is a downstream consumer, not a changed visual component. No implementation bug was established in this focused proof audit.

## Changed call paths and input partition

`configureSession` is local to `makeAcpAdapterV2.openSession`. It has four direct call sites:

- Initial `openSession`, after native startup; covers both `session/new` and startup with `initialNativeThreadId` (load/resume). Configuration failure becomes `ProviderAdapterOpenSessionError`; a configuration error occurs after the special startup load/resume fallback and therefore does not use that fallback.
- `startTurnUnlocked` when the requested native session differs or a restart requires activation; configuration follows `activateSession` (load preferred, otherwise resume).
- `startTurnUnlocked` on an already active session when selection differs, interaction mode differs, or `activeSelection` is null. Failure becomes `ProviderAdapterTurnStartError` before prompt dispatch.
- `resumeThread` when activation is needed; failure becomes `ProviderAdapterResumeThreadError`.

Each invocation clears `activeSelection` before configuration; callers set it only on success. Model configuration precedes ordinary options. The changed loop operates on `ModelSelection.options` after owned-option/runtime-ceiling filtering, empty-choice decoding, and synthetic-mode separation. Missing option IDs and unadvertised string select values skip before the changed catch. An advertised option returning `-32602` logs and continues; any other request error now aborts the loop. Later options, policy application, mode/plan changes, validation, and the final configuration-update callback cannot run after that failure. Earlier successful writes remain live; retry reconfigures, with runtime current-value checks avoiding redundant writes. Non-request errors already propagated.

Production callers of the shared adapter are ACP Registry, Grok, Kiro, FX, and Antigravity. Their option lists, owned option IDs, policy ceilings, and custom model hooks determine whether an option reaches this loop. This is one shared error rule, so a separate paid-provider run per flavor is unnecessary. Codex, Claude's native adapter, Cursor, OpenCode, Pi, and Muse are not callers of this implementation.

The app path includes `ProviderSessionManager` open/resume/start wrappers, `ProviderTurnStartService`, and `RunExecutionService`. The latter catches turn-start failures and persists a failed terminal/error item. Existing web consumers include `ChatView` → `ChatBannerLane` / `ThreadErrorBanner`, `TimelineWorkEntryRow`, and `V2ItemInspector`; desktop wraps web. Mobile consumes failed runtime/list state and error work-log items in `thread-work-log`. No props, layout, counts, lengths, empty/loading states, colors, themes, widths, platform code, settings form, keybinding, command palette, transport contract, or RPC is changed. Start/retry/reconnect/compact and scheduled or agent-dispatched turns converge on the same server paths. A `/compact` supported by this adapter delegates to `startTurn`; no separate visual fixture is needed.

## Fixture matrix

Status is a pass-1 evidence inventory, not a claim of runtime execution. No tests, provider turns, server, browser, or captures were run by this auditor. “Test-backed” means inspected assertions/frames at the reviewed local head; the PR reports prior test results at an older base.

All required rows have viewport × theme = **N/A × N/A** and client image/video capture = **none**. Ordered transcripts are **recording** proof for interactions; still images cannot prove ordering, absence of a prompt, or a retry.

| ID / named fixture | Data and trigger | Diff effect / expected observation | Proof and minimum capture | Seed / production method | Pass-1 status |
|---|---|---|---|---|---|
| R1 — transient option during model switch | Open on fixture model `default`; switch to `named`, whose model response advertises select `thinking=low`, choices low/high; request high; reply `-32603 temporary failure` | Changed: startTurn fails, preserves underlying request failure; no session/prompt; selection remains retryable | Existing regression test + short runtime transcript, **recording**; no client capture | Existing stored-picks replay; standalone adapter replay with protocol/request and call-result logs; real-server local ACP stub also possible | **TEST-BACKED; TRANSCRIPT MISSING**. Failure code/message not explicitly asserted |
| R2 — same selection retried successfully | R1 followed by same model and options; second thinking write returns high; prompt ends end_turn | Changed recovery: thinking is resent and turn completes; current model need not be rewritten | Same test and same R1→R2 transcript, **recording** | Existing replay already scripts the second write and terminal completion | **TEST-BACKED; TRANSCRIPT MISSING** |
| R3 — advertised option explicitly refused | Advertised thinking low/high; select high; agent replies `-32602`; follow with an accepted later option and prompt | Preserved boundary of changed conditional: warning, continue, successful configuration/turn; no transient retry implied | Automated assertion **or runtime transcript**, **recording**; combine with R4 in one run | New synthetic replay/stub scenario; keep value advertised so prefilter cannot mask refusal branch | **MISSING**. No relevant option-refusal fixture found |
| R4 — absent option skipped | Request unavailable=x alongside R1/R2; live config never contains unavailable | Unchanged guard: warning, no write of unavailable, eventual successful prompt | Existing regression test; may appear in same runtime recording as R1/R2 | Already in stored-picks scenario; replay completeness proves no extra write | **TEST-BACKED; no separate capture needed** |
| R5 — value filtered before request | Live select thinking allows low only (flat/grouped equivalent); stored high, or ID absent/empty option list | Unchanged: unsupported value/ID never reaches request-error handling; agent defaults remain | Source/control fixture; no extra mandatory runtime recording or screenshot | Stub advertises restricted choices; request stale pick; check no set; can fold into R3/R4 | **SOURCE-ONLY CONTROL; not a separate proof blocker** |
| R6 — new session open fails | session/new advertises thinking low/high; initial stored high; set reply is -32603 | Changed: openSession fails with OpenSessionError cause; no ready runtime/prompt or selection commit | Short runtime transcript, **recording**; no client capture | Adapter driver with options on initial selection; fresh stub per case; real-server first dispatch alternative | **MISSING; producible on Linux for free**. Existing helper accepts turn options only, so needs a standalone driver or new fixture |
| R7 — cold restore open fails | Initial native session ID; advertise load/resume support; successful native load/resume exposes thinking; stored high returns -32603 | Changed: initial open fails after configuration; no fresh-session fallback for this configuration failure | Runtime transcript, **recording**; load/resume can share one configuration fixture; distinguish wrapper from R8 | Standalone driver openSession(initialNativeThreadId); stub implements load or resume; optional real-server reconnect with persisted native thread | **MISSING; producible**. Current new replay advertises loadSession=false |
| R8 — explicit live resume fails | Open native A successfully; resumeThread to B with requested high; B advertises thinking; response -32603 | Changed: resumeThread fails with ResumeThreadError; activeSelection stays null; no success return/prompt | Runtime transcript, **recording** | Driver calls runtime.resumeThread on a different native ID; real-server two native-thread flow is possible but heavier | **MISSING; producible**. Same-ID resume without activation would not hit this call site |
| R9 — turn activates another native session | Open A; startTurn targets B; activate B succeeds, thinking write fails; retry same target then succeeds | Changed activation path: TurnStartError, no first prompt; next start retries configuration despite activeSessionId already B | Runtime transcript, **recording** | Driver routes startTurn to another providerThread/native ID; load/resume stub; retry on same session | **MISSING; producible**. Existing test exercises in-place selection, not activation |
| R10 — partial multi-option configuration | Request thinking=high followed by another advertised option; first option fails non-refusal; retry succeeds | Changed short circuit: later writes and final policy/mode/config-update work stop; earlier model writes stay live; next configuration recovers | Fold into R1/R2 runtime transcript, **recording**; assert ordering/callback counts, not elapsed time | Add later advertised option to synthetic driver and log final configuration callback; no new visual state | **SOURCE-TRACED; runtime observation missing**. Current unavailable option cannot prove stopping a later supported write |
| R11 — ordinary success/filter controls | No options; already-current value; boolean option; empty choice sentinel; owned/policy-filtered picks; synthetic mode; policy/plan config | Unchanged: only options actually reaching changed catch are affected; non-select values use same request error rule; synthetic/policy setters have separate behavior | Existing/source controls; no cartesian test/capture expansion required | Existing empty-default and mode/partial-selection tests; a boolean response can substitute for R1 if desired | **OUTSIDE CHANGED BRANCH / CONTROL**. Synthetic mode failure must not be cited as R3 proof |
| R12 — other request codes / non-request transport error | Advertised option returns e.g. -32000 or -32800; compare transport termination | Changed for every request code except -32602, not just -32603; transport errors already failed | R1 is the representative changed branch; extra diagnostic transcript optional; no extra capture requirement | Change stub error code; if asserting a connection-loss retry, stub must also support reconnect, since retryability is not a promise that a dead connection survives | **SOURCE-TRACED; OPTIONAL DIAGNOSTIC** |
| O1 — persisted server failed run | Through real orchestrator, dispatch R1 and R2; read stored failed/completed run and error item | Existing persistence path consumes newly propagated failure; adapter replay alone cannot prove projection/wire/client rendering | Optional stronger server transcript, **recording**; use if claiming end-to-end failed-run behavior | Isolated real server + local ACP stub; server.updateSettings creates ACP Registry instance; thread.create/message.dispatch provide selections; await stored events/worker milestones | **NOT PRODUCED; OPTIONAL for this server-only proof** |
| O2 — user sees failure and retries | O1 displayed in web thread, then user sends next turn with same selection | Existing error item/banner then successful turn; no renderer change | Optional **recording**, 1280×800 × dark × R1→R2; still insufficient. Native desktop/mobile recordings only if claiming those clients were verified | Route to the actual seeded thread; drive same stub through server; use current browser/mobile verification skill | **NOT PRODUCED; NOT REQUIRED**. Linux can show web; native Electron needs a display; no mobile/desktop availability was established |
| U1 — real upstream transient incident | A production ACP service spontaneously returns the target option-write error | Provider authenticity/incidence, not adapter correctness | Authentic upstream runtime recording only; no synthetic test can prove it | Would need a controllable upstream failure or a sanitized existing trace | **CANNOT be truthfully produced by the free stub/replay**; not required for this fix |

## Minimum evidence set

Keep the focused regression test. One downloadable short transcript may concatenate separately labelled runs:

- **Run A, R1/R2/R4/R10:** model response exposes thinking, transient option error, start failure and underlying cause, no prompt/later supported option, second thinking write, later accepted option, one successful prompt and completed terminal, no unavailable write.
- **Run B, R3:** advertised value reaches agent; -32602 produces the warning; next supported option and prompt still occur.
- **Runs C–F, R6–R9:** new open, cold restore open, explicit live resume, and turn-time activation failures; include the returned wrapper/cause and no prompt. R9 includes its retry. The same scripted error/data can be reused; no provider/theme/platform cross product is needed.

This is a behavior matrix, not a demand for a new test per row. Shared-branch automated evidence may cover multiple rows. The missing runtime observations can be one compact artifact. No screenshot or UI recording is required. Optional O1/O2 and U1 do not block the verdict.

## Producing the transcript on this Linux VM without paid turns

**Fastest: a labelled standalone adapter replay with logs.** Use the real ACP subprocess runtime and actual `makeAcpRegistryAdapterV2`, with the same Node services / test host layers as the current test. Reuse `apps/server/scripts/acp-replay-agent.ts` and the logical frames from the regression. Put a new driver, transcript inputs, persistent status file, stdout/stderr, and observed protocol/outcome NDJSON in a new timestamped directory below `/tmp/lead-33118-r1/proof-audit-1/`. Use synthetic model IDs and text only. Run from `apps/server` so workspace packages resolve, e.g. a plain JavaScript driver via Node module stdin, rather than importing bare dependencies from a scratch file outside the workspace.

Instrument `AcpSessionRuntime` via `protocolLogging.logger` / `requestLogger`, and record the actual result of open/start/resume with its nested request code/message, completed event, and final callback counts. `makeAcpReplayRuntime` already forwards runtime input into `AcpSessionRuntime.layer`; a scratch wrapper can attach these hooks. Keep status/log paths persistent in the run directory: the existing test's scoped temporary directory is removed on cleanup, so plain rerunning the test does not retain them. Await child exit and run the same replay completeness check. State clearly: **synthetic ACP process, real adapter/runtime, not a running T3 server or live upstream agent**. A bare test PASS line or expected-frame script is not an observed runtime transcript. This audit did not implement or execute such a driver.

**Stronger alternative: a stub through the real isolated server.** Write a small Node NDJSON ACP executable to support initialize, session/new, session/load or session/resume, session/set_config_option, and session/prompt. Append observed inbound/outbound messages to a separate file, leaving stdout protocol-only. Support multiple process starts/probes, and arm the failure only for the chosen fixture/session so probing does not consume it. Install it as an ACP Registry provider instance with settings `source: local`, `commandPath: <absolute Node executable>`, `commandArgs: [<absolute stub path>]`, using `server.updateSettings` and `providerInstanceMutation`. Use only an isolated server home under the timestamped scratch run, disable real-data seeding, and avoid the live home. Through authenticated app RPC create a project/thread, dispatch the selections above, await persisted run events, and extract sanitized observed protocol and failed/completed projection lines. For lifecycle rows that normal app reuse cannot force reliably, the standalone driver is a labelled harness transcript, not a client capture. This requires no credentials for a paid provider. Capture no reusable auth or pairing token in the shared artifact.

All ordering evidence should include stable sequence numbers, entry-point labels, native session/turn IDs, error code, prompt count, and observed result. Before upload remove credentials, user content, paths outside the fixture as appropriate, and all private runtime model identifiers. No artifact in this audit includes private runtime model identifiers.

## Source anchors and audit limits

- Changed catch and prefilters: [adapter.ts:7353](/home/user/wt/pr-33118/packages/provider-acp/src/server/adapter.ts:7353).
- Initial configuration: [adapter.ts:7480](/home/user/wt/pr-33118/packages/provider-acp/src/server/adapter.ts:7480).
- Two turn-start call sites: [adapter.ts:8088](/home/user/wt/pr-33118/packages/provider-acp/src/server/adapter.ts:8088).
- Resume configuration: [adapter.ts:8753](/home/user/wt/pr-33118/packages/provider-acp/src/server/adapter.ts:8753).
- Test failure/result handling: [AcpRegistryAdapterV2.test.ts:611](/home/user/wt/pr-33118/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts:611).
- Added fixture: [AcpRegistryAdapterV2.test.ts:809](/home/user/wt/pr-33118/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts:809).
- Replay completeness: [AcpAdapterV2.testkit.ts:122](/home/user/wt/pr-33118/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.testkit.ts:122).
- Real subprocess replay: [AcpAdapterV2.testkit.ts:189](/home/user/wt/pr-33118/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.testkit.ts:189).
- Turn-start persistence: [RunExecutionService.ts:1830](/home/user/wt/pr-33118/apps/server/src/orchestration-v2/RunExecutionService.ts:1830).
- Rule: [AGENTS.md:111](/home/user/wt/pr-33118/AGENTS.md:111).

Covered: complete two-file diff, configuration callers, active-selection retry state, shared adapter flavors, runtime value validation/current-value suppression, scripted replay completeness, server failure propagation, downstream web/mobile error consumers, and PR description/review/check metadata. `git diff --check` passed. No test suite or runtime verification was run; no capture exists. The forge reports checks on the published head, not the unpublished merge. No source edits, pushes, comments, or server processes were made. Disk availability was 108 GB.

FRICTION: none

VERDICT: INSUFFICIENT missing runtime transcript for R1/R2 and lifecycle rows R6–R9; advertised -32602 proof R3; partial-loop observation R10
