handoff
CLAUDE.md4 starsChanged 24 days agoMITView on GitHub
This file records shared contributor guidance. Claude Code reads it directly; Codex is pointed here by AGENTS.md. This repo is a Claude Code and local Codex plugin named handoff. It is not an application. It ships a hook-driven project-memory system plus an owned development workflow layer that other repos install. There is no build step and no dependency tree — the deliverable is Python hook scripts (stdlib only), a skill set, and a template tree. CI (.github/workflows/ci.yml) runs the stdlib…
- Commits and pushes
We split this CLAUDE.md into the rules it gives an agent
8 rules. About 0 are common, 2 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 25
Do not confuse the two layers:
- Very specific: line 65
Stop emits decision: block plus reason, the supported continuation response for both hosts.
- Very specific: line 74
pushed: every session; everything else (BOOT.md, MAP.md, context/*, tasks/, CHANGELOG.md, folder CONTEXT.md) is pulled on demand, zero cost …
- Very specific: line 82
Fail-safe always: Every hook wraps main() so any exception prints nothing and exits 0.
- Distinctive: line 85
Keep that contract; callers assume it.
- Very specific: line 100
Run python3 tests/test_hooks.py — the same stdlib smoke suite CI runs (fixture repos piped through all three hooks, including the fail-safe …
- Very specific: line 117
No output + exit 0 is the common (correct) result — most invocations are intentionally silent.
- Very specific: line 121
Keep hooks stdlib-only — no third-party imports.
≈ “Common” is an estimate: at least 1 in 5 of the 25,680 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
CLAUDE.md
This file records shared contributor guidance. Claude Code reads it directly;
Codex is pointed here by AGENTS.md.
What this repo is
This repo is a Claude Code and local Codex plugin named handoff. It is not an application.
It ships a hook-driven project-memory system plus an owned development
workflow layer that other repos install. There is no build step and no
dependency tree — the deliverable is Python hook scripts (stdlib only), a skill
set, and a template tree. CI (.github/workflows/ci.yml) runs the stdlib smoke
tests in tests/test_hooks.py on every push/PR.
The skill set (v1.2.0+): setup (scaffolding), workflow (the 6-phase,
risk-tiered pipeline — the constitution that owns sequencing), and thirteen
technique skills (brief, plan, design, tdd, debug, architecture,
delegate, verify-done, security, browser-qa, ship, learn,
grill-me) forked
from MIT-licensed superpowers, mattpocock-skills, gstack, and rampstack-skills
with all cross-pack orchestration stripped (see ATTRIBUTION.md). When editing
a technique skill, never reintroduce pack-style enforcement preambles or
cross-skill process handoffs — workflow alone decides sequencing and tiering.
Do not confuse the two layers:
- The plugin (
hooks/,skills/,.claude-plugin/,.codex-plugin/) — what a developer installs into Claude Code or local Codex. Editing here changes behavior for every repo that uses it. - The
templates/tree — the payload thesetupskill copies into a target repo as.handoff/. This is committed to the target repo so memory travels with the code. Editing here changes what newly-set-up repos get; it does not affect already-set-up repos.
Runtime model (how the pieces connect)
Everything is driven by three hooks registered in hooks/hooks.json (auto-loaded
because it lives at hooks/hooks.json — the plugin manifest deliberately does not
declare a hooks field; see commit 1844000). Each hook is a thin python3 entry that
imports shared logic from hooks/_common.py:
sessionstart.py(SessionStart) — if the target repo has.handoff/, injectsSTATUS.md+RULES.mdin full plus an on-demand pointer, captures a git baseline for the session (HEAD sha + dirty paths with status codes — so the Stop hook sees committed work too), and checks.handoff/VERSIONagainst the plugin version (template-drift nag; missing stamp nags once per repo). If no.handoff/, emits a one-time "run setup" tip per repo (marker file in~/.cache/handoff/) and stays silent forever after.pre_edit_context.py(PreToolUse: Edit/Write/MultiEdit/NotebookEdit/apply_patch) — walks up from the file being edited to find the nearestCONTEXT.mdand injects it once per session; also corrects task files about to be written to thetasks/ROOT (they belong ininbox|now|done). InjectsadditionalContextonly — never apermissionDecision— so it can't silently auto-approve edits. Codex supplies patch text intool_input.command; every add/update/delete/move header is considered, with patch paths resolved against the hook'scwd.stop.py(Stop) — nudges when the session made new repo changes (committed or uncommitted — session-changed = commits since baseline HEAD + dirty paths whose code changed) but didn't update durable state:STATUS.md/CHANGELOG.md/MAP.md(structural changes only) / a folder'sCONTEXT.md. Also flags: task files stranded in thetasks/root (shown once per session per stray-set),STATUS.mdover 40 lines, sentinel paths (auth/migration/billing/… — configurable via.handoff/SENTINELS, forces the T3 question into the transcript), and an open.handoff/GATE(machine-readable T3 stall). The nudge instructs that state updates ride in the SAME commit as the code (amend only if unpushed AND the last commit is this session's own work). Stop emitsdecision: blockplusreason, the supported continuation response for both hosts. Context-onlyhookSpecificOutputis for SessionStart/PreToolUse.
Without CLAUDE_PROJECT_DIR, root discovery uses git from the hook's cwd;
without git, it looks for an ancestor .handoff/. Codex's native PLUGIN_DATA
is preferred for persistent markers, with the Claude alias as fallback. Codex
also sets CLAUDE_PLUGIN_ROOT, so the shared registered commands work in both.
The push/pull principle behind all of it: always-relevant state (STATUS+RULES) is
pushed every session; everything else (BOOT.md, MAP.md, context/*, tasks/,
CHANGELOG.md, folder CONTEXT.md) is pulled on demand, zero cost until read.
Invariants — do not break these when editing hooks
These are the load-bearing correctness properties. Most of the git history is fixing regressions against them.
- Fail-safe always. Every hook wraps
main()so any exception prints nothing and exits 0. A hook bug must never break a user's session. New code paths must preserve this. _common.pyhelpers swallow errors (returnNone/{}/default) rather than raise. Keep that contract; callers assume it.stop.pyhas three independent loop-breakers — do not remove any:stop_hook_activeguard, the session git baseline (only flags changes made this session, not pre-existing dirt), and the nudge-signature cooldown (never repeats the same unresolved nudge). Removing one risks an infinite Stop-continuation loop.- PreToolUse injects context only, no permission decision.
- Path safety in
pre_edit_context.py: edits outside the project root are ignored, Claude relative file paths resolve against the project root; Codex patch paths resolve againstcwd. Resolve symlinks before checking the boundary, and stop the walk-up at the root. Preserve these checks (original fix: commit d6810e4). - Changes under
.handoff/are excluded from the Stop hook's "new work" set, so updating state doesn't itself trigger a nudge.
Working / testing the hooks
Run python3 tests/test_hooks.py — the same stdlib smoke suite CI runs (fixture
repos piped through all three hooks, including the fail-safe garbage-stdin
checks). Hooks are plain python3 reading a JSON payload on stdin and emitting
JSON context or a Stop continuation response on stdout. Poke one by hand by piping a payload:
# SessionStart against a target repo that has .handoff/
echo '{"cwd":"/abs/path/to/target-repo","session_id":"t1"}' | python3 hooks/sessionstart.py
# PreToolUse — nearest CONTEXT.md for a file about to be edited
echo '{"cwd":"/abs/path/to/target-repo","session_id":"t1","tool_input":{"file_path":"src/foo.ts"}}' | python3 hooks/pre_edit_context.py
# Stop — needs a baseline first (run sessionstart with the same session_id, then make
# a change); after that, a second identical run stays silent (signature cooldown).
echo '{"cwd":"/abs/path/to/target-repo","session_id":"t1","stop_hook_active":false}' | python3 hooks/stop.pyNo output + exit 0 is the common (correct) result — most invocations are intentionally
silent. Session state (git baseline, nudge signature, per-CONTEXT.md dedup markers) lives
in <tmp>/handoff-session-<hash>/; delete that dir to reset a "session" between tests.
Keep hooks stdlib-only — no third-party imports. templates/ is markdown plus
.gitkeep files only.
The setup skill
skills/setup/SKILL.md is the guided-discovery flow (set up handoff /
/handoff:setup). Its contract: copy templates/ → .handoff/, read the real
codebase to draft STATUS/MAP/TECH_STACK/PRODUCT, confirm with the user before saving
(never invent project facts — RULES rule 5), optionally seed folder CONTEXT.md, and
append a thin-fallback block to the target repo's host instruction file (CLAUDE.md
for Claude, AGENTS.md for Codex). Preserve both files if present and keep their
handoff blocks consistent. It must refuse to overwrite an existing .handoff/
(offer refresh instead). Codex setup must explain hook trust and cloud limitations.
Releasing
.claude-plugin/plugin.json, .codex-plugin/plugin.json, and
.claude-plugin/marketplace.json carry the version — bump all three together
(never hardcode the current number in docs; check the manifests).
Release-bump commit subjects follow type: summary (vX.Y.Z); non-release commits omit
the version suffix. The marketplace registration reads GitHub, not the local clone —
a release is only live after git push.
Further reading
README.md— install/use, design notes, and the documented known limitations (Bash edits bypass CONTEXT injection,git status10s timeout,/compactre-baselining, non-git repos disable the Stop nudge).docs/SKILL-WORKFLOW.md— how handoff (the state/context layer) composes with other skill families rather than replacing them.docs/CODEX.md— Codex installation, hook contracts, trust, and host limitations.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

