Skip to content

fix: exit cluster run process after terminal state#28

Merged
mark-dingwall merged 1 commit into
mainfrom
fix/cluster-run-exit-on-complete
Apr 15, 2026
Merged

fix: exit cluster run process after terminal state#28
mark-dingwall merged 1 commit into
mainfrom
fix/cluster-run-exit-on-complete

Conversation

@mark-dingwall

Copy link
Copy Markdown
Owner

Summary

  • Both zeroshot run and zeroshot run -d kept their process alive indefinitely after CLUSTER_COMPLETE. Foreground hung at the shell; daemon mode accumulated 30+ orphans.
  • Adds Orchestrator.shutdown() that clears safety timeouts, stops snapshotters, and closes each cluster's messageBus/ledger. Legacy flag-only close() retained.
  • MessageBus.close() now drops EventEmitter listeners before ledger close so lingering subscribers can't pin the instance.
  • CLI: extracts waitForTerminalState helper used by both modes, adds waitForDaemonCompletion. Daemon path now waits → shuts down → process.exit(0). Foreground path shuts down after stream ends.

Test plan

  • New tests/integration/daemon-exit-on-complete.test.js — 3 cases: terminal-state wait, shutdown closes ledger, idempotency.
  • Existing orchestrator/integration suites: 1589 passing on push hook.
  • Manual daemon verification: zeroshot run --config code-review-bell "..." -d — cluster reached stopped, daemon process exited, [DAEMON] Cluster ... reached terminal state in daemon log, no orphan in ps.
  • Manual foreground verification: confirm shell prompt returns ~1s after Cluster ... completed. without Ctrl+C.

🤖 Generated with Claude Code

Both `zeroshot run` and `zeroshot run -d` kept their process alive
indefinitely after CLUSTER_COMPLETE. Foreground hung at the shell prompt;
daemon mode accumulated orphaned processes because there was no
completion-wait path at all.

- orchestrator: add `async shutdown()` that clears safety timeouts, stops
  snapshotters, and closes each cluster's messageBus/ledger. Legacy
  `close()` flag-only path kept for backwards-compat callers.
- message-bus: `close()` now removes EventEmitter listeners before the
  ledger closes so lingering subscribers can't pin the instance.
- cli/index.js: extract `waitForTerminalState` polling helper used by
  both modes. Add `waitForDaemonCompletion`; daemon path now waits,
  shuts down, and exits. Foreground path shuts down after stream ends.
- tests: new integration/daemon-exit-on-complete.test.js covers
  terminal-state wait, shutdown releasing ledger, and idempotency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mark-dingwall mark-dingwall merged commit 89d6d79 into main Apr 15, 2026
5 checks passed
@mark-dingwall mark-dingwall deleted the fix/cluster-run-exit-on-complete branch April 16, 2026 06:02
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.

1 participant