Context
PR #13838 (Resolves #13818) shipped the REM consolidation-liveness watchdog with passive observability: it records failed/completed health outcomes every check and emits a one-shot WARN log on stall-onset (latched), making a stalled REM cycle observable in the health projection / handoff. What it does not yet do is the active escalation — a swarm/operator-visible A2A alarm — which the sibling embedDrainLivenessWatchdog already has via embedDrainLivenessAlarmDispatcher. This follow-up adds the symmetric active leg.
The Problem
The passive health-record + WARN log surface a consolidation stall to anyone reading the handoff/health projection — but nothing actively notifies the swarm/operator. A silent dream-stall ("green-but-rotting": forecast fresh, graph not draining) can therefore sit unnoticed until someone happens to read the projection. The embed-drain watchdog avoids this by firing a one-shot A2A (MailboxService.addMessage to the AGENT:* sentinel) plus a wake-pulse nudge on stall-onset. The consolidation watchdog should have the symmetric active escalation.
The Architectural Reality
runRemConsolidationLivenessWatchdogTask (ai/daemons/orchestrator/scheduling/pipeline.mjs) already computes shouldAlarm (the one-shot latch via evaluateConsolidationStallAlarm) and currently routes it to a WARN log — the active dispatch plugs in exactly there.
- Mirror
embedDrainLivenessAlarmDispatcher (ai/daemons/orchestrator/Orchestrator.mjs) — a bound arrow passed as services.remConsolidationLivenessAlarmDispatcher, building the A2A payload + best-effort WakeSubscriptionService pulse; each leg independently guarded so one failure never suppresses the other.
- Gate the active alarm on a deployment-owns-the-dream flag (the embed-drain active alarm is gated on
embedDrainLivenessWatchdogAlarmEnabled = embedDaemonEnabled), so a deployment that does not run the local dream never false-alarms on a stall another host is responsible for draining.
The Fix
- Add
remConsolidationLivenessAlarmDispatcher to Orchestrator.mjs, mirroring the embed-drain dispatcher: one-shot A2A to AGENT:* (subject = consolidation stall + staleness age) + a WakeSubscriptionService.emitHeartbeatPulse nudge.
- Wire it into
services (pipeline.mjs) and invoke it from runRemConsolidationLivenessWatchdogTask on shouldAlarm, gated on the alarm-enabled flag; the passive health-record path stays unchanged.
- Unit coverage: dispatcher fires once on stall-onset (latch prevents re-alarm), gated by the enable flag, never throws.
Decision Record impact
Aligned with the DreamService organism decision record (consolidation-liveness invariant). No decision-record change.
Acceptance Criteria
Out of Scope
- The passive observability (health-record + WARN log + persisted latch) — shipped in PR #13838.
- Changing the stall threshold or check cadence.
Avoided Traps
- Do not fire the active alarm unconditionally — gate on deployment-owns-the-dream, or a host that does not drain locally will false-alarm on another host's backlog (the exact
embedDrainLivenessWatchdogAlarmEnabled rationale).
Related
Builds on #13818 (watchdog) / PR #13838 (passive observability). Sibling pattern: embedDrainLivenessAlarmDispatcher. Parent epic: #13624.
Release classification: boardless — follow-up enhancement (active escalation), non-blocking; the passive observability is the shipped acceptance criterion.
Origin Session ID: e193849e-afc9-4190-b36c-41095d97147a
Retrieval Hint: "remConsolidationLivenessAlarmDispatcher active alarm consolidation watchdog escalation"
Context
PR #13838 (Resolves #13818) shipped the REM consolidation-liveness watchdog with passive observability: it records
failed/completedhealth outcomes every check and emits a one-shotWARNlog on stall-onset (latched), making a stalled REM cycle observable in the health projection / handoff. What it does not yet do is the active escalation — a swarm/operator-visible A2A alarm — which the siblingembedDrainLivenessWatchdogalready has viaembedDrainLivenessAlarmDispatcher. This follow-up adds the symmetric active leg.The Problem
The passive health-record +
WARNlog surface a consolidation stall to anyone reading the handoff/health projection — but nothing actively notifies the swarm/operator. A silent dream-stall ("green-but-rotting": forecast fresh, graph not draining) can therefore sit unnoticed until someone happens to read the projection. The embed-drain watchdog avoids this by firing a one-shot A2A (MailboxService.addMessageto theAGENT:*sentinel) plus a wake-pulse nudge on stall-onset. The consolidation watchdog should have the symmetric active escalation.The Architectural Reality
runRemConsolidationLivenessWatchdogTask(ai/daemons/orchestrator/scheduling/pipeline.mjs) already computesshouldAlarm(the one-shot latch viaevaluateConsolidationStallAlarm) and currently routes it to aWARNlog — the active dispatch plugs in exactly there.embedDrainLivenessAlarmDispatcher(ai/daemons/orchestrator/Orchestrator.mjs) — a bound arrow passed asservices.remConsolidationLivenessAlarmDispatcher, building the A2A payload + best-effortWakeSubscriptionServicepulse; each leg independently guarded so one failure never suppresses the other.embedDrainLivenessWatchdogAlarmEnabled=embedDaemonEnabled), so a deployment that does not run the local dream never false-alarms on a stall another host is responsible for draining.The Fix
remConsolidationLivenessAlarmDispatchertoOrchestrator.mjs, mirroring the embed-drain dispatcher: one-shot A2A toAGENT:*(subject = consolidation stall + staleness age) + aWakeSubscriptionService.emitHeartbeatPulsenudge.services(pipeline.mjs) and invoke it fromrunRemConsolidationLivenessWatchdogTaskonshouldAlarm, gated on the alarm-enabled flag; the passive health-record path stays unchanged.Decision Record impact
Aligned with the DreamService organism decision record (consolidation-liveness invariant). No decision-record change.
Acceptance Criteria
remConsolidationLivenessAlarmDispatchermirrors the embed-drain dispatcher (one-shot A2A + wake pulse); never throws (each leg guarded).shouldAlarm, gated on a deployment-owns-the-dream enable flag; the passive health-record path is unchanged.Out of Scope
Avoided Traps
embedDrainLivenessWatchdogAlarmEnabledrationale).Related
Builds on #13818 (watchdog) / PR #13838 (passive observability). Sibling pattern:
embedDrainLivenessAlarmDispatcher. Parent epic: #13624.Release classification: boardless — follow-up enhancement (active escalation), non-blocking; the passive observability is the shipped acceptance criterion.
Origin Session ID: e193849e-afc9-4190-b36c-41095d97147a Retrieval Hint: "remConsolidationLivenessAlarmDispatcher active alarm consolidation watchdog escalation"