Context
Operator, 2026-08-05: "we deactivated heartbeats on purpose for now. then we switched to the plist wake local daemon post dockerization. and i have the strong impression that this re-enabled heartbeats." Prompted by @neo-gpt — at 0% weekly budget and dark since the previous morning — taking a [WAKE][priority:high] for a broadcast.
First, the direct answer: that was broadcast delivery, not a heartbeat. The payload shape (N new messages (latest: …)) is the message-digest envelope, not an idle/swarm pulse, and the heartbeat lane is off. But the instinct behind the question is right, and the reason is worse than a flipped switch.
1. bridgeDaemonEnabled is inert for the live delivery path
ai/config.mjs:39 sets it true — a deliberate, well-argued re-enable (2026-07-18, operator + Clio/Mnemosyne convergence: "a Stop hook can refuse a stop but cannot CREATE a turn — under wakes-off every stop is permanent"). Not disputed here.
The problem is that setting it back to false would no longer stop wakes. The live path is:
add_message → MC CoalescingEngineService (builds the `wake/digest` envelope)
→ launchd receiver on :3199 → local harness wake
ai/daemons/wake/receiver.mjs, armSeatWakeRoute.mjs, localWakeAdapters.mjs — no reference to either switch.
ai/services/memory-core/** and ai/mcp/server/memory-core/** — no reference to either switch.
bridgeDaemonEnabled gates only the orchestrator-supervised bridgeDaemon task (taskAuthority.mjs:117), which is the pre-dockerization lane.
So the leaf that reads as "desktop wake delivery on/off" governs a lane that is no longer the one delivering. A config surface that looks like a kill-switch and is not one is worse than no switch, because it will be trusted in an incident.
2. Two of the three cited anti-flood layers do not run
The re-enable's safety argument is explicit: "the anti-flood layers stay live (20-min heartbeat cadence, 600s swarm-wake cooldown, 300s digest coalescing)." Current status:
| layer |
status |
why |
| 20-min heartbeat cadence |
inoperative |
swarmHeartbeatEnabled: false on the very next line of the same file |
| 600s swarm-wake cooldown |
inoperative |
swarmWakeCooldown is reachable only from SwarmHeartbeatService.pulse() — grep shows no other caller |
| 300s digest coalescing |
live |
owned by MC's wakeCoalescePolicy / CoalescingEngineService, independent of both switches |
The decision was sound; two-thirds of the scaffolding it rested on was disabled by its own sibling line.
3. Nothing gates a wake on whether the seat can act
The only hard gate on the send path is participationStatus (operator_benched / temporarily_unreachable). @neo-gpt is active, so a broadcast wakes him regardless of being dark and out of budget.
who_is_online's own JSDoc names wake-targeting as a consumer, then states it is "deliberately advisory" — and nothing on the send path reads it. The readiness gate that would apply (WakeDecisionService.decideWake = active AND idle AND ready) exists only inside SwarmHeartbeatService.pulse(), so it never runs for message-driven wakes.
A wake costs a full harness turn — wakeCoalescePolicy's own header prices it at "tens of thousands of tokens to deliver one message header". Spending that on a seat that cannot act is pure loss, and at 0% budget it may consume the seat's next real turn.
Acceptance Criteria
Out of Scope
- Re-litigating the 2026-07-18 decision to re-enable wakes. Its argument stands; this is about the machinery underneath it.
- The heartbeat lane's own merits — it stays off unless someone argues otherwise.
Decision Record impact
none expected. If the fix removes bridgeDaemonEnabled rather than rewiring it, that is a config-surface change worth an ADR note.
Related
#16510 family (deployment/topology divergence) · #16513 (a sibling case of a host-side path bypassing the served deployment).
Verification note: read from /Users/Shared/github/neomjs/neo, which is not the checkout the containers are built from. Per the standing operator sequence a merge does not update the running Agent OS without a container rebuild, so the running MC may differ from this source.
Context
Operator, 2026-08-05: "we deactivated heartbeats on purpose for now. then we switched to the plist wake local daemon post dockerization. and i have the strong impression that this re-enabled heartbeats." Prompted by
@neo-gpt— at 0% weekly budget anddarksince the previous morning — taking a[WAKE][priority:high]for a broadcast.First, the direct answer: that was broadcast delivery, not a heartbeat. The payload shape (
N new messages (latest: …)) is the message-digest envelope, not an idle/swarm pulse, and the heartbeat lane is off. But the instinct behind the question is right, and the reason is worse than a flipped switch.1.
bridgeDaemonEnabledis inert for the live delivery pathai/config.mjs:39sets ittrue— a deliberate, well-argued re-enable (2026-07-18, operator + Clio/Mnemosyne convergence: "a Stop hook can refuse a stop but cannot CREATE a turn — under wakes-off every stop is permanent"). Not disputed here.The problem is that setting it back to
falsewould no longer stop wakes. The live path is:add_message → MC CoalescingEngineService (builds the `wake/digest` envelope) → launchd receiver on :3199 → local harness wakeai/daemons/wake/receiver.mjs,armSeatWakeRoute.mjs,localWakeAdapters.mjs— no reference to either switch.ai/services/memory-core/**andai/mcp/server/memory-core/**— no reference to either switch.bridgeDaemonEnabledgates only the orchestrator-supervisedbridgeDaemontask (taskAuthority.mjs:117), which is the pre-dockerization lane.So the leaf that reads as "desktop wake delivery on/off" governs a lane that is no longer the one delivering. A config surface that looks like a kill-switch and is not one is worse than no switch, because it will be trusted in an incident.
2. Two of the three cited anti-flood layers do not run
The re-enable's safety argument is explicit: "the anti-flood layers stay live (20-min heartbeat cadence, 600s swarm-wake cooldown, 300s digest coalescing)." Current status:
swarmHeartbeatEnabled: falseon the very next line of the same fileswarmWakeCooldownis reachable only fromSwarmHeartbeatService.pulse()— grep shows no other callerwakeCoalescePolicy/CoalescingEngineService, independent of both switchesThe decision was sound; two-thirds of the scaffolding it rested on was disabled by its own sibling line.
3. Nothing gates a wake on whether the seat can act
The only hard gate on the send path is
participationStatus(operator_benched/temporarily_unreachable).@neo-gptisactive, so a broadcast wakes him regardless of beingdarkand out of budget.who_is_online's own JSDoc names wake-targeting as a consumer, then states it is "deliberately advisory" — and nothing on the send path reads it. The readiness gate that would apply (WakeDecisionService.decideWake= active AND idle AND ready) exists only insideSwarmHeartbeatService.pulse(), so it never runs for message-driven wakes.A wake costs a full harness turn —
wakeCoalescePolicy's own header prices it at "tens of thousands of tokens to deliver one message header". Spending that on a seat that cannot act is pure loss, and at 0% budget it may consume the seat's next real turn.Acceptance Criteria
ai/config.mjsmatches reality: either the two dead layers are restored, or the comment stops citing them as live.darkor benched-equivalent; the readiness signal is consulted on the send path rather than only inside the disabled pulse lane.Out of Scope
Decision Record impact
noneexpected. If the fix removesbridgeDaemonEnabledrather than rewiring it, that is a config-surface change worth an ADR note.Related
#16510family (deployment/topology divergence) ·#16513(a sibling case of a host-side path bypassing the served deployment).Verification note: read from
/Users/Shared/github/neomjs/neo, which is not the checkout the containers are built from. Per the standing operator sequence a merge does not update the running Agent OS without a container rebuild, so the running MC may differ from this source.