Skip to content

Fix serve-agent reinstall race in schedule:weekly - #7

Merged
howieyoung merged 1 commit into
mainfrom
fix/install-serve-bootstrap-race
Aug 8, 2026
Merged

Fix serve-agent reinstall race in schedule:weekly#7
howieyoung merged 1 commit into
mainfrom
fix/install-serve-bootstrap-race

Conversation

@howieyoung

Copy link
Copy Markdown
Owner

Follow-up to #6. Re-running npm run schedule:weekly reliably left the resident receiver down.

Cause

installAgent() boots out the old LaunchAgent then immediately bootstraps the new one. launchctl bootout returns before launchd finishes teardown, so bootstrapping into a domain that still holds the (KeepAlive) label fails with Bootstrap failed: 5: Input/output error. The installer exits on that failure — and since serve is reinstalled last and was already booted out, the receiver stayed down and captures silently returned 401 until a manual launchctl bootstrap.

Reproduced deterministically: it failed on two consecutive schedule:weekly runs.

Fix

  • bootoutAndWait(): after bootout, poll launchctl print until the label is actually gone (bounded ~3s) before bootstrapping.
  • installAgent(): retry bootstrap a few times on the transient EIO, clearing half-registered state between attempts; still fails loudly on a genuine error.
  • sleepMs via Atomics.wait (no sleep binary / async needed in this flow).

Verification

After the fix, npm run schedule:weekly reinstalls all three agents cleanly and idempotently; receiver stays up (/health 200), and the /capture gate is intact (no token → 401, correct token + {} → 400). serve runs compiled dist/server.js under Node 22 via the wrapper.

🤖 Generated with Claude Code

installAgent() booted out the old LaunchAgent and immediately bootstrapped
the new one. launchctl bootout returns before launchd finishes tearing the
job down, so bootstrapping into a domain that still holds the (KeepAlive)
label reliably failed with "Bootstrap failed: 5: Input/output error". The
installer then exited, and because serve is reinstalled last and had already
been booted out, every re-run left the resident receiver DOWN — captures
silently 401'd until a manual bootstrap.

- bootoutAndWait(): after bootout, poll `launchctl print` until the label is
  actually gone (bounded ~3s) before bootstrapping.
- installAgent(): retry bootstrap a few times on the transient EIO, clearing
  half-registered state between tries; still fail loudly on a real error.
- sleepMs via Atomics.wait (no sleep binary, no async needed here).

Verified: `npm run schedule:weekly` now reinstalls all three agents cleanly
and idempotently; the receiver stays up (/health 200, /capture gate intact).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@howieyoung
howieyoung merged commit 107870f into main Aug 8, 2026
1 check 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.

1 participant