fix: drain stdio then hard-exit - piped JSON truncated at 64KiB - #63
Open
thossullivan wants to merge 1 commit into
Open
fix: drain stdio then hard-exit - piped JSON truncated at 64KiB#63thossullivan wants to merge 1 commit into
thossullivan wants to merge 1 commit into
Conversation
check.mjs called process.exit() straight after console.log(JSON.stringify(...)), and Node discards unflushed pipe writes on exit, so any --json output past the 64KiB pipe buffer was silently truncated (repro: check on a large repo | jq). File redirection was unaffected, which is why CI logs never showed it. The CLI body now runs in main() returning the exit code; top level drains stdout and stderr through an error-capturing zero-byte write, then hard process.exit()s. Three review rounds shaped this: bare process.exitCode waited on the whole event loop (stray timer hangs the CLI, beforeExit rewrites the code), and a naive drain discarded stream errors (EPIPE truncation exited 0; a dead stderr plus a usage error crashed 1 instead of 2). Exit codes: 0/1/2 unchanged; new exit 3 when output fails on a clean run for any reason other than EPIPE, which stays 0 (consumer closed the pipe). Regression tests cover piped >64KiB stdout and stderr, setInterval preload, beforeExit preload, early-closed stdout, ended stderr with a usage error, and callback-level EBADF. Also converts the two badge tests from live feed dates, which rotted when claude-opus-4-1 retired on 2026-08-05, to synthetic fixtures. Implemented by Codex (gpt-5.6-luna) over three review rounds; verified locally.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
check.mjscalledprocess.exit()straight afterconsole.log(JSON.stringify(...)), and Node discards unflushed pipe writes on exit — so any--jsonoutput past the 64KiB pipe buffer was silently truncated.check <large-repo> --json | jqgot broken JSON; file redirection was unaffected, which is why CI logs never showed it. Found by the cross-repo UAT sweep (three local repos produce >64KiB of findings).The fix, shaped by three adversarial review rounds:
process.exitCode+ return) was rejected: it waits on the whole event loop, so a stray timer hangs the CLI and abeforeExithook can rewrite the exit code.main()returns the code; the top level drains both stdio streams through an error-capturing zero-byte write (temporary error listener, always settles, no unhandled-error race), then hardprocess.exit()s.Exit codes: 0/1/2 unchanged. New exit 3 (documented in help) when output fails on a clean run for any reason other than EPIPE; EPIPE stays 0 — the consumer closed the pipe deliberately, and the pipeline's exit code is the consumer's anyway.
Tests: regressions for piped >64KiB stdout and stderr,
setIntervalpreload (must exit, not hang),beforeExitpreload (must not alter the code), early-closed stdout, ended stderr + usage error, callback-level EBADF. Also converts the two badge tests from live feed dates — which rotted when claude-opus-4-1 retired on 2026-08-05 — to synthetic date-stable fixtures.Implemented by Codex (gpt-5.6-luna) under adversarial review, run until clean. Verified locally: full suite green; a 456KB / 807-finding real-repo check now parses whole through a pipe.
🤖 Generated with Claude Code