# Proof audit, pass 2: [#22803](https://git.cafe/pingdotgg/t3code/pulls/22803)

Auditor: Claude Opus 5.5 (Claude Code). Read-only: nothing on the PR branch was edited and nothing was posted to a forge. Date: 2026-10-22.

## Revisions and source identity

- The AFTER worktree `~/wt/pr-22803` HEAD is `2fb01453927cad79c5b7d54e74764500c2e54488` and it is clean. Its parent is [e7f57a52be](https://git.cafe/pingdotgg/t3code/commit/e7f57a52be493928cfd0b79983a772b7c53011ef).
- `git diff e7f57a52be 2fb0145392 --stat` lists only the fixture (`claudeQuestionMockCli.mjs`, +248, mode 100755) and the driver (`claude-question-withdrawal-evidence.ts`, +648).
- `git diff 8d3b061121 2fb0145392 --stat` lists those two files plus `ClaudeAdapterV2.ts` (+35/−13) and `ClaudeAdapterV2.test.ts` (+89). `git diff 8d3b061121 e7f57a52be` is byte-identical to the PR's own `git diff e27a03e94d b0e0163e80`.
- The committed files' sha256 values match the claim: fixture `d488d671…6b0067`, driver `eff31b46…dce34c`. `before/manifest.json` records revision `8d3b061121fe…`, with the same two sha256 values, and lists only those two files as untracked. `after/manifest.json` records `2fb01453…` with `dirty: ""`. The BEFORE worktree has since been removed, so its bytes are attested only by the manifest hashes. They were not re-read.
- Every uploaded transcript names the correct revision in its header. All 12 uploaded transcripts are byte-identical to the local `/tmp/ev-22803/upload` copies (re-downloaded and compared with `cmp`).

## Per row

| Row | Verdict | Reasons |
|---|---|---|
| A1 interrupt | **SUFFICIENT** (no-regression row) | Both revisions show `-- checkpoint Q1 pending (before action)` with stream pending `["toolu_q1"]` before `run.interrupt`. Persisted events show Q1 going from pending to exactly one cancelled trio (3 cancelled lines in each revision), and the run ends `interrupted`. The trace in its row-and-PID file shows `can_use_tool cu_q1 toolu_q1`, an incoming `interrupt`, no `control_response` for `cu_q1`, and `exit code=0`. In AFTER the cancel is at seq 31, before the terminal at 36. In BEFORE it sits in the terminal batch at 33–35. The row does not discriminate (expected: `dismissNativeUserInputs` converges). |
| A2 stop | **SUFFICIENT** (no-regression row) | Same shape as A1 via `thread.stop`. Pending was observed first, there is one cancelled trio in each revision, and the run is `interrupted`. The trace shows an incoming `interrupt` and exit 0. In AFTER the cancel lands about 16 ms before the terminal. |
| A3 provider exit | **SUFFICIENT** (no-regression row) | Pending was observed first. The trace shows `exit code=1` after `cu_q1`. Each revision has one cancelled trio and the run is `failed`. |
| A4 provider withdrawal | **SUFFICIENT, discriminating** | Pending was observed first in both revisions. At checkpoint K (Q2 pending, turn alive), BEFORE has stream pending `[toolu_q1, toolu_q2]` and DB projection rows Q1 `pending` and Q2 `pending`. Q1 is cancelled only at seq 174, in the completion batch. AFTER has stream pending `[toolu_q2]` and DB rows Q1 `cancelled` and Q2 `pending`. Q1 is cancelled at seq 160, before Q2 pending at 166. The stub traces are identical in both revisions: `control_cancel_request cu_q1`, then a deny `control_response cu_q1` ("User cancelled tool execution."), then `can_use_tool cu_q2 toolu_q2`, then allow with Blue. So the fixture behaved the same and only server output differs. Each revision has exactly one Q1 cancelled trio. |
| B answer | **SUFFICIENT** | Pending was observed first. Q1 goes pending → resolved, with 0 cancelled lines in persisted events in both revisions. The trace shows `control_response cu_q1 behavior=allow answers={"Red or Blue?":"Blue"}`. The run completed. |
| C stale cancel | **SUFFICIENT** | Pending was observed first. The trace shows the allow followed by `out control_cancel_request cu_q1`. Q1 is resolved with 0 cancelled lines in both revisions. |

**Trace correlation:** each row's stub trace is a separate file named `<ROW>-<pid>.ndjson`, and the row comes from the `ROW:` tag in that thread's prompt. Inside it, `tool_use_id`/`request_id` (`toolu_q1`/`cu_q1`, `toolu_q2`/`cu_q2`) match the projection's `native=` id. The `toolu_*` ids are fixed strings reused across rows, so correlation between rows rests on the per-row file and thread, not on unique ids. That is adequate because each row has its own thread and stub process.

## Duplicate-cancel claim (AFTER A1–A3)

- **Observed:** each AFTER A1–A3 row persisted exactly one Q1 cancelled trio. The adapter's trio came first, and the ingestor then dismissed nothing.
- **Not impossible:** `dismissNativeUserInputs` reads via `getPendingNativeUserInputs`, which filters `status = 'pending'` (`ProjectionStore.ts:4650`). It therefore skips a request that is already cancelled, so the adapter-first order can never produce a duplicate.
- **The reverse order can:** the ingestor's `runtime_request.updated` and `turn_item.updated` cases (`ProviderEventIngestor.ts:646`, `:631`) write unconditionally, with no stored-status guard. `node.updated` dedupes only an identical node, and `completedAt` differs. In `interruptTurn`, `query.close()` and `finalizeActiveTurnAfterQueryExit` (`ClaudeAdapterV2.ts:9819`) run in the interrupt fiber. The aborted `canUseTool` callback resumes on its own fiber. Nothing orders the two.
- **Residual risk:** if the terminal is ingested first, a second cancelled trio is written with a later `resolvedAt`. The status stays `cancelled` either way, so this is cosmetic (an extra event and a changed timestamp), not a state regression. It was unobserved in about 8 AFTER runs of A1–A3. It is not blocking for the PR's claim.

## Rebuild (A4 against AFTER)

```
cd ~/wt/pr-22803
env -u T3CODE_HOME -u T3CODE_OTLP_METRICS_URL -u T3CODE_OTLP_LOGS_URL -u T3CODE_OTLP_TRACES_URL \
  node apps/server/scripts/claude-question-withdrawal-evidence.ts --worktree "$PWD" \
  --home /tmp/ev-22803/audit2/home --out /tmp/ev-22803/audit2/out --row A4
```

- **Result:** exit 0, `error: null`. Q1 statuses `26:pending, 30:cancelled`, with one cancel. At K, stream and DB pending are both `["toolu_q2"]`. The K projection row for Q1 is `cancelled`. `q1CancelledBeforeQ2: true` (Q2 pending at seq 36). The run completed.
- **Comparison with the uploaded `pr22803-A4-after.txt`:** after normalising sequence numbers, UUIDs, ports, PIDs and SDK control request ids, the client stream, persisted events, K projection and fixture trace are identical. The only differences are thread-id fragments and the random `initialize`/`mcp_set_servers` request ids.
- **Isolation:** the driver strips `T3CODE_*` from the server env and passes `--base-dir`. The server wrote `/tmp/ev-22803/audit2/home/userdata/statev2.sqlite`, and `server.log` contains 0 references to `/home/ubuntu/.t3`.
- **Cleanup:** the only server I spawned was PID 920263 (recorded at spawn in `pids.txt`, killed by its process group in the driver). Afterwards `kill -0 920263` reported no such process, no process remained in that group, and port 35855 was free.

## Scoped test (AFTER)

`cd apps/server && env -u T3CODE_HOME vp test run src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts`: **1 file, 208/208 passed** (16.8 s).

## Gaps (non-blocking)

- All runtime rows use the `claudeQuestionMockCli` fixture. No real Claude was run, and A4 cannot be produced by a real model.
- No UI rendering was captured. The surface is server and wire only, as scoped.
- BEFORE source bytes are attested by the manifest hashes, because that worktree was removed. BEFORE runs were not rebuilt in this pass.
- The duplicate-cancel race described above is possible in principle and cosmetic.

VERDICT: SUFFICIENT
