Repository navigation
fix(OMN-20389): hooks feed variable-size text through a process substitution, never a here-string - #2508
Conversation
…itution, never a here-string (plugin 2.4.20) Homebrew bash 5.3.9 writes a here-string body of 512 to 65535 bytes into a pipe inside the process that will read it. When macOS pipe memory is exhausted a fresh pipe holds 512 bytes, the write blocks forever and the hook hangs with no output. Replaces all 27 here-string lines in the pre-commit hooks and the tool hook scripts with < <(printf '%s\n' "$var"), whose writer is a separate process. Adds a static ratchet and 60 KB and 64 KB payload runs of reject-deploy-gate-skip-token.sh and validate-occ-pr-stamp.sh under Homebrew bash. Draft: codex=01a0fe09-d407-7c82-a2e4-f802401c5bbb Onex-Lane: mac-bash-herestring-2a21 Onex-Session: bf22f698e0ec441e8ce65c6e67d86040
|
Lab: head=e1e7927566e837a86d2a01d44d64eec84fcd437b host=launching-mac (Darwin 25.6, /opt/homebrew/bin/bash 5.3.9) lane=mac-bash-herestring-2a21 command=during ambient pipe pressure (a fresh pipe measured at 512 bytes before and after each run), /opt/homebrew/bin/bash .pre-commit-hooks/reject-deploy-gate-skip-token.sh on a 61440-byte markdown file, this head versus origin/dev's copy of the hook observed=this head exited 0 in three of three pressure windows; the origin/dev copy hung to the 5 s timeout in three of three (and in the earlier window at 8 s). Focused suites: 42 passed (new ratchet and 60/64 KB payload tests plus the five hook suites), 220 passed across the guard suites. No runtime or tenant path is touched, so no onex-api surface applies. |
✅ Hostile Reviewer — PASSEDBlocking findings (critical/error): 0 Findings
Gate semantics (pilot phase)
Powered by omniintelligence.review_pairing.cli_review — node-based adversarial review via HandlerLlmCliSubprocess (OMN-8524) |
Merging origin/dev (5e71656) into jonah/omn-20389-here-string-free-hooks for PR 2508 (ticket OMN-20389). Two version-line conflicts resolved in plugins/onex/.claude-plugin/plugin.json and plugins/onex-dev-marketplace/.claude-plugin/marketplace.json. Branch had plugin version 2.4.20; dev has 2.4.22. Version set to 2.4.23, one above dev, keeping the PR's version bump. No hook script conflicts.
|
Lab: head=06346da4663c462f7ff6527d46cc7aa23fe1697a. Host h201 (Linux lab host) executed the command "pytest tests/hooks/test_here_string_free_hooks.py tests/hooks/test_refusal_row_lane_omn19381.py" in a worktree of this head for lane claude-2508-conflict-2a21. The run observed 76 passed in 1.44s. This head is a merge of origin/dev (5e71656) into the branch; the only conflicts were the plugin version lines (plugin.json and marketplace.json, resolved to 2.4.23), so the hook scripts are unchanged from the head e1e7927 whose Mac pipe-pressure measurement is in the earlier Lab comment. No runtime or tenant path is touched, so no onex-api surface applies. |
Ticket: OMN-20389
Problem
Homebrew bash 5.3.9 on macOS deadlocks on a here-string or heredoc body of 512 to 65535 bytes whenever macOS pipe memory is exhausted. XNU sizes each new pipe from system-wide pipe memory use, so under load a fresh pipe holds only 512 bytes. bash writes the body into that pipe inside the process that will go on to read it (sampled stack:
execute_disk_command->do_redirections->heredoc_write->write), so the write blocks forever and nothing can drain it. Three lanes lost 25 to 60 minutes each on 2026-10-02: the pre-commit hooksreject-deploy-gate-skip-token.shandvalidate-occ-pr-stamp.shhung 13 to 42 minutes per run on staged files of 1.3 KB to 36 KB, andpr-handoffhung with no output.Measured with a 512-byte pipe: 511 B ok, 512 B to 65535 B hang, 65536 B and above ok (bash falls back to a temp file). Heredocs, here-strings and the builtin
readall hang. A process substitution,printf | cmdand a temp file do not, because the writer is a separate process or a regular file.Upstream fixed this in bash53-016 (2026-07-07,
PIPESIZE_DYNAMICon darwin). Homebrew stable is 5.3.20; this host has 5.3.9, and nothing here upgrades it.Change
All 27 here-string lines in the pre-commit hooks (
.pre-commit-hooks/) and the tool hook scripts (plugins/onex/hooks/scripts/,plugins/onex/hooks/lib/) become< <(printf '%s\n' "$var"), which keeps the trailing newline a here-string added. Sixteen scripts: the six pre-commit hooks that read staged or file content, the three PreToolUse payload guards (actor line, done flip, lane liveness), the Bash guard entrypoint, the agent result verifier, cost accounting, env var sync, the Kafka poison guard, user-prompt-submit and the refusal recorder.test_here_string_free_hooks.pyadds a static ratchet (no<<<outside a comment in those three directories) and runs the realreject-deploy-gate-skip-token.shandvalidate-occ-pr-stamp.shunder/opt/homebrew/bin/bashon 60 KB and 64 KB payloads with a 60 s timeout. The one existing test that asserted the refusal recorder used a here-string now asserts the process substitution.plugins/onex/.claude-plugin/plugin.json2.4.19 to 2.4.20 with the matchingonexentry inplugins/onex-dev-marketplace.Not changed
python3 - <<'PY',cat > f <<EOF) carry the same hazard above 512 bytes and are a separate sweep.reject-deploy-gate-skip-token.shin omnimarket, omnibase_infra, omnibase_core, onex_change_control, omnibase_compat, omnibase_spi and omnimemory each keep two here-string lines.Draft: codex=01a0fe09-d407-7c82-a2e4-f802401c5bbb (high effort), reviewed and amended by the lane.
Verification
Focused, on the launching host: 42 passed (the new file plus the skip-token, OCC stamp, defect anchor, handoff infra health and UTC discipline hook tests); 220 passed across the done-flip, lane liveness, agent result verifier, refusal row, shell input safety and actor line guard tests;
pre-commit runon the changed files clean; ruff and mypy strict clean on the new test.tests/hooks_system/test_bash_guards_differential.pyshows 16 mismatches that are all the/tmpversus/private/tmpmacOS symlink spelling in worktree-path reasons (none mention a here-string).Evidence-Source: OCC#12404
Evidence-Ticket: OMN-20389