primer.md — The Living Handoff Document Pattern
In short: Implement the primer.md living-handoff pattern (credited to @keshavsuki, lineage from @ykdojo's HANDOFF.md) in a real project so every Claude Code session starts from an accurate state snapshot and ends by rewriting it, without the human remembering.
| Key fact | Detail |
|---|---|
| What it covers | agents |
| Tools | Claude, Claude Code |
| Posts covered | 1 post (1 S-tier) · top: primer.md + memory.sh — live-updating… |
| Creators | @keshavsuki |
| Sources dated | Mar 2026 |
| Prompt system | Run-it-yourself prompts |
| Last verified | 2026-10-22 |
Attributed to: @keshavsuki (on X/Twitter), though the broader "living handoff document" lineage predates the specific primer.md naming — Yusuke Kamiyamane (@ykdojo) popularized a similar HANDOFF.md pattern, and various SESSION.md / STATE.md / CONTEXT.md variants circulate in the awesome-claude-code ecosystem. The primer.md naming and the specific "Claude rewrites it at session end" mechanic are the contribution being catalogued here.
Research note: The specific @keshavsuki post was not directly recoverable through web search at the time of writing. The pattern described below is reconstructed from the broader family of handoff-document patterns it belongs to, cross-referenced with the Claude Code hook system that makes the "auto-rewrite on session end" mechanic possible. Treat specific code snippets below as reference implementations, not verbatim copies of keshavsuki's original.
What it is
primer.md is a sibling file to CLAUDE.md that serves a fundamentally different purpose:
- CLAUDE.md = permanent project knowledge (conventions, stack, behavioral rules). Rarely changes.
- primer.md = live project state (what's happening right now, what's blocked, what's next). Rewritten every session.
The core mechanic: at the end of every Claude Code session, Claude rewrites primer.md with a snapshot of where the project currently stands and what the next session should do first. At the start of every session, Claude reads primer.md (via a CLAUDE.md pointer or via the additionalDirectories hook) and resumes exactly where the previous session left off — no "what were we doing again" dance, no re-reading five files to rebuild context.
It is a living handoff document between Claude and its future self.
Why this pattern exists
Claude Code has no memory across sessions. Every new session starts cold. The options for rebuilding context are:
- Re-explain to Claude manually — slow, lossy, requires you to remember.
- Rely on CLAUDE.md — it holds permanent rules but not current state ("we just finished phase 2, phase 3 is blocked on the Redis migration").
- Use
/resume— works within a single conversation thread but not across branch-switches, compactions, or new terminals. - Ask Claude to write a handoff document before starting fresh — the @ykdojo pattern. Works, but requires you to remember to ask.
- Make the handoff document self-maintaining — the primer.md pattern. The hook handles it automatically.
primer.md is option 5, and its value is that it removes the human from the loop. You never have to remember to update it.
How it works under the hood
The pattern has three parts:
1. The file itself — structured session state
# Project Primer
_Last updated: 2026-04-08 14:32 UTC (session ended)_
## Current status
Phase 3 of the trading-agent-team integration. Arena wire-up is COMPLETE
and green. Legion port is IN PROGRESS — we're 60% through migrating the
Command tab from /finance into /unicron/command.
## What changed this session
- Ported ARSENAL tab component + its three sub-widgets (bot cards, metric
rows, status dots). Tests green.
- Discovered: the old Legion Command chart code depends on Plotly 5.x;
/unicron is on Plotly 6.x. Needs shim or upgrade.
- Added your CSS vars to `src/styles/legion-theme.css`.
## Next up (start here)
1. Resolve the Plotly version mismatch — decide: shim or upgrade all of unicron.
Relevant files: `src/charts/LegionChart.tsx`, `package.json` (line 42).
2. Finish migrating RANKINGS tab (parallel structure to ARSENAL — should
be ~30 min if Plotly is resolved).
3. Run full visual-regression suite before declaring Phase 3 done.
## Blockers
- Plotly version decision (above). Needs your input if we go the
upgrade route since it touches other tabs.
## Open questions
- Does the old `session_state` singleton in `legion_command.py` need to
survive the port, or can we replace it with Zustand like the rest of
unicron?
## Don't forget
- The `.env` in `/unicron` has a placeholder HL_PRIVATE_KEY — you
need to set this before running live.
- Arena's WebSocket reconnect logic is the reference implementation for
any new real-time wiring.
Key sections:
- Current status — one paragraph, no more. Where is the project right now?
- What changed this session — delta from last primer. Keeps momentum visible.
- Next up — explicit ordered list. First item should be immediately actionable by a fresh Claude with zero other context.
- Blockers — things that need human input or external resolution.
- Open questions — uncertainties worth surfacing before the next session dives in.
- Don't forget — sharp edges, gotchas, state that lives outside the code (env vars, external services, half-applied migrations).
2. The CLAUDE.md anchor — tell Claude the file exists
Add this to your project CLAUDE.md:
## Session continuity
- At the **start** of every session: read `primer.md` first. It contains
the current project state and next steps from the previous session.
- Follow the "Next up" list unless the user redirects.
- At the **end** of every session: rewrite `primer.md` from scratch.
Do not append — the file is a snapshot, not a log.
- Structure: Current status / What changed / Next up / Blockers / Open
questions / Don't forget.
- Keep it under 150 lines. If it's growing, something is wrong — the
project has either stalled or needs a real retro.
3. The hook — automate the rewrite
Claude Code hooks (configured in .claude/settings.json) can run arbitrary commands on lifecycle events. The relevant one here is the Stop hook — fires when a session ends (user exits, or agent finishes its final turn).
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "claude -p 'Rewrite primer.md to reflect the current project state. Use the structure defined in CLAUDE.md under Session continuity. Do not append — replace the file entirely. Be specific about what the next session should do first.' --allowedTools Read,Write,Glob,Grep"
}
]
}
]
}
}
How this works:
- Session ends → Stop hook fires.
- The hook spawns a fresh
claude -p(non-interactive) subprocess. - That subprocess reads the current transcript context (via the
CLAUDE_TRANSCRIPT_PATHenv var the hook provides), analyzes what happened, and writes a newprimer.md. - The subprocess is restricted to
Read,Write,Glob,Grep— it can't accidentally run tests, commit, or touch anything else. - Next session starts → reads
primer.mdper the CLAUDE.md instruction → resumes cleanly.
Alternative: PreCompact hook. If your sessions are long and you rely on compaction rather than explicit exits, use the PreCompact hook instead. Same logic, fires before Claude Code compresses context rather than when the session terminates.
Alternative: manual command. If you don't want automation, add a slash command:
# .claude/commands/prime.md
Rewrite primer.md with the current project state per the structure in
CLAUDE.md. Then summarize what you wrote in 2 sentences.
Now /prime at the end of any session does the same thing without a hook.
Key mechanics
What makes primer.md different from HANDOFF.md / SESSION.md / a changelog:
- Snapshot, not log. It is rewritten, not appended to. This is critical — a log grows unboundedly and loses the "what matters now" signal. A snapshot stays focused.
- Self-maintaining via hook. The human never types it. This is what keshavsuki's contribution adds over the manual handoff pattern.
- Action-oriented "Next up." The first line of the first next-up item should be immediately executable. No "continue working on the thing" — specific file, specific change.
- Paired with CLAUDE.md, not replacing it. CLAUDE.md is permanent rules; primer.md is state. Don't conflate them.
Quick start
# 1. Create the file with a starter template
cd my-project
cat > primer.md <<'EOF'
# Project Primer
_Last updated: [session init]_
## Current status
Project just bootstrapped. No work yet.
## Next up
1. Define what we're actually building.
EOF
# 2. Add the CLAUDE.md anchor
# (paste the "Session continuity" block above into CLAUDE.md)
# 3. Configure the Stop hook
# Edit .claude/settings.json and paste the hook JSON above
# 4. Start a session, do some work, exit
claude
> (work happens)
> exit
# 5. Verify primer.md was rewritten
cat primer.md
If the hook didn't fire, check claude --debug output and .claude/settings.json syntax — hooks silently skip on JSON errors.
Cross-reference: how this complements your MEMORY.md system
you already run a related but distinct pattern at ~/.claude/projects/<project>/memory/MEMORY.md — an index of per-topic memory files covering feedback, projects, and references. It is auto-updated across conversations and provides a global knowledge substrate.
The overlap:
- Both patterns solve the "Claude has no memory across sessions" problem.
- Both are markdown files that Claude reads at session start.
- Both get written back to by Claude, not by hand.
The differences:
| Dimension | your MEMORY.md | primer.md |
|---|---|---|
| Scope | Global across all projects | Single project |
| Shape | Index pointing to many sub-files | Single self-contained snapshot |
| Lifetime | Permanent — entries accumulate over months | Ephemeral — rewritten every session |
| Granularity | Learnings, project summaries, reference docs | Active state + next steps |
| Update trigger | forge / retro / manual capture |
Stop hook at session end |
| What it answers | "What do I know about you and his projects?" | "Where did we stop and what's next?" |
They are complementary, not redundant. MEMORY.md says "project_unicron.md: Arena fully wired end-to-end, Legion port pending" — the high-altitude state. A primer.md inside ~/projects/unicron/ would add "Legion port is 60% done, blocked on Plotly version mismatch, next file to touch is src/charts/LegionChart.tsx line 42."
Recommendation for you: Adopt primer.md per-project for active builds (unicron, trading-agent-team, ai-catalogs). Keep MEMORY.md as the global flywheel it already is. The primer.md hook can even update MEMORY.md at project milestones — fold "session ended" into "if this was a significant milestone, also update the relevant project_*.md in MEMORY.md."
Gotchas
- Rewrite, don't append. The moment you start appending, primer.md becomes a changelog and loses its "next session starts here" focus. Enforce this in the CLAUDE.md anchor.
- The Stop hook runs a second Claude call. That's extra tokens and a few seconds of latency on exit. Worth it for important projects; overkill for throwaway scripts.
- Hook failures are silent by default. Test the hook with
claude --debugthe first time you set it up. JSON syntax errors in.claude/settings.jsonkill hooks without warning. - Don't put secrets in primer.md. It's meant to be committable. If you need to note "set HL_PRIVATE_KEY before running," reference the variable name, never the value.
- Git commit it. primer.md should be in version control so team members (or your future self on a different machine) can pick up state too.
- Size discipline. Target ~100 lines. If it balloons past 200, the project has either stalled (too many open questions accumulating) or needs a real retrospective — not a longer primer.
- Don't read primer.md in CLAUDE.md twice. If you load primer.md via
additionalDirectoriesAND reference it in CLAUDE.md, Claude sees it twice. Pick one mechanism.
What we could build with it
- A
primer-mdskill that formalizes the rewrite discipline with a structured prompt — handles the rewrite-not-append rule, enforces the section structure, checks length budget. - A
/primeslash command with a PreCompact + Stop hook preset — one-command setup for any project. - A primer.md → MEMORY.md bridge — if the session ended with a significant milestone, also update your global memory index.
- A fleet primer dashboard — scan all projects under
~/projects/, read eachprimer.md, render a single "status across all projects" view. Natural fit with your/flywheeland/statusskills. - A primer-diff alarm — alert if primer.md hasn't changed in N days (project stalled) or if the same "Next up" item has persisted for N sessions (blocked, needs human intervention).
- A Git-aware primer — cross-check primer.md's "What changed" section against actual
git logsince last rewrite, flag discrepancies (primer lies, or work that never got committed).
Sources
- Claude Code Session Commands gist (Tmeister)
- iannuttall/claude-sessions — session tracking slash commands
- willseltzer/claude-handoff — handoff document automation
- @ykdojo — "write a handoff document before starting fresh"
- ykdojo/claude-code-tips — handoff skill SKILL.md
- Claude-Command-Suite — session/handoff-continue command
- Never Lose Your Flow: Smart Handoff for Claude Code
- Claude Code handoff skill gist (edwilde)
- Feature request: Auto-documentation on CLI session exit (issue #3401)
- Feature request: Session Handoff / Continuity Support (issue #11455)
- Claude Code memory docs
Prompt system — run this yourself
Everything below is pasteable into Claude Code, Claude, or any agent. Use the one-shot when you want the whole result in one run; use the step prompts when you want to steer. Every step carries its own audit, and the gap audit at the end makes the agent find and fill what it missed.
Mission. Implement the primer.md living-handoff pattern (credited to @keshavsuki, lineage from @ykdojo's HANDOFF.md) in a real project so every Claude Code session starts from an accurate state snapshot and ends by rewriting it, without the human remembering. Running this produces a verified hook setup, a primer linter, a SessionStart injection, and a resume trial against a no-primer control. Done means hooks fire on the right events without recursion, primer.md stays under 150 lines with all six sections, and fresh sessions reach the first correct action faster than the control.
1 · Deeper, not bigger
Before running anything, make the task deeper rather than larger. Ask: what would a top professional do here that I am not asking for; what is the rawest material; which methods and tools; what, other than the model or me, proves it right. Then ask for exactly that.
- What a top professional would do (that you didn't ask for): Verify hook semantics against the current Claude Code docs before copying the guide's config: the guide wires the rewrite to the Stop hook, but Stop fires whenever the main agent finishes a response, so the guide's config would spawn a rewrite after every turn, and the spawned claude -p session fires its own Stop hook, which is a recursion risk. A professional uses SessionEnd and PreCompact for the rewrite, a recursion guard, a timeout, and reads transcript_path from the hook's stdin JSON rather than the guide's CLAUDE_TRANSCRIPT_PATH env var. They lint the output for structure and line count, commit primer.md so history shows drift, and measure the benefit with a blind resume trial instead of assuming it.
- Rawest material to start from: - Claude Code hooks reference and hooks guide on docs.claude.com: event list (SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Stop, SubagentStop, PreCompact, SessionEnd), stdin JSON schema, exit-code semantics, additionalContext output for SessionStart, timeouts.
- claude --help for the installed version: -p, --allowedTools, --output-format, --max-turns, settings sources.
- Real transcripts under ~/.claude/projects/<project>/*.jsonl to see what a rewrite agent can read.
- Victor's existing primer.md files and the global CLAUDE.md Session Continuity rules (six sections, under 150 lines, snapshot not log).
- @ykdojo's claude-code-tips HANDOFF.md write-up and awesome-claude-code entries for SESSION.md and STATE.md variants.
- Methods and tools: A test project in a scratch directory; .claude/settings.json hooks with matchers; bash hook scripts that read stdin with jq, check a guard env var such as PRIMER_REWRITE=1 to stop recursion, and run claude -p in the background with nohup and a timeout; --allowedTools Read,Write,Glob,Grep to limit the rewriter; a Python primer linter (six required headings, under 150 lines, Next up item 1 names a file path or command, no secrets regex); git log -p primer.md to inspect drift; claude --debug to see hook execution; a SessionStart hook printing primer.md into additionalContext; a resume trial with timed sessions.
- What proves it right (other than you or the model): claude --debug logs showing exactly one rewrite per session end and per compaction, and none per turn. The linter passing on 10 consecutive rewrites. git history of primer.md where each version reflects that session's actual changes, checked against git log of code commits. A blind resume trial: 5 fresh sessions with primer versus 5 with no primer on the same project state, measuring time and clarifying questions until the first correct action, scored by someone who did not write the primer.
Rewritten prompt — ask for exactly that:
Set up the primer.md handoff pattern in my project, but verify it against the current Claude Code hook docs first instead of copying the guide.
1. Read the hooks reference for my installed version. Confirm when Stop, SessionEnd, PreCompact, and SessionStart fire, what JSON arrives on stdin (transcript_path, session_id, cwd), and how SessionStart can inject additionalContext. Tell me whether the guide's Stop hook running claude -p would fire every turn and whether the child process would trigger hooks recursively, and whether a CLAUDE_TRANSCRIPT_PATH env var exists.
2. Write a primer template with Current status, What changed, Next up, Blockers, Open questions, Don't forget, and a linter: all six headings, under 150 lines, Next up item 1 names a file or command, no secret-shaped strings.
3. Implement rewrite hooks on SessionEnd and PreCompact with a recursion guard, a timeout, background execution, the rewriter limited to Read, Write, Glob, Grep, and the transcript path taken from stdin.
4. Add a SessionStart hook that injects primer.md so reading it does not depend on CLAUDE.md compliance.
5. Commit primer.md after each rewrite.
6. Run a resume trial: 5 fresh sessions with primer versus 5 without, timing to the first correct action.
Return: the doc findings table, the config, the linter, debug logs proving hook counts, and trial results.
2 · One-shot prompt
ROLE: You are a Claude Code harness engineer. You verify hook behavior against the installed version's documentation and debug logs, not against blog posts. You are implementing the primer.md living-handoff pattern for Victor, who already keeps primer.md files by hand per his global CLAUDE.md (six sections, under 150 lines, rewritten as a snapshot at session end).
CONTEXT:
- Pattern: primer.md is live project state, sibling to CLAUDE.md (permanent rules). Rewritten from scratch at session end, read at session start. Sections: Current status, What changed, Next up, Blockers, Open questions, Don't forget.
- Guide config to test, not copy: a Stop hook running claude -p 'Rewrite primer.md ...' --allowedTools Read,Write,Glob,Grep; the claim that the subprocess reads context via a CLAUDE_TRANSCRIPT_PATH env var; PreCompact as an alternative; a /prime slash command at .claude/commands/prime.md as the manual fallback; the claim that hooks silently skip on JSON errors.
- Known risks to check: Stop fires at the end of every main-agent response, not only at session end; a child claude -p inherits project hooks and can trigger its own Stop hook; hook input arrives as JSON on stdin.
HARD CONSTRAINTS:
- Work in a scratch test project first; touch Victor's real projects only after the trial passes, and only the settings file he names.
- The rewriter may only use Read, Write, Glob, Grep. It must never write secrets into primer.md; the linter enforces a secret regex (private keys, sk-ant, HL_PRIVATE_KEY=, 0x followed by 64 hex chars).
- A recursion guard is mandatory: the hook exits 0 immediately when PRIMER_REWRITE=1 is set, and sets it for the child.
- Hooks must time out (60-120 s) and must not block session exit for the user.
- primer.md is a snapshot: rewritten whole, under 150 lines.
DELIVERABLES, in order:
1. hook_facts.md: for the installed version (claude --version), when each relevant event fires, the stdin JSON fields, exit-code behavior, SessionStart additionalContext, and verdicts on each guide claim, with doc URLs.
2. templates/primer.md and lint_primer.py (six headings, line cap, Next up item 1 contains a path or command, secret regex, Last updated stamp).
3. hooks/primer_rewrite.sh and the settings.json block wiring it to SessionEnd and PreCompact with the guard, timeout, background run, and transcript_path from stdin.
4. hooks/primer_inject.sh on SessionStart emitting primer.md as additional context.
5. Auto-commit of primer.md after a successful lint, with a message naming the session id.
6. trial.md: 5 sessions with primer versus 5 without on the same project state; minutes and clarifying questions until the first correct action; scored by a reviewer who did not write the primer.
GAP ANALYSIS (built in): keep gaps.md. Record every behavior you could not observe directly (SessionEnd on terminal kill, PreCompact on auto-compaction) and fill by forcing the event (/compact, exit, kill -TERM) with --debug on. State the rest with the next action.
SELF-QUESTIONS before finishing:
- Does my rewrite fire once per session end and once per compaction, never per turn?
- Does the child claude -p trigger any hook, and does the guard stop it?
- Can the rewriter actually read the transcript at transcript_path, and is it large enough to need a summary step?
- What happens when the session is killed rather than exited?
- Does SessionStart injection duplicate the CLAUDE.md read instruction or replace it cleanly?
- Is Next up item 1 executable by a fresh session with zero other context?
- Did any rewrite drop a still-open blocker from the previous primer?
- What does each rewrite cost in tokens and time?
STOP CONDITIONS: stop if the rewrite fires per turn or recurses (fix before any trial); stop before editing any of Victor's real project settings; stop if the linter finds a secret in any rewrite.
RETURN FORMAT: markdown report: Hook facts table, Config (full JSON), Scripts (paths), Debug evidence (hook fire counts), Linter results across 10 rewrites, Trial table, Gaps ranked, file paths.
3 · Step-by-step prompts
Step 1 — Ground truth: hook semantics for this version
Goal: Replace the guide's hook assumptions with documented and observed behavior.
Run claude --version and record it. Read the Claude Code hooks reference and hooks guide on docs.claude.com. For each event (SessionStart, Stop, SubagentStop, PreCompact, SessionEnd) record: when it fires, matcher support, stdin JSON fields (look for session_id, transcript_path, cwd, hook_event_name, reason, trigger), exit-code meanings, timeout default, and output options (additionalContext for SessionStart).
Then observe: in a scratch project, register a logging hook for all five events that appends the stdin JSON plus env | grep -i claude output to hooks.log in the scratch project. Run a session with three turns, run /compact, then exit. Count events.
Write hook_facts.md with a verdict per guide claim: Stop fires at session end (true or false, observed count), CLAUDE_TRANSCRIPT_PATH env var exists, hooks silently skip on invalid JSON, a child claude -p inherits project hooks. Cite doc URLs and log lines.
Expect: hook_facts.md with an event table and five claim verdicts; hooks.log showing event counts for a 3-turn session plus compaction plus exit.
Audit before moving on: Event counts in hooks.log must match your table; if Stop appears 3 times for 3 turns, the guide's config is per-turn and must not be used. Any verdict without a doc URL or log line is unverified. Delete the logging hooks after the step.
Step 2 — Template and linter
Goal: Define what a valid primer is and enforce it mechanically.
Write templates/primer.md with the six sections in order: Current status (one paragraph), What changed (bullets), Next up (numbered, item 1 immediately executable with a file path or command), Blockers, Open questions, Don't forget; plus a Last updated line with UTC timestamp and session id.
Write lint_primer.py taking a path, exiting non-zero with messages on: missing or misordered headings; over 150 lines; Current status over 120 words; Next up item 1 lacking a path-like token or backticked command; secret-shaped strings (BEGIN PRIVATE KEY, sk-ant-, HL_PRIVATE_KEY=, 0x plus 64 hex, AKIA plus 16 uppercase alphanumerics); a Last updated stamp older than the file's mtime by more than a day.
Write 8 fixture primers (2 valid, 6 each breaking one rule) and a test runner proving each fixture produces the expected result. Run the linter against Victor's existing primer.md in this repo, read-only, and report its findings without editing it.
Expect: templates/primer.md, lint_primer.py, fixtures/ with 8 files, test output showing 8/8 expected results, a findings list for the existing primer.md.
Audit before moving on: All 8 fixtures must behave as expected; a valid fixture failing means the rules are too strict. Confirm the secret regex does not print the matched value, only the line number.
Step 3 — Rewrite hook with guard
Goal: Rewrite primer.md exactly once per session end and per compaction, safely.
Write hooks/primer_rewrite.sh: read stdin JSON with jq; exit 0 immediately if PRIMER_REWRITE=1; extract transcript_path, session_id, cwd, hook_event_name; copy the current primer.md to primer.prev.md; launch in the background with nohup and timeout 120: PRIMER_REWRITE=1 claude -p with a prompt that reads primer.prev.md, the transcript at transcript_path (tail the last 400 lines if larger than 2 MB), and git log --oneline -20 output saved to a temp file, then rewrites primer.md whole per the template, keeping unresolved blockers from the previous version, with --allowedTools Read,Write,Glob,Grep. After the child exits, run lint_primer.py; on failure restore primer.prev.md and write the lint errors to primer.lint.log.
Wire it in .claude/settings.json of the scratch project for SessionEnd and PreCompact only. Validate the JSON with jq before saving. Run three sessions with --debug, including one with /compact and one killed with kill -TERM.
Expect: hooks/primer_rewrite.sh, settings.json block, three debug logs, primer.md versions after each event, lint results.
Audit before moving on: Count rewrites per debug log: exactly one per exit and one per compaction, zero per turn, zero from the child. If the killed session produced no rewrite, record it as a gap and document the manual /prime command at .claude/commands/prime.md as the fallback. Fail if a rewrite dropped an unresolved blocker; tighten the prompt.
Step 4 — SessionStart injection and commit
Goal: Make the next session see the primer without relying on instruction-following.
Write hooks/primer_inject.sh for SessionStart: if primer.md exists in cwd, output the documented JSON with additionalContext containing the file content prefixed by a one-line instruction to start from Next up item 1 unless the user redirects; cap at 150 lines; exit 0 silently if absent. Register it in the scratch settings.json.
Add to primer_rewrite.sh: after a passing lint, git add primer.md and commit with message primer: session <session_id> <event>, only if the working tree has no other staged changes (never sweep other files into the commit). Start a fresh session and ask: what is the first thing to do? Record the answer and whether it matches Next up item 1.
Decide whether the CLAUDE.md Session continuity block stays (as a fallback) or is shortened, and justify the choice in one paragraph.
Expect: hooks/primer_inject.sh, updated settings.json, a commit history with primer-only commits, a transcript excerpt where the new session names Next up item 1.
Audit before moving on: Run git show --stat on each primer commit: any file other than primer.md is a fail; fix the staging logic. If the new session does not reference the primer content, check the hook output format against hook_facts.md.
Step 5 — Resume trial against a control
Goal: Measure whether primer.md actually shortens cold starts.
Prepare a project state with real mid-task work: a half-finished feature, one blocker, one gotcha outside the code (an env var that must be set). Snapshot the repo with git stash or a branch so every trial starts identical.
Run 5 fresh sessions with the primer hooks enabled and 5 with primer.md removed and hooks disabled (control). In each, the first user message is only: continue. Record: minutes until the first correct action (an edit or command that advances Next up item 1), number of clarifying questions, files read before acting, whether the gotcha was respected, and tokens used. A reviewer who did not write the primer scores correct action from transcripts, blind to arm where possible.
Write trial.md with a per-session table and medians per arm.
Expect: trial.md with 10 rows and arm medians; transcripts saved; reviewer name or id noted.
Audit before moving on: Confirm both arms started from the identical commit. If the primer arm is not faster on median time and questions, the step does not fail silently: record it and inspect whether Next up item 1 was actually executable. Note that 5 per arm is a small sample and report ranges, not just medians.
Step 6 — Validate against oracles, gap report
Goal: Prove the setup works and list what remains.
Run the oracles and record pass or fail with evidence: (1) debug logs show one rewrite per exit and per compaction, zero per turn, zero recursive; (2) lint_primer.py passes on 10 consecutive rewrites; (3) primer git history matches code history (each What changed list is consistent with that session's commits); (4) the resume trial shows the primer arm faster on median with fewer clarifying questions, scored by a reviewer who did not write it; (5) no secret pattern in any primer version (run the linter over git log -p primer.md).
Write gap_report.md: every guide claim (Stop hook at session end, CLAUDE_TRANSCRIPT_PATH, additionalDirectories reading, hooks skipping on JSON errors, /prime fallback) and every deliverable, marked verified / unverified / false with evidence, ranked by impact. Fix the top three. Then write rollout.md: the exact settings block and files to copy into a real project, for Victor to approve.
Expect: oracles.md with five rows, gap_report.md ranked, rollout.md ready for approval.
Audit before moving on: Any oracle without a log or file reference is unverified. Do not apply rollout.md to any real project in this step; it waits for Victor.
4 · Gap analysis and audit
Run this after the build. It makes the agent enumerate its own claims, mark what is verified, rank the gaps and fix the top ones.
You are reviewing a primer.md hook implementation you did not build. Read hook_facts.md, lint_primer.py, the hook scripts, settings.json, debug logs, primer git history, trial.md, oracles.md, and gap_report.md.
1. Enumerate every claim: event semantics, rewrite counts, recursion guard, transcript access, injection working, lint pass rate, trial results, guide-claim verdicts. Mark each verified, unverified, or false. Re-run one session with --debug and count hook firings yourself.
2. List missing pieces: killed-session behavior, very large transcripts, concurrent sessions in the same repo racing to rewrite, primer commits sweeping other files, blockers silently dropped across rewrites, token cost per rewrite.
3. Rank by impact: anything that loses state or leaks secrets first, then wrong-event firing, then trial weakness.
4. Fix the top three now and show evidence.
5. Report what you could not fix and why, with the exact next action.
Return a claim table (claim, status, evidence, fix) and the ranked gap list.
Questions the agent must ask itself before it says done:
- Does the guide's Stop hook fire once per session or once per turn on my installed version?
- Does the child claude -p load the same project hooks, and does PRIMER_REWRITE=1 actually stop recursion?
- Where does transcript_path come from, stdin JSON or an env var, and can the rewriter read it?
- What happens on kill -TERM, a closed terminal, or a crash: does SessionEnd fire at all?
- If two sessions run in the same repo, which rewrite wins, and is state lost?
- Did any rewrite drop an unresolved blocker or a Don't forget item from the previous primer?
- Is Next up item 1 truly executable cold, or vague like continue the thing?
- Does the SessionStart injection make the CLAUDE.md read instruction redundant?
- Could a secret from the transcript end up in primer.md and then in git?
- Is the primer arm faster in the trial, and is 5 per arm enough to say so?
- What does each rewrite cost in tokens, and is per-compaction rewriting worth it?
5 · What proves it right
- claude --debug logs across exit, /compact, and multi-turn sessions: exactly one rewrite per exit and per compaction, zero per turn, zero recursive.
- lint_primer.py passes on 10 consecutive automatic rewrites, and its 8-fixture test suite returns 8/8 expected results.
- git log -p primer.md: each version's What changed matches that session's code commits, and the linter finds no secret pattern in any version.
- Blind resume trial, 5 sessions per arm on an identical commit, scored by a reviewer who did not write the primer: primer arm lower median time to first correct action and fewer clarifying questions.
6 · Failure modes
- Rewrite on every turn. Tell: debug log shows the hook after each response; token use balloons. Fix: move to SessionEnd and PreCompact.
- Recursive rewrites. Tell: many claude processes spawn at exit. Fix: PRIMER_REWRITE=1 guard checked first in the script and set for the child.
- Rewriter has no context. Tell: primer.md becomes generic or repeats the previous version. Fix: pass transcript_path from stdin and the git log into the prompt.
- Blockers silently dropped. Tell: an open blocker disappears without a matching fix in git. Fix: prompt requires carrying forward unresolved items; diff check against primer.prev.md.
- Primer commit sweeps other files. Tell: git show --stat lists code files. Fix: commit only when nothing else is staged and add primer.md by explicit path.
- Session killed, no rewrite. Tell: Last updated stamp older than recent commits. Fix: /prime manual command and a SessionStart warning when the stamp is stale.
- Primer grows into a log. Tell: line count creeps toward 150. Fix: linter cap and a retro when it is hit.
7 · Agent direction (unattended runs)
Do everything in a scratch project first; Victor's real projects get only an approved rollout.md. Reach first for the hooks docs and claude --debug; observed fire counts beat any blog post. Cache hook_facts.md and refer to it rather than re-reading docs. A sub-agent can write the linter and fixtures in parallel with hook research. Never git add -A; stage primer.md by path only. Never print secrets; the linter reports line numbers, not matches. Stop and ask Victor before editing any real project's settings.json or CLAUDE.md, and if hooks fire per turn or recurse after your fix. Write outputs to docs/primer_pattern/: hook_facts.md, lint_primer.py, fixtures/, hooks/, trial.md, oracles.md, gap_report.md, rollout.md. Return the oracle table, the gap list, and the rollout path.
Prompt system updated 2026-10-22 · all prompt systems · JSON