Problem
Session: Windows 11, Claude Code 2.1.278, auto mode, guardrails 0.35.0, PowerShell lane.
Root cause
Traced by reading the installed source; not executed (the audit session's Bash tool was blocked by another plugin, so nothing was profiled and packet integrity is ungradable).
F5. The dispatcher runs every guard after a block.
run-guards.sh lines 367-393: run_from sources every remaining guard after one exits 2. The code only raises RC (lines 346-347) and never stops early.
- On the PowerShell lane, 8 guards run, and the
ps-command.sh library is sourced once per event (lines 126-131). That is 2300+ lines of bash parsed on every PowerShell call.
RUN_GUARDS_PROFILE=1 (lines 340-345) is the built-in way to measure. Not run.
F6. README gap.
Related: #3508, #3623 (hook performance).
Proposed fix
Cheapest first:
- README, block-hook-bypass section:
& $var script arg1 arg2 (a call through a variable followed by two or more positionals with at least one bare word) is blocked as a file write. Rewrite it as & 'C:/literal/path.exe' script args, or put a flag first.
- README, PowerShell sink section: a
foreach { git ... } loop is refused by the git guards regardless of verb. Unroll it into flat git -C <path> ...; statements.
- Profile the allow path with
RUN_GUARDS_PROFILE=1 on a Windows host to confirm or refute the per-call cost.
- Stop after the first block. This removes duplicate denial paragraphs and saves time on the block path. Weigh it against losing all reasons in one denial.
Acceptance criteria
Found by /plugin-quality:audit (run 20260919T170952Z).
Problem
Session: Windows 11, Claude Code 2.1.278, auto mode, guardrails 0.35.0, PowerShell lane.
& $var script record dirblocked as a file write; block reason names the wrong cause #4234 and guardrails: read-only git inside {} blocked by two guards, with irrelevant commit guidance, Bash-lane advice, and no narrow lever #4235 are not described in the README, so an operator or agent hitting them has no documented rewrite.Root cause
Traced by reading the installed source; not executed (the audit session's Bash tool was blocked by another plugin, so nothing was profiled and packet integrity is ungradable).
F5. The dispatcher runs every guard after a block.
run-guards.shlines 367-393:run_fromsources every remaining guard after one exits 2. The code only raises RC (lines 346-347) and never stops early.ps-command.shlibrary is sourced once per event (lines 126-131). That is 2300+ lines of bash parsed on every PowerShell call.RUN_GUARDS_PROFILE=1(lines 340-345) is the built-in way to measure. Not run.F6. README gap.
& $, "computed", "positional", and "Path+Value" found no coverage of the guardrails:& $var script record dirblocked as a file write; block reason names the wrong cause #4234 class, although it is a deliberate choice pinned in the tests.Related: #3508, #3623 (hook performance).
Proposed fix
Cheapest first:
& $var script arg1 arg2(a call through a variable followed by two or more positionals with at least one bare word) is blocked as a file write. Rewrite it as& 'C:/literal/path.exe' script args, or put a flag first.foreach { git ... }loop is refused by the git guards regardless of verb. Unroll it into flatgit -C <path> ...;statements.RUN_GUARDS_PROFILE=1on a Windows host to confirm or refute the per-call cost.Acceptance criteria
& $varpositional rule and the literal-path rewrite.{}/()git refusal and the unrolled-statement rewrite.RUN_GUARDS_PROFILE=1measurement of the PowerShell allow path on Windows is recorded in the issue or the README.Found by /plugin-quality:audit (run 20260919T170952Z).