Agent handoff document generator (HANDOFF.md)
Updated
Fill in the form and a complete HANDOFF.md builds itself in the preview: goal, current state, how to run and test, key files, decisions with their reasons, open questions, ordered next steps, constraints, owners, and links. Tick the option to add a preamble written for an AI coding agent. Copy it, download it, or publish it as a link the next person or agent can fetch. Everything runs in your browser; the draft autosaves locally.
Project
What "done" means. The next reader judges every step against this line.
Current state — one item per line
How to run and test — one command per line
Key files
Decisions and open questions
Write “decision — why”. The part after the dash becomes the Why column.
What happens next
Rendered as a numbered list; the first line is what the next reader should do first.
People and links
Options
What the agent should read, and what it should not load.
HANDOFF: Invoice export service
Repo: acme/billing-api · Branch: feat/csv-export · Written: 2026-10-15 14:46 UTC
Goal
Customers can download a CSV of all invoices for a date range from the billing dashboard.
For an AI agent
- Read this whole file before changing anything, then continue from Next steps in order.
- Context budget: Read this file, then only the files listed under Key files. The repo is large; do not scan it whole.
- Do not touch:
lib/invoices/csv.ts,prisma/migrations/ - Do not re-do items listed under Done; verify them with the commands below if in doubt.
- If the branch head differs from what this file describes, treat the file as a starting point, not the truth.
Verify before and after your changes:
npm run lint
npm test -- invoices
curl -s "http://localhost:3000/api/invoices/export?from=2026-01-01&to=2026-01-31" | head -3
Status
| Item | Status |
|---|---|
| Added GET /api/invoices/export?from=&to= returning text/csv (streamed, 10k rows/sec in local tests) | Done |
| Unit tests for the CSV writer, including quoted commas and UTF-8 names | Done |
Feature flag csv_export wired through config/flags.ts, default off | Done |
| Dashboard button in app/billing/InvoiceList.tsx — renders, but the date picker is not passed through yet | In progress |
| Exports over 50k rows time out at the 30 s gateway limit — needs a decision on background jobs vs pagination | Blocked |
Current state
Done
- Added GET /api/invoices/export?from=&to= returning text/csv (streamed, 10k rows/sec in local tests)
- Unit tests for the CSV writer, including quoted commas and UTF-8 names
- Feature flag
csv_exportwired through config/flags.ts, default off
In progress
- Dashboard button in app/billing/InvoiceList.tsx — renders, but the date picker is not passed through yet
Blocked
- Exports over 50k rows time out at the 30 s gateway limit — needs a decision on background jobs vs pagination
How to run and test
Run:
npm install
cp .env.example .env.local
npm run dev
Test:
npm run lint
npm test -- invoices
Key files
| File | What it does |
|---|---|
app/api/invoices/export/route.ts | The export endpoint; streams rows from the repository |
lib/invoices/csv.ts | CSV writer with RFC 4180 quoting — do not swap for a library |
app/billing/InvoiceList.tsx | Dashboard list; the new Export button lives here |
config/flags.ts | Feature flags; csv_export gates the whole feature |
Decisions
| Decision | Why |
|---|---|
| Stream the response instead of buffering | memory stayed flat at 100k rows |
| Dates are exported as ISO 8601 in UTC | matches the existing JSON API, avoids locale bugs |
| No Excel BOM prefix | the two customers who asked use Google Sheets, which does not need it |
Open questions
- Should exports over 50k rows become an emailed background job?
- Do we need per-currency columns, or is a single amount + currency column enough?
Next steps
- Pass the selected date range from the picker into the Export button
- Add an integration test for the 50k-row case (currently fails with a gateway timeout)
- Turn the flag on for the internal workspace and watch error rates for a day
- Write the help-center article and link it from the button tooltip
Constraints and gotchas
- The invoices table has no index on created_at in production — the query plan is fine under 100k rows only
- A pre-existing lint warning in lib/legacy/totals.ts is unrelated; leave it
- CI runs on Node 20;
Array.fromAsyncis not available
Owners and contacts
- Feature owner: Priya (billing team)
- Reviewer: Marco
- On-call for the gateway: #platform channel
Links
Handoff written 2026-10-15. Update this file before you stop; the next reader starts here.
Nothing leaves your browser unless you click Share as link. The draft is kept in this browser's localStorage only.
What does a good handoff document contain?
The generator emits the sections below in this order. The third column is the reason the section exists from the reader's side, which is also the test for whether yours is good enough: if an agent given only the file would still have to ask, the section is too thin.
| Section | What to write | Why the next agent needs it |
|---|---|---|
| Goal | One sentence that defines "done" and names what must not change. | Every later judgment ("is this step worth doing?") is made against this line. Without it an agent optimizes for the wrong thing. |
| Repo and branch | owner/name and the branch the work lives on; the base branch if it is not main. | An agent cannot guess which checkout the rest of the document describes. |
| Current state | Three lists: done, in progress, blocked. Facts, not intentions. | Prevents redoing finished work and flags the half-finished edit that will otherwise look like a bug. |
| How to run and test | The exact commands, one per line, in a code block. | Commands are the fastest way for an agent to verify the state before trusting the prose. |
| Key files | Path and one line on what it does, as a table. | Turns "explore the repo" (slow, expensive in context) into "read these four files". |
| Decisions and why | Each choice with its reason, as a table. | The reason is what stops the next person from reversing a decision that was made for a cause they cannot see. |
| Open questions | Things nobody has decided yet. | Marks where the agent should ask instead of assume. |
| Next steps | A numbered list, first thing first. | An ordered list is executable; a paragraph of suggestions is not. |
| Constraints and gotchas | Limits, known-bad paths, pre-existing warnings to ignore. | Saves the hour that would be spent "fixing" something that is unrelated or intentional. |
| Owners and links | Who to ask, and the ticket, design doc, PR. | Gives the reader an escalation path and the source documents behind the summary. |
| For an AI agent (optional) | Context budget, do-not-touch paths, verification commands. | Agents follow explicit limits well and implicit ones badly; stating them costs three lines. |
Minimal or full template: which one do I need?
A handoff for a one-hour fix and a handoff for a week of feature work are not the same document. Fill in only the fields you need; the generator drops empty sections.
| Minimal (same-day, one reader) | Full (multi-day, agent or new teammate) | |
|---|---|---|
| Sections | Goal, Current state, Next steps | All eleven, including the AI-agent preamble and status table |
| Length | 10–25 lines | 60–120 lines |
| Commands | Only if they differ from the README | Run, test, and verification commands, each in its own code block |
| Key files | Skip, or list the one or two you touched | Table of every file the next step needs, with a one-line purpose |
| Decisions | Bullets, no reasons | Decision and reason as a table, so reversals are deliberate |
| Where it lives | Pull request description or a chat message | HANDOFF.md in the repo root, or a shared link pasted into the next session |
| Timestamps | Optional | Yes, so a stale handoff can be spotted against the git log |
How do I hand the file to the next agent?
Three ways, in increasing order of ceremony. Commit HANDOFF.md to the branch and start the next session with "read HANDOFF.md and continue from its next steps". Or paste the markdown into the first message of the new session. Or click Share as link and give the agent the raw URL, which returns the file as text/markdown for any assistant with an HTTP tool. The HANDOFF.md walkthrough covers the MCP and curl variants, and sharing an implementation plan covers the review step that often precedes a handoff. For the markdown syntax used in the output, see the markdown cheat sheet; for the project-level document that sits next to the handoff, use the README generator.
What is a HANDOFF.md file?
HANDOFF.md is a markdown file written at the end of a work session that records the goal, what is done, what is in progress or blocked, how to run and test the code, which files matter, which decisions were made and why, and the ordered next steps. It is read by the next developer or AI coding agent so they can continue without reconstructing the context. The name is a convention, not a standard; agent-handoff.md and HANDOVER.md are the same thing.
How is a handoff different from a README?
A README describes a project for anyone, at any time: what it is, how to install and use it. A handoff describes one piece of work, right now, for one reader: where it stands and what to do next. A README is stable; a handoff is rewritten every time work stops. Generate the README with the README generator and keep the handoff next to it, or in the pull request description.
What should the "for an AI agent" preamble say?
Three things an agent cannot infer: how much to read (the context budget: "this file plus the key files, do not scan the repo"), what not to modify (paths with a reason), and how to verify state before and after changes (the exact commands). The generator writes these as the first section so the agent sees them before it acts. It also tells the agent to compare the branch head with the handoff and treat the document as a starting point if they differ.
Is anything uploaded while I type?
No. The markdown is assembled in your browser and the draft is saved to this browser's localStorage so a reload does not lose it. The only network request happens if you click Share as link, which publishes the markdown to a URL that expires in 30 days and returns a raw text URL an agent can fetch. Do not share a handoff that contains tokens, keys, or customer data; the link is readable by anyone who has it.
How long should a handoff be?
Long enough that the reader does not have to ask you anything, short enough to read in two minutes: usually 40 to 120 lines. Empty sections are omitted automatically, so a small task produces a small file. If the next steps list is longer than ten items, the task is probably two tasks and deserves two handoffs.