{"slug": "show-hn-stop-parallel-ai-coding-sessions-clobbering-each-other-s-handoffs", "title": "Show HN: Stop parallel AI coding sessions clobbering each other's handoffs", "summary": "A developer released a tool that prevents parallel AI coding sessions from overwriting each other's handoff files. The solution uses a PreToolUse hook that enforces ownership markers on handoff files, blocking writes from sessions that don't match the file's session ID. The tool addresses a gap in existing handoff systems, which focus on preserving context across sessions but fail to prevent concurrent overwrites that silently destroy needed context.", "body_md": "Hook-enforced ownership for AI coding session handoffs.\n\nMost \"handoff\" tools solve *amnesia*: capture state to a markdown file, restore it after compaction\nor a new session. That problem is well covered. This one solves the problem nobody enforces:\n**concurrent clobber**. When two sessions work the same repo, or you resume on a second machine, or a\nbackground agent runs alongside an interactive one, they overwrite each other's handoff notes and you\ndo not find out until the context you needed is gone.\n\nThe fix here is not a better template. It is a PreToolUse hook that makes a cross-session overwrite\n*structurally* blocked, not merely discouraged.\n\nEvery handoff file's first line is an ownership marker:\n\n``` php\n<!-- claude-session: 9e0d3802-... -->\n```\n\nThere is no sidecar `.lock`\n\nfile. Ownership travels with the artifact through git, across devices,\nthrough a `mv`\n\n. A PreToolUse hook reads the calling session's id and compares it to the marker in the\ncontent being written and the marker already on disk. Mismatch blocks the write.\n\nTo write a handoff that says who wrote it, the session needs to know its own id. **It does not.** The\nmodel has no native access to its `session_id`\n\n.\n\nSo the first write of a fresh handoff is *designed to fail*. The block reason carries the id:\n\n```\nHandoff write missing or wrong ownership marker.\n\nYour session_id: `9e0d3802-4f...`.\n\nPrepend exactly this as line 1:\n  <!-- claude-session: 9e0d3802-4f... -->\n\nThen retry.\n```\n\nThe model copies the id from the failure and retries. One block per fresh handoff, and the file is now self-identifying for every future session. The missing capability becomes a one-time handshake.\n\nA model that is blocked on `Write`\n\nwill route around you. So the guard covers every way a file can be\nmutated:\n\n**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 ownership with a Write).**Bash** matches shell redirects to handoff paths (`>`\n\n,`>>`\n\n,`tee`\n\n,`sed -i`\n\n) and blocks unowned writes that try to sneak past the file tools.\n\nIt also accepts both the Claude Code tool schema (`Write`\n\n/`Edit`\n\n/`Bash`\n\n) and the Gemini CLI schema\n(`write_file`\n\n/`replace`\n\n/`run_shell_command`\n\n) in one hook, because gating on one silently disables the\nguard for the other client.\n\n```\nhooks/handoff-write-guard.mjs            PreToolUse: the ownership guard\nhooks/handoff-session-start.mjs          SessionStart: surface existing handoffs + slug overlaps\nhooks/handoff-stop-gate.mjs              Stop: once-per-session \"you have no handoff\" nudge\nhooks/pre-commit-staged-marker-check.mjs git pre-commit: block commits mixing two sessions' handoffs\nhooks/test/handoff-write-guard.test.mjs  node --test suite (8 cases)\nscripts/handoff-migrate-archive.mjs      archive stale, marker-less legacy handoffs\nscripts/install-git-hooks.sh             per-device installer for the pre-commit hook\nskills/handoff/SKILL.md                  the /handoff slash command\nrules/session-handoff.md                 the convention the hooks enforce\nsettings.example.json                    hook wiring to merge into ~/.claude/settings.json\n```\n\nHandoffs are expected under the standard Claude Code memory layout:\n`~/.claude/projects/<encoded-cwd>/memory/handoff-<branch>-<topic>.md`\n\n, where `<encoded-cwd>`\n\nis the\nabsolute working directory with `/`\n\n, `\\`\n\n, and `.`\n\nreplaced by `-`\n\n.\n\n```\n# 1. Copy hooks/skills/rules into your ~/.claude\ncp hooks/*.mjs        ~/.claude/hooks/\ncp -r hooks/test      ~/.claude/hooks/\ncp scripts/*          ~/.claude/scripts/\ncp -r skills/handoff  ~/.claude/skills/\ncp rules/*            ~/.claude/rules/\n\n# 2. Merge settings.example.json into ~/.claude/settings.json (additive arrays)\n\n# 3. Install the per-device git pre-commit hook (handoffs live in a git repo)\nbash ~/.claude/scripts/install-git-hooks.sh\n\n# 4. Verify\nnode --test ~/.claude/hooks/test/*.test.mjs\n```\n\nEvery hook wraps its body in try/catch and exits 0 on any internal error. A bug in the guard\ndegrades to convention; it never bricks a session. The Stop nudge is non-blocking and fires at most\nonce per session. The escape hatches (`touch /tmp/handoff-guard-bypass-<file>`\n\n, or\n`HANDOFF_GUARD_BYPASS=1`\n\n) exist precisely because a structural guard you cannot override becomes a\nstructural guard you rip out. Bypass use is logged so silent disabling is auditable.\n\nThis makes the *unaware* clobber impossible. It does not make the *chosen* one impossible.\n\n- A session correctly shown a foreign marker can still archive the file or set the bypass env. Human review of the visible block message is the backstop for that class.\n- The\n`<branch>`\n\ntoken in a filename is not checked against the real branch (intentional: cross-device resume deliberately inherits a foreign branch's topic). - A TOCTOU race exists if two sessions create the\n*same new*filename in the gap between the hook's read and the tool's write. Vanishingly rare for solo dev; deliberately not locked.\n\nMIT.", "url": "https://wpnews.pro/news/show-hn-stop-parallel-ai-coding-sessions-clobbering-each-other-s-handoffs", "canonical_source": "https://github.com/joshduffy/claude-handoff-guard", "published_at": "2026-05-29 16:54:22+00:00", "updated_at": "2026-05-29 17:18:18.808531+00:00", "lang": "en", "topics": ["ai-tools", "ai-agents", "ai-infrastructure", "ai-products", "ai-research"], "entities": ["Claude"], "alternates": {"html": "https://wpnews.pro/news/show-hn-stop-parallel-ai-coding-sessions-clobbering-each-other-s-handoffs", "markdown": "https://wpnews.pro/news/show-hn-stop-parallel-ai-coding-sessions-clobbering-each-other-s-handoffs.md", "text": "https://wpnews.pro/news/show-hn-stop-parallel-ai-coding-sessions-clobbering-each-other-s-handoffs.txt", "jsonld": "https://wpnews.pro/news/show-hn-stop-parallel-ai-coding-sessions-clobbering-each-other-s-handoffs.jsonld"}}