Skip to content

fix(dev): stop orphaning the antfly child on dev-loop shutdown and EADDRINUSE#460

Open
markhayden wants to merge 1 commit into
mainfrom
fix/dev-loop-orphans
Open

fix(dev): stop orphaning the antfly child on dev-loop shutdown and EADDRINUSE#460
markhayden wants to merge 1 commit into
mainfrom
fix/dev-loop-orphans

Conversation

@markhayden

Copy link
Copy Markdown
Owner

Closes #459.

Two dev-loop defects that minted orphaned antfly processes across bun run dev generations (full diagnosis in the issue):

  1. scripts/dev.ts signal handlers preempted the lifecycle shutdown. They're registered before server.ts loads and called process.exit(0) — signal listeners run in registration order, so src/core/lifecycle.ts's async shutdown (the only path that stops the antfly child) never executed. The dev handler now defers once the lifecycle owns shutdown, via a globalThis flag set by registerShutdownHandlers() — deliberately checked without importing lifecycle, so an early Ctrl-C during the build phase doesn't drag in the server module graph.

  2. Unhandled EADDRINUSE crashed after full boot, skipping cleanup. server.listen() had no 'error' listener; a squatted 3737 threw an uncaught exception after antfly was spawned and the watcher started. Now: clear error naming the port + lsof remediation, then self-SIGTERM through the lifecycle shutdown, exiting 1 (lifecycle's final process.exit honors a pre-set process.exitCode).

Verified end-to-end (isolated BAKIN_HOME, two servers on one port): second server exits 1 with the named remediation and Shutdown complete; first keeps serving and exits cleanly on SIGTERM.

Related but intentionally NOT here (they live on the migration branch / PR #457, since that's where the adapter and the ~/.bakin/antfly dir are owned):

  • watcher ignore for the private antfly data dir (the Bun-native deadlock that made processes unkillable by SIGTERM)
  • sync process.on('exit') net in the adapter that kills the antfly child on any JS-level exit

Suite: 4658 pass / 0 fail; typecheck + lint clean.

🤖 Generated with Claude Code

…DDRINUSE (#459)

Two dev-loop defects that minted orphaned antfly processes across
generations (diagnosed live; full writeup in #459):

- scripts/dev.ts registered SIGINT/SIGTERM handlers before server.ts loads
  and called process.exit(0) — Node runs signal listeners in registration
  order, so the lifecycle's async shutdown (the only thing that stops the
  antfly child) never executed. The dev handler now defers once the
  lifecycle owns shutdown (globalThis flag set by registerShutdownHandlers;
  checked without importing lifecycle so an early Ctrl-C during the build
  phase doesn't load the server module graph).

- server.ts had no 'error' listener on listen(): EADDRINUSE threw as an
  uncaught exception AFTER the full boot (antfly spawned, watcher running),
  bypassing cleanup. It now logs the port + lsof remediation, routes
  through the lifecycle shutdown via self-SIGTERM, and exits 1 — the
  lifecycle's final exit honors a pre-set process.exitCode instead of
  stamping 0 over it.

Verified end-to-end with two isolated-BAKIN_HOME servers on one port:
second exits 1 with the named remediation and "Shutdown complete"; first
keeps serving and exits cleanly on SIGTERM.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

Dev loop leaves orphaned processes: dev.ts signal handlers preempt lifecycle shutdown; unhandled EADDRINUSE crashes after full boot

1 participant