Long-Horizon Agents
Keeping state across context windows: compaction, external notes and memory files, and structured handoffs between sessions.
The problem: tasks longer than memory
A context window is the model's working memory — everything it can "see" right now. Big refactors, multi-day migrations, and thousand-step tasks simply don't fit in one window. A long-horizon agent therefore needs state that lives outside the model: files, tests, and git history that any future session can rediscover.
The mental shift: stop treating the conversation as the source of truth. The filesystem is the source of truth; the conversation is a scratchpad that will be wiped.
Give the first window a different job
The first context window is special — it's the only one that sees your original, full-fidelity instructions. Anthropic's guidance: use a different prompt for window one, focused on building scaffolding that outlasts it:
- Write the tests first, in a structured file like
tests.json— a machine-readable list of what "done" means. - Create an
init.shsetup script so every future session can rebuild the environment (install deps, start services) with one command. - Write the plan down as a todo list in a file, not just in conversation.
Later windows don't need your original essay of instructions. They need the todo list, the tests, and the setup script.
Protect the spec from the agent
An agent grinding against a failing test will eventually be tempted to edit the test. Forbid it explicitly:
"It is unacceptable to remove or edit tests, because this could lead to missing or buggy functionality."
The tests are the contract. If the agent believes a test is wrong, it should say so — not quietly weaken the assertion at 2 a.m. of its long run.
The three storage formats
Use each format for what it's good at:
| Format | Use for | Why |
|---|---|---|
Structured JSON (tests.json, task lists) | state machines, checklists, pass/fail data | machine-checkable, no drift in meaning |
Freeform text (progress.txt, notes) | narrative progress, dead ends tried, gotchas | cheap to write, rich to read |
| Git commits | code checkpoints | free rollback, and git log is a timeline any session can read |
The latest models are genuinely good at rediscovering state from a filesystem — reading the notes, the test results, and the git log, then picking up where a predecessor left off.
Fresh window vs. compaction
When a window fills, you have two options:
- Compaction: summarize the conversation and continue with the compressed version. Convenient, but summaries lose detail, and losses compound over many compactions.
- A brand-new window: start clean and re-enter from the filesystem. Often better for long tasks — but only if you're prescriptive about re-entry.
A good re-entry prompt reads like a runbook:
1. Run pwd to confirm where you are.
2. Read progress.txt, tests.json, and the last 10 git log entries.
3. Run ./init.sh, then run the integration test suite.
4. Only after the suite runs: pick the next failing test and work on it.
Notice the ordering: verify the world before touching it. A session that starts implementing based on stale assumptions wastes its whole window.
Tell the agent about its own harness
Context-aware models behave better when told the rules of their environment:
"Your context will be automatically compacted as it fills. Do not stop tasks early due to token budget concerns. As you approach the limit, save progress to your memory files."
Without this, models sometimes wrap up prematurely — hoarding budget for a shutdown that the harness would have handled anyway.
Close the loop with verification tools
A long-horizon agent that can't check its own work needs a human checking every window — which defeats the point. Give it verification it can run alone: the test suite, a build command, browser automation (e.g. a Playwright MCP server) to click through the actual UI, or screenshot comparison. Correctness should come from the environment, not from continuous human feedback.
A worked example: the multi-day migration
Migrating 200 components from an old framework:
- Window 1: writes
tests.jsonwith 200 entries (one per component), aninit.sh, andprogress.txt. Commits the scaffolding. - Windows 2–N: each starts with the re-entry runbook, runs
init.sh, sees "148/200 passing," picks the next failing component, migrates it, runs its test, commits, and appends one line toprogress.txt— including dead ends ("component 149 needs the legacy polyfill — don't remove it"). - Any window can die at any moment and the next one loses at most a few minutes of work, because every fact that matters lives in files and git, not in the conversation.
That resilience — not any single prompt trick — is what makes an agent "long-horizon."