Found while running the 17.1.0 QA sweep (#10224 family). Filing because the failure mode pushes an agent toward OS_ALLOW_MAIN_EDITS=1, which is the one outcome a write guard must never encourage.
What happened
This command is pure read — one node -e that requires a JSON file and prints to stdout. It was blocked:
node -e "const j=require('./qa/platform-smoke.test.json'); …
console.log(' roundtrip steps:', (sc.steps||[]).map(st=>st.action||st.type).join(' → '));
console.log(' teardown:', …);"
The hook reported:
⛔ Blocked: this Bash command WRITES into the shared PRIMARY checkout, not a worktree.
target: st.action||st.type).join( → ));
console.log( teardown:,
repo: /home/user/objectstack
The target it extracted is a fragment of my JavaScript string, split at the → (U+2192) inside .join(' → '). Nothing is redirected and nothing is written; the arrow is a display separator in a log line.
Re-running with .join(' then ') — same command, same file, same read — passed immediately. That is the isolating experiment: the arrow character alone decides it.
Why this is worth fixing rather than working around
The guard's own header says anything it cannot parse with confidence is allowed through. Here it parsed with false confidence: it produced a concrete target from a character that is not a shell operator in any position. A guard that names a specific target is much more convincing than one that says "unsure", so the failure is more likely to be trusted and worked around.
And the documented way around it is OS_ALLOW_MAIN_EDITS=1 — the switch that disables the guard for that call. An agent that hits this on a read-only command has every reason to reach for it, and the next command it runs under that flag may not be read-only. False positives on safe commands are how a write guard gets trained out of the workflow.
Suggested direction
Match redirection on the ASCII operators only (>, >>, 2>, &>, <>), and do not treat any non-ASCII codepoint as one. → ⇒ ➜ all plausibly appear in log strings, and ➜ in particular is already in this repo's own CLI banner output (➜ API:, ➜ Console:), so a command that echoes or greps a boot banner can hit the same shape.
A regression probe worth adding to guard-main-checkout-bash.selftest.sh: a node -e/echo whose string literal contains →, asserted to be allowed.
Scope note
Only the Bash-side guard is affected — guard-main-checkout.sh (Edit/Write/NotebookEdit) does not parse shell text. Verified on 19f98fa1.
Filed unassigned; the file lives under .claude/hooks/, so routing is the skills lane's call.
Found while running the 17.1.0 QA sweep (#10224 family). Filing because the failure mode pushes an agent toward
OS_ALLOW_MAIN_EDITS=1, which is the one outcome a write guard must never encourage.What happened
This command is pure read — one
node -ethatrequires a JSON file and prints to stdout. It was blocked:The hook reported:
The
targetit extracted is a fragment of my JavaScript string, split at the→(U+2192) inside.join(' → '). Nothing is redirected and nothing is written; the arrow is a display separator in a log line.Re-running with
.join(' then ')— same command, same file, same read — passed immediately. That is the isolating experiment: the arrow character alone decides it.Why this is worth fixing rather than working around
The guard's own header says anything it cannot parse with confidence is allowed through. Here it parsed with false confidence: it produced a concrete
targetfrom a character that is not a shell operator in any position. A guard that names a specific target is much more convincing than one that says "unsure", so the failure is more likely to be trusted and worked around.And the documented way around it is
OS_ALLOW_MAIN_EDITS=1— the switch that disables the guard for that call. An agent that hits this on a read-only command has every reason to reach for it, and the next command it runs under that flag may not be read-only. False positives on safe commands are how a write guard gets trained out of the workflow.Suggested direction
Match redirection on the ASCII operators only (
>,>>,2>,&>,<>), and do not treat any non-ASCII codepoint as one.→⇒➜all plausibly appear in log strings, and➜in particular is already in this repo's own CLI banner output (➜ API:,➜ Console:), so a command that echoes or greps a boot banner can hit the same shape.A regression probe worth adding to
guard-main-checkout-bash.selftest.sh: anode -e/echowhose string literal contains→, asserted to be allowed.Scope note
Only the Bash-side guard is affected —
guard-main-checkout.sh(Edit/Write/NotebookEdit) does not parse shell text. Verified on19f98fa1.Filed unassigned; the file lives under
.claude/hooks/, so routing is the skills lane's call.