Context
Surfaced while investigating #17646. The operator raised heartbeat delivery as a possible cause of A2A wake noise and flagged the guess as uncertain — "absolutely NOT sure here, because we previously disabled it on purpose."
That uncertainty is the defect. No live surface can answer it, and the one that looks like it can gives the same answer either way.
The Problem
healthcheck returns:
"wake": {"gateState": "unknown", "daemonRunning": false, "lastPulseAt": null, …}HealthService.mjs documents daemonRunning as a heuristic over the heartbeat-liveness file's mtime, and the block degrades to those values without throwing when that file cannot be read at all. So a false produced by looking and finding nothing is byte-identical to a false produced by not being able to look — a permission wall, or a path belonging to another container's realm.
An unobservable value rendered as a measured negative is the failure mode, and it is not hypothetical: it cost the #17646 wake-noise investigation its first wrong turn. daemonRunning: false was read as evidence the heartbeat was uninvolved — the right conclusion, reached from an instrument that would have reported identically had the heartbeat been the cause.
What this ticket does NOT claim (corrected 2026-08-23): that the payload can identify why a pulse file is absent. It cannot, and after this fix it still cannot. A configured-off lane and an enabled lane that has never pulsed are the same ENOENT. The repair is to stop a non-answer from wearing an answer's clothes, not to add a configuration verdict.
The Architectural Reality
WakeSubscriptionService.mjs:111 — validHarnessTargets = ['mcp-notifications', 'a2a-webhook', 'bridge-daemon', 'disabled', 'none'].
configBase.mjs:1968 / :1987 — bridgeDaemonEnabled and swarmHeartbeatEnabled both default false, deliberately: "because the Stop hook makes wake + heartbeat redundant."
HealthService.mjs:480-481 — the degraded defaults are literals in the failure path, indistinguishable from measured values.
The block is not wrong about what it measures. It is scoped to one route and reports as if it described the wake plane, and it cannot signal its own blindness.
The Fix
Narrowed 2026-08-23 (@neo-gpt-emmy, RA-1 on PR #17652). The original four bullets prescribed a configuration surface this block cannot deliver — it reads one liveness file and consults no config. They are struck below rather than deleted, so a later reader sees what was proposed and why it was dropped.
Make the liveness figure report what the read did, so a non-answer cannot wear an answer's clothes:
daemonRunning becomes tri-state. true fresh, false when the file was looked for and is absent, null when the read could not happen at all — a permission wall, or a path in another container's realm. An unobservable value must never render as a measured negative.
- A companion
livenessReason names which read occurred: observed | no-pulse-file | unreadable. no-pulse-file is defined as exactly ENOENT and carries no claim about configuration.
- Durable prose states the limit plainly: a configured-off lane and an enabled-but-silent lane are the same absent file and stay indistinguishable here.
- Distinguish disabled from unreadable. gateState should carry a third value…
- Report the configured route (harnessTarget)…
- Report liveness for the route in use…
- Include the effective swarmHeartbeatEnabled / bridgeDaemonEnabled values…
Why those four are dropped, not deferred: all of them require reading the orchestrator's config tree or the subscription's route, neither of which the memory-core config slice carries. Reaching across that boundary to improve a diagnostic label costs more than it buys, and the urgency behind them died with #17648 — the wake noise had nothing to do with the heartbeat. The one that survived, daemonRunning: null, is the only one that was ever a correctness problem rather than a labelling one.
Contract Ledger Matrix
Narrowed with The Fix above. The prior three rows required route identity and effective enable flags; they are struck and replaced by the two surfaces this contract actually changes.
| # |
Target surface |
Source of authority |
Before |
After |
Fallback |
Evidence |
| 1 |
healthcheck.features.wake.daemonRunning |
HealthService.mjs |
false for both "absent" and "unreadable" |
tri-state: null when the read could not happen |
false stays the answer for a genuine absent-file read |
the catch swallowed every fs.stat failure into one default |
| 2 |
healthcheck.features.wake.livenessReason |
same |
absent |
observed | no-pulse-file | unreadable; no-pulse-file is ENOENT |
none — always present |
names the read, never the configuration |
| gateState — distinct states for configured-off vs unobservable | · | daemonRunning scoped to the named route | · | wake carries harnessTarget + effective enable flags | — dropped per The Fix.
Acceptance Criteria
Narrowed 2026-08-23 after @neo-gpt-emmy's RA-1 on PR #17652. The original four ACs described a configuration surface this block cannot deliver — see Out of Scope. What remains is the observation contract: this function reads one file, and its payload must report what that read did.
Out of Scope
Distinguishing a configured-off heartbeat from an enabled-but-dead one. This was the ticket's original headline and it is dropped, not deferred. buildWakeFeaturesBlock reads one liveness file and no configuration; swarmHeartbeatEnabled lives in the orchestrator's config tree, which the memory-core slice does not carry. Reaching across that boundary to improve a diagnostic label buys less than the coupling costs. The two states remain the same ENOENT and the payload now says so plainly instead of implying otherwise.
Dropped rather than parked because the pressure behind it is gone: the wake noise that made this urgent was #17648's schema-default defect, fixed and deployed, and the heartbeat is confirmed disabled by config default. If someone later needs the distinction, they will need a config read and should file it against that need, not inherit a stale pointer from here.
Reporting the delivery route (harnessTarget) alongside the liveness figure. Same boundary, and #17646 already owns the delivery-route question.
The wake-noise defect itself — #17646 / #17648. This ticket is about the instrument, not the noise.
Changing any default. swarmHeartbeatEnabled: false and bridgeDaemonEnabled: false are deliberate and correct.
Avoided Traps
- "It reports
false and the thing is false, so it is correct." It is accidentally correct today, for a reason unrelated to what it measures, and would be identically false if the heartbeat were enabled and broken. A value that cannot be wrong cannot be evidence.
- Adding a new health field and leaving the old one.
daemonRunning is the field readers already consult; a truer sibling beside a misleading incumbent means the misleading one keeps being read.
- Fixing this by documenting it. The conflation is already documented at
HealthService.mjs:437 — I read that docblock and still had to go to the config and the container logs to answer the question. Prose that accurately describes an ambiguous value does not disambiguate it.
Decision Record impact
none — a diagnostics-surface correction; no wake semantics change.
Structure-map gate: N/A — lands in the existing HealthService seam, no file introduced or relocated.
Live latest-open sweep: checked latest 20 open issues at 2026-08-23T19:28Z; no equivalent found. A2A in-flight claim sweep: latest 40 all-state messages; no overlapping claim.
Related
#17646 (the wake-noise defect this was raised against; heartbeat exonerated there) · #14486 (established that the heartbeat/wake disable toggles already existed and the real gap was discoverability — this is that same gap, one surface over)
Origin Session ID: eb671e6e-ca17-4a53-8069-64fd5885ce84
Retrieval Hint: query_raw_memories("healthcheck wake gateState unknown daemonRunning liveness file disabled vs unreadable harnessTarget")
Context
Surfaced while investigating #17646. The operator raised heartbeat delivery as a possible cause of A2A wake noise and flagged the guess as uncertain — "absolutely NOT sure here, because we previously disabled it on purpose."
That uncertainty is the defect. No live surface can answer it, and the one that looks like it can gives the same answer either way.
The Problem
healthcheckreturns:"wake": {"gateState": "unknown", "daemonRunning": false, "lastPulseAt": null, …}HealthService.mjsdocumentsdaemonRunningas a heuristic over the heartbeat-liveness file's mtime, and the block degrades to those values without throwing when that file cannot be read at all. So afalseproduced by looking and finding nothing is byte-identical to afalseproduced by not being able to look — a permission wall, or a path belonging to another container's realm.An unobservable value rendered as a measured negative is the failure mode, and it is not hypothetical: it cost the #17646 wake-noise investigation its first wrong turn.
daemonRunning: falsewas read as evidence the heartbeat was uninvolved — the right conclusion, reached from an instrument that would have reported identically had the heartbeat been the cause.What this ticket does NOT claim (corrected 2026-08-23): that the payload can identify why a pulse file is absent. It cannot, and after this fix it still cannot. A configured-off lane and an enabled lane that has never pulsed are the same
ENOENT. The repair is to stop a non-answer from wearing an answer's clothes, not to add a configuration verdict.The Architectural Reality
WakeSubscriptionService.mjs:111—validHarnessTargets = ['mcp-notifications', 'a2a-webhook', 'bridge-daemon', 'disabled', 'none'].configBase.mjs:1968/:1987—bridgeDaemonEnabledandswarmHeartbeatEnabledboth defaultfalse, deliberately: "because the Stop hook makes wake + heartbeat redundant."HealthService.mjs:480-481— the degraded defaults are literals in the failure path, indistinguishable from measured values.The block is not wrong about what it measures. It is scoped to one route and reports as if it described the wake plane, and it cannot signal its own blindness.
The Fix
Narrowed 2026-08-23 (@neo-gpt-emmy, RA-1 on PR #17652). The original four bullets prescribed a configuration surface this block cannot deliver — it reads one liveness file and consults no config. They are struck below rather than deleted, so a later reader sees what was proposed and why it was dropped.
Make the liveness figure report what the read did, so a non-answer cannot wear an answer's clothes:
daemonRunningbecomes tri-state.truefresh,falsewhen the file was looked for and is absent,nullwhen the read could not happen at all — a permission wall, or a path in another container's realm. An unobservable value must never render as a measured negative.livenessReasonnames which read occurred:observed|no-pulse-file|unreadable.no-pulse-fileis defined as exactlyENOENTand carries no claim about configuration.- Distinguish disabled from unreadable.gateStateshould carry a third value…- Report the configured route (harnessTarget)…- Report liveness for the route in use…- Include the effectiveswarmHeartbeatEnabled/bridgeDaemonEnabledvalues…Why those four are dropped, not deferred: all of them require reading the orchestrator's config tree or the subscription's route, neither of which the memory-core config slice carries. Reaching across that boundary to improve a diagnostic label costs more than it buys, and the urgency behind them died with #17648 — the wake noise had nothing to do with the heartbeat. The one that survived,
daemonRunning: null, is the only one that was ever a correctness problem rather than a labelling one.Contract Ledger Matrix
Narrowed with The Fix above. The prior three rows required route identity and effective enable flags; they are struck and replaced by the two surfaces this contract actually changes.
healthcheck.features.wake.daemonRunningHealthService.mjsfalsefor both "absent" and "unreadable"nullwhen the read could not happenfalsestays the answer for a genuine absent-file readcatchswallowed everyfs.statfailure into one defaulthealthcheck.features.wake.livenessReasonobserved|no-pulse-file|unreadable;no-pulse-fileisENOENT|·gateState— distinct states for configured-off vs unobservable ||·daemonRunningscoped to the named route ||— dropped per The Fix.wakecarriesharnessTarget+ effective enable flags |Acceptance Criteria
Narrowed 2026-08-23 after @neo-gpt-emmy's RA-1 on PR #17652. The original four ACs described a configuration surface this block cannot deliver — see Out of Scope. What remains is the observation contract: this function reads one file, and its payload must report what that read did.
false. Verified by making it unreadable, not by reading the code path — an unobservable-vs-false distinction asserted only in prose is the defect restated.observed|no-pulse-file|unreadable— andno-pulse-fileis defined as exactlyENOENT, never as a configuration verdict.daemonRunningreadstrueandlastPulseAtis populated. Without this, every arm above passes on a payload that is permanently and uninformatively "off".Out of Scope
Distinguishing a configured-off heartbeat from an enabled-but-dead one. This was the ticket's original headline and it is dropped, not deferred.
buildWakeFeaturesBlockreads one liveness file and no configuration;swarmHeartbeatEnabledlives in the orchestrator's config tree, which the memory-core slice does not carry. Reaching across that boundary to improve a diagnostic label buys less than the coupling costs. The two states remain the sameENOENTand the payload now says so plainly instead of implying otherwise.Dropped rather than parked because the pressure behind it is gone: the wake noise that made this urgent was #17648's schema-default defect, fixed and deployed, and the heartbeat is confirmed disabled by config default. If someone later needs the distinction, they will need a config read and should file it against that need, not inherit a stale pointer from here.
Reporting the delivery route (
harnessTarget) alongside the liveness figure. Same boundary, and #17646 already owns the delivery-route question.The wake-noise defect itself — #17646 / #17648. This ticket is about the instrument, not the noise.
Changing any default.
swarmHeartbeatEnabled: falseandbridgeDaemonEnabled: falseare deliberate and correct.Avoided Traps
falseand the thing isfalse, so it is correct." It is accidentally correct today, for a reason unrelated to what it measures, and would be identicallyfalseif the heartbeat were enabled and broken. A value that cannot be wrong cannot be evidence.daemonRunningis the field readers already consult; a truer sibling beside a misleading incumbent means the misleading one keeps being read.HealthService.mjs:437— I read that docblock and still had to go to the config and the container logs to answer the question. Prose that accurately describes an ambiguous value does not disambiguate it.Decision Record impact
none— a diagnostics-surface correction; no wake semantics change.Structure-map gate: N/A — lands in the existing
HealthServiceseam, no file introduced or relocated.Live latest-open sweep: checked latest 20 open issues at 2026-08-23T19:28Z; no equivalent found. A2A in-flight claim sweep: latest 40 all-state messages; no overlapping claim.
Related
#17646(the wake-noise defect this was raised against; heartbeat exonerated there) ·#14486(established that the heartbeat/wake disable toggles already existed and the real gap was discoverability — this is that same gap, one surface over)Origin Session ID: eb671e6e-ca17-4a53-8069-64fd5885ce84
Retrieval Hint:
query_raw_memories("healthcheck wake gateState unknown daemonRunning liveness file disabled vs unreadable harnessTarget")