diff --git a/docs/porting-to-a-new-harness.md b/docs/porting-to-a-new-harness.md index 4ae9603d..7b8af219 100644 --- a/docs/porting-to-a-new-harness.md +++ b/docs/porting-to-a-new-harness.md @@ -745,8 +745,10 @@ single file that's valid as both a Windows batch script and a Unix shell script. On Windows, `cmd.exe` runs the batch portion, which locates `bash` (Git for Windows, then `bash` on PATH) and runs the named hook script; if no bash is found it exits cleanly so the harness still works, just without injection. On -Unix, the leading `:` makes the batch block a no-op and the shell runs the -script directly. +Unix, the shell executes the leading `:;` lines and `exec`s the hook script +before reaching the batch block (cmd.exe skips those lines as labels). +Heredocs are banned throughout hooks/ — see issue #571 and the fence test +`tests/hooks/test-no-heredocs-in-hooks.sh`. Two rules this enforces, which you must respect: diff --git a/docs/windows/polyglot-hooks.md b/docs/windows/polyglot-hooks.md index d1bcb84e..9bf21542 100644 --- a/docs/windows/polyglot-hooks.md +++ b/docs/windows/polyglot-hooks.md @@ -65,9 +65,9 @@ The path is quoted because `${CLAUDE_PLUGIN_ROOT}` may contain spaces. ## How `run-hook.cmd` Works at a High Level -`run-hook.cmd` is a polyglot script: Windows treats the first block as batch -commands, while Unix shells treat that block as a no-op heredoc and continue -after it. +`run-hook.cmd` is a polyglot script: its first lines start with `:;`, which +cmd.exe skips as labels and Unix shells execute — the shell execs the hook +before ever reaching the batch block that Windows runs. Do not copy an implementation from this document. Read `hooks/run-hook.cmd` directly when changing the dispatcher, and run `tests/hooks/test-session-start.sh` @@ -89,10 +89,14 @@ afterward. ### How it works on Unix (bash/sh) -1. `: << 'CMDBLOCK'` opens a heredoc on a no-op command. -2. The entire CMD batch block is consumed by the heredoc and ignored. -3. After `CMDBLOCK`, bash resolves the script directory and `exec`s the named - extensionless script directly. +1. Each leading `:;` line is a no-op label to cmd.exe but real commands to a + POSIX shell. +2. The shell checks bash exists (silent exit 0 if not), resolves the script + directory CDPATH-proof, and `exec`s the named extensionless script — it + never reads the batch block at all. +3. Heredocs are banned in this file and all hooks/ executables (issue #571: + bash >= 5.1 pre-fork pipe writes deadlock on macOS under pipe pressure); + `tests/hooks/test-no-heredocs-in-hooks.sh` enforces the ban. ### Key design decisions