Extend guard_master.py's worktree gate to Edit/Write/NotebookEdit - #444
Merged
Conversation
…trap Four sessions on 2026-07-23/24 accidentally edited the main checkout via absolute-path file-tool calls; PreToolUse now blocks writes that resolve outside .claude/worktrees/ from a worker worktree session (WORKERS.md and journal/ still exempt; Read stays ungated).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
guard_master.py's PreToolUse hook only ever matchedBashcommands. Fourindependent sessions on 2026-07-23 and 2026-07-24 accidentally edited the
shared main checkout via absolute-path
Read/Edit/Writecalls run froma worker worktree — the failure mode
docs/lessons.md's "Absolute paths tothe repo root silently target the wrong checkout in a worktree session"
entry already documents, but via a file tool instead of
git/ghinBash, so the existing rules never saw it. All four were self-caughtbefore landing anything, but the near-misses stacked up fast enough to
close the gap at the tool layer instead of relying on every session to
catch its own mistake.
This PR:
.claude/settings.json's PreToolUse matcher to also fire onEdit,Write, andNotebookEdit(wasBashonly).check_worktree_write_guard()toguard_master.py: when thesession's cwd is under a worker worktree (
.claude/worktrees/<name>/...)and the tool's target path (
file_pathfor Edit/Write,notebook_pathfor NotebookEdit) resolves inside the main checkout root but outside
.claude/worktrees/, the call is blocked with a message mirroring theexisting Bash-rule wording.
WORKERS.md/journal/exception (perdocs/lessons.mdandCLAUDE.local.md's multi-worker protocol) —those are gitignored files that intentionally live in the main
checkout, so writes there from a worktree session stay allowed.
Readentirely ungated — blocking reads would break legitimatecross-referencing against the main checkout's on-disk state, and a read
can't silently land content on the wrong branch the way a write can.
any other worktree, or paths outside the repo entirely (
/tmp, theorchestration repo, the memory dir).
Checklist
pre-commitand installed the hooks withpre-commit installbefore creating any commits..claude/hooks/test_guard_master.pysuite (there was already a test precedent for this hook) with 13 new end-to-end cases and 9 new unit cases against the new helper functions; no new--self-testmode was needed since the precedent already existed. This suite has no CI wiring (same as before this change) — it's a local-only dev-loop check, run manually.python3 .claude/hooks/test_guard_master.py— all 58 cases pass (36 pre-existing + 22 new).python3 -m py_compile .claude/hooks/guard_master.py .claude/hooks/test_guard_master.pyblack --checkand the repo'spre-commithooks (ruff, isort, black, mypy, prettier) all pass.python3 .github/scripts/docs_lint.py— clean..claude/hooks/guard_master.pyguard_master.pyentry indocs/troubleshooting.md, and added a "closed at the tool layer" note to the existing worktree-path-trap entry indocs/lessons.md.