- 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>
Carry forward PR #3055 at 92f53c4c onto ef648e01, preserving contributor attribution and current main. Focused maintainer fixes reuse shared input ceilings, preserve late root context through outer bootstrap, bound frontmatter before immutable parsing, and assert real oversized input in the limits workflow.