The held-open subprocess cases inherited ECC_HOOKS_ENABLED and
ECC_DISABLED_HOOKS from the caller, so disabling block-no-verify or all
hooks made them fail even though the dispatcher behaved correctly. Pin
both flags in the spawned environment.
Refs #3665
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The held-open subprocess cases only sent partial JSON. Add a complete
`git commit --no-verify` request with stdin left open and assert the
dispatcher reaches block-no-verify instead of reporting incomplete input.
Refs #3665
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- bash-hook-dispatcher: honor the reader's truncated flag. pre-Bash
blocks (exit 2) because safety checks need the complete request;
post-Bash skips its hooks and does not echo a partial payload.
- resolveStdinIdleTimeout: treat whitespace-only values as unset instead
of Number(' ') === 0 silently disabling the deadline.
- isCompleteJsonObject: do not accept a JSON array as a hook payload.
- tests: wait for the child's close event so stdio is drained before
asserting; cover both bash-hook-dispatcher modes.
- hooks/README.md: keep the example below the 5 s per-hook timeout.
Refs #3665
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
setTimeout fires after 1 ms for any delay above 2147483647 ms, so a large
ECC_HOOK_INPUT_IDLE_TIMEOUT_MS turned the deadline into ~1 ms and marked
every hook input incomplete. Clamp to the maximum with a diagnostic, and
surface the invalid-value diagnostic when readStdinRaw resolves the env.
Refs #3665
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
readStdinRaw() only settled on end, error or close. If the parent kept
stdin open without writing EOF, every hook that reads through it waited
until the harness killed it (an 8-10 s stall per call).
Add an idle deadline (default 3000 ms, ECC_HOOK_INPUT_IDLE_TIMEOUT_MS,
0 disables). It is reset on each chunk, unref()ed, and on expiry detaches
from and pauses the stream so a held-open pipe cannot keep the hook alive.
Input that already parses as a complete JSON object is accepted; anything
else is marked truncated so fail-closed safety hooks still block.
bash-hook-dispatcher.js now reads through the shared reader instead of its
own end/error-only loop.
Fixes#3665
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>