A single dev arc spanned two evenings. The first night I shipped one pipeline phase; the second night I shipped the next. The thing that made night two cheap wasn’t memory, and it wasn’t chat history. It was a document I wrote in the last ten minutes of night one, on purpose, as the actual deliverable of the session.
I’ve started treating the end-of-session handoff the way I treat the code: as a thing I owe, not a courtesy I extend if I have time. Because the alternative is paying to rebuild state every time I sit back down, and that bill compounds.
What the model does not remember
Here’s the trap, and it’s worse with AI in the loop, not better.
When I pick up a project the next evening, I open a fresh session. The model has no idea what happened last night. The chat that did the work is either gone or buried, and even if I have it, re-reading a 200-prompt transcript to reconstruct “where was I” is the most expensive way to answer that question. The transcript is the raw material, not the answer. Reading it back means re-deriving every decision I already made, including the ones I made and then reversed.
So the next session starts by reconstructing state from whatever’s lying around: git log, half-remembered trade-offs, a TODO comment I left myself that no longer makes sense out of context. That reconstruction is pure overhead. None of it ships anything. And if I get it slightly wrong, I relitigate a decision that was already settled, change something that was deliberate, and burn an hour discovering why past-me did it the way past-me did it.
The fix is to stop making the next session derive state, and hand it state instead.
The handoff doc, with a non-obvious structure
The doc I wrote had six sections. Five of them are the kind of thing you’d guess. One of them is the whole point.
- What got built. The phases that shipped, in one line each.
- Decisions baked in (don’t relitigate). The settled trade-offs, stated as settled.
- Exact repo state. Each repo, its HEAD SHA, what’s in it, and whether it’s pushed and clean.
- Ready to pick up. The next step, written as something you can actually run.
- Memory updates this session. New rules I learned, named.
- What’s NOT load-bearing yet. The things that look done but aren’t wired in.
Sections 1, 3, 4, 5, 6 are bookkeeping. Useful, but mechanical. Section 2 is the one that changes the economics.
”Decisions baked in, don’t relitigate” is the load-bearing section
When you’ve spent a long session making calls, you accumulate a stack of trade-offs that felt hard in the moment and are settled by the end. Where the data lives. Why this format and not that one. Why the final synthesis stage gets less tool access than the upstream stages feeding it, not more, because the blast radius of a hijacked tool call is highest closest to where output goes public.
If the next session can’t see that those were decided, it re-decides them. Worse, a fresh model will happily re-derive a “better” answer to a question you already closed, and now you’re defending a choice instead of building on it.
So I write them down as closed, not as leanings. The entry reads: “Raw data is cold storage on the box, gitignored; the index is the hot path; they don’t live together. Decided. Move on.” The next session reads that and treats it as a foundation, not an open question. That single section is the difference between resuming work and reopening a debate.
It also does something for me specifically. I’m the kind of dev who will, given an opening, redesign the thing I shipped last night. Writing the decision down as baked-in is me pre-committing past-me’s judgment so present-me doesn’t waste a session improving something that was fine.
Exact SHAs turn “where was I” from an investigation into a lookup
The repo-state section is short and boring and saves the most clock time. For each repo: name, HEAD SHA, contents, pushed-and-clean status.
repo-A b1b3206 phase (a)+(b) shipped, pushed, cleanrepo-B 695ff76 raw + manifest, pushed, cleanThat looks trivial. It is the entire difference between “where was I” being a lookup versus an investigation. With the SHA written down, I check out exactly the tree the handoff describes and I’m standing where I stood when I wrote it. No git archaeology, no “wait, did I push that branch,” no discovering mid-session that the clone I’m in is two commits behind the one I actually worked in. The SHA is the coordinate.
This matters more the more parallel work you run. On a busy day I’ll have the same product checked out in several clones at once, each on a different branch. “Which tree had the work” is a real question, and a SHA answers it with zero ambiguity.
”Ready to pick up” has to be runnable, not vague
The next-step section fails if it says “continue the pipeline.” That’s a heading, not an instruction. It only earns its place if the next session can execute it cold.
So the entry for the next phase read like a runbook: generate the export from Settings, the ZIP arrives by email, the download URL expires in 24 hours (a real gotcha that would have cost the next session an hour if it wasn’t flagged), reuse the same paired-output shape from the phase that already shipped, the idempotency key is the conversation id, output goes to a specific directory. That’s the future self’s first three steps, pre-written, including the trap.
The test for this section: could a different person, or a fresh model with no context, run it without asking you a question? If not, it’s a heading wearing a runbook’s clothes.
What’s NOT load-bearing yet prevents false confidence
The last section is the inverse of “decisions baked in.” It catches the things that look finished and aren’t.
The example that earned its spot: the privacy scrubber was wired into one pipeline stage, and it worked, and the output was clean. But the wiring is not automatic. Every new stage has to wire the scrubber in explicitly, or it silently ships unredacted data. If the handoff said “scrubber: done,” the next session would build the next stage on a false floor and leak. Saying “scrubber works here, NOT inherited, wire it per stage” is the guardrail.
This is the section that stops “I shipped X” from being read as “X is structurally true everywhere.” It’s the difference between a thing being demonstrated once and a thing being load-bearing.
This is a shared knowledge store applied to time
The reason I care about this enough to write a post about it: it’s the same move as the thing I’d already built for machines.
When you run AI on several fronts at once, every agent has partial context and you spend your day restating yourself. The fix there was a shared knowledge store, a single place for durable operational state that the agents read instead of asking. That solved context loss across machines and agents.
The handoff doc solves the same problem across time. The next session is, functionally, a different machine with no shared memory. The handoff is the entry it reads on startup so it doesn’t have to ask past-me anything. Same infrastructure move, different axis. One is memory across space, the other is memory across time, and both convert an expensive re-derivation into a cheap lookup.
That reframe is what flips the handoff from optional to mandatory in my head. I don’t write durable state into the shared store because it’s polite to my agents. I write it because re-establishing it from scratch every session is the dominant cost once AI makes everything else fast. The handoff is exactly that, scoped to one project across one night’s gap. Any state I don’t hand off, I end up paying to rebuild the next evening.
Related
- Bootstrapping a New Agent Station, and Giving It a Phone: the station-level state that the handoff doc needs to capture when work spans agents
- When Working Notes Become Operational Infrastructure: how session notes compound into durable infrastructure over time
- My AI agent’s memory was per-folder, so my four clones never learned from each other: the failure mode that a proper handoff doc prevents, state that doesn’t cross context boundaries
- Writing an AI_CONTEXT.md So Your Assistant Stops Rediscovering Your Codebase: the persistent context file that complements the per-session handoff
- When a Manual Workflow Hurts in Seven Small Ways, the Seven Ways Are Your Spec: encoding session-close friction into automation, the next step after the handoff becomes routine