Conversation
…ers, rendering, status (#615) - service install/uninstall/status/restart/update with exit codes 0-3 - launchd (macOS) + systemd --user (Linux) adapters behind an injectable command runner (unit tests never touch a live supervisor) - plist/unit rendering with XML + systemd escaping; atomic same-dir writes - launcher resolution precedence: NANO_BRAIN_BIN → global npm wrapper → direct executable; npx/local launchers rejected for persistent install - direct /health probe with no auto-start side effects; status contract JSON - transactional install with definition rollback on manager failure
…uncher metadata (#615) - storage.NewPoolWithRetry: 1s/2s/4s backoff capped at 30s for the foreground server; malformed DSN still fails immediately; NewPool stays fail-fast - startServer uses the retry helper so a managed service survives PostgreSQL startup gaps - npm/run.js spawns the Go child and forwards SIGTERM/SIGINT so a managed service never leaves an orphaned process; exits with child status/signal - run.js sets NANO_BRAIN_NPM_{LAUNCHED,RUNJS,NODE,GLOBAL} metadata so the CLI pins a stable [node, run.js] pair (global root only) for service install
) - recoverFromConnectionRefused: registered managed services are restarted through the native supervisor; legacy serve -d only when unregistered; a managed definition that fails to start never falls back to legacy - init wizard stepServe routes through the same managed path - register() no longer takes a spec (manager transition only) so recovery can (re)start an existing definition without rewriting it - unit tests for the managed/legacy recovery matrix via injected platform factory and health-wait hooks
…y, evidence (#615) - README + SETUP_AGENT document service install/status/restart/update/ uninstall, launcher requirements (global npm / NANO_BRAIN_BIN / direct binary), loginctl enable-linger, log/status guidance, serve -d migration - startServer loads config first and guards on the configured port (guardBeforeStartPort): a managed service on a non-default port is neither blocked by an unrelated :3100 server nor blind to a duplicate on its own port (found by the macOS smoke test) - launchd register retries bootstrap with a 700ms bounded backoff: launchctl fails with 'Input/output error' when bootstrap runs immediately after bootout while the old job is SIGTERMed (found by the smoke test) - buildServiceSpec uses build-tagged legacyDaemonRunning so the service subsystem adds no Windows build breakage (pre-existing daemon.go break on Windows verified identical on origin/master, out of scope) - evidence: unit/integration validation + full macOS native smoke transcript (install → active → /health → status contract → kill -9 auto-restart → update → uninstall)
… delayed-PG integration test (#615)
Major fixes: - systemd register uses restart instead of start: a plain start is a no-op on an active unit, so service update never reloaded the new binary on Linux (spec scenario 'update restarts the service' was broken) - unregistered status now reports supervisor_state=inactive plus the install remediation per the managed-daemon-service JSON contract - status resolves the config path pinned at install time via a sidecar marker (~/.nano-brain/service/config-path) instead of the interactive env, so the probe stays on the pinned endpoint - install rollback re-registers the restored previous definition so a failed update no longer leaves a previously-running service stopped - NANO_BRAIN_BIN is canonicalized to an absolute path before being pinned (launchd/systemd spawn from /; a relative path would crash-loop) - launchd register creates the service log directory before bootstrapping (the plist references it and launchd refuses jobs with unopenable log paths) - unregister treats 'No such process' (launchd) and 'not loaded' (systemd) as absent-state success so uninstall can still remove the definition - client recovery restarts a registered managed service before the TTY/NO_AUTO_START prompt gate (a managed service is designed to run) Minor fixes: - npm wrapper re-raises the child's death signal (dropping its own handlers first) so the exit status reflects the signal; new node test covers it - pool retry logs the actual next_retry delay before sleeping - service definition files written 0600 (user-only) per the spec - validation.md documents the pre-existing Windows build break honestly (identical on origin/master; service files add zero new errors)
- status keeps the unregistered install remediation when the /health probe also reports an error (probe error no longer overwrites it) - uninstall removes the config marker even when the definition was already removed manually (no stale endpoint in status) - install rollback surfaces a failed restore-write explicitly instead of re-registering the wrong definition - unit tests for the launchd 'No such process' and systemd 'not loaded' absent-state idempotent unregister branches
Archives the change and applies the managed-daemon-service and service-resilient-startup capabilities to the main specs (10 requirements added). Adds the independent review evidence (REQUEST-CHANGES round 1 → APPROVE-WITH-NITS round 2 → nits closed).
…too) (#615) CI (ubuntu) failed 6 tests that passed locally on macOS because they hardcoded the launchd adapter while its usable() gate rejects non-darwin, and the legacy recovery path polled a real /health wait (locally it happened to pass because the dev server runs on :3100). - tests now wrap real adapters in a usable-neutral testPlatform (embed + usable()=nil) so the same tests run on any GOOS; the real register/ unregister/restart logic is still exercised via the fake runner - client recovery uses the serviceHealthyWaitFn hook for the legacy path too, so tests never depend on a live server - TestPlatformUsablePlatformGate asserts launchd/systemd reject non-native GOOS; the root/container test skips where the platform gate applies
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Completes the install-UX workstream (#601): the one-line installers (#533 curl|bash, #603 opencode init, #534 zero-config init) get the binary onto the machine — this change registers the daemon with the OS supervisor so it autostarts at login, restarts after crashes, and survives temporary PostgreSQL outages.
New CLI surface (macOS launchd / Linux systemd --user):
Exit codes: 0 ready · 1 degraded/lifecycle failure · 2 not registered · 3 unsupported (Windows/root/container rejected).
Key pieces
Validation
Notes