LearnNewsExamplesServices
Frontmatter
id11994
titleSub-i: WakeSubscriptionService.emitHeartbeatPulse + ADR 0002 §6.1.6 update
stateClosed
labels
enhancementaiarchitecture
assigneesneo-gpt
createdAtMay 25, 2026, 11:07 PM
updatedAtJun 7, 2026, 7:15 PM
githubUrlhttps://github.com/neomjs/neo/issues/11994
authorneo-opus-ada
commentsCount1
parentIssue11993
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMay 26, 2026, 2:16 AM

Sub-i: WakeSubscriptionService.emitHeartbeatPulse + ADR 0002 §6.1.6 update

Closed v13.0.0/archive-v13-0-0-chunk-13 enhancementaiarchitecture
neo-opus-ada
neo-opus-ada commented on May 25, 2026, 11:07 PM

Context

Sub-i of Epic #11993 (Wake substrate evolution: 3-signal model + Shape B heartbeat pulse). Graduated from Discussion #11992 (cycle-3 RESOLVED). See Epic body for full architectural context, 3-cycle convergence history, Signal Ledger, and §6.6 four required sections.

Origin Session ID: 8f1a91ee-3ee4-4e4b-9865-b5810f6be353

The Problem

The current wake substrate uses MailboxService.addMessage for heartbeat pulses (via idleOutNudge.mjs), which persists durable MESSAGE nodes and produces transcript-injected wake-blocks visible in Codex Desktop's mailbox UI. Per @neo-gpt cycle-2 V-B-A: MailboxService.mjs:337,548 + bridge-daemon :522-538 make Shape A unacceptable for production mailbox UX (~96 durable msgs/day/identity at default 15m cadence).

The substrate-correct shape is Shape B: a new ephemeral GraphLog entry that bridge-daemon's resync() picks up via a new trigger semantic (or discriminated subtype on existing eval), without persisting any MESSAGE node or SENT_TO edge that would surface in listMessages() inbox views.

The Fix

  1. Implement WakeSubscriptionService.emitHeartbeatPulse({targetIdentity}) in ai/services/memory-core/WakeSubscriptionService.mjs:
    • Creates an ephemeral GraphLog entry tagged as heartbeat-pulse
    • Does NOT create a MESSAGE node
    • Does NOT create a SENT_TO edge that would surface in listMessages()
    • Idempotent if no active bridge-daemon subscription exists for targetIdentity (log + no-op, not throw)
  2. Extend bridge-daemon's resync() consumption path (ai/daemons/bridge/daemon.mjs) to recognize the heartbeat-pulse entry — either via a new trigger semantic (e.g., HEARTBEAT_PULSE) or a discriminated subtype on existing trigger eval.
  3. Update ADR 0002 §6.1.6 to document the ephemeral-GraphLog-entry semantic. The existing resync contract (SENT_TO_ME / TASK_STATE_CHANGED / PERMISSION_GRANTED triggers) remains unchanged; this is an additive extension so future maintainers don't read the resync contract too narrowly.

Architectural Reality

  • WakeSubscriptionService.subscribe / resync / _evaluateEdgeAgainstSubscription (existing surface — ai/services/memory-core/WakeSubscriptionService.mjs) is the substrate this sub extends.
  • ADR 0002 §6.1.6 (resync-based wake event semantics) is the spec being amended.
  • Bridge-daemon's resync({sinceLogId}) polling and Shape C harnessTarget routing (osascript / codex-app-server / antigravity-cli / claude-cli) remain unchanged.
  • Subscription edges (SUBSCRIBES_TO) and node properties (createdAt / updatedAt) remain unchanged.

Contract Ledger

Target Surface Source of Authority Proposed Behavior Fallback / Edge Case Docs Evidence
WakeSubscriptionService.emitHeartbeatPulse({targetIdentity}) Epic #11993 + Discussion #11992 cycle-3 Creates ephemeral GraphLog entry tagged as heartbeat-pulse; bridge-daemon resync() picks it up; no MESSAGE node, no SENT_TO edge Idempotent on missing subscription (log + no-op) New JSDoc + ADR 0002 §6.1.6 update Unit tests assert no MESSAGE / SENT_TO / inbox row / wake-visible artifact created
Bridge-daemon resync() heartbeat-pulse consumption Epic #11993 New trigger semantic OR discriminated subtype on existing eval; dispatches via existing adapter set None — failure to dispatch falls through to bridge-daemon's existing error-logging JSDoc in bridge-daemon Integration test: emit-pulse → bridge-daemon dispatches to appropriate harness adapter
ADR 0002 §6.1.6 (extension) Epic #11993 + @neo-gpt cycle-3 residual #2 Document ephemeral-GraphLog-entry semantic; existing resync contract preserved (backward-compatible additive extension) None — required ADR update ADR file edit in this sub's PR ADR diff present in this sub's PR; cited in Epic body

Acceptance Criteria

  • AC1 — WakeSubscriptionService.emitHeartbeatPulse({targetIdentity}) implemented in ai/services/memory-core/WakeSubscriptionService.mjs
  • AC2 — Unit test asserts NO MESSAGE node created when emitHeartbeatPulse fires
  • AC3 — Unit test asserts NO SENT_TO edge created when emitHeartbeatPulse fires
  • AC4 — Unit test asserts the ephemeral entry does NOT surface in listMessages() inbox query results
  • AC5 — Unit test asserts the ephemeral entry IS picked up by resync({sinceLogId}) (i.e., it IS a wake-event from the bridge-daemon's POV)
  • AC6 — Bridge-daemon (ai/daemons/bridge/daemon.mjs) recognizes heartbeat-pulse entries and dispatches via existing adapter set (osascript / codex-app-server / etc.)
  • AC7 — ADR 0002 §6.1.6 updated with ephemeral-GraphLog-entry semantic documentation
  • AC8 — Idempotent behavior: emit on identity with no active bridge-daemon subscription logs gracefully and does not throw
  • AC9 — Cross-family review per pull-request §6.1

Out of Scope

  • 3-signal derivation logic (active/idle/ready) — Sub-ii of Epic #11993
  • tmux-inject removal + Shape A heartbeat-mailbox-path cleanup — Sub-iii of Epic #11993
  • New MCP tool surface — explicitly excluded by Epic #11993 (MCP tool cap constraint)
  • Push-based rewrite of WakeSubscriptionService (drop resync polling) — explicitly deferred by Discussion #11992 cycle-3

Avoided Traps

  • ❌ Implementing emitHeartbeatPulse via MailboxService.addMessage with wakeSuppressed: false — this is the rejected Shape A path; it persists MESSAGE nodes
  • ❌ Creating a new graph node type for heartbeat-pulses — adds substrate cost; ephemeral GraphLog entry tagged appropriately is sufficient per Discussion #11992 cycle-3
  • ❌ Updating ADR 0002 §6.1.6 in a sub other than this one — the ephemeral-entry semantic is THIS sub's contribution; future maintainers should find the ADR diff alongside the implementation

Related

  • Parent Epic: #11993
  • Graduation Discussion: #11992
  • Wake substrate primitives consumed: WakeSubscriptionService (subscribe / resync / _evaluateEdgeAgainstSubscription), bridge-daemon (ai/daemons/bridge/daemon.mjs Shape C consumer)

Handoff Retrieval Hints

  • Semantic: query_raw_memories({query: "emitHeartbeatPulse ephemeral GraphLog wake substrate Sub-i"})
  • Empirical: git log --all --oneline -- ai/services/memory-core/WakeSubscriptionService.mjs ai/daemons/bridge/daemon.mjs
tobiu referenced in commit 8d4eced - "feat(ai): add heartbeat pulse wake event (#11994) (#11998) on May 25, 2026, 11:54 PM
tobiu closed this issue on May 25, 2026, 11:54 PM
tobiu referenced in commit f047598 - "feat(orchestrator): WakeDecisionService 3-signal + readiness parser + backoff (#11995) (#11997) on May 25, 2026, 11:55 PM
tobiu referenced in commit 6c1e3c8 - "fix(memory-core): route heartbeat pulses through bridge routes (#11994) (#12004) on May 26, 2026, 2:16 AM
tobiu closed this issue on May 26, 2026, 2:16 AM