The Third State

Midway through generating release notes for a recent release, the agent hit its context limit. Not a crash, not an error: it just ran out of room to keep the whole task in its head at once.

It picked back up cleanly. I’d built the workflow to write its intermediate state to disk as it went, so the resumed run read the saved artifact and continued from where it stopped, instead of starting over or producing something incoherent.

That save came from having been burned before, not from foresight. An earlier version of the same workflow had died the ugly way, halfway through with nothing to show for it, and the checkpoint I added afterward is the only reason the release-notes run recovered instead of dying the same death twice.

The third state

The instinct most of us have about a long agent run is that it has two outcomes. It finishes or it fails. You kick it off, watch the output scroll, and end up with a good result or a bad one. That holds up while the run fits comfortably in a single pass, which most short tasks do. It stops holding up as the work gets longer: more steps, more surface area, more context burned before the end. And that’s the direction the work keeps going. We hand agents bigger jobs every month.

Between finish and fail there’s a third outcome, and on long runs it’s the common one: the run that got partway. Not zero progress. Real progress. The agent did three of the five things you needed, then hit a wall. The context limit, a timeout, a rate limit, a killed process, a flaky tool call. The work up to the wall was good. It just didn’t reach the end in one go.

What happens next is the whole game. Does the run resume from that progress, or restart from nothing? Design for the third state and resuming is cheap; the earlier work still counts. Ignore it and every retry is a fresh coin flip on clearing the entire task in one uninterrupted pass before something trips it again.

This is not something you can prompt your way out of. No instruction, however clever, makes a run that doesn’t fit in one pass suddenly fit. It’s an architecture question, the same one you’d answer for any long job that can’t assume it finishes on the first try.

Checkpoint, the boring way

The fix is old and dull, which is exactly why it’s easy to skip. It’s checkpointing: write intermediate state somewhere durable as you go, and let the run start from that state instead of from the top. We’ve done this for batch jobs and ETL pipelines for decades.

For the release notes it was small and concrete. As the agent worked down the changelog, it wrote its progress to a file: which commits it had processed, the notes drafted so far, where it was in the list. The resumed run read that file first. Saved progress, pick up there. None, start fresh. That’s the whole mechanism. A structured artifact on disk, and a run that reads it before it does anything else.

The specifics vary with the task. Sometimes the state is a scratch file, sometimes a row in a table, sometimes a directory of partial outputs. What has to be true is the same either way: durable state written as the work happens, and a resume path that treats it as the starting point. You’re making “get interrupted and come back” a supported operation instead of a catastrophe.

A file you can read is a file you can review

Writing the state down buys a second thing, and it’s a good part of why this is worth doing at all. The artifact the run resumes from is also an artifact a person can open and read.

Mid-run, I can see exactly what the agent has done: which commits it grouped, how it’s wording things, where it is in the list. When the final output looks off, I don’t have to reconstruct the model’s reasoning from the result. Its working notes are sitting in a file. A run that keeps its state only in the model’s head is opaque; when it goes wrong you get a bad result and no account of how it got there. A run that checkpoints leaves a trail, and the same file that lets it resume lets a human audit it.

That was part of the design, not a happy accident. If you’re going to write the state down anyway, write it where a person can read it.

Workflows are workflows

None of this is free, and on a short task that reliably finishes in one pass it’s wasted effort. You don’t checkpoint a job that always completes on the first try. The cost only earns its keep on the long runs: the many-step, expensive ones that are both the most likely to get interrupted and the most painful to redo from zero. Those are exactly the runs where a little plumbing, written once, saves you from repeating the work you least want to repeat at the worst possible moment. Weigh the cost there, on the runs that actually hurt when they fail, and the trade isn’t close.

And the plumbing really is small, which is the part you can act on today. The next time you build an agent or write a reusable skill for a long task, decide up front where its progress gets written, and have it read that back before it starts. That’s most of the job. You don’t need a framework or a queue; a file the run appends to as it works and checks on startup covers the common case. Build it into the first version and you never have to meet the third state the way I did, in the middle of a run that had already failed.

The mistake is treating an agent run as a special kind of thing, exempt from that math. It isn’t. The worker is a model, but the run is still a long-running process, and long-running processes get the same failure-mode thinking we’d bring to any other job. “It usually just works” describes the runs where you got lucky. It is not a resumption strategy.