Hit directly while running a pure-curl command from the devx PM seat. .claude/** is a
governed surface, so this is filed as a finding rather than fixed. Filed unassigned.
What happened
The command was four curl calls plus one bash comment:
# sitting 1 landed; card stays open and ready for sitting 2 -> pm:dispatched must go, pm:queue returns
The hook blocked it:
⛔ Blocked: this Bash command WRITES into the shared PRIMARY checkout, not a worktree.
command: card stays open and ready for sitting 2 -> pm:dispatched must go, pm:queue returns
target: pm:dispatched
repo: /home/user/objectstack
There is no write in that command. The hook read the comment.
Mechanism, from the hook's own source
split_segments() (:128-139) splits on ; | & ( ) { } \n, honouring quotes. My comment
contains a ;, so it is cut in two and the trailing clause becomes its own "segment".- Nothing strips
# comments. The hook strips heredoc bodies (strip_heredocs, :97)
and it makes separators inert inside quotes — but a # comment is neither, so the tail of the
comment is analysed as a command. - That tail contains
->. The tokenizer marks > as an unquoted redirection operator
(:171, :244-245) and takes the next token, pm:dispatched, as the redirect target. target_is_shared_checkout() (:189) resolves that relative target against the cwd — the
shared primary checkout — and the guard fires.
Why this is a defect rather than acceptable over-blocking
The hook's own header declares "PRECISION OVER RECALL" (:32) and deliberately declines
several genuinely-ambiguous shapes: node -e, awk >, targets containing $VAR / globs,
relative targets with no cwd. The intent is explicit at :125:
"A separator inside '…' or "…" does NOT split, so writing about the ban is never
caught."
That is exactly the property a comment should have, and it is implemented for quotes and for
heredocs but not for #. A comment is the purest case of writing about a write rather than
performing one.
The concrete harm is the remedy the block points at. The message's only escape is
OS_ALLOW_MAIN_EDITS=1, which disables the guard for the whole command. A false positive on
a harmless comment trains the operator to reach for the total-override flag on commands that
genuinely do not need it — and the next command it is reached for might. A guard whose false
positives are cheapest to resolve by turning it off is being pushed in the wrong direction.
Same family as #5790, which is why this hook exists at all: the Edit-only guard let the
identical write slip through as a shell command. This is that lesson's mirror image — the shell
parser now sees writes the Edit hook could not, including ones that are not there.
Direction (not a decision)
Strip # comments before split_segments(), with the same care strip_heredocs() already
takes: a # only begins a comment at the start of a word, and never inside '…', "…", or a
heredoc body — so curl 'https://…/#frag', sed 's/#//' and grep '#' must be untouched. The
existing quote-aware scanner already has the state needed to decide that.
⛔ Not proposing any change to what the hook blocks once it has a real command. The recall side
is right; this is only about what counts as a command.
Repro
Any Bash command containing a comment with both a ; and a > — e.g.
# step one; then a -> b — from a cwd inside the shared checkout.
Refs
.claude/hooks/guard-main-checkout-bash.sh:32, :97, :125, :128-139, :171, :189,
:244-245 · #5790 (why the Bash-side guard exists) · governed surface, Prime Directive #14
Generated by Claude Code
Hit directly while running a pure-
curlcommand from the devx PM seat..claude/**is agoverned surface, so this is filed as a finding rather than fixed. Filed unassigned.
What happened
The command was four
curlcalls plus one bash comment:The hook blocked it:
There is no write in that command. The hook read the comment.
Mechanism, from the hook's own source
split_segments()(:128-139) splits on; | & ( ) { } \n, honouring quotes. My commentcontains a
;, so it is cut in two and the trailing clause becomes its own "segment".#comments. The hook strips heredoc bodies (strip_heredocs,:97)and it makes separators inert inside quotes — but a
#comment is neither, so the tail of thecomment is analysed as a command.
->. The tokenizer marks>as an unquoted redirection operator(
:171,:244-245) and takes the next token,pm:dispatched, as the redirect target.target_is_shared_checkout()(:189) resolves that relative target against the cwd — theshared primary checkout — and the guard fires.
Why this is a defect rather than acceptable over-blocking
The hook's own header declares "PRECISION OVER RECALL" (
:32) and deliberately declinesseveral genuinely-ambiguous shapes:
node -e,awk >, targets containing$VAR/ globs,relative targets with no cwd. The intent is explicit at
:125:That is exactly the property a comment should have, and it is implemented for quotes and for
heredocs but not for
#. A comment is the purest case of writing about a write rather thanperforming one.
The concrete harm is the remedy the block points at. The message's only escape is
OS_ALLOW_MAIN_EDITS=1, which disables the guard for the whole command. A false positive ona harmless comment trains the operator to reach for the total-override flag on commands that
genuinely do not need it — and the next command it is reached for might. A guard whose false
positives are cheapest to resolve by turning it off is being pushed in the wrong direction.
Same family as #5790, which is why this hook exists at all: the
Edit-only guard let theidentical write slip through as a shell command. This is that lesson's mirror image — the shell
parser now sees writes the Edit hook could not, including ones that are not there.
Direction (not a decision)
Strip
#comments beforesplit_segments(), with the same carestrip_heredocs()alreadytakes: a
#only begins a comment at the start of a word, and never inside'…',"…", or aheredoc body — so
curl 'https://…/#frag',sed 's/#//'andgrep '#'must be untouched. Theexisting quote-aware scanner already has the state needed to decide that.
⛔ Not proposing any change to what the hook blocks once it has a real command. The recall side
is right; this is only about what counts as a command.
Repro
Any Bash command containing a comment with both a
;and a>— e.g.# step one; then a -> b— from a cwd inside the shared checkout.Refs
.claude/hooks/guard-main-checkout-bash.sh:32,:97,:125,:128-139,:171,:189,:244-245· #5790 (why the Bash-side guard exists) · governed surface, Prime Directive #14Generated by Claude Code