Let one copy of handoff.md become the "entry document for the next session's AI to take over." Produce it proactively before context is passively compacted, so the new session does not have to rely on memory and can continue working just by reading this one file.
Core principle
Claude Code's automatic compaction discards content according to a character budget, and does not know which parts are judgment logic, which are failed approaches, or which is the first step to do next. Taking over after passive compaction is extremely costly: the new session can only reverse-engineer intent from the code and git log, while the paths that failed, the approaches that were rejected, and the current mental model — none of these are in the code.
A handoff is manual compaction. At the moment when you understand the current state best, fix in place the minimum information needed so that "the next session can get to work immediately upon receiving this document." It is not a session diary, and it is not meant for humans to review; the target reader is another LLM session (including tomorrow morning's you + tomorrow morning's Claude).
When to start
Write it when any one of these signals appears:
- Context usage exceeds 60%, and the current task still needs to continue
- About to proactively trigger compaction, or sensing that automatic compaction is imminent
- A long-running task has reached a natural breakpoint (a round of debugging ends, a submodule is finished, a set of tests passes)
- Proactively switching topics (a P1 bug comes in and you need to stop what you are doing / switch to another PR)
- Continuing across workdays (stopping tonight, continuing tomorrow morning or Monday)
- The task is complex and involves a lot of trial-and-error (two or three approaches were tried without convergence)
When to skip
- A single-file change is wrapped up in one commit → write it into the commit body
- Pure Q&A or explanatory session → there is nothing to hand off
- One-off scripts, throwaway experiments
- Purely exploratory chat (there is not even a task boundary — hand off what?)
- The task is so small that starting from scratch next session costs less than maintaining a handoff
If after writing it you find the work is too small, delete the file. An empty handoff is worse than none.
File location
By default, place handoff.md in the repository root.
When there are multiple unfinished tasks in parallel, instead place it in .handoff/<slug>.md, one per task, to avoid overwriting each other.
Whether to commit to git: by default gitignored (add to .gitignore). Rationale: handoffs change frequently and are session-level state, so they should not pollute the commit history. When a task spans multiple people and needs review, make a manual exception and let the handoff travel with the PR.
The four sections
Each section answers a different question; do not conflate them. Each entry should be one to three sentences, citing paths, line numbers, and commit SHAs.
Completed / 已完成
Things that actually work and have verification evidence.Do notwrite in "plan to do" or "half done."
- Good: "User registration API done,
POST /auth/registerpasses 6 tests. bcrypt rounds set to 12, seeapi/auth.ts:34。" - Bad: "Implemented user registration" — there is no verification evidence, and the next person taking over does not know whether to trust it.
Blocked / in progress but stuck
Things that are in progress and have not yet converged.Mustinclude: where you are stuck, what has already been tried, and why it did not work. This is the key thing that distinguishes a handoff from "listing TODOs."
- Good: "JWT refresh token is stuck on token rotation. Tried
jsonwebtoken'sverify+ re-signing, and there is a race condition (two concurrent requests both get the same old token and both pass validation). Next step: consider using a single-key Redis lock, see the TODO inauth/refresh.ts:88." - Bad: "JWT isn't done yet" — the next person picking it up will have to walk through all the failure paths again.
Next steps / Next steps
The first action of a new session. Order by priority, and make each one specific down to "which file to open and what to do."
- Good: "1) In
auth/refresh.tsadd a Redis lock. 2) Write a race condition test covering concurrency. 3) Runpnpm test authto confirm it's green." - Bad: "Keep working on refresh token" — too abstract, equivalent to saying nothing.
Context / Necessary context
Things a new session doesn't know and that can't be seen from the code. This includes environment state, the why behind key decisions, explicitly rejected approaches, and quirks of external dependencies.
- Good: "The database uses Postgres 16, the JWT secret is in
.envinJWT_SECRET(don't get it fromprocess.env.SECRET, that's something else). Passport.js was tried but the configuration was too complex, so we usejsonwebtoken+ a self-written middleware consistently." - Bad: "The project is Node.js + Express + Postgres" — git log / package.json can reveal that on its own.
Writing rules
- Write as you go. Append at the moment of the decision, don't wait until the end of the session and write in bulk. Writing it up afterward compresses "tried A, tried B, finally chose C" into "chose C," and the next person picking it up will walk through A and B again.
- Reference instead of copying. Paste file paths, line numbers, commit SHAs. Don't paste large chunks of code into the handoff, it will quickly become outdated.
- Delete what git log can tell you. "Added
auth.tsinvalidateToken" — the diff already says that. In the handoff write "why it'svalidateTokenrather thancheckToken", or "why validate expiration before validating the signature." - Explicitly record failures. Failed attempts are more valuable to the next person picking it up than the successful path. For example: tried Passport.js but the configuration was too complex → switched to jsonwebtoken. The next person picking it up won't go through Passport.js again.
- Don't write a novel. Each entry should be one to three sentences. If it needs a long explanation → it's probably two decisions crammed together, split them apart.
Anti-patterns
| Anti-pattern | Countermeasure |
|---|---|
| Chronological play-by-play ("first do A, then B, then C") | Change it to a structured "done / stuck / next steps," timing doesn't matter |
| Copying git log as the handoff body | git log is already in the repository itself, in the handoff write the "why" that git log doesn't write |
| Writing the background as a long document (project history, tech stack introduction) | Only write what the next person picking it up can't do without reading, assume the other person will read the README |
| Cramming decisions into body paragraphs, without sections | Keep decisions, context, and next steps separate, so a new session can locate them easily |
| Maintaining multiple handoff files that cross-reference each other | One handoff per task, if it exceeds that, split .handoff/<slug>.md |
Cadence
- Add a new entry: right when a decision is made, right when a failed attempt is rejected, right when a blocker becomes clear
- Rewrite: if the handoff exceeds 200 lines, it's probably because completed items weren't cleaned up in time, rewrite the whole thing
- Archive: after the task is complete, merge the handoff content into the PR description or commit body, and delete the handoff file
It's normal for the handoff to gain 0 entries during a session — sessions where no real decision was made and no blocker was encountered don't need writing. Forcing filler is worse than leaving it blank.
Picking up in a new session
First action of a new session (regardless of whether the user has asked):
- Check the repository root for
handoff.mdand the.handoff/directory - If found, read it through completely first, do not skim
- Confirm with the user in one sentence: "Planning to start from handoff's Next steps item 1 [specific content], correct?"
- After the user confirms, begin the first action,and at the same timekeep handoff updated in sync (cross off each item as it is completed, append each new failure)
- Do not let handoff drift out of sync with the current state — the user may start another session at any time
If handoff looks outdated (git log is much newer than handoff), first use git log -10 to compare, and ask the user about inconsistencies instead of guessing.
Integration with other skills
implementation-notes: when implementing a spec, record decisions, deviations, and open questions. handoff handles cross-session state, while implementation-notes handles the judgment details of a single spec. The two coexist — handoff's "Context" section can reference specific entries in implementation-notes.- Planning document / plan: an architectural proposal should have a separate plan document (produced by the planning workflow in your environment). handoff does not replace plan; it only mentions which step of which plan is currently being executed.
git-commit: for things that are done and already committed, write the "why" in the commit body, and leave only a one-sentence pointer in handoff. Do not duplicate change records in two places.
Minimal template
# Handoff — <task slug>
**Last updated**: <YYYY-MM-DD HH:MM>
**Branch**: <branch>
**Spec / Plan**: <link or path>
## Completed
- (nothing yet)
## Blocked
- (nothing yet)
## Next steps
1. (first concrete action)
## Context
- (anything the new session can't get from code or git)
Keep the four section headings even if they are empty — an empty heading is a question to yourself.
Quality checklist
Before a new session starts taking over, handoff must satisfy:
- no section is empty (or explicitly state "none")
- every Completed item has verification evidence (tests, command output, file paths)
- every Blocked item includes "what was tried, why it did not work"
- every Next steps item is specific to a file + action, not an abstract goal
- the Context section does not repeat what README / package.json can tell a new session
- the entire document can be read by a new session within 60 seconds