WIP: Reproduce a hung ACP session on demand: pathological scripts in scripted_acp_agent driven against the real runner #614

Draft
toasterson wants to merge 3 commits from claude/wi-019ff2f4-reproduce-a-hung-acp-session-on-demand-p into main
Owner

Anima work item 019ff2f4-67e4-7ef0-9a96-292e9142f56e.

What to build

An integration rig that makes the 2026-08-11 session death reproducible in seconds, deterministically, with no model and no network — then the four behaviours any future liveness mechanism must be judged against.

crates/anima-runner/examples/scripted_acp_agent.rs already speaks the agent side of ACP over stdio and is keyword-driven (perm, slow, fail, artifact, inlineimage), selected via ANIMA_RUNNER_EXEC_SCRIPTED. Extend it with pathological scripts and add a test that drives the real runner against them:

script keyword agent behaviour required verdict
hangpost completes the ACP handshake, accepts the prompt, then emits nothing forever must be detectable as dead
hangpre starts, never completes the handshake / never emits a first update must be detectable as dead
thinkforever emits AgentThoughtChunk indefinitely, no tool calls, no writes must be detectable as alive (this is the progress question, not the liveness one)
longpause silent for a configurable interval (default well past any candidate threshold), then resumes and completes normally must be detectable as alive — the false-kill guard

longpause is the important one: it is the test that fails if a threshold is picked wrong, and it is why ADR 0039 requires the number to be measured rather than chosen.

The rig asserts on observable runner state (what the runner reports about the session), not on internals, so it keeps working as the liveness mechanism itself changes.

Pushed context

  • ADR 0039 — the lease model these scripts exist to verify. Read it first; the four scripts map 1:1 to its verification section.
  • Unit tests are explicitly not the bar here. The failure being reproduced was a runner-level hang that no unit test would have caught — the executor started, burned ~1s of CPU, and stopped, while every in-process timer sat in a state that never ran.
  • Start from: crates/anima-runner/examples/scripted_acp_agent.rs (the existing stub and its keyword dispatch), crates/anima-runner/src/acp.rs (session/update handling, ~line 754; the prompt request and its budget, ~line 1322; the warm-idle loop, ~line 1159), crates/anima-runner/src/attach.rs (heartbeat, ~line 373).
  • Pace is already configurable via FAKE_ACP_STEP_MS; follow that convention for the new intervals rather than hardcoding.
  • This item ships no production behaviour change — it is pure test infrastructure, which is why it can run in any order relative to the rest of the plan.

Acceptance criteria

  • scripted_acp_agent supports hangpost, hangpre, thinkforever and longpause, with interval configurable by env.
  • An integration test starts the real runner against the scripted agent for each script and asserts the runner's externally-observable session state.
  • hangpost and hangpre are distinguishable from each other — one hung after acquiring the session, one never acquired it.
  • The tests are deterministic and require no model provider, no network and no Postgres, and complete fast enough to run in CI.
  • longpause completes successfully and is never reported as dead.

Blocked by

None — can start immediately. Touches only example/test code.

Anima work item `019ff2f4-67e4-7ef0-9a96-292e9142f56e`. ## What to build An integration rig that makes the 2026-08-11 session death reproducible in seconds, deterministically, with no model and no network — then the four behaviours any future liveness mechanism must be judged against. `crates/anima-runner/examples/scripted_acp_agent.rs` already speaks the agent side of ACP over stdio and is keyword-driven (`perm`, `slow`, `fail`, `artifact`, `inlineimage`), selected via `ANIMA_RUNNER_EXEC_SCRIPTED`. Extend it with pathological scripts and add a test that drives the **real** runner against them: | script keyword | agent behaviour | required verdict | |---|---|---| | `hangpost` | completes the ACP handshake, accepts the prompt, then emits nothing forever | must be detectable as dead | | `hangpre` | starts, never completes the handshake / never emits a first update | must be detectable as dead | | `thinkforever` | emits `AgentThoughtChunk` indefinitely, no tool calls, no writes | must be detectable as **alive** (this is the progress question, not the liveness one) | | `longpause` | silent for a configurable interval (default well past any candidate threshold), then resumes and completes normally | must be detectable as **alive** — the false-kill guard | `longpause` is the important one: it is the test that fails if a threshold is picked wrong, and it is why ADR 0039 requires the number to be measured rather than chosen. The rig asserts on **observable runner state** (what the runner reports about the session), not on internals, so it keeps working as the liveness mechanism itself changes. ## Pushed context - **ADR 0039** — the lease model these scripts exist to verify. Read it first; the four scripts map 1:1 to its verification section. - Unit tests are explicitly not the bar here. The failure being reproduced was a *runner*-level hang that no unit test would have caught — the executor started, burned ~1s of CPU, and stopped, while every in-process timer sat in a state that never ran. - Start from: `crates/anima-runner/examples/scripted_acp_agent.rs` (the existing stub and its keyword dispatch), `crates/anima-runner/src/acp.rs` (`session/update` handling, ~line 754; the prompt request and its budget, ~line 1322; the warm-idle loop, ~line 1159), `crates/anima-runner/src/attach.rs` (heartbeat, ~line 373). - Pace is already configurable via `FAKE_ACP_STEP_MS`; follow that convention for the new intervals rather than hardcoding. - This item ships **no production behaviour change** — it is pure test infrastructure, which is why it can run in any order relative to the rest of the plan. ## Acceptance criteria - [ ] `scripted_acp_agent` supports `hangpost`, `hangpre`, `thinkforever` and `longpause`, with interval configurable by env. - [ ] An integration test starts the **real** runner against the scripted agent for each script and asserts the runner's externally-observable session state. - [ ] `hangpost` and `hangpre` are distinguishable from each other — one hung after acquiring the session, one never acquired it. - [ ] The tests are deterministic and require no model provider, no network and no Postgres, and complete fast enough to run in CI. - [ ] `longpause` completes successfully and is never reported as dead. ## Blocked by None — can start immediately. Touches only example/test code.
toasterson force-pushed claude/wi-019ff2f4-reproduce-a-hung-acp-session-on-demand-p from 1486c17bf5 to b2f395e9a3 2026-08-18 10:50:44 +00:00 Compare
toasterson force-pushed claude/wi-019ff2f4-reproduce-a-hung-acp-session-on-demand-p from b2f395e9a3 to c1067ba66b 2026-08-18 20:42:45 +00:00 Compare
This pull request is marked as a work in progress.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin claude/wi-019ff2f4-reproduce-a-hung-acp-session-on-demand-p:claude/wi-019ff2f4-reproduce-a-hung-acp-session-on-demand-p
git switch claude/wi-019ff2f4-reproduce-a-hung-acp-session-on-demand-p

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch main
git merge --no-ff claude/wi-019ff2f4-reproduce-a-hung-acp-session-on-demand-p
git switch claude/wi-019ff2f4-reproduce-a-hung-acp-session-on-demand-p
git rebase main
git switch main
git merge --ff-only claude/wi-019ff2f4-reproduce-a-hung-acp-session-on-demand-p
git switch claude/wi-019ff2f4-reproduce-a-hung-acp-session-on-demand-p
git rebase main
git switch main
git merge --no-ff claude/wi-019ff2f4-reproduce-a-hung-acp-session-on-demand-p
git switch main
git merge --squash claude/wi-019ff2f4-reproduce-a-hung-acp-session-on-demand-p
git switch main
git merge --ff-only claude/wi-019ff2f4-reproduce-a-hung-acp-session-on-demand-p
git switch main
git merge claude/wi-019ff2f4-reproduce-a-hung-acp-session-on-demand-p
git push origin main
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
toasterson/Anima!614
No description provided.