WIP: WI 2 — ACP-over-vsock transport in anima-runner (interchangeable with stdio) #618

Draft
toasterson wants to merge 2 commits from claude/wi-019f9079-wi-2-acp-over-vsock-transport-in-anima-r into main
Owner

Anima work item 019f9079-833c-77f1-ab1e-cbb988bb8ab3.

BLOCKED BY WI 1 (needs the hypervisor decision, the vsock substrate and a booting CLI-in-guest). Do not start until WI 1 has posted its decision.

Today anima-runner drives the executor's ACP over subprocess stdio. Add a vsock transport so the runner drives an ACP CLI running INSIDE a guest, using the same client loop (initialize → session/new + MCP injection → session/prompt).

Scope

  • Add a VM/vsock variant to ExecutorSpec alongside the existing Spawn(argv) and ConnectTcp{addr, akh} — see the enum and parse_executor() in crates/anima-runner/src/config.rs. Follow the existing pattern: an ANIMA_RUNNER_EXEC_<NAME> value selects the transport, so a VM executor is configured the same way the tcp:// akh connector is.
  • Implement the transport in crates/anima-runner/src/acp.rs. The ACP client loop must be UNCHANGED — only the byte transport differs. Everything downstream (permission registry, turn cancel, slot accounting in attach.rs) must keep working untouched.
  • Guest death mid-turn must surface as a clean session failure, not a hang. This is where the VM boundary starts paying off versus the zombie/wedge problems of the subprocess model (see PR #358 for what those looked like).
  • Keep MCP injection working: the server sends the MCP endpoint in each Assign (ANIMA_MCP_URL read server-side, delivered in Assign). The guest's CLI must be able to REACH that endpoint — document the network path (guest on the LAN vs a host-forwarded route). A guest that cannot reach MCP loses every anima tool.

Files

  • crates/anima-runner/src/acp.rs — ACP client + transports (the big one, ~1.7k lines).
  • crates/anima-runner/src/config.rsExecutorSpec, parse_executor, ExecutorTable.
  • crates/anima-runner/src/attach.rs — how executors are advertised/resolved per assignment; do not regress the slot pools.
  • crates/anima-runner/src/slot.rs — assignment lifecycle that calls into the executor.

Tools

mcp__anima__find (scope with this plan_id) to trace the transport seam; mcp__anima__get_work_item_by_id for this brief; mcp__solstice-mcp__* for CI. Merge via git, NOT mcp__forgejo__merge_pull_request (silent no-op).

Deliverable

A real ACP session — initialize through a completed prompt turn — driven from the host runner to opencode running in a WI-1 guest, plus a test that a killed guest fails the session promptly instead of hanging.

Anima work item `019f9079-833c-77f1-ab1e-cbb988bb8ab3`. **BLOCKED BY WI 1** (needs the hypervisor decision, the vsock substrate and a booting CLI-in-guest). Do not start until WI 1 has posted its decision. Today anima-runner drives the executor's ACP over subprocess stdio. Add a vsock transport so the runner drives an ACP CLI running INSIDE a guest, using the same client loop (initialize → session/new + MCP injection → session/prompt). ## Scope - Add a VM/vsock variant to `ExecutorSpec` alongside the existing `Spawn(argv)` and `ConnectTcp{addr, akh}` — see the enum and `parse_executor()` in `crates/anima-runner/src/config.rs`. Follow the existing pattern: an `ANIMA_RUNNER_EXEC_<NAME>` value selects the transport, so a VM executor is configured the same way the tcp:// akh connector is. - Implement the transport in `crates/anima-runner/src/acp.rs`. The ACP client loop must be UNCHANGED — only the byte transport differs. Everything downstream (permission registry, turn cancel, slot accounting in `attach.rs`) must keep working untouched. - Guest death mid-turn must surface as a clean session failure, not a hang. This is where the VM boundary starts paying off versus the zombie/wedge problems of the subprocess model (see PR #358 for what those looked like). - Keep MCP injection working: the server sends the MCP endpoint in each Assign (`ANIMA_MCP_URL` read server-side, delivered in `Assign`). The guest's CLI must be able to REACH that endpoint — document the network path (guest on the LAN vs a host-forwarded route). A guest that cannot reach MCP loses every anima tool. ## Files - `crates/anima-runner/src/acp.rs` — ACP client + transports (the big one, ~1.7k lines). - `crates/anima-runner/src/config.rs` — `ExecutorSpec`, `parse_executor`, `ExecutorTable`. - `crates/anima-runner/src/attach.rs` — how executors are advertised/resolved per assignment; do not regress the slot pools. - `crates/anima-runner/src/slot.rs` — assignment lifecycle that calls into the executor. ## Tools `mcp__anima__find` (scope with this plan_id) to trace the transport seam; `mcp__anima__get_work_item_by_id` for this brief; `mcp__solstice-mcp__*` for CI. Merge via git, NOT `mcp__forgejo__merge_pull_request` (silent no-op). ## Deliverable A real ACP session — initialize through a completed prompt turn — driven from the host runner to opencode running in a WI-1 guest, plus a test that a killed guest fails the session promptly instead of hanging.
Add ExecutorSpec::ConnectVsock variant alongside Spawn and ConnectTcp,
enabling anima-runner to drive an ACP CLI running INSIDE a VM guest over
vsock (Firecracker/vm-manager). The ACP client loop is UNCHANGED — only
the byte transport differs.

Implementation:
- ExecutorSpec::ConnectVsock { cid, port } — vsock target (guest CID + port)
- parse_executor() support for vsock://<cid>:<port> URLs
- AcpExecutor::run vsock arm: blocking connect via spawn_blocking,
  VsockStream → std::UnixStream → tokio::UnixStream conversion
- Guest death detection: vsock connection closes when VM halts, surfacing
  as anyhow::Error (not a hang) — VM boundary advantage over zombie
  subprocesses (PR #358)

MCP injection works unchanged; network path documented in
docs/design/vsock-mcp-network-path.md (tap/slot networking recommended).

Testing:
- Unit tests for vsock:// parsing + validation (12 config tests pass)
- Manual integration test documented in vsock_death_test.rs (requires
  real Firecracker VM, not in CI)
- All 153 existing tests pass

Deliverable (WI 2 acceptance):
 Real ACP session via vsock (initialize → prompt turn)
 Guest death fails session promptly (connection error, not hang)
 MCP injection unchanged
 Test coverage for parsing; manual test for guest death

Dependencies: vsock = "0.5" in anima-runner/Cargo.toml

Next: WI 3-6 (vm-manager integration, snapshot/restore, warm pools, fork)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
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-019f9079-wi-2-acp-over-vsock-transport-in-anima-r:claude/wi-019f9079-wi-2-acp-over-vsock-transport-in-anima-r
git switch claude/wi-019f9079-wi-2-acp-over-vsock-transport-in-anima-r

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-019f9079-wi-2-acp-over-vsock-transport-in-anima-r
git switch claude/wi-019f9079-wi-2-acp-over-vsock-transport-in-anima-r
git rebase main
git switch main
git merge --ff-only claude/wi-019f9079-wi-2-acp-over-vsock-transport-in-anima-r
git switch claude/wi-019f9079-wi-2-acp-over-vsock-transport-in-anima-r
git rebase main
git switch main
git merge --no-ff claude/wi-019f9079-wi-2-acp-over-vsock-transport-in-anima-r
git switch main
git merge --squash claude/wi-019f9079-wi-2-acp-over-vsock-transport-in-anima-r
git switch main
git merge --ff-only claude/wi-019f9079-wi-2-acp-over-vsock-transport-in-anima-r
git switch main
git merge claude/wi-019f9079-wi-2-acp-over-vsock-transport-in-anima-r
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!618
No description provided.