TAI BUI
Back to curriculum
BuildPython

Multi-Session Handoff

The session is going to end, but the work is not. The handoff packet turns "the agent worked for an hour" into "the next session is productive in the first minute." Build it on purpose.

TIME: ~50 minutesPREREQUISITES: Phase 14 · 34 (Repo Memory), Phase 14 · 38 (Verification), Phase 14 · 39 (Reviewer)

Learning Objectives

  • Identify the seven fields every handoff packet needs.
  • Generate a handoff from the workbench artifacts without hand-writing prose.
  • Trim large feedback logs into a handoff-sized summary.
  • Make the next session's first action deterministic.

The Problem

The session ends and the agent says "great, we made progress." The next session opens and the next agent asks where the last one left off. That answer is gone. So the next agent rediscovers, re-runs the same commands, re-asks the human the same questions, and burns thirty minutes recovering the last thirty seconds of the previous session.

A bad handoff gets paid for every session for the life of the task. The fix is a packet generated automatically at session end: what changed, why, what was tried, what failed, what is left, and what to do first next time.

Warm-up0/3
1. A fresh agent session starts with an empty context window. Where can it learn what the previous session did?Warm-up
2. On the reviewer's 0-2 scale across five dimensions, what total is the soft-fail line?Warm-up
3. A reviewer gives a diff 9 out of 10 overall, but one dimension scored 0. What is the verdict?Warm-up

The Concept

A handoff packet is a generated artifact, in markdown and JSON, that carries seven fields from one session to the next.

Mermaid diagram

Seven fields every handoff carries

FieldQuestion it answers
summaryOne paragraph of what was done
changed_filesThe diff at a glance
commands_runWhat was actually executed
failed_attemptsWhat was tried and why it did not work
open_risksWhat could bite next session, with severity
next_actionThe first concrete step next session takes
verdict_pointerPath to the verification + review reports

The next_action field is the load-bearing one. A handoff with everything except next_action is a status report, not a handoff.

Handoffs are generated, not written

A hand-written handoff gets skipped on a hard day. The generator reads the workbench artifacts and emits the packet, so the agent's job is to leave the workbench in a state the generator can summarize, not to write the summary.

Two forms: human-readable and machine-readable

handoff.md is what the human reads, and handoff.json is what the next agent loads. Both come from the same source artifacts. If they diverge, the JSON wins.

Feedback log trimming

The full feedback_record.jsonl can run to hundreds of entries. The packet carries the last K plus every entry with a non-zero exit. The next session can load the full log if it needs to, and the packet stays small.

Leave a clean state

A handoff describes the work. A clean state makes the work resumable. Those are different things. A perfect handoff.md is worthless if the next session opens to a half-applied diff, a temp file the agent forgot, a stray branch, and tests that error before they even run. The next agent then spends its first ten minutes cleaning up after the last one instead of building, and that cost repeats every session for the life of the task.

So the session does not end when the feature works. It ends when the workbench is in a state the generator can summarize and the next session can trust. Cleanup is its own phase, run before the handoff. It is a check rather than a habit, because a habit gets skipped on a hard day.

CheckClean meansDirty blocks because
Working treeEvery change committed or explicitly stashed with a noteA half-applied diff looks like intentional work to the next agent
Temp artifactsNo *.tmp, scratch dirs, debug prints, or commented-out blocks left behindStray files pollute the diff and the next agent's mental model
TestsGreen, or red with the failure named in open_risksA silent red test is a trap the next session steps in
Feature boardfeature_list.json status reflects reality (Phase 14 · 36)A stale board sends the next session to work that is already done
BranchOn the expected branch, no detached HEAD, no orphan branchesWrong branch means the next session's first commit lands in the wrong place

The cleanup phase emits a clean_state.json of blocking issues, and an empty list is the precondition a production handoff generator should assert before it writes a packet. (The demo in Build It skips this check and reads only state, verdict, review, and feedback.) A handoff built on a dirty tree is a forwarded mess. The two artifacts pair: cleanup proves the workbench is safe to leave, and the handoff proves the next session knows where to start.

Check: The Concept0/19
1. What is a handoff packet?Define
2. What is the next_action field in a handoff?Define
3. What does feedback trimming produce for the packet?Define
4. What makes a document a status report instead of a handoff?Define
5. What is the verdict_pointer field in a packet?Define
6. What does a clean state mean when a session ends?Define
7. A packet has summary, changed_files, commands_run, failed_attempts, open_risks, and verdict_pointer. What is missing?Explain
8. handoff.md and handoff.json disagree about a risk. Which do you trust?Explain
9. Why does a generator read the workbench artifacts instead of the agent writing the handoff itself?Explain
10. Why does the trim keep every non-zero exit and not only the last K entries?Explain
11. How does recording a next_action make the next session's first move deterministic?Explain
12. Why is a perfect handoff.md not enough on its own?Explain
13. A session logged 300 feedback entries, 12 of them with non-zero exits. What goes in the packet's feedback tail?Apply
14. Tests pass and the handoff is ready, but the working tree holds a half-applied diff. What happens first?Apply
15. A packet lists summary, changed files, commands, failures, risks and receipts, yet the next agent still asks the human what to do first. Which change addresses the cause?Analyze
16. The next session's first commit lands on the wrong branch. Which cleanup check was skipped?Analyze
17. The next session hits a test that errors before running, and the packet never mentioned it. Which rule was broken?Analyze
18. The next session starts work that was already finished. Which cleanup check was stale?Analyze
19. A production generator finishes a run with no next_action recorded. Following the design the lesson argues for, what should it do?Design

Build It

code/main.py implements:

  • A loader that gathers state, verdict, review, and feedback into a single WorkbenchSnapshot.
  • A generate_handoff(snapshot) -> (markdown, payload) function.
  • A filter that picks the last K feedback entries plus all non-zero exits.
  • A demo run that writes handoff.md and handoff.json next to the script.

The demo does not implement the cleanup phase. It never reads or writes clean_state.json and will happily generate a packet from a dirty workbench. Treat the clean_state.json precondition from the Concept as the first thing you add when you wire the generator into a real runtime.

Run it:

python3 code/main.py

The output is a printed handoff body, plus both files on disk.

Check: Build It0/7
1. The demo feedback list has four records, one of them a failing pytest run, and TAIL_K is 5. How many records does trim_feedback return?Apply
2. In the demo snapshot the verdict has one warn finding, state has one blocker, and the review total is 8. How many risks does derive_risks return?Apply
3. The state dict has no next_action key. What does generate_handoff put in the packet?Apply
4. What does failed_attempts hold for the demo feedback, where pytest test_signup.py first exits 1 and later exits 0?Apply
5. The review is {"verdict": "fail", "total": 5}, with no findings and no blockers. What does derive_risks return?Apply
6. What summary string does the demo snapshot produce?Apply
7. The Build It generator writes a packet from a workbench with stray temp files and a half-applied diff, and raises no complaint. Why?Analyze

Production patterns in the wild

Codex CLI, Claude Code, and OpenCode each ship a different compaction story. The structured handoff packet sits on top of all three.

Compaction strategies vary; the packet schema does not. Codex CLI's POST /v1/responses/compact is a server-side opaque AES blob (the fast path for OpenAI models), and the fallback is a local "handoff summary" appended as a _summary user-role message. Claude Code runs five-stage progressive compaction at 95% of context. OpenCode does timestamp-based message hiding plus a 5-heading LLM summary. Three mechanisms, one need: serialize what survives compression into a portable artifact. The packet is that artifact.

Fresh-session handoff is not compaction. Compaction extends a session. Handoff closes one cleanly and starts the next. The Hermes Issue #20372 framing (April 2026) is right: when in-place compression starts degrading, the agent should write a compact handoff, end the session, and resume in fresh context. The packet makes that transition cheap. Compressing until quality collapses is the mistake, and the fix is to budget for an early, clean handoff.

One active handoff per branch and topic. Multi-agent coordination breaks down on stale handoffs more than on bad model output. Always include branch, last_known_good_commit, and a status of active | superseded | archived. Stale handoffs get archived, and only the active one drives the next session. That is the difference between handoff-as-notes and handoff-as-state.

Wrap up at 50-75% context, not at the wall. The hand-written-pattern playbook (CLAUDE.md + HANDOVER.md) reports best results when the session ends at 50-75% of the context budget instead of waiting for the 95% point where Claude Code compacts. The packet generator then runs before compression artifacts pollute the source state. Writing it is cheap while context is intact and expensive once the model is already losing its place.

Check: Production patterns in the wild0/5
1. Why does each packet carry a status of active, superseded or archived?Explain
2. How does the lesson distinguish compaction from handoff?Explain
3. Which pairing of tool and compaction mechanism matches the lesson?Explain
4. An agent keeps compressing its context as quality drops and carries on past 95%. What mistake does the lesson name?Analyze
5. A long task is at 60% of context with work remaining and tests passing. What is the best move?Design

Use It

Three places the generator earns its keep in production:

  • Session-end hook. The runtime fires the generator when the user closes the chat, and the packet lands in outputs/handoff/<session_id>/.
  • PR template. The generator's markdown doubles as a PR body, so reviewers read it without opening five other files.
  • Cross-agent handoff. Build with one product (Claude Code), continue with another (Codex). The packet is the lingua franca.

The packet is small, regular, and cheap to produce, and the savings compound with every session.

Check: Use It0/2
1. You want every closed chat to leave a packet without relying on the agent to remember. Which design fits?Design
2. You build with Claude Code and a colleague continues with Codex CLI. Their compaction mechanisms differ. What should move between them?Design

Ship It

outputs/skill-handoff-generator.md produces a generator tuned to a project's artifact paths, an end-of-session hook that runs it, a handoff.json schema the next agent reads on startup, and the cleanup precondition: the generator refuses to write a packet while clean_state.json lists any blocking issue.

Author's Note

I treat next_action as the gate for the whole packet. Every other field can be filled in and the packet still only records what happened, so I'd have the generator refuse to emit it without a first step, the same way a production generator refuses to write on a dirty tree. My bet is on the hard stop: a production generator asserts an empty clean_state.json before it writes, and next_action deserves the same precondition because without it the packet is a status report.

Exercises

  1. Add an assumptions_to_validate field that surfaces every assumption the builder logged but the reviewer did not score above 1 on the reviewer's 0-2 scale (Phase 14 · 39).
  2. Trim the feedback summary differently for failing runs versus passing ones. Defend the asymmetry.
  3. Include a "questions for the human" list. What is the threshold for a question to make it into the packet versus into a chat message?
  4. Make the generator idempotent so that running it twice produces the same packet. What needs to be stable for that to hold?
  5. Add a "next session prereqs" section listing exactly the artifacts the next session must load before acting.

Key Terms

TermWhat people sayWhat it actually means
Handoff packet"Session summary"Generated artifact carrying the seven fields, both markdown and JSON
Next action"What to do first"The one concrete step that starts the next session
Feedback trim"Log summary"Last K records plus every non-zero exit
Status report"What we did"A document missing next_action; useful, but not a handoff
Verdict pointer"Receipt"Path to the verification + review reports for traceability

Further Reading

Code

Ships With