Writing session handoffs
Write a handoff that a fresh session can pick up and finish, and let it lint clean once the work is done.
What a handoff is
A handoff is a ready-to-run prompt for the next session, one file per handoff
under docs/handoffs/<NNN>-<name>.md. It is never deleted. A session that
picks one up sets its status to in_progress, then to consumed once the
work lands.
1. Add the namespace
ctxgrd pack add handoffThis writes [HANDOFF] into ctxgrd.toml, path-claimed at
docs/handoffs/**. If your project already uses the workflow pack for
SPEC/TASK/PROMPT, you already have it – workflow depends on handoff,
so ctxgrd pack add workflow writes both:
Added pack 'handoff' — 1 namespace, 0 rule families.
Added pack 'workflow' — 3 namespaces, 2 rule families.2. Scaffold one
ctxgrd new HANDOFF "Ship the widget"This writes docs/handoffs/001-ship-the-widget.md with an id, an empty
status, and five empty H2 headings: Goal, Context, Next Steps, Routing,
Verification.
3. Fill in the required headings
While a handoff is open, all five headings must hold content. Goal, Context, Next Steps, and Routing take prose:
---
id: HANDOFF-1
title: Ship the widget
status: pending
---
# HANDOFF-1: Ship the widget
## Goal
Ship the widget to production.
## Context
The widget passed review.
## Next Steps
Deploy it.
## Routing
developer4. Put a runnable command under Verification
Verification is different: it must contain a fenced code block, not prose.
Under the ## Verification heading, put the command in its own fence:
curl -sf https://widget.example.com/healthA handoff whose Verification holds only prose reports:
error[core.required-headings]: section 'Verification' contains no fenced code block
help: put the command a reader runs in a fenced block under `## Verification`A fresh session runs that command to find out whether the work is actually done, instead of taking your word for it.
5. Pick a status from the fixed vocabulary
ctxgrd rules core.allowed-values[HANDOFF] allows five values: pending, in_progress, consumed,
superseded, deferred. There is no done or open – done would
duplicate consumed and open would duplicate pending.
6. Let a closed handoff stay as it is
The heading and code-block checks apply only while status is pending or
in_progress. A handoff with no status at all is still checked – leaving it
out is not the same as closing it. Once you mark a handoff consumed,
superseded, or deferred, it can keep whatever shape it already has:
$ ctxgrd
ok: 1 document · 8 rules · 0 diagnosticsThis holds even for a consumed handoff missing every heading. It is a record of finished work, not a live document, so nothing more is asked of it.
7. Watch the size budget
[HANDOFF] warns past 20,000 characters, because a fresh session loads a
handoff whole:
warning[core.file-budget]: file is 21112 characters (budget 20000)
help: trim 1112 characters — move settled detail into a linked file and reference it, or archive the parts that are doneIt is a warning, so it never fails your build. Trim the handoff, or move settled detail into a file it links to.
8. Lint and iterate
ctxgrd
ctxgrd --format json | jq '.diagnostics[] | select(.file | startswith("docs/handoffs"))'Exit codes follow the standard contract: 0 clean, 1 diagnostics reported,
2 kernel or config error.
Reusing the status-scoped check elsewhere
statuses and code_in are general keys on core.required-headings, not
specific to handoffs. Any namespace with a status lifecycle can require
headings, or a code block in one of them, only while a document is in a given
state:
[RUN."core.required-headings"]
headings = ["Trigger", "Steps", "Rollback"]
statuses = ["active"]
code_in = ["Rollback"]Run ctxgrd rules core.required-headings for the full parameter list.
Next steps
- CLI reference –
pack add,pack show, andnewin full. - Keeping adopted packs current – if your
[HANDOFF]block was written beforehandoffexisted as its own pack,ctxgrd pack migratemoves its stamp with no text change. - How ctxgrd decides what to lint –
why
[HANDOFF]is path-claimed, so a handoff with noidis still checked instead of silently skipped.