Skip to content

fix(daemon): lifecycle races and port-in-use handling#244

Merged
vreshch merged 1 commit into
masterfrom
fix/daemon-lifecycle-races
Jul 7, 2026
Merged

fix(daemon): lifecycle races and port-in-use handling#244
vreshch merged 1 commit into
masterfrom
fix/daemon-lifecycle-races

Conversation

@vreshch

@vreshch vreshch commented Jul 7, 2026

Copy link
Copy Markdown
Member

What

Fixes four verified daemon-lifecycle bugs (plus a pid-reuse guard):

  • P0 - autostart race no longer wipes the live daemon's state files. daemon-entry gates pid/port cleanup on an ownership flag set only after this process writes them; an EADDRINUSE loser now exits with a distinct code and touches nothing.
  • Malformed vaults.json edits no longer kill the daemon. The watchFile reschedule runs each channel independently under a try/catch (safeReschedule), logging one line and keeping the last-good schedule. Added an uncaughtException last-resort guard that cleans up only owned state.
  • Shutdown no longer stalls on idle keep-alive sockets. server.stop() calls closeIdleConnections() and forces closeAllConnections() after a short unref'd grace timer.
  • A foreign process on the daemon port fails fast. daemon-entry exits with a distinct code on EADDRINUSE; spawnDaemon races the child exit event against the health wait and returns a typed SpawnOutcome, so a busy port short-circuits instead of burning the full ~4s. daemon start prints "port is in use by another process - set AGENTAGE_DAEMON_PORT to use a different port" and exits 1; memory verbs fall back to the DirectClient quickly.
  • pid-reuse guard on stop. stopDaemonSafely probes /health and only signals when the recorded port confirms our pid, else (no port recorded) falls back with a printed caveat, else refuses rather than kill a recycled pid.

Why

Concurrent memory verbs racing autostart could orphan the live daemon; hand-editing config could crash it; SIGTERM could hang; and a stray port holder made every verb silently pay ~5s.

Scope

Server changes are limited to start()/stop() lifecycle (no request-path / auth changes - sibling PR owns those). Unit tests added/updated for every behavior change.

Verified: npm run verify green locally.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

🎉 PR Validation ✅ PASSED

Commit: b8ec96e224f5960a464fc87b09495975309e40d3
Branch: fix/daemon-lifecycle-races

Checks:

  • ✅ Release guard (no version/changelog changes)
  • ✅ Dependencies installed
  • ✅ Type check passed
  • ✅ Linting passed
  • ✅ Format check passed
  • ✅ Tests + coverage passed
  • ✅ Build successful

Ready to merge!


🔗 View workflow run
⏰ Generated at: 2026-07-07T22:18:48.601Z

@vreshch
vreshch marked this pull request as ready for review July 7, 2026 22:09
@vreshch
vreshch force-pushed the fix/daemon-lifecycle-races branch from 78e9b60 to b8ec96e Compare July 7, 2026 22:18
@vreshch
vreshch merged commit 34c48a9 into master Jul 7, 2026
3 checks passed
@vreshch
vreshch deleted the fix/daemon-lifecycle-races branch July 7, 2026 22:22
vreshch added a commit that referenced this pull request Jul 7, 2026
## What

README overhaul plus a hand-authored architecture diagram, aligned to
master after #238-#245. Ready as the npm landing page.

Touches only `README.md` and `docs/**` - no `src/`, `package.json`, or
workflows.

## Changes

- **`docs/architecture.svg`** - hand-written SVG (no render tooling).
Neutral background with dark text on light boxes, so it reads in both
light and dark GitHub themes and on npm. Shows AI tools -> MCP (stdio
`agentage mcp` + daemon HTTP `/mcp` on `127.0.0.1:4243`) -> CLI + local
daemon (single writer) -> local vaults (git-per-vault,
`@agentage/memory-core`) -> sync out to git remotes and account sync
(labeled protocol-neutrally, no internal tech named).
- **`README.md`** restructured for a first-time reader: What is this ->
Install -> Quickstart (local-only first, then optional `agentage setup`)
-> Architecture (diagram) -> Command reference -> Sync -> MCP
integration -> Daemon -> Env vars -> Development.
- **`docs/architecture.md`** - short walk-through of the diagram.

## Reality-check vs master

Built the CLI (`npm run build`) and verified every command and flag
against the live `--help` output. Corrected drift from the merged PRs:

- `vault add` now defaults to an **account** vault; `--local [path]`,
`--git <remote>`, `--path <dir>` are the alternatives.
- `vault sync [name]` now covers git and the account channel; progress
prints per vault.
- Documented the `update` command and `--no-daemon` global flag.
- Daemon section notes the loopback-only, token-guarded,
cross-origin-rejecting API and port-in-use reporting (#243, #244).
- Friendly memory errors + non-zero exits (#240); 64 KB read clamp and
secret refusal.
- Describes no specific `src/` file paths (a sibling PR is moving
`src/sync/` paths).

## Verification

- `npm run verify` green: 405 unit tests pass, type-check + lint +
format:check + build clean.
- Ran the offline quickstart end-to-end against the built `dist/cli.js`
(vault add --local, write via stdin, list, search, read `@vault/path`) -
all pass.
- Rendered `docs/architecture.svg` to PNG and eyeballed legibility.

Note: `format:check` only globs `{src,e2e}/**/*.ts`, so it does not lint
markdown; README changes are prose-only.
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