feat(registry): live-reload + supervisor reconcile (PRD-005) — fix onboarding fleet-green deadlock - #20
Conversation
Doctor read its supervised-daemon registry only once, at boot. When onboarding installs doctor first (booting it on a hive-only registry), honeycomb and nectar register AFTER doctor booted and were never supervised — deadlocking hive's "Bringing the fleet up green" gate (which requires honeycomb) until a reboot. Doctor now re-reads the registry at runtime and reconciles its live supervisor set without a reboot and without any cross-product coordination: - src/registry-reload.ts: a mtime-gated periodic re-resolve of BOTH registry locations (fleet + legacy). Unchanged => strict no-op (single stat). A torn/ malformed read preserves the live set (never falls back to the primary, never advances mtimes) and records needs-attention; recovers when the file parses. - src/registry-reconcile.ts: diff the fresh entry list against the live set — add (build+arm+grace), remove (stop only; no process kill, no shard delete — zero fs I/O by construction), rebuild a changed entry, no-op an identical one. honeycomb (primary) is never dropped on a transient omission and is re-pointed on rebuild so the process-global surfaces never dangle. Telemetry poll loop entry set kept in lockstep via the existing PollLoop.reload seam. - src/compose/index.ts: built[] -> mutable Map, reassignable primary, armDaemon seam, reload loop armed in start()/disarmed first in stop(). - src/config.ts: DOCTOR_REGISTRY_RELOAD_INTERVAL_MS (default 2000ms). Zero new runtime dependencies (Node built-ins only); can't-crash preserved. All 28 acceptance criteria (module + 005a + 005b) implemented, tested, and independently verified; security audit clean (0 Critical/High). CI: 832 tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe daemon registry is re-read periodically using mtime gating. Valid changes reconcile live supervisors, telemetry entries, and status rows while preserving the primary supervisor. Configuration, tests, QA records, security audit results, and the package version were updated. ChangesRegistry live-reload and reconciliation
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant RegistryReloadLoop
participant RegistryResolver
participant SupervisorReconciler
participant DoctorState
participant TelemetryPollLoop
RegistryReloadLoop->>RegistryResolver: resolve changed registry
RegistryResolver-->>RegistryReloadLoop: DaemonEntry list
RegistryReloadLoop->>SupervisorReconciler: reconcile entries
SupervisorReconciler->>DoctorState: update supervisors and primary
SupervisorReconciler->>TelemetryPollLoop: update polled entries
Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
|
Approved Release |
|
✅ Approved — applying the minor bump. The release-gate check will go green; it publishes on merge. |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@library/requirements/in-work/prd-005-registry-live-reload-and-supervisor-reconcile/qa/2026-07-12-qa-report.md`:
- Line 129: Update the fenced code block in the QA report to include the text
language identifier, using the requested ```text opening fence while preserving
the block contents and closing fence.
- Line 25: Update the correctness statement describing removal behavior to
replace the “zero fs I/O” claim with the precise guarantee that removal performs
no process kill or shard deletion. Apply the same wording change to the
additional occurrence, while preserving the existing telemetry and SQLite-handle
reconciliation behavior.
In
`@library/requirements/in-work/prd-005-registry-live-reload-and-supervisor-reconcile/qa/2026-07-12-security-audit.md`:
- Line 94: Add the text language identifier to the fenced code block in the
security audit document, changing the opening fence to use ```text while
preserving the block’s contents and closing fence.
- Line 72: Correct the remediation summary in the security audit to classify
Low-3 as PR-introduced unless prior behavior is demonstrated. Update the
disposition to explicitly document how Low-3 is handled, and remove the blanket
statement that all findings are pre-existing and deferred; preserve the separate
scope and disposition for Findings 1–2.
- Around line 108-110: Remove the stale “Ordering note” section claiming that no
QA report exists or that quality-worker-bee has not run. Keep the security audit
content intact and ensure the document no longer contradicts the completed QA
report or validation ordering.
In `@src/registry-reconcile.ts`:
- Around line 187-211: The reconcile update path must build and prepare the
replacement before stopping the existing supervisor. In the rebuild logic, call
buildDaemon and armStartupGrace first; only after that succeeds stop
existing.supervisor, arm the replacement, update current, and call setPrimary
for the primary daemon. Preserve the existing warning behavior while ensuring a
failed build leaves the current active daemon and primary reference unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 17efc5ca-8a69-4dd1-9940-1a3722803790
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (16)
CHANGELOG.mdlibrary/ledger/EXECUTION_LEDGER-prd-005.mdlibrary/requirements/in-work/prd-005-registry-live-reload-and-supervisor-reconcile/prd-005-registry-live-reload-and-supervisor-reconcile-index.mdlibrary/requirements/in-work/prd-005-registry-live-reload-and-supervisor-reconcile/prd-005a-registry-live-reload-and-supervisor-reconcile-reload-trigger.mdlibrary/requirements/in-work/prd-005-registry-live-reload-and-supervisor-reconcile/prd-005b-registry-live-reload-and-supervisor-reconcile-supervisor-reconcile.mdlibrary/requirements/in-work/prd-005-registry-live-reload-and-supervisor-reconcile/qa/.gitkeeplibrary/requirements/in-work/prd-005-registry-live-reload-and-supervisor-reconcile/qa/2026-07-12-qa-report.mdlibrary/requirements/in-work/prd-005-registry-live-reload-and-supervisor-reconcile/qa/2026-07-12-security-audit.mdpackage.jsonsrc/compose/index.tssrc/config.tssrc/registry-reconcile.tssrc/registry-reload.tstests/compose/registry-reload-reconcile.test.tstests/registry-reconcile.test.tstests/registry-reload.test.ts
Code:
- registry-reconcile.ts UPDATE path builds the replacement BEFORE stopping the
old supervisor, so a throwing buildDaemon leaves the current (still-running)
supervisor and the primary reference intact instead of a stopped-but-referenced
daemon (strengthens b-AC-10). +2 regression tests.
- compose armDaemon: run-promises now self-splice from supervisorRuns on settle
(.finally), bounding the join list under sustained add/remove reload churn
(security audit Low-3 — PR-introduced, now fixed).
Docs (QA + security audit):
- narrowed the "zero fs I/O" claim to the precise "no process kill or shard
deletion" guarantee (reconcile still triggers a telemetry handle close).
- added `text` language identifiers to fenced blocks (markdownlint MD040).
- reclassified security Low-3 as PR-introduced-and-remediated.
- corrected the stale "quality-worker-bee has not run" ordering note.
npm run ci green (834 tests, typecheck clean); dependencies still {}.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Thanks @coderabbitai — all six comments addressed in Code
Docs
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/compose/index.ts (1)
745-763: 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick winSelf-splice boot-seeded supervisor runs too
src/compose/index.ts:1028-1050—start()seedssupervisorRunswith baresupervisor.start()promises, so a daemon that boots and is later removed/rebuilt via reload leaves its original settled promise in the array until shutdown. Share the same track-and-splice helper here; the reload test doesn’t cover this boot-seeded removal path.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/compose/index.ts` around lines 745 - 763, The boot path in start() still adds bare supervisor.start() promises to supervisorRuns, leaving settled references after reload removal. Reuse the same tracking and cleanup helper used by armDaemon so boot-seeded runs are caught, tracked, and removed from supervisorRuns in finally, while preserving shutdown snapshot behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/compose/index.ts`:
- Around line 745-763: The boot path in start() still adds bare
supervisor.start() promises to supervisorRuns, leaving settled references after
reload removal. Reuse the same tracking and cleanup helper used by armDaemon so
boot-seeded runs are caught, tracked, and removed from supervisorRuns in
finally, while preserving shutdown snapshot behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 5183451d-8bc8-47e8-b7e1-b9c3bc58892c
📒 Files selected for processing (6)
library/ledger/EXECUTION_LEDGER-prd-005.mdlibrary/requirements/in-work/prd-005-registry-live-reload-and-supervisor-reconcile/qa/2026-07-12-qa-report.mdlibrary/requirements/in-work/prd-005-registry-live-reload-and-supervisor-reconcile/qa/2026-07-12-security-audit.mdsrc/compose/index.tssrc/registry-reconcile.tstests/registry-reconcile.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- library/ledger/EXECUTION_LEDGER-prd-005.md
- library/requirements/in-work/prd-005-registry-live-reload-and-supervisor-reconcile/qa/2026-07-12-qa-report.md
- src/registry-reconcile.ts
|
834/834 green with a clean typecheck and empty (ᵔᴥᵔ) ✅ Action performedComments resolved. Approval is disabled; enable |
Summary
Doctor read its supervised-daemon registry only once, at boot. When onboarding installs doctor first (booting it on a
hive-only registry), honeycomb and nectar register their entries after doctor has already booted, so doctor never supervises them — deadlocking hive's "Bringing the fleet up green" gate (which requireshoneycomb,V1_REQUIRED_PEERS) until the machine reboots. Multiple users hit this on freshirm | iex/curl | shinstalls.This PR makes doctor re-read the registry at runtime and reconcile its live supervisor set — without a reboot and without any cross-product coordination (product installers are unchanged). It is the durable fix for the onboarding deadlock and also fixes the residual case where a daemon registered after doctor boots stays unsupervised until reboot.
Implements PRD-005 (
library/requirements/in-work/prd-005-registry-live-reload-and-supervisor-reconcile/).What changed
src/registry-reload.ts(new) — a mtime-gated periodic re-resolve of both registry locations (fleet~/.apiary/registry.json+ legacy~/.honeycomb/doctor.daemons.json). Unchanged registry ⇒ strict no-op (a singlestat). A torn/malformed read preserves the live set (never falls back to the primary, never advances the observed mtimes), records needs-attention, and recovers when the file parses again.src/registry-reconcile.ts(new) — diff the fresh entry list against the live set: add (build + arm + startup-grace), remove (supervisor.stop()only — no process kill, no shard delete; zero filesystem I/O by construction), rebuild a changed entry (state shard persists), no-op an identical one. honeycomb (primary) is never dropped on a transient omission and is re-pointed on rebuild so the process-global surfaces never dangle. The telemetry poll loop's entry set is kept in lockstep via the existingPollLoop.reloadseam.src/compose/index.ts—built[]→ mutableMap<name, BuiltDaemon>, reassignableprimary, anarmDaemonseam, reload loop armed instart()and disarmed first instop(); status-page / exposed getters read the live map.src/config.ts—DOCTOR_REGISTRY_RELOAD_INTERVAL_MS(default 2000 ms).Zero new runtime dependencies (Node built-ins only); doctor's can't-crash invariant preserved (every new async loop/seam swallows all exceptions).
Verification
npm run ci(typecheck + vitest): 832 tests passing, 69 files, zero pre-existing tests broken.npm run build(tsc && esbuild) succeeds.package.jsondependenciesunchanged:{}.Wave plan (executed via
/the-smoker)typescript-node-worker-beecompose/index.tschange); 24 new tests. CI green.security-worker-beehealthUrl+ traversaltelemetryDbPath) is neutralized on the reload path.quality-worker-beeModel routing per the model-comparison rubric: a subtle concurrency + can't-crash composition-root refactor and its security/quality close-out →
opus(deep reasoning, autonomous multi-file, top code quality).Close-out results
qa/2026-07-12-security-audit.md): 0 Critical / 0 High. Reload resolves through the exact production coercion path boot uses — loopback-onlyhealthUrlSSRF gate,assertWithinBasetelemetryDbPathcontainment — so no bypass; reconcile performs no fs writes and no process signals. Only pre-existing Lows at boot parity (documented as follow-ups:pidPathcontainment, adaemons[]length cap, splicing settledsupervisorRuns).qa/2026-07-12-qa-report.md): all 28 ACs VERIFIED with meaningful tests; strict no-op on unchanged registry, malformed-reload preserves the live set, removal does zero fs I/O, primary invariant holds, telemetry coherence viaPollLoop.reload. Two beyond-AC suggestions (S-1 the reload signature is spec-mandated by a-AC-3; S-2 an integration test for thearmDaemonrunning-branch) — neither an AC gap.Ordering was correct (security before quality).
AC Ledger — 28/28 VERIFIED
Module (index): AC-1 add honeycomb within interval ✅ · AC-2 onboarding order converges, gate greens ✅ · AC-3 removal stops+drops, no process kill ✅ · AC-4 unchanged = strict no-op ✅ · AC-5 malformed preserved + needs-attention, resumes ✅ · AC-6 primary stays bound ✅ · AC-7 telemetry coherence ✅ · AC-8 failures swallowed, never crashes ✅ · AC-9 Node built-ins only ✅
005a — reload trigger: a-AC-1 stat both locations ✅ · a-AC-2 unchanged no-op ✅ · a-AC-3 changed → resolve + onEntries ✅ · a-AC-4 malformed: no reconcile, mtimes not advanced, logged once ✅ · a-AC-5 recovers on next healthy read ✅ · a-AC-6 never falls back to primary ✅ · a-AC-7 interval knob honored ✅ · a-AC-8 stop() idempotent, injected clock ✅ · a-AC-9 exceptions swallowed ✅
005b — reconcile: b-AC-1 add build+arm+grace ✅ · b-AC-2 added in status + telemetry ✅ · b-AC-3 onboarding convergence ✅ · b-AC-4 remove stop+drop ✅ · b-AC-5 no process kill, shards kept ✅ · b-AC-6 primary rebuild re-points surfaces ✅ · b-AC-7 transient omission keeps primary ✅ · b-AC-8 identical untouched ✅ · b-AC-9 changed field rebuilds one only ✅ · b-AC-10 per-supervisor fault isolated ✅
Full ledger:
library/ledger/EXECUTION_LEDGER-prd-005.md.Notes
doctorsubmodule; the superproject submodule-pointer bump follows on merge.🤖 Generated with Claude Code
Summary by CodeRabbit