ADR 0213: Persist the Host-owned turn queue in host-core
- Status: Accepted
- Date: 2026-09-10
- Decision: D386
- Related: ADR 0205 (D375),
03-runtime/04-data-storage.md§4.6b,03-runtime/06-host-rpc-protocol.md,02-architecture/05-remote-agent-control.md§6
Context
D375 moved the per-session prompt queue out of renderer memory into the Host and chose to persist it. The headless Agent Host module (packages/agent-host) owns admission and draining, but it needs a store that survives a restart and never starts work on its own. Rust host-core owns SQLite exclusively (frozen decision 12), so the store is a host-core table and RPC surface, not a file the module writes.
Decision
- Schema v15 adds
turn_queue. Columns:id,session_id(FK, cascade),principal,idempotency_key,input_hash,content,attachments_json,permission_mode,position,created_at; a per-sessionpositionindex and a unique(session_id, principal, idempotency_key)index. The v14→v15 migration is additive: it creates the table and indexes, every existing chain gains the step, and api.sqlite.v14.bakcopy is kept like every prior migration. - Four additive RPC methods.
session.queuePushis idempotent on(session, principal, idempotencyKey), returns the existing entry when the input hash matches, fails withIDEMPOTENCY_CONFLICTwhen a key is reused with other input, and fails withAGENT_BUSYwhen the session already holds eight entries.session.queueListreturns one session's entries or every entry in position order.session.queueRemovedeletes one entry.session.queuePrioritizemoves one entry to the head of its session (the desktop's "send now", RACPturn/prioritize). Protocol version stays v11. - The store never decides execution. The Agent Host module restores entries when host-core answers, holds every restored session until a controller attaches, and drains one entry only after the active turn's terminal event. The startup fence still never replays or auto-starts work.
- The renderer's in-memory queue is retired. The composer pushes through
agent/queue/push, mirrorsagent/event/queueChanged, and its "send now" isagent/queue/prioritizefollowed by a graceful stop.
Consequences
- A reboot no longer loses queued prompts; they reappear, held, in the snapshot's
queuedTurns. - Every client of a Host sees the same queue once the renderer switch lands.
- Deleting a session cascades to its queue entries.
- Fresh installs and every migration path produce schema v15; the
04-data-storage.mdchain, the baseline, and the README move to v15.
Alternatives considered
- Keep the queue in Agent Host memory, as the D373 draft did. Rejected by D375: a restart loses prompts.
- Persist in the renderer's
localStorage. Rejected: invisible to other clients and to a headless Host.