# Local-agent backend handoff
Contract: [Local-agent stdio protocol](local-agent-protocol.md). Backend work does not depend on a frontend release. Ma-gi must independently validate its decoder, application state and native controls before closing its integration milestone.
Historical validation record for pinned revision below. Current repository retains no process-test suite or trace recapture runner. These observations do not verify current backend behavior.
## Tested backend
Synthetic process fixtures tested commit `490c366c35011ea5aa583eb8406a69ec7f869753`, package `0.96.0`, on macOS. Protocol name `magi-code.local-agent`, protocol version `1`; trace wrapper `fixture_version=1` is a separate format. Package version alone does not prove capability support. Require all nine advertised operations before enabling chat.
Build in a disposable checkout of that commit:
```sh
git checkout 490c366c35011ea5aa583eb8406a69ec7f869753
cargo build --locked --bin magi-code
```
Use an explicit absolute executable path to that build. Start one owned subprocess with argv `serve --stdio`, stdin/stdout pipes and a separately drained stderr pipe. Set cwd to an existing `~/.ma-gi/agent`; do not pass cwd as a CLI argument. Resolve `MC_HOME` normally (default `~/.magi-code`), independently of cwd. Do not run the child through a TTY or redirect protocol stdout to a file.
Configure credentials/models through existing Mission Control `/login`, `/model` and `/settings` before starting the child. Provider environment variables must reach the child. Do not copy credential files, mutate global settings automatically or fall back to another provider after readiness failure. Initialization checks local readiness, not remote availability. See [provider authentication](provider-authentication.md).
Approved enabled MCP definitions can spawn processes or perform HTTP/OAuth refresh during initialization. Global approval of the canonical definition source remains mandatory; a project cannot approve itself. Per-command `approval.answer` is separate. Backend tools are not an OS sandbox. Existing CLI retention defaults to 30 days; `sessions.retention_days=0` disables automatic cleanup. The stdio endpoint adds no retention sweep.
## Synthetic traces
Captured from passing actual-child workflows using temporary cwd/home, local HTTP/MCP fixtures and synthetic conversations. Identifiers and context digests are illustrative, not reusable session IDs. Each JSON document contains metadata plus ordered `{ "direction":"client"|"backend", "frame":object }` entries. Serialize only `frame` plus LF when testing a wire decoder; do not send the wrapper. Recorded ordering is one valid execution, not a promise of identical delta boundaries or activity timing.
Repository artifacts under `docs/features/local-agent-traces/`:
| Trace | Observed outcome |
| --- | --- |
| [tool-turn.json](https://github.com/magimetal/magi-code/blob/main/docs/features/local-agent-traces/tool-turn.json) | Handshake, lazy session, persisted input, Code Mode write, text continuation, history, independent prompt, shutdown; exactly one controlled write |
| [resume-history.json](https://github.com/magimetal/magi-code/blob/main/docs/features/local-agent-traces/resume-history.json) | New child opens the same durable session, pages history, shuts down without executing another turn |
| [approval-allow.json](https://github.com/magimetal/magi-code/blob/main/docs/features/local-agent-traces/approval-allow.json) | Status remains responsive; stale/duplicate replies rejected; one allowed command effect; completed/durable |
| [approval-deny.json](https://github.com/magimetal/magi-code/blob/main/docs/features/local-agent-traces/approval-deny.json) | Denied command has no effect; failed/durable, partial assistant text preserved |
| [mcp-turn.json](https://github.com/magimetal/magi-code/blob/main/docs/features/local-agent-traces/mcp-turn.json) | Approved MCP call executes once; text continues; completed/durable; raw MCP result omitted from history |
| [cancelled-turn.json](https://github.com/magimetal/magi-code/blob/main/docs/features/local-agent-traces/cancelled-turn.json) | Stop acknowledgement precedes cancelled/durable terminal; partial reply survives |
| [degraded-turn.json](https://github.com/magimetal/magi-code/blob/main/docs/features/local-agent-traces/degraded-turn.json) | Required input persisted; later append fails; displayed reply reconciles; completed/degraded with null revision |
Fixtures contain protocol data, not live credentials or private reasoning. Recapture requires independent client validation using isolated cwd/home and synthetic inputs. Review captures before publication; production approval expiry remains 60 seconds.
## Client obligations
- Preflight `initialize` with required capabilities. Enforce protocol/version, UTF-8, frame/depth/content bounds and response/event identity before changing UI state; stdout contains only protocol frames.
- Keep draft/UI state locally. Save session ID for relaunch only after `input.persisted`. Admission is not persistence. Rejected preflight or failed required append must not appear as an acknowledged user submission.
- Replace deltas with complete `message.reconcile` parts. Tool-separated assistant segments remain distinct. `last_part=true` completes a message replacement, not execution. Do not append cumulative snapshots or treat a missing chunk as complete.
- Run/session/approval IDs are scoped to one child. Only explicit Allow can approve; Deny, Stop, EOF, disconnect and expiry cannot authorize execution. Denial/expiry end this protected turn as failed; cancellation ends it as cancelled. Neither implies rollback of prior effects.
- Wait for `run.finished` before another prompt/history request. Use its revision, not the input-ack revision. Completed/degraded is not durable; null revision requires explicit reopen. Resume restores history, not unfinished execution.
- Never retry an uncertain accepted prompt automatically. Child death or a lost terminal may leave durable input and completed tool effects. If an output deadline cuts a frame mid-write, discard its unterminated tail and keep outcome uncertain until explicit history inspection.
- On app quit, Stop/close input, continue draining output and stderr, wait under a bounded deadline, then terminate/reap a stuck child. Backend output drain and cleanup have separate 2-second bounds. MCP leases/resources may remain held until cleanup finishes. Agent lifetime equals application lifetime; no daemon, reconnect, heartbeat, sleep/reboot or crash-continuation guarantee.
## Verification evidence and limits
At the tested revision: full native suite passed (4,288 tests, 37 ignored), including 21 stdio process workflows, 9 CLI smoke tests and one public-contract integration test. Formatting, locked check, strict all-target/all-feature Clippy and diff checks passed. Full suite includes existing TUI regressions; it does not prove physical terminal compatibility or native GPUI behavior.
Process coverage includes malformed/oversized input, readiness/auth failure, cross-process writer conflicts, required/later append failures, EOF during provider/MCP tool/approval waits, broken output, stalled reader with Stop, shutdown during stalled provider work, ordered terminal delivery and explicit partial replay without automatic retry. MCP fixture logs count one `tools/call` across reopen and verify child cleanup. Approval tests use a pinned synthetic local judgment cache and a dead loopback HTTPS proxy; they exercise real protection/approval callbacks without contacting TypeSafe. Cache drift fails the workflow rather than opening a live service.
Seven captures were checked for metadata, frame size, monotonic sequence, matching request IDs, admission/persisted-input/event ordering, one terminal per run, no events after terminal and sensitive/raw-reasoning exclusion. Authoritative message/history reconciliation is asserted by their source workflows. Pressure/disconnect tests accept that terminal delivery may be impossible; replay still preserves one user input and partial assistant content.
Only production change is a cancellation check after Bash approval returns. Runtime APIs remain private; no new public Rust exports. Local `cargo semver-checks` is unavailable. GitHub checks for prerequisite PR #844 never started due to account billing/spending limits; no Linux, Windows or automated public-API-check success is claimed here. Unix pipe support is implemented for macOS/Linux; Windows serving explicitly remains unsupported. Additional platform/CI checks require separate execution.
Ma-gi native input/focus/resize/shortcuts/accessibility/visual checks and independent decoder/state validation remain frontend work. No live provider checks or user-state access performed. Backend handoff belongs to magi-code #824; integrated closure still requires Ma-gi #2, #3 and #4, tracked by magi-code #819 and Ma-gi #1.