Skip to content

Add exit(code): stop the system, then exit the process with the code - #21

Merged
cressie176 merged 1 commit into
mainfrom
exit-code
Sep 28, 2026
Merged

cressie176 merged 1 commit into
mainfrom
exit-code

Conversation

@cressie176

Copy link
Copy Markdown
Member

Closes #20.

system.exit(code) stops the system and exits the process once the stop has finished. exitOn becomes: on each signal, call exit() with no code. Its observable behaviour is unchanged.

Behaviour

  • After a successful stop the process exits with the given code. With no code it calls a bare process.exit(), so process.exitCode is honoured.
  • After a failed stop the caller's non-zero code is kept; 0 or none becomes 1. The alternative, always 1 as exitOn did, would lose a fatal handler's code on the way to the orchestrator.
  • The code is validated eagerly: "The exit code must be an integer".

Found empirically

  • On Node 24 process.exit(undefined) is an explicit 0 and does not honour process.exitCode. The existing restart scenario, which expects code 3, caught it.
  • Signal listeners receive the signal's name and number, and process.exit('SIGTERM') throws. The custom-event scenarios carry no arguments, so a scenario raising a real SIGTERM was added; it is the only one which catches exitOn passing its arguments through.
  • A signal listener does not keep the event loop alive, so the child program's postgres now holds a timer while started, the way a server holds a socket.

Tests

Six new child-process scenarios and three in-process validation scenarios in test/features/exiting.md. Each deliberate breakage is caught, detailed in #20. 1339 steps pass, lint and typecheck clean.

🤖 Generated with Claude Code

An application which gives up, from a component_failed listener or a
fatal error handler, had to write the stop-then-process.exit dance itself,
although exitOn already performed it in lib/exit.js. That primitive is
now public as system.exit(code), and exitOn is: on each signal, call
exit() with no code.

After a successful stop the process exits with the given code. After a
failed stop the caller's non-zero code is kept and 0 or none becomes 1:
the caller's reason for exiting outranks the stop's failure, which the
events announce anyway. The alternative, always 1 as exitOn did, would
lose a fatal handler's code on the way to the orchestrator.

Three things found empirically shape the code. On Node 24
process.exit(undefined) is an explicit 0 and does not honour
process.exitCode, so the no-code path calls a bare process.exit(); the
existing restart scenario, which expects exitCode 3, caught the
difference. Signal listeners are called with the signal's name and
number, and process.exit('SIGTERM') throws, so exitOn's listener
discards its arguments; a new scenario raising a real SIGTERM is the
only one which catches that, since custom events carry no arguments.
A signal listener does not keep the event loop alive, so the child
program's postgres now holds a timer the way a server holds a socket.

Closes #20.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@codecov-commenter

Copy link
Copy Markdown

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

Thanks for integrating Codecov - We've got you covered ☂️

@cressie176
cressie176 merged commit 0633f92 into main Sep 28, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

exit(code): stop the system, then exit the process with a code

2 participants