State and Subagents
An agent that runs for five minutes can keep everything in its head. An agent that runs for five hours cannot. The difference is not a bigger context window. It is state that lives outside the model.
Three kinds of state
Most agent systems blur these together, then suffer for it:
| State | What it is | Where it lives | Lifetime |
|---|---|---|---|
| Transcript | The messages and tool results shown to the model | Context window / log | One run, compacted |
| Task state | Goals, constraints, decisions, plan, open questions | Structured store | One task, survives compaction |
| World state | Files, branches, tickets, deployments, database rows | The actual environment | Until changed |
The transcript is a view. It is reconstructed from the event log and the task state, then trimmed to fit. It is not the source of truth. If your harness treats the transcript as the only memory, compaction becomes data loss and a crash becomes amnesia.
The world state is the most important and the easiest to ignore. If the agent edited a file, the file is the truth — not the summary that says it did. Verification must read the world, not the transcript.
The event log
An append-only event log is the backbone of a durable harness. Every meaningful thing that happens becomes an event:
{
"id": "evt_01J8...",
"taskId": "task_42",
"ts": "2026-11-10T14:02:11.482Z",
"type": "tool.completed",
"actor": "agent:planner",
"tool": "run_tests",
"args": { "suite": "unit" },
"result": { "exitCode": 1, "failed": ["cart applies tax before discount"] },
"tokens": { "input": 842, "output": 96 },
"durationMs": 1840,
"policy": { "decision": "allow", "rule": "workspace-exec" },
"parent": "span_01J8..."
}
Properties that matter:
- Append-only. Never mutate history to make it look cleaner.
- Correlated. Every event belongs to a task, a span and a parent action.
- Attributed. Which agent, which tool, which policy decision, which user.
- Sufficient to replay. Store tool arguments and results, not just “tool ran.”
- Redacted. Secrets and PII are removed at write time, not at display time.
The log is also the eval dataset. A failed run can become a regression test. A successful run can become a demonstration. Without it, every incident starts from “can you reproduce it?”
Checkpoints and resumption
A checkpoint is a consistent snapshot of task state plus a pointer into the event log. On resume, the harness should be able to answer:
- What was the goal and the acceptance criteria?
- What has been tried, and what did it produce?
- Which files changed, and are they committed?
- Which external side effects already happened?
- What is the next allowed action?
A checkpoint is not “save the messages.” Messages can be regenerated. Side effects cannot. The hard part is making resumption safe:
- Idempotency. If the log says an email was sent, resuming must not send it again. Use an idempotency key and check the external system.
- Transaction boundaries. A tool that writes three files should either complete or leave a recoverable marker. Partial writes are a classic source of corruption.
- Workspace snapshots. For long tasks, snapshot the repo (commit, stash, or copy) at checkpoints. A transcript that says “tests pass” is not evidence if the working tree changed afterward.
- Fencing. If a worker crashes and a new one resumes, the old one must not still be running tools. Use leases or epochs.
Context is a cache, not a database
Compaction should rebuild the context from durable state, not summarize the transcript into a lossy substitute. A good context builder:
- Loads the task state: goal, constraints, decisions, open questions.
- Loads the current plan and the last N events.
- Retrieves relevant files by path or search, not by dumping the repo.
- Adds a compact summary of completed work.
- Reserves output tokens.
This is why the context/compaction lesson insisted on pinning decisions. The pinned facts belong in task state, not in a model-generated summary. If the builder can always reconstruct them, compaction stops being a correctness hazard.
Subagents: isolation or parallelism
A subagent is a separate model session with its own context, tools and budget. It is useful for two different reasons:
Isolation. A research task can read 40 files and return a five-line answer. The parent never pays the context cost of those 40 files. A security review can run with read-only tools while the main agent edits.
Parallelism. Independent subtasks — “map the auth flow,” “find the failing test,” “summarize the API docs” — can run concurrently. The wall-clock win can be large.
But subagents add coordination cost:
- The parent must serialize enough context for the child to succeed.
- The child must return a result the parent can trust and merge.
- Both may edit the same files.
- Failures can be silent if the parent does not inspect the child’s evidence.
- Cost and latency multiply.
A subagent is not free intelligence. It is a distributed-systems boundary with a language model on both sides.
Handoff and return contracts
Treat the subagent boundary like an API. Define both directions.
Handoff (parent → child):
{
"task": "Find every place the signup endpoint returns 400 for validation errors.",
"acceptance": "Return file paths and line numbers; do not edit files.",
"context": ["src/routes/signup.ts", "docs/api.md"],
"constraints": ["read-only", "no network", "max 12 tool calls"],
"returnFormat": { "findings": [{ "path": "string", "line": "number", "note": "string" }] }
}
Return (child → parent):
{
"status": "success",
"summary": "Three validation paths return 400; two are intentional legacy paths.",
"findings": [
{ "path": "src/routes/signup.ts", "line": 42, "note": "email format" },
{ "path": "src/routes/signup.ts", "line": 57, "note": "password length" }
],
"confidence": "high",
"unresolved": ["legacy /v1/signup still returns 400 by contract"],
"evidence": ["evt_01J8...", "evt_01J9..."]
}
The parent should not accept a prose blob. It should validate the shape, check the evidence, and decide whether to trust it. A subagent that says “done” with no evidence is a claim, not a result.
Shared workspace, shared danger
Parallel subagents editing the same repository create merge conflicts, lost updates and confusing test results. Options:
- Read-only by default. Most research subagents should not write.
- Separate worktrees. Give each writer its own git worktree or branch, then merge deliberately.
- File ownership. Assign disjoint file sets and reject edits outside them.
- Serialized writes. Let subagents propose patches, and have the parent apply them one at a time.
- Snapshot and diff. Capture the workspace before and after; attribute every change to a child.
The same rules apply to tools with external side effects. Two subagents both “create a ticket” can create duplicates. Use idempotency keys tied to the subtask, not to the model call.
A supervisor pattern
A practical topology for a long coding task:
- Planner (parent) maintains the task state and decides the next milestone.
- Researcher (read-only child) maps the code and returns findings with evidence.
- Implementer (write child) works in an isolated worktree on a scoped change.
- Verifier (read-only child) runs the test suite and inspects the diff.
- Parent merges only when the verifier produces evidence and the acceptance criteria are met.
The parent spends most of its context on decisions, not raw file contents. Children spend their context on bounded work. The trace shows the whole tree.
This is more machinery than a single loop. It pays off when tasks are long, parallel or high-risk. For a one-file fix, a single agent with a good test loop is simpler and cheaper.
Failure modes to design for
- Lost handoff. The child lacks a constraint and does something the parent forbids.
- Hallucinated return. The child reports success without running the check.
- Stale state. The parent edits a file while the child is reading an old version.
- Duplicate side effect. Both agents retry the same external action.
- Orphaned child. The parent times out; the child keeps running and spending money.
- Context mismatch. The parent’s summary of the child’s result differs from the child’s actual output.
- Merge conflict. Two writers touch the same lines.
Each has a structural fix: explicit contracts, read-only defaults, idempotency, cancellation propagation, evidence validation, and isolated workspaces.
Next: reliability — retries, timeouts, cancellation and the failure modes that only appear when a tool is slow, flaky or wrong.