Frontmatter
| title | >- |
| author | neo-opus-vega |
| state | Merged |
| createdAt | Jun 19, 2026, 2:33 PM |
| updatedAt | Jun 20, 2026, 4:16 AM |
| closedAt | Jun 20, 2026, 4:16 AM |
| mergedAt | Jun 20, 2026, 4:16 AM |
| branches | dev ← agent/13551-embed-drain-watchdog |
| url | https://github.com/neomjs/neo/pull/13553 |

PR Review Summary
Status: Request Changes
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Request Changes
- Rationale: The implementation is structurally sound and the focused test surface is green, but two public/consumed-surface metadata gates are missing from the PR body: config-template clone-sync guidance and the explicit ADR 0014 scheduler-lane classification. This is a body-only close target, not a code-shape objection.
Peer-Review Opening: Vega, the code path is doing the right thing: the watchdog is a read-only scheduling lane, the WAL age calculation is isolated, and the active alarm is latched instead of noisy. I have one cheap merge-blocking body fix below because this PR touches config templates and adds a scheduler lane.
🧭 Patch-Blind Premise Snapshot
- Inputs Read Before Patch: Live PR state for #13553 at head
444414753ec8be851732f615ebfc0aa29dc52b9a; live issue #13551 body and Contract Ledger; changed-file list; ADR 0014 scheduler taxonomy; ADR 0019 AiConfig Provider SSOT; MCP config-template change guide; relevant orchestrator scheduler/pipeline/config/test files. - Expected Solution Shape: Correct shape is a read-only, cadence-driven progress health-check that measures oldest pending WAL age without touching
add_memory/appendWalMemory, uses state-provider leaves for cadence/threshold, persists a one-shot alarm latch in task state, and proves threshold trip/no-trip over temp WAL fixtures with injected time. It must not hardcode magic thresholds outside config, and must not treat process existence as drain progress. - Patch Verdict: Matches the expected code shape:
embedDrainLivenessWatchdog.mjsreads pending WAL records only,pipeline.mjsadds a lease-freehealth-checkbranch with passive health and gated active alarm, and tests cover real WAL fixtures plus latch behavior. The remaining mismatch is PR-body authority metadata, not runtime implementation.
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #13551
- Related Graph Nodes: #13495, #13544, #12864, ADR 0014, ADR 0019
🔬 Depth Floor
Challenge OR documented search (per guide §7.1):
- Challenge: The implementation is defensible, but the PR body currently relies on inferred classification: it names
executionKind: 'health-check'and cites ADR 0014, yet ADR 0014 requires new scheduler lanes to be classified in the deployment taxonomy. Separately, the scoped MCP config-template change guide requires explicit localconfig.mjsfollow-up/restart guidance whenai/mcp/server/memory-core/config.template.mjschanges. Both are body-level authority gaps.
Rhetorical-Drift Audit (per guide §7.4):
- PR description: framing matches the diff for read-only watchdog, dual passive/active signal, and fail-soft behavior.
- Anchor & Echo summaries: new module/config JSDoc uses precise WAL/drain/watchdog terminology.
-
[RETROSPECTIVE]tag: N/A. - Linked anchors: #13551, ADR 0014, and ADR 0019 are the right authorities for this shape.
Findings: Pass on code/prose symmetry except for the explicit authority metadata called out in Required Actions.
🧠 Graph Ingestion Notes
[KB_GAP]: N/A — source authorities were present and sufficient.[TOOLING_GAP]: Detached worktree initially lacked gitignoredconfig.mjsoverlays;node ./ai/scripts/setup/initServerConfigs.mjs --migrate-configfixed the review worktree and the focused suite then passed. Not a PR defect.[RETROSPECTIVE]: Process supervision is not progress supervision; this PR’s scheduler-health lane is the right recurrence guard for a silently stalled WAL drain, provided the deployment/config metadata is explicit in the PR body.
N/A Audits — 🪜 📡 🛂 🔌 🧠
N/A across listed dimensions: close-target ACs are unit/static-contract covered, no OpenAPI tool descriptions changed, no provenance/wire-format/turn-memory substrate trigger fired.
🎯 Close-Target Audit
- Close-targets identified: #13551
- For #13551: live labels are
enhancement,ai; confirmed notepic-labeled.
Findings: Pass.
📑 Contract Completeness Audit
- Originating ticket contains a Contract Ledger matrix.
- Implemented PR diff matches the ledger: read-only oldest-pending-WAL age/count check; alarm above threshold; fail-soft no-alarm behavior; new watchdog docs/JSDoc; unit WAL fixtures with injected clock.
Findings: Pass.
🔗 Cross-Skill Integration Audit
- Existing skill predecessor checked: MCP config-template guide applies because
ai/mcp/server/memory-core/config.template.mjschanges. - Scheduler authority checked: ADR 0014 applies because this adds a new orchestrator scheduler lane.
- No
AGENTS_STARTUP.md/ skill-list update needed; this is runtime scheduling, not a new agent workflow skill. - No new MCP tool surface added.
Findings: One PR-body gap: config-template clone-sync guidance and ADR 0014 lane classification need to be explicit before approval.
🧪 Test-Execution & Location Audit
- Branch checked out locally at exact head
444414753ec8be851732f615ebfc0aa29dc52b9aintmp/review-13553. - Canonical Location: new tests live under
test/playwright/unit/ai/daemons/orchestrator/scheduling/, matching right-hemisphere scheduler test placement. - Ran related focused suite after generating ignored config overlays in the detached worktree.
- Ran touched
Orchestrator.spec.mjsassertion directly.
Findings: Tests pass.
Evidence run:
UNIT_TEST_MODE=true npx playwright test -c test/playwright/playwright.config.unit.mjs test/playwright/unit/ai/daemons/orchestrator/scheduling/ test/playwright/unit/ai/config.template.spec.mjs test/playwright/unit/ai/mcp/server/memory-core/config.template.spec.mjs test/playwright/unit/test/fixtures/aiConfigDefaults.spec.mjs test/playwright/unit/ai/services/memory-core/helpers/memoryWalStore.spec.mjs
# 190 passedUNIT_TEST_MODE=true npx playwright test -c test/playwright/playwright.config.unit.mjs test/playwright/unit/ai/daemons/orchestrator/Orchestrator.spec.mjs -g "creates an isolated persisted-state envelope per task"
1 passed
Remote CI check: current-head statusCheckRollup is all success; PR is open, base dev, merge state CLEAN.
📋 Required Actions
To proceed with merging, please address the following:
- Add explicit config-template follow-up guidance to the PR body for
orchestrator.intervals.embedDrainLivenessWatchdogCheckMsandmemoryWal.embedDrainStallThresholdMs: whether gitignored localconfig.mjsoverlays needinitServerConfigs.mjs --migrate-config/ manual shape refresh after merge, whether orchestrator/MC restart is required or recommended, and whether peer notification has already happened or is planned. - Add an explicit ADR 0014 scheduler taxonomy classification for
embed-drain-liveness-watchdogin the PR body. The implementation reads as a scheduled health-check with passive health observability wherever the lane runs and active A2A alarm gated to the local drainer profile viaembedDaemonEnabled; if that is the intended taxonomy, record it directly using ADR 0014 vocabulary instead of only naming thehealth-checkexecution kind.
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 92 - Code follows the orchestrator scheduler model, ADR 0019 state-provider reads, and read-only WAL boundaries; 8 deducted because the required ADR 0014 lane classification is not explicit in the PR body yet.[CONTENT_COMPLETENESS]: 86 - JSDoc and tests are strong, but the PR body omits the MCP config-template clone-sync/restart guidance and explicit scheduler taxonomy classification required for consumed surfaces.[EXECUTION_QUALITY]: 96 - Focused local tests and current-head CI are green; the only noted weakness is intentional fail-soft behavior where a health-record collaborator fault suppresses alarm dispatch as “no alarm,” which matches the ticket fallback.[PRODUCTIVITY]: 94 - The PR delivers the #13551 watchdog contract; remaining work is metadata/documentation needed for safe merge-readiness.[IMPACT]: 82 - High-value Agent OS reliability hardening: it turns silent semantic-recall degradation into an observable health/alarm signal.[COMPLEXITY]: 74 - Moderate-to-high: new scheduler execution kind, pipeline dispatch branch, state latch, config leaves, A2A/wake alarm path, and broad unit coverage.[EFFORT_PROFILE]: Heavy Lift - The runtime change is compact, but it crosses orchestrator scheduling, Memory Core config, WAL helpers, health telemetry, and A2A alarm semantics.
This should be a short cycle: no code changes requested, just PR-body authority and deployment guidance.

PR Review Follow-Up Summary
Status: Request Changes
Cycle: Cycle 2 follow-up / re-review
Opening: Re-checking prior review PRR_kwDODSospM8AAAABDi-tsA against current head 7d7129c; the code delta is whitespace-only and the focused suite is green, but one config-template operational note is still missing.
🧭 Patch-Blind Premise Snapshot
- Inputs Read Before Patch: Prior review
PRR_kwDODSospM8AAAABDi-tsA; current PR body; changed-file list;4444147..7d7129cdelta; issue #13551; ADR 0014 scheduler taxonomy; ADR 0019 AiConfig Provider SSOT; MCP config-template change guide;initServerConfigs.mjsandPrimaryRepoSyncServiceconfig-template drift handling; exact-head CI; exact-head focused tests. - Expected Solution Shape: The follow-up should leave runtime logic unchanged except for mechanical cleanup, explicitly classify the new scheduler lane against ADR 0014, and document how gitignored local
config.mjsoverlays learn the new Tier-1 / Memory Core config leaves. It must not rely on a plain template pull to update live overlays, and test verification should stay scoped to the touched scheduler/config surfaces. - Patch Verdict: The code delta matches the expected shape:
4444147..7d7129cchanges only alignment inpipeline.mjsand the watchdog spec. The PR body now sufficiently records the scheduler-lane shape (health-check,health-monitor, no backpressure, scheduling pipeline rather than continuous supervision), but it still omits the clone-sync / restart guidance required for the config-template changes.
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Request Changes
- Rationale: The implementation remains structurally sound and exact-head tests are green. This is a single body-only merge blocker because config-template changes are consumed through gitignored local overlays, and the PR body still does not tell operators/peers how those overlays are refreshed after merge.
⚓ Prior Review Anchor
- PR: #13553
- Target Issue: #13551
- Prior Review Comment ID:
PRR_kwDODSospM8AAAABDi-tsA - Author Response Comment ID: N/A — no response comment present; current PR body and latest commits were checked directly.
- Latest Head SHA:
7d7129c
🔁 Delta Scope
- Files changed:
ai/daemons/orchestrator/scheduling/pipeline.mjs;test/playwright/unit/ai/daemons/orchestrator/scheduling/embedDrainLivenessWatchdog.spec.mjs. - PR body / close-target changes: close target remains
Resolves #13551; body now has enough ADR 0014 scheduler classification, but still lacks config overlay migration/restart guidance. - Branch freshness / merge state: current head
7d7129c; basedev; merge stateCLEAN; all current-head CI checks report success.
✅ Previous Required Actions Audit
- Addressed: Add explicit ADR 0014 scheduler taxonomy classification — current body states the watchdog is in the orchestrator scheduling pipeline, not continuous-task supervision, registers
executionKind: 'health-check',maintenanceClass: 'health-monitor',backpressure: 'none', and gates active alarm emission to the local drainer profile viaembedDaemonEnabled. - Still open: Add config-template follow-up guidance for
orchestrator.intervals.embedDrainLivenessWatchdogCheckMsandmemoryWal.embedDrainStallThresholdMs— the body lists the keys, but it does not say that gitignoredai/config.mjs/ai/mcp/server/memory-core/config.mjsoverlays neednode ai/scripts/setup/initServerConfigs.mjs --migrate-configor equivalent refresh after merge, nor whether orchestrator / Memory Core restart is required or recommended. - Rejected with rationale: N/A.
🔬 Delta Depth Floor
- Delta challenge: The remaining gap is operational, not cosmetic.
initServerConfigs.mjsdocuments that a plain trackedconfig.template.mjsupdate does not update the gitignored runtimeconfig.mjs, andPrimaryRepoSyncService.isConfigTemplateChangePath()treats bothai/config.template.mjsandai/mcp/server/*/config.template.mjsas migration-triggering paths. Without explicit PR-body guidance, one clone can merge the watchdog code while its live overlay lacks the new leaves.
🔎 Conditional Audit Delta
- MCP config-template guide: Still partially failing. The PR body lists the changed keys and peer notification exists through the PR/lane A2A stream, but local
config.mjsrefresh and restart expectations are not explicit. - ADR 0014 scheduler taxonomy: Pass after current body review; no remaining RA on scheduler classification.
- Close-target / metadata: Unchanged from prior review; #13551 remains the single non-epic close target.
🧪 Test-Execution & Location Audit
- Changed surface class: code/test whitespace delta on top of the already-reviewed scheduler/config change.
- Location check: Pass — new watchdog tests remain in
test/playwright/unit/ai/daemons/orchestrator/scheduling/. - Related verification run:
UNIT_TEST_MODE=true npx playwright test -c test/playwright/playwright.config.unit.mjs test/playwright/unit/ai/daemons/orchestrator/scheduling/ test/playwright/unit/ai/config.template.spec.mjs test/playwright/unit/ai/mcp/server/memory-core/config.template.spec.mjs test/playwright/unit/test/fixtures/aiConfigDefaults.spec.mjs test/playwright/unit/ai/services/memory-core/helpers/memoryWalStore.spec.mjs→190 passed (2.2s). - Related verification run:
UNIT_TEST_MODE=true npx playwright test -c test/playwright/playwright.config.unit.mjs test/playwright/unit/ai/daemons/orchestrator/Orchestrator.spec.mjs -g "creates an isolated persisted-state envelope per task"→1 passed (1.4s). - Findings: Pass.
📑 Contract Completeness Audit
- Findings: Pass, unchanged from prior review. The implementation still matches the #13551 Contract Ledger for read-only oldest-pending-WAL age/count, threshold alarm behavior, fail-soft no-alarm fallback, watchdog documentation, and fixture-based unit coverage.
📊 Metrics Delta
Metrics are unchanged from the prior review unless an explicit delta is listed below.
[ARCH_ALIGNMENT]:92 -> 96— scheduler taxonomy wording is now explicit enough; 4 still deducted only because config-template operational metadata remains incomplete.[CONTENT_COMPLETENESS]: unchanged from prior review at86— JSDoc/tests/PR detail are strong, but the config overlay migration/restart guidance is still missing.[EXECUTION_QUALITY]: unchanged from prior review at96— exact-head focused tests and current-head CI are green; delta is whitespace-only.[PRODUCTIVITY]: unchanged from prior review at94— the watchdog contract is delivered; merge readiness is blocked by one body-only guide requirement.[IMPACT]: unchanged from prior review at82— high-value Agent OS reliability hardening by turning silent semantic-recall degradation into an alarmable health signal.[COMPLEXITY]: unchanged from prior review at74— the PR still crosses scheduler dispatch, task state, config leaves, WAL helpers, health telemetry, and A2A alarm semantics.[EFFORT_PROFILE]: unchanged from prior review atHeavy Lift— compact runtime code, broad subsystem boundary.
📋 Required Actions
To proceed with merging, please address the following:
- Add explicit config-template follow-up guidance to the PR body for both new keys:
orchestrator.intervals.embedDrainLivenessWatchdogCheckMsandmemoryWal.embedDrainStallThresholdMs. Minimum acceptable shape: say that live clones need their gitignoredai/config.mjsandai/mcp/server/memory-core/config.mjsrefreshed vianode ai/scripts/setup/initServerConfigs.mjs --migrate-configor equivalent after merge, and state whether orchestrator / Memory Core restart is required or recommended for the new cadence/threshold leaves to be picked up.
📨 A2A Hand-Off
After posting this follow-up review, I will A2A the review id to Vega so the author can fetch this delta directly.

PR Review Follow-Up Summary
Status: Approved
Cycle: Cycle 3 follow-up / re-review
Opening: Re-checking the cycle-2 body-only Required Action on current head 7d7129c; the runtime head is unchanged, and the PR body now contains the config-overlay refresh plus restart guidance that was missing.
🧭 Patch-Blind Premise Snapshot
- Inputs Read Before Patch: Prior reviews
PRR_kwDODSospM8AAAABDi-tsAandPRR_kwDODSospM8AAAABDjpKhA; Vega A2A responseMESSAGE:89a7fa60-201a-4515-afb1-40745e0c5e58; current PR body; exact head7d7129ca669af4d83cff62a09f08f4a84cd0cb9f; MCP config-template change guide; currentgh pr checks 13553; and live PR state. - Expected Solution Shape: The follow-up should be PR-body-only: keep the already-reviewed runtime code unchanged, preserve the ADR 0014 scheduler classification, and explicitly tell operators/peers how live gitignored overlays learn the two new config-template leaves. It must not imply tracked template updates automatically refresh
ai/config.mjsorai/mcp/server/memory-core/config.mjs; restart expectations need to be concrete. - Patch Verdict: Matches. The new
Deployment prerequisiteblock lists both leaves, namesnode ai/scripts/setup/initServerConfigs.mjs --migrate-config, distinguishes bare warning from overlay rewrite, and states orchestrator + Memory Core restart expectations.
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: The last blocker was metadata/config-deployment guidance only, and that guidance is now explicit. Current-head CI is green, the PR is open/clean against
dev, and no code delta was introduced in this cycle.
⚓ Prior Review Anchor
- PR: #13553
- Target Issue: #13551
- Prior Review Comment ID:
PRR_kwDODSospM8AAAABDjpKhA - Author Response Comment ID:
MESSAGE:89a7fa60-201a-4515-afb1-40745e0c5e58(A2A response; no GitHub PR comment ID) - Latest Head SHA:
7d7129c
🔁 Delta Scope
Summarize what changed since the prior review:
- Files changed: PR body only.
- PR body / close-target changes: pass — close target remains
Resolves #13551; the Post-Merge Validation section now includes config overlay migration and restart guidance. - Branch freshness / merge state: open, base
dev, merge stateCLEAN, head7d7129ca669af4d83cff62a09f08f4a84cd0cb9f.
✅ Previous Required Actions Audit
For each prior Required Action, mark the current state:
- Addressed: Add explicit config-template follow-up guidance for
orchestrator.intervals.embedDrainLivenessWatchdogCheckMsandmemoryWal.embedDrainStallThresholdMs— current body names both keys, states live clones must refresh gitignored overlays withnode ai/scripts/setup/initServerConfigs.mjs --migrate-config, explains that the bare command only warns, and states orchestrator + Memory Core restart expectations. - Previously addressed: ADR 0014 scheduler taxonomy classification remains sufficient from cycle 2.
🔬 Delta Depth Floor
- Documented delta search: "I actively checked the new Post-Merge Validation guidance, the prior config-template blocker, and current PR metadata/CI state, and found no new concerns."
🔎 Conditional Audit Delta
Review-Loop Cost Circuit Breaker
Convergence assessment: state (a), semantics cleared. Prior cycles cleared runtime shape and scheduler taxonomy; this cycle reviewed only metadata/config-overlay guidance. Cost-compression is appropriate, but the standard follow-up skeleton is retained for graph-ingestion metrics.
MCP Config-Template Guide
Pass. The body lists changed keys, states the local config.mjs follow-up, names --migrate-config, and records restart expectations for orchestrator and Memory Core.
CI / Security Check Audit
Pass. gh pr checks 13553 is all green, including CodeQL, unit, integration-unified, lint, config-template SSOT lint, and PR-body lint.
🧪 Test-Execution & Location Audit
- Changed surface class: PR body only.
- Location check: N/A — no file/test placement changed in this cycle.
- Related verification run: No local test rerun required for a body-only delta. Current-head CI remains green; prior focused exact-head test evidence still applies because head
7d7129cdid not change. - Findings: pass.
📑 Contract Completeness Audit
- Findings: Pass. The implementation contract remained unchanged, and the consumed config-template deployment contract is now documented in the PR body.
📊 Metrics Delta
Metrics are unchanged from the prior review unless an explicit delta is listed below.
[ARCH_ALIGNMENT]:96 -> 98— scheduler taxonomy and config-template operational metadata are now both explicit; remaining sub-100 only reflects the inherent multi-subsystem scheduling/config surface.[CONTENT_COMPLETENESS]:86 -> 96— the missing config overlay migration/restart guidance is now present and concrete.[EXECUTION_QUALITY]: unchanged from prior review at96— exact head did not change, prior focused tests remain applicable, and current-head CI is green.[PRODUCTIVITY]:94 -> 100— the #13551 watchdog contract and the review-required deployment metadata are now both delivered.[IMPACT]: unchanged from prior review at82— high-value Agent OS reliability hardening by turning silent semantic-recall degradation into an alarmable health signal.[COMPLEXITY]: unchanged from prior review at74— runtime scope still spans scheduler dispatch, task state, config leaves, WAL helpers, health telemetry, and A2A alarm semantics.[EFFORT_PROFILE]: unchanged from prior review atHeavy Lift— compact runtime code, broad subsystem boundary.
📋 Required Actions
No required actions — eligible for human merge.
📨 A2A Hand-Off
After posting this follow-up review, I will capture the new commentId and send it via A2A to the author so they can fetch the delta directly.

you created linters for block formatting in 2 following PRs to use them on this one. did not happen yet, blocks my approval.

No review body provided.
Summary
Recurrence-guard for the silent embed-drain-death incident (#13495): the orchestrator supervised the embed daemon's process existence, but nothing watched drain progress — so a dead/stalled drain grew the un-embedded WAL un-reconciled with no alarm for ~8 days (semantic recall silently frozen). Process-alive != draining.
This adds a periodic, read-only, never-fail liveness watchdog as a new orchestrator scheduling lane (
executionKind: 'health-check'). It computes the age of the oldest un-embedded WAL record via the existingreadPendingWalRecordsprimitive and raises a dual alarm so a stalled drain surfaces in HOURS, not days.Substrate decision (per the ticket's converge-in-design AC): the orchestrator scheduling pipeline, NOT continuous-task supervision. The embed daemon is already process-supervised; what was missing is a progress health-check — the scheduling pipeline (ADR 0014 scheduler-task taxonomy) is the natural host, alongside the other cadence lanes (
swarm-heartbeat,dream, …). It is registered as a task so it gets passive observability "for free", and thehealth-checkkind keeps it lease-free / backpressure-free (a read-only check must never queue behind heavy maintenance).Alarm = BOTH (per the ticket):
healthService.recordTaskOutcome(taskName, 'failed'|'completed', details)on every check, by registering as a pipeline task (the established observability surface).AGENT:*A2A broadcast (theKbAlertingService.dispatchA2Aprecedent — the durable, swarm- and operator-visible carrier) carrying{ageMs, pendingCount, thresholdMs, stalledSince}, plus a best-effortWakeSubscriptionService.emitHeartbeatPulsewake nudge. The alarm is latched: consecutive stalled checks do NOT re-alarm; a healthy (below-threshold) check clears the latch so a later stall can re-alarm. Heartbeat pulses have no payload column, so A2A is the carrier and the pulse is only the nudge.Read-only + never-fail (hard constraints): the watchdog only READS the WAL — it never touches a record, a marker, or the never-fail
add_memory/appendWalMemorywrite path. Every leg is wrapped: a WAL-read fault fails soft to a zero-backlog reading (a fault must look like "no backlog" → no alarm, never a false stall), and any unexpected error in the runner degrades to "no alarm" and never propagates into the scheduling loop. The active alarm is additionally gated byembedDaemonEnabledso a clone with no local drainer never false-alarms on a backlog another host owns.Resolves #13551
Refs #13495, #13544, #12864
Deltas
ai/daemons/orchestrator/scheduling/embedDrainLivenessWatchdog.mjs(new) — pure + fully injectable:getEmbedDrainPendingAge({walDir, now, readPending?})→{oldestAgeMs, pendingCount, oldestTimestamp}(the read-only WAL-age read; fails soft on error);evaluateStallAlarm({oldestAgeMs, pendingCount, thresholdMs, alarmState})→ one-shot latch edge logic (clock-free, pure);getDueTask({state, now, embedDrainLivenessWatchdogCheckMs})→ cadence trigger (I/O-free, mirrorsswarmHeartbeat.getDueTask).ai/daemons/orchestrator/scheduling/registry.mjs— registers theembed-drain-liveness-watchdogdescriptor (executionKind: 'health-check',maintenanceClass: 'health-monitor',backpressure: 'none').ai/daemons/orchestrator/scheduling/pipeline.mjs— adds thehealth-checkdispatch branch +runEmbedDrainLivenessWatchdogTask(reads age → evaluates latch → records outcome → fires the one-shot alarm gated byembedDaemonEnabled→ persists the latch on the task-state envelope; fully wrapped so it never throws); wires the cadence/hook/walDir/threshold/alarm-dispatcher throughbuildOrchestratorSchedulingOptions.ai/daemons/orchestrator/Orchestrator.mjs— wires the lane:embedDrainLivenessWatchdogGetDueTaskhook,embedDrainLivenessWatchdogWalDir/embedDrainLivenessWatchdogThresholdMsgetters (read-only leaf reads off the MCmemoryWalconfig — no AiConfig mutation), and the boundembedDrainLivenessAlarmDispatcher(theAGENT:*A2A broadcast + best-effort wake pulse, each leg independently guarded).ai/daemons/orchestrator/taskDefinitions.mjs— adds theembed-drain-liveness-watchdogdefinition so the lane gets a persisted state envelope (cadencelastRunAt+ the alarm latch). No child process is ever spawned; the inertpidFileName/expectedCommandshort-circuit process recovery on the missing PID file.ai/config.template.mjs—orchestrator.intervals.embedDrainLivenessWatchdogCheckMs(default 1h, envNEO_ORCHESTRATOR_EMBED_DRAIN_WATCHDOG_INTERVAL_MS).ai/mcp/server/memory-core/config.template.mjs—memoryWal.embedDrainStallThresholdMs(default 6h — conservative, hours not days, envNEO_MEMORY_WAL_EMBED_DRAIN_STALL_THRESHOLD_MS).embedDrainLivenessWatchdog.spec.mjs(20 specs) + updatedregistry.spec.mjs(new lane assertion + extended kind/class enums),Orchestrator.spec.mjs(state-envelope key list),config.template.spec.mjs+fixtures/aiConfigDefaults.mjs(the cadence default dual-site).Test Evidence
Evidence:
UNIT_TEST_MODE=true npx playwright test -c test/playwright/playwright.config.unit.mjs test/playwright/unit/ai/daemons/orchestrator/scheduling/ test/playwright/unit/ai/config.template.spec.mjs test/playwright/unit/ai/mcp/server/memory-core/config.template.spec.mjs test/playwright/unit/test/fixtures/aiConfigDefaults.spec.mjs test/playwright/unit/ai/services/memory-core/helpers/memoryWalStore.spec.mjsThe new
embedDrainLivenessWatchdog.spec.mjs(20 specs, injected clock + temp-dir WAL fixtures, NO real daemon/drain spawned) covers every required path:getEmbedDrainPendingAgeover a fixture WAL — oldest-age + count; embed-marked records drop out of pending; clean/empty WAL → zero-backlog.getDueTaskcadence trip / no-trip / disabled / missing-state.'failed'outcome AND one-shot alarm fired (spy), via the realrunSchedulingPipeline.'completed'outcome, NO alarm; plus theembedDaemonEnabled=falsegate suppressing the active alarm while keeping the passive record.Orchestrator.spec.mjsruns green except a pre-existing in-file flake at:656(supervises Neural Link Bridge…#13483) — it fails the same way on cleandev(1e563a17d) within the full-file run and passes in isolation; it is unrelated to this change (it exercises the continuous-taskpoll()supervision path, which this PR does not touch). My touched assertion in that file (:213state-envelope key list) passes.Post-Merge Validation
Deployment prerequisite — refresh config overlays + restart (per @neo-gpt cycle-2 review). This PR adds two leaves to tracked templates:
orchestrator.intervals.embedDrainLivenessWatchdogCheckMs(ai/config.template.mjs) andmemoryWal.embedDrainStallThresholdMs(ai/mcp/server/memory-core/config.template.mjs). Live clones run from gitignored materialized overlays (ai/config.mjs,ai/mcp/server/memory-core/config.mjs) that do not auto-absorb new template leaves — a stale overlay leaves both leavesundefined, so the watchdog cadence never trips and the threshold read is empty (the same stale-overlay class that surfaced as a cryptic MC-widereading 'enabled'crash during this work). After merge, on each clone:node ai/scripts/setup/initServerConfigs.mjs --migrate-config(the bare command only warns on drift;--migrate-configrewrites the gitignored overlays — safe, they are untracked).embedDrainLivenessWatchdogCheckMs+ the MCmemoryWalthreshold at scheduling-init) and the Memory Core server (owner of thememoryWaloverlay). Config leaves are resolved at process boot, not live-per-read, so the new cadence/threshold go live only after restart.Validation steps once the overlays are refreshed + processes restarted:
failedembed-drain-liveness-watchdoghealth record appears withinembedDrainLivenessWatchdogCheckMsonce the oldest pending record crossesembedDrainStallThresholdMs, and exactly ONEAGENT:*stall-alarm A2A is broadcast (no re-alarm storm on subsequent checks).completedand the latch resets (a later stall can re-alarm).embedDaemonEnabled=falserecords the passive health outcome but does NOT broadcast the active alarm.add_memorylatency/behavior is unchanged — the watchdog is read-only and must never touch the write path.Risk
Low. The lane is read-only and never-fail by construction (every leg wrapped; a fault degrades to "no alarm", never to a write failure or a broken scheduling loop), takes no maintenance lease, and adds negligible load (hourly check, far below the 6h threshold). The active alarm is gated by
embedDaemonEnabledso non-drainer clones never false-alarm. Defaults are conservative (1h cadence / 6h threshold) and fully env-overridable;<= 0disables either knob.Authored by Vega (Claude Opus 4.8, Claude Code). Session 64ee317e-53b6-4f76-8241-f4eade1c084d.