Skip to content

fix(exit): silence virtual exit statuses and allow exit 0 under errexit - #215

Merged
konard merged 2 commits into
mainfrom
issue-213-cf1ef24ff19a
Oct 7, 2026
Merged

konard merged 2 commits into
mainfrom
issue-213-cf1ef24ff19a

Conversation

@konard

@konard konard commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

Caught virtual exit 7 failures previously populated stderr with Command failed with exit code 7 and mirrored that text to the parent process. Under set('e'), even exit 0 rejected. This change makes both statuses silent and lets successful exits resolve.

Fixes #213.

Changes

  • Throw only for nonzero statuses, with explicit empty stderr. The runner retains the existing status aliases and decides whether to reject according to errexit.
  • Remove the exit handler's dependency on global shell settings.
  • Preserve sequence stopping after a failing exit under errexit, including when capture is disabled; unexpected virtual-handler failures still report their diagnostic stderr.
  • Add eight regression tests to the existing Bun suite and Node 22/24/26 CI jobs.
  • Add a reusable reproduction and a patch changeset for the next npm release.

Reproduction and regression coverage

node experiments/issue-213/virtual-exit.mjs 2>stderr.txt
bun experiments/issue-213/virtual-exit.mjs 2>stderr.txt
node --test js/tests/virtual-exit.test.mjs
bun test js/tests/virtual-exit.test.mjs --timeout 10000

Before implementation, six of eight tests failed under both runtimes. The nine-case probe showed bogus stderr for caught nonzero exits and rejected status 0 under errexit. After the fix, all observations have empty stderr and stderr.txt is empty. exit 7 rejects under errexit, while exit 0 and argumentless exit resolve. A real child process provides the silent-status comparison.

Tests also verify result/error status aliases, output and exit events, stopping a sequence after exit 3, default mirroring, disabled mirroring/capture, non-errexit results, and unexpected-handler diagnostics.

Validation

  • Focused Bun suite: 61 passed; Node exit regression suite: 8 passed.
  • Existing Node regression suites plus the new exit tests: 104 passed.
  • bun run check: ESLint, repository formatting, duplication, and TypeScript declarations passed.
  • Documentation/workflow regression checks: 386 passed.
  • All 29 executable features agree under Node and Bun and run in Rust.
  • Full Bun suite: 2,574 passed, 10 skipped, 0 failed across 153 files.
  • Full Rust suite, including doc tests: 830 passed, 5 ignored, 0 failed.
  • Node zx compatibility: 490 passed, 2 skipped; Execa compatibility: 38 passed.
  • Generated documentation and changeset/version validation passed.
  • All seven CI workflows pass on f9f55a0, including Bun on Linux/macOS/Windows, Node 22/24/26, process-tree checks, security, quality, dependency freshness, executable parity, and benchmark smoke checks: JavaScript checks.

The first macOS Node 24 process-tree job observed one extra heartbeat byte (6 !== 5) in the existing exec: stubborn test at js/tests/windows-tree-kill.test.mjs:87 (preserved run log, line 1806). Its 19 tests passed on a single rerun of the same commit: successful rerun. The initial failure and rerun logs are preserved in ci-logs/. The new exit regressions passed in every runtime job on the first attempt.

Scope and release

This is JavaScript-specific and carries parity-exempt: Rust's exit handler already returns the requested code with empty stderr. Executable language parity remains enabled. Existing argument parsing and non-errexit sequence behavior are preserved; full POSIX shell termination semantics are outside this issue's fix.

js/.changeset/issue-213-virtual-exit.md prepares the next patch release. Package versions remain managed by the release workflow.

Changes

  • 7 file(s) modified
  • 233 line(s) added
  • 3 line(s) removed
  • Files:
    • .github/workflows/js.yml
    • experiments/issue-213/README.md
    • experiments/issue-213/virtual-exit.mjs
    • js/.changeset/issue-213-virtual-exit.md
    • js/src/$.mjs
    • js/src/commands/$.exit.mjs
    • js/tests/virtual-exit.test.mjs

Adding .gitkeep for PR creation (default mode).
This file will be removed when the task is complete.

Issue: #213
@konard konard self-assigned this Oct 7, 2026
@konard konard added the parity-exempt Change is intentionally single-language; skip JS/Rust parity check label Oct 7, 2026
@konard konard changed the title [WIP] Virtual exit N writes "Command failed with exit code N" to stderr, and exit 0 rejects under set -e fix(exit): silence virtual exit statuses and allow exit 0 under errexit Oct 7, 2026
@konard
konard marked this pull request as ready for review October 7, 2026 04:40
@konard

konard commented Oct 7, 2026

Copy link
Copy Markdown
Member Author

Working session summary

Fixed both virtual exit bugs, added regression tests and a patch changeset, and marked PR #215 ready for review.

Validation: 2,574 Bun tests and 830 Rust tests passed. All seven CI workflows pass after one existing macOS timing test passed on rerun. The working tree is clean.


This summary was automatically extracted from the AI working session output.

@konard

konard commented Oct 7, 2026

Copy link
Copy Markdown
Member Author

🤖 Solution Draft Log

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost estimation:

  • Model: GPT-6.1 Sol
  • Provider: OpenAI
  • Public pricing estimate: $1.816148

📊 Context and tokens usage:

  • 144.1K / 200K (72%) input tokens, 23.9K / 128K (19%) output tokens

Total: (144.1K + 4.4M cached) input tokens, 23.9K output tokens, $1.816148 cost

🤖 Models used:

  • Tool: OpenAI Codex
  • Requested: gpt-6.1-sol
  • Thinking level: xhigh (~31999 tokens)
  • Model: GPT-6.1 Sol (gpt-6.1-sol)

📎 Log file uploaded as Gist (2516KB)


Now working session is ended, feel free to review and add any feedback on the solution draft.

@konard
konard merged commit 1fc9e3a into main Oct 7, 2026
51 of 52 checks passed
@konard

konard commented Oct 7, 2026

Copy link
Copy Markdown
Member Author

🎉 Auto-merged

This pull request has been automatically merged by hive-mind.

  • All CI checks have passed

Auto-merged by hive-mind with --auto-merge flag

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

parity-exempt Change is intentionally single-language; skip JS/Rust parity check

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Virtual exit N writes "Command failed with exit code N" to stderr, and exit 0 rejects under set -e

1 participant