GitHub - joshduffy/claude-handoff-guard: Hook-enforced ownership for AI coding session handoffs · GitHub
/" data-turbo-transient="true" />
Skip to content
Search or jump to...
Search code, repositories, users, issues, pull requests...
-->
Search
Clear
Search syntax tips
Provide feedback
--><br>We read every piece of feedback, and take your input very seriously.
Include my email address so I can be contacted
Cancel
Submit feedback
Saved searches
Use saved searches to filter your results more quickly
-->
Name
Query
To see all available qualifiers, see our documentation.
Cancel
Create saved search
Sign in
/;ref_cta:Sign up;ref_loc:header logged out"}"<br>Sign up
Appearance settings
Resetting focus
You signed in with another tab or window. Reload to refresh your session.<br>You signed out in another tab or window. Reload to refresh your session.<br>You switched accounts on another tab or window. Reload to refresh your session.
Dismiss alert
{{ message }}
joshduffy
claude-handoff-guard
Public
Notifications<br>You must be signed in to change notification settings
Fork
Star
master
BranchesTags
Go to file
CodeOpen more actions menu
Folders and files<br>NameNameLast commit message<br>Last commit date<br>Latest commit
History<br>2 Commits<br>2 Commits
hooks
hooks
rules
rules
scripts
scripts
skills/handoff
skills/handoff
.gitignore
.gitignore
LICENSE
LICENSE
README.md
README.md
package.json
package.json
settings.example.json
settings.example.json
View all files
Repository files navigation
claude-handoff-guard
Hook-enforced ownership for AI coding session handoffs.
Most "handoff" tools solve amnesia: capture state to a markdown file, restore it after compaction<br>or a new session. That problem is well covered. This one solves the problem nobody enforces:<br>concurrent clobber . When two sessions work the same repo, or you resume on a second machine, or a<br>background agent runs alongside an interactive one, they overwrite each other's handoff notes and you<br>do not find out until the context you needed is gone.
The fix here is not a better template. It is a PreToolUse hook that makes a cross-session overwrite<br>structurally blocked, not merely discouraged.
The core idea: the lock lives inside the file
Every handoff file's first line is an ownership marker:
">
There is no sidecar .lock file. Ownership travels with the artifact through git, across devices,<br>through a mv. A PreToolUse hook reads the calling session's id and compares it to the marker in the<br>content being written and the marker already on disk. Mismatch blocks the write.
The chicken-and-egg (the interesting part)
To write a handoff that says who wrote it, the session needs to know its own id. It does not. The<br>model has no native access to its session_id.
So the first write of a fresh handoff is designed to fail. The block reason carries the id:
Then retry.">Handoff write missing or wrong ownership marker.
Your session_id: `9e0d3802-4f...`.
Prepend exactly this as line 1:
Then retry.
The model copies the id from the failure and retries. One block per fresh handoff, and the file is<br>now self-identifying for every future session. The missing capability becomes a one-time handshake.
Defense across all three mutation surfaces
A model that is blocked on Write will route around you. So the guard covers every way a file can be<br>mutated:
Write validates the marker in the new content.
Edit validates the marker on disk (and blocks edits to legacy marker-less files until you take<br>ownership with a Write).
Bash matches shell redirects to handoff paths (>, >>, tee, sed -i) and blocks unowned<br>writes that try to sneak past the file tools.
It also accepts both the Claude Code tool schema (Write/Edit/Bash) and the Gemini CLI schema<br>(write_file/replace/run_shell_command) in one hook, because gating on one silently disables the<br>guard for the other client.
What ships
hooks/handoff-write-guard.mjs PreToolUse: the ownership guard<br>hooks/handoff-session-start.mjs SessionStart: surface existing handoffs + slug overlaps<br>hooks/handoff-stop-gate.mjs Stop: once-per-session "you have no handoff" nudge<br>hooks/pre-commit-staged-marker-check.mjs git pre-commit: block commits mixing two sessions' handoffs<br>hooks/test/handoff-write-guard.test.mjs node --test suite (8 cases)<br>scripts/handoff-migrate-archive.mjs archive stale, marker-less legacy handoffs<br>scripts/install-git-hooks.sh per-device installer for the pre-commit hook<br>skills/handoff/SKILL.md the /handoff slash command<br>rules/session-handoff.md the convention the hooks enforce<br>settings.example.json hook wiring to merge into ~/.claude/settings.json
Handoffs are expected under the standard Claude Code memory layout:<br>~/.claude/projects//memory/handoff--.md, where is the<br>absolute working directory with /, \, and . replaced by -.
Install
# 1. Copy hooks/skills/rules into your ~/.claude<br>cp hooks/*.mjs ~/.claude/hooks/<br>cp -r hooks/test ~/.claude/hooks/<br>cp scripts/* ~/.claude/scripts/<br>cp -r skills/handoff...