Skip to content

doctor: detect and repair daemons that lost their ownership record - #1523

Open
snimu wants to merge 7 commits into
mainfrom
eng-5302
Open

doctor: detect and repair daemons that lost their ownership record#1523
snimu wants to merge 7 commits into
mainfrom
eng-5302

Conversation

@snimu

@snimu snimu commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

During a real incident, a daemon supervisor got fully wedged: its socket was still alive and answering the version handshake, but its ownership record had been deleted out from under it. Every actual command it received failed with an ownership-lost error, yet prime-agent doctor reported it as status "current" — and the only way out was hand-replicating internal spawn code or prime-agent shutdown, which kills ALL daemons on the machine.

What doctor now says and does

Detection (doctor, ps): doctor cross-checks each healthy-looking daemon's hello identity (generation + pid + process start id) against the supervisor ownership registry, using purely local reads — no daemon commands, since a wedged daemon can't serve them anyway. A daemon whose owner record is missing or was replaced is reported with the new status ownership-lost (human table + --json), plus a remedy footer:

! /path/to/daemon.sock: supervisor lost its ownership record — run "prime-agent doctor --fix" to restart it (sessions are preserved)

No false positives on daemons that are starting up: the owner record is written before listen(), so a daemon that hasn't written its record yet can never answer a probe. Stopping daemons fail a final pid/start-id liveness re-check; replaced daemons fail the generation match; old/foreign daemons without a hello generation are skipped.

Repair (doctor --fix): restarts only the affected daemon, not the whole fleet:

  1. proves the daemon belongs to the doctor's agent dir via its per-generation snapshot-cache state dir (declines safely otherwise — repairing a foreign-agent-dir daemon would restart it under the wrong agent dir and orphan its workers);
  2. takes the daemon shutdown admission and re-verifies the exact same wedged instance under it (no TOCTOU kill of a replacement);
  3. kills the supervisor, confirms process exit after SIGKILL before touching the socket, holds admission across kill/socket cleanup/startup-fence wait so no concurrent autostart can bind the socket mid-kill;
  4. relaunches on the same socket via the standard launch path; workers and their sessions are re-adopted by the existing adoption machinery.

If the kill or relaunch fails, doctor reports honestly and leaves the system no worse (nothing is unlinked or spawned after a failed kill).

Messages: ownership-lost errors now name the cause (released vs record missing on disk vs record replaced by another owner), and the raw proper-lockfile Lock file is already being held on the socket lease is wrapped to name the incumbent supervisor (pid + generation).

Not included (deliberately): self-heal in assertCurrent (rewriting a deleted owner record). Post-#1449 the registry lives in ~/.prime/supervisor-owners and is not OS-reaped, so external deletion is implausible; self-heal would also fight doctor --fix (a supervisor rewriting its record between detection and repair) and let a supervisor resurrect ownership an operator deliberately revoked. Both the plan and the reviewer signed off on leaving it out.

Checks

  • npm run check: pass
  • Focused tests: daemon-ps, daemon-supervisor-ownership, public-command, daemon-ps-format, daemon-socket — 5 files, 85 tests, all pass (new coverage: detection true/false cases incl. young-daemon and shutdown-race guards, repair call order under admission, foreign-agent-dir decline, survive-SIGKILL abort, ELOCKED contract, ownership-reason texts)
  • E2E on a real isolated daemon (isolated agent dir + --daemon-socket): started daemon + session, deleted its owner record, doctor reported ownership-lost with remedy, doctor --fix killed pid 11323 and relaunched on the same socket (new pid 12570), owner record re-created, the session survived with the same worker pid (re-adoption confirmed), and a second healthy control daemon was untouched (same pid before/after). Evidence bundle: /tmp/prime-agent-features/eng-5302-e2e/

LOC: +660/−29 (feature code +290/−26 in daemon-ps, daemon-ps-format, daemon-supervisor, daemon-supervisor-ownership; tests +340/−3; changelog +1).

Fixes ENG-5302 (https://linear.app/primeintellect/issue/ENG-5302/doctor-must-detect-and-repair-daemon-ownership-loss)


Note

Medium Risk
Repair force-kills a live supervisor and unlinks its socket. Guards (admission, pid/start-id recheck, agent-dir match) reduce TOCTOU risk, but a failed relaunch still leaves the daemon down until autostart.

Overview
doctor / ps now flag daemons whose supervisor is still answering hello but whose on-disk ownership record is missing or replaced (ownership-lost), instead of reporting them as healthy. doctor --fix restarts only those daemons on the same socket so workers/sessions can be re-adopted.

Detection compares hello generation + pid + start id against the local ownership registry (no daemon commands). Repair holds shutdown admission across kill, socket cleanup, and startup-fence wait, then relaunches; it declines daemons whose snapshot-cache lives under a different agent dir, and will not unlink the socket if SIGKILL does not confirm exit. Ownership-lost and socket-lock errors now name the cause and incumbent owner.

Reviewed by Cursor Bugbot for commit b4c652d. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Detect and repair daemons that lost their supervisor ownership record in doctor

  • Adds a new ownership-lost daemon status, detected by cross-checking the daemon's reported supervisor generation against the on-disk ownership registry via detectDaemonOwnershipLost in daemon-ps.ts.
  • Introduces repairOwnershipLostDaemon, a session-preserving restart flow that force-kills the wedged supervisor, removes the socket, and relaunches — with PID-reuse fencing and admission scoping throughout.
  • planReap now schedules a restart action for ownership-lost daemons, including the default daemon, instead of skipping them.
  • forceKillDaemon now returns a boolean confirming the target is gone and accepts an optional expectedProcessStartId to avoid signaling a reused PID.
  • ps output appends a per-daemon repair hint (prime-agent doctor --fix) for each ownership-lost entry, rendered in red.
  • Risk: the repair path force-kills the wedged supervisor process; if autostart does not recover it, the operator must restart manually.

Macroscope summarized b4c652d.

…r ownership record

During a real incident a fully wedged supervisor (socket alive, ownership
record deleted) was reported by doctor as status "current" and the only
recovery was `shutdown`, which kills ALL daemons.

- doctor now cross-checks each current daemon's hello identity against the
  supervisor ownership registry (purely local reads) and reports status
  "ownership-lost" with a one-line remedy; young/stale/foreign daemons are
  never falsely flagged (owner record is written before listen()).
- doctor --fix repairs exactly the affected daemon: verifies it belongs to
  the doctor's agent dir via its snapshot-cache state dir, holds shutdown
  admission across kill/socket cleanup/startup-fence wait, confirms process
  exit after SIGKILL, then relaunches on the same socket; workers/sessions
  are re-adopted by the existing adoption path. Foreign-agent-dir daemons
  are declined safely.
- ownership-lost errors now name the cause (released vs record missing vs
  record replaced) and ELOCKED socket-lease errors name the incumbent
  supervisor (pid + generation).

fixes ENG-5302
Comment thread packages/coding-agent/src/cli/daemon-ps.ts Outdated
# Conflicts:
#	packages/coding-agent/CHANGELOG.md
Comment thread packages/coding-agent/src/cli/daemon-ps.ts
Comment thread packages/coding-agent/src/modes/daemon/daemon-supervisor.ts Outdated
Comment thread packages/coding-agent/src/cli/daemon-ps.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 676e577. Configure here.

Comment thread packages/coding-agent/src/cli/daemon-ps.ts Outdated
Comment thread packages/coding-agent/src/modes/daemon/daemon-supervisor.ts
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