Lesson15 min

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.sh setup 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:

FormatUse forWhy
Structured JSON (tests.json, task lists)state machines, checklists, pass/fail datamachine-checkable, no drift in meaning
Freeform text (progress.txt, notes)narrative progress, dead ends tried, gotchascheap to write, rich to read
Git commitscode checkpointsfree 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.json with 200 entries (one per component), an init.sh, and progress.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 to progress.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."