Conversations
A crash can interrupt an agent after it has started an action. Polychrome records requests before execution and recovers eligible incomplete turns under their original ids.
The state plane commits a turn_dispatched marker with your message before the control plane calls the harness. See the dispatch contract. After the harness returns, a turn_complete marker commits with the output. A dispatch without a completion marker is an orphaned dispatch. Replay retries eligible dispatches under the same turn id, excluding quarantined ambiguous outcomes. The marker contract defines these markers.
turn-42Same id throughout- Accept the messageBatch one · recorded
The state plane commits the input before the harness runs.
turn_startuser_msgturn_dispatched - Run the harnessInterrupted
Execution stops before batch two commits. Earlier committed steps remain in the log; tools may already have acted.
- Commit the resultBatch two · missing
There is no completion marker for this turn.
Noturn_complete
Recovery retries this turn under its original id before starting a new one. Explore a successful recovery below.
Illustrative outcomes, not a live recovery. Commit markers govern transcript visibility; they do not undo external tool effects.
What one turn is
A turn processes your message and produces a reply, including any intermediate model and tool calls. The model and tool loop permits eight round trips by default. Deployment configuration and each agent's budget can change that limit. If the budget expires without text, the loop attempts a closing reply that describes what it tried. If that attempt is empty, the system returns a fallback asking you to retry or rephrase. See the agent loop for each step's behavior.
What a turn writes down
| Batch | What it records |
|---|---|
turn_start, user_msg, turn_dispatched | Batch one: the turn opened, your input, and the fact that it was accepted |
output_msg, usage, model_call | Batch two: what the agent said, the tokens it cost, and the model call's determinism inputs |
caller, participant | Who started the turn and who was present |
approval_request, question_request, turn_complete | Any pause the turn opened, and the marker that closes it |
An ingress_directive event records policy that an edge sends with the turn in batch one. Examples include a lower step budget or a required approver. See the event kinds and the kind table.
Commit before execution
The two batches determine how failed and interrupted turns recover.
Batch one commits atomically before the harness call. If that commit fails, the control plane rejects dispatch and the caller can retry. The harness cannot run side effects for that uncommitted input. See the dispatch error contract.
Batch two commits after the harness returns. It uses the original turn id and does not append the input again.
A crash between batches leaves a dispatch marker without a completion marker. The next replay retries the oldest eligible incomplete turn under its original id before running a new turn. Turns marked turn_ambiguous remain quarantined because retrying could duplicate tool effects. See the marker contract.
The transcript includes turn output only when both markers are present. An interrupted batch does not appear as a partial answer in the transcript. The committed-turn filter enforces this rule.
Batch one commits against the exact log head it observed. The work claim determines the turn id. These checks prevent concurrent callers from opening separate turns on one conversation. An exact retry uses the same durable turn id.
The conversation record
The state plane alone writes each conversation's signed, append-only partition, named conv-{id}. A new replica reconstructs conversation state by replaying that partition. The transcript and the agent's history use the same record.
# Replay committed turns without calling live models or tools.
polychrome conversation replay <conversation-id> --actor <admin-identity>
# Recompute every signed integrity root over the same record.
polychrome conversation verify <conversation-id> --actor <admin-identity>Both commands require an admin identity in provider:scope:external_id or provider:external_id format. They are read-only and exit non-zero on failure, so you can use them as CI checks. --what-if changes one recorded step and reports where replay diverges. polychrome conversation repair removes unreadable entries so the remaining record can replay. See conversation replay and the replay invariants.
Approvals and questions
When an action requires approval, the conversation pauses during the turn. Examples include a wallet payment or a gated message outside the chat. The paused turn commits an approval_request in its batch. An interrupted pause remains invisible on replay until that batch completes.
The system signs and records your answer. The next turn replays the log and applies the answer when it retries the approved call. The approval binds to the request id, tool name, and arguments you reviewed. Different arguments require another approval. See approval binding. A denial returns a denial result to the agent, which can continue. The system withholds model text from the paused turn and displays an approval card generated from the pending call.
Clarifying questions use question_request and question_response events, separate from approval decisions. See Trust for approval conditions and Payments for spending permissions.
Conversation lifecycle
Inspect a conversation's Kubernetes object to check its current phase.
kubectl get conversations
# NAME PHASE READY MODEL AGE| Phase | What it means |
|---|---|
| Pending | The sandbox is being provisioned or is booting |
| Ready | The conversation can take a turn now |
| Closing | The sandbox is being torn down |
| Paused | Idle and resumable |
| RollingHarness | Replacing the harness with a new image |
The phase is a display value. The object also exposes standard Ready, Progressing, and Degraded conditions for Kubernetes tooling. See the Conversation CRD.
The control plane records the last-turn time on every turn. Once the configured idle timeout expires, it closes the conversation and releases its pod. A missing timestamp or a zero timeout disables idle closing. The next turn reopens the conversation and resets the timer. The reconciler deletes a fully closed conversation's object after the retention window, which defaults to 24 hours. A malformed retention setting disables deletion. See the idle and retention rules.
One sandbox per conversation
Each conversation reconciles to one sandbox claim and one isolated pod under a gVisor runtime class. See the sandbox template. A conversation cannot access another conversation's files, history, or running work. The pod holds no Kubernetes credential and cannot call the cluster API. See harness socket access.
A replacement pod restores conversation state by replaying the record. Replacing a harness during a turn stops that pod. Recovery resumes eligible interrupted turns after the last committed step when available; ambiguous tool outcomes remain quarantined. Schedule image changes during an idle period if you need to avoid interrupting turns. See the harness replacement rule.
Exactly one writer
Within one replica, a per-conversation lock serializes concurrent connections. Across replicas, a Kubernetes lease provides exclusive access through version-checked creation and replacement. Each conversation has one writer at a time, even when a load balancer sends its turns to different pods. See the lease module.
Who is in a conversation
An edge connects a conversation to chat, the web, or another supported interaction pattern. Conversations can be private or shared. A persona identifies a person and determines their permissions. Turn batches record caller and participant attribution supplied by the edge. An approval response records the person who answered it. See Memory to inspect or clear information retained across turns.