# ACP terminal kill runtime evidence — matrix 3/3

PR [#18383](https://git.cafe/pingdotgg/t3code/pulls/18383), [GitHub mirror #18383](https://github.com/pingdotgg/t3code/pull/18383).

Checkout: `/home/ubuntu/wt/pr-18383`, branch `takeover/pr-18383`, clean throughout.
Before: merge base [3cc31ed27c](https://git.cafe/pingdotgg/t3code/commit/3cc31ed27c8956b36cee35a50509fb7c6869592b), full SHA `3cc31ed27c8956b36cee35a50509fb7c6869592b`.
After: [222d071698](https://git.cafe/pingdotgg/t3code/commit/222d071698229a5413d17be49735bc56a5e1fa35), full SHA `222d071698229a5413d17be49735bc56a5e1fa35`.
Original contribution reference: [f4ef53f86a](https://github.com/saphid/t3code/commit/f4ef53f86a8c47a6bc6820e294700dac981313b6), `saphid/t3code`, branch `t3code/sweep/acp-terminal-force-kill`; not the before source used here.

Environment: Linux x86_64, kernel 6.1.102, Node v26.12.1, machine fs-11. No server, browser, T3 home, credentials, or live database used. Scratch directory retained for auditor pass 2: `/tmp/ev-18383`.

## Results

Times below are measured from the driver's kill dispatch using performance.now(); actual incoming ACP kill arrived 4–9 ms later. Raw JSON-RPC IDs, requests and responses are timestamped in each transcript. OS ps commands can add tens of ms under load. The deterministic tests supply exact clock-boundary assertions.

| Row | Before | After | Result |
| --- | --- | --- | --- |
| D — omitted/default 5 s | Captured shell and sleep alive at +5.455 s; kill and wait pending. Only subsequent labeled safety SIGKILL made them complete. | Alive at +4.536 s; wait response SIGKILL at +5.028 s, kill {} at +5.030 s; shell and sleep absent by +5.335 s, before release/scope close. | PASS |
| C — configured 250 ms | Captured shell and sleep alive at +0.694 s; kill and wait pending. Only subsequent labeled safety SIGKILL made them complete. | Alive with kill/wait pending at +0.135 s; wait response SIGKILL at +0.269 s, kill {} at +0.270 s; shell and sleep absent by +0.644 s. | PASS |
| S — cooperative default | Ready handler installed before READY; wait SIGTERM at +19.832 ms, kill {} at +20.828 ms; PID absent at first sample. | Ready handler installed before READY; wait SIGTERM at +17.471 ms, kill {} at +18.445 ms; PID absent at first sample. | PASS |

All six runs assert terminal/output exitStatus equals wait_for_exit. Their final output still includes READY, and release occurs after observation. Before ignoring cases explicitly label safety-cleanup-start; their later SIGKILL responses are cleanup evidence, not success evidence. After cases need no safety signal.

D/C command is exactly `sh -c "trap '' TERM; echo READY:$$; sleep 600"`. READY establishes trap installation; PID is parsed from terminal output, not the agent PID. Descendant sleep PIDs are captured by parent PID and sampled separately. S uses `sh -c` to exec Node, installs a SIGTERM handler, then prints READY; the handler removes itself and re-raises SIGTERM so the OS reports the actual signal.

## Boundary exercised

Real AcpSessionRuntime.make/start, mock-agent ACP stdio transport, actual agent.client.extRequest terminal/create/output/kill/wait_for_exit/release, and real makeAcpClientTerminals handlers with NodeServices real process spawner. No fake process handles. Same driver, runtime and peer for before/after; only imported terminal-manager module differs. Before module is git-show output from the merge base; after module is a byte-identical copy of current production source.

Configured 250 ms is passed directly to makeAcpClientTerminals, not a public adapter configuration. Production adapter registration was inspected at packages/provider-acp/src/server/adapter.ts:5896: all five handlers match this driver. Adapter startup/policy guards themselves were not executed. This is the standalone runtime integration boundary requested by the matrix.

## Focused tests

Command: `vp test run packages/provider-acp/src/server/clientTerminals.test.ts` in the specified checkout. Result: 1 file, 13 tests passed, 12.61 s. See tests.log. These include default/configured exact clock assertions and cooperative coverage; runtime transcripts independently prove real protocol/process behavior.

## Reproduce

Retained driver.ts, agent.ts, before.ts, after.ts, run.sh, summarize.py and six logs form the replay bundle. Restore `/tmp/ev-18383/node_modules` as a symlink to `/home/ubuntu/wt/pr-18383/apps/server/node_modules` if needed. Run `bash /tmp/ev-18383/run.sh`; it runs all six cases without modifying the checkout. Run `python3 /tmp/ev-18383/summarize.py` for protocol timing. Agent source is a scratch copy of apps/server/scripts/acp-mock-agent.ts, with its cancellation helper import absolute and two test extension handlers added. Use the source files as shipped, not the exploratory setup/patch scripts.

## FRICTION / limits

- Stock mock never sends terminal requests. Scratch peer adds an asynchronous request/result extension bridge. A nested synchronous unknown-extension handler deadlocked the first exploratory attempt because it prevented reading its own response. The final bridge forks outgoing requests and polls stored results; terminal requests themselves still cross real stdio ACP. Final transcript polling is bounded to 15 s per request.
- That initial exploratory attempt was interrupted before READY could be captured. Its 600 s shell/sleep were identified read-only and allowed to expire naturally; no PID discovered by name/path matching was signaled. Final six runs have explicit PID readiness and cleanup boundaries.
- Evidence is POSIX/Linux only. Windows behavior is not established by these TERM/escalation fixtures.
- No production edits, scratch commits, branch changes, pushes, PR comments, watches, or servers. No outstanding matrix rows.

Harness: Codex in T3 Code, gpt-6.1-sol.

## Uploaded files

[driver.ts](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/017b7652b890a8ab/driver.ts)
[agent.ts](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/439d86df44a54b69/agent.ts)
[before-D.log](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/d343e4f5bd079d09/before-D.log)
[after-D.log](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/62974b9cba02d21f/after-D.log)
[before-C.log](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/25a05c1a3c5af89d/before-C.log)
[after-C.log](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/47ab2123b1d8e24d/after-C.log)
[before-S.log](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/59f417a220f06b57/before-S.log)
[after-S.log](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/a32359eef2bbd3b4/after-S.log)
[tests.log](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/1e4b20fde98e9d6e/tests.log)
[timing.txt](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/4cbc5ee9a266f52d/timing.txt)

Cleanup completed: at 2026-10-24 23:31:29 UTC, both exploratory PIDs 1428728 and 1428729 were absent from ps after natural 600 s expiry. No signals were sent to these process-matched PIDs. All final-run captured terminal/descendant PIDs were already absent before release or after explicit captured-PID safety cleanup.
[evidence-18383.tar.gz](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/528cb4ff18e6d943/evidence-18383.tar.gz)
