Session Handoff
A handoff is judged by one test: can someone with zero memory of this conversation pick up in under five minutes and not repeat a mistake we already made? Write for that reader.
When to trigger
- Context window is more than about two-thirds used, or the conversation has been running for more than an hour of edits.
- You are about to switch tools, models or people.
- Work is being paused for the day, or a blocking question was sent to a human.
- The user asks for a summary, status or handoff.
Step 1 — Capture ground truth, not memory
Run these and paste the relevant output into the handoff rather than paraphrasing from memory:
git status --short
git branch --show-current
git log --oneline origin/main..HEAD
git diff --stat
# the project's test command, e.g.
pnpm test 2>&1 | tail -20
Also list any files you created outside git (scratch scripts, notes, downloaded data) with their paths.
Step 2 — Write the document
Save to docs/handoffs/<yyyy-mm-dd>-<topic>.md (or HANDOFF.md at the repo root if the project has no docs folder). Keep it under one screen where possible; link to files instead of quoting them.
# Handoff — <topic> — <date>
## Goal (1–2 sentences)
What we are trying to achieve and how we will know it is done.
## Current state
- Branch: feat/invoice-status (3 commits ahead of main)
- Tests: 44 pass, 2 fail (invoice.test.ts: "rejects refund after 30 days" — expected, not yet implemented)
- Uncommitted: src/invoices/repo.ts (half-done setStatus)
## Done
- Bullet list of completed steps with commit hashes.
## Decisions made (and why)
- Chose check constraint over Postgres enum because migrations rename badly. (user agreed)
- Rejected: storing status in JSONB — not indexable.
## Things that did not work — do not retry
- `prisma migrate dev` fails on CI because shadow DB is missing; use `prisma migrate deploy` there.
- The mock for `sendEmail` must be reset between tests or the second test sees two calls.
## Open questions / blockers
- Waiting on @user: should partial refunds be allowed? (asked 14:20)
## Next steps (ordered, each verifiable)
1. Finish `setStatus` in repo.ts; verify with `pnpm test src/invoices`.
2. Wire the Server Action in app/invoices/actions.ts; verify by submitting the form.
3. ...
## How to run
- Setup: `pnpm i && cp .env.example .env && pnpm db:migrate`
- Tests: `pnpm test`
- Dev: `pnpm dev` → http://localhost:3000/invoices
## Key files
- src/invoices/repo.ts — data access
- docs/specs/2026-09-01-invoice-status.md — spec
Step 3 — Prune the conversation's noise
Do not include: the full narrative of what you tried in order, long code pastes, tool output that is reproducible by running a command, or opinions without a decision. If a detail matters only if a specific failure recurs, put it under "did not work" as one line.
Step 4 — Verify the handoff
- Every "next step" names a file and a verification command.
- Every decision has a reason; every rejected option is listed once.
- The test and git state is pasted, not described.
- Open questions name who is expected to answer.
- A new reader needs no other document to start (or the links are present).
- Secrets, tokens and personal data are not in the file.
Then commit the handoff file (or tell the user where it is) and, if the session continues, keep working from the handoff rather than from memory — it is now the source of truth.
Resuming from a handoff
When starting a session with a handoff present: read it first, re-run the "How to run" and test commands to confirm the state still matches, then begin at the first unfinished next step. If the state has drifted (new commits, failing tests), update the handoff before doing anything else.
Pitfalls
- Summaries written from memory drift from reality; run the commands.
- "Refactor the auth module" is not a next step; "extract
verifySessionfrom middleware.ts into lib/auth.ts, verify withpnpm test lib/auth" is. - Listing every file touched is noise; list the files the next person must open.
- Do not bury the blocker at the bottom; if work is blocked, say so in the first section.
- A handoff that is longer than the work remaining is a sign to cut it down.