LearnNewsExamplesServices
Frontmatter
id11995
titleSub-ii: 3-signal wake derivation + orchestrator-local backoff + readiness-sentinel parser
stateClosed
labels
enhancementaiarchitecture
assigneesneo-opus-ada
createdAtMay 25, 2026, 11:08 PM
updatedAtJun 7, 2026, 7:15 PM
githubUrlhttps://github.com/neomjs/neo/issues/11995
authorneo-opus-ada
commentsCount0
parentIssue11993
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMay 25, 2026, 11:55 PM

Sub-ii: 3-signal wake derivation + orchestrator-local backoff + readiness-sentinel parser

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

Context

Sub-ii 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 and Signal Ledger.

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

The Problem

The current wake substrate has no model of harness readiness — it uses partial signals (subscription presence, push-capability, tmux session existence) instead of activity-derived state. Three failure modes follow:

  1. Wakes wake long-departed harnesses (no active signal) — wastes bridge-daemon dispatch budget
  2. Wakes interrupt focused work (no idle signal) — operator's "stop bothering this agent" intervention has no mechanism
  3. Wakes restart-loop on structurally-blocked harnesses (no ready signal) — rate-limited (Anthropic weekly limit at 5d), operator-benched (vendor-side ecosystem issues), or crash-loop-broken harnesses get banged on every 15m, producing rejection responses or zombie processes

Per Discussion #11992 cycle-3 architectural conclusion: the wake-decision must be a pure function of (identity, current-time, A2A-graph-state, readiness-sentinel-state, orchestrator-state-backoff) — Wake = active AND idle AND ready.

The Fix

Implement the 3-signal wake-decision substrate:

  1. active signal — query A2A graph for any MESSAGE node via SENT_TO / DELIVERED_TO edges adjacent to the identity in last 3h. Active subscriptions only; archived messages excluded.
  2. idle signal — same query, narrower 15m window. Received activity counts (operator's prompt at t=0 protects the harness from wakes until t=15m+).
  3. ready signal — composition of two sub-signals:
    • (a) No [wake-readiness] self/operator A2A sentinel with future expiresAt (structured task envelope metadata, not subject parsing)
    • (b) No orchestrator-local backoff window active for this identity
  4. Orchestrator-local backoff state — persisted in orchestrator-state.json (per-task block) OR sibling .neo-ai-data/wake-daemon/backoff.json (decide between these per V-B-A of restart-resilience + corruptibility profile, citing TaskStateService.mjs:96-124, Orchestrator.stateFile at Orchestrator.mjs:439-453, and trioWakeCooldown.mjs:20-92 as precedent).
  5. [wake-readiness] sentinel parser — reads structured A2A task envelope field: {type: 'wake-readiness', ready: bool, reason: string, expiresAt: ISO timestamp}. Subject + taggedConcepts are human-visible only (not parser authority). Operator/self sentinels compose most-restrictive-wins on expiresAt. Subject-only spoofing rejected.

The 3-signal-decision function is consumed by Sub-iii (SwarmHeartbeatService.pulse() Step 7 replacement), so Sub-ii ships the pure logic + state primitives.

Architectural Reality

  • A2A graph query primitives exist (MailboxService.listMessages, GraphService node/edge access)
  • TaskStateService (ai/daemons/orchestrator/services/TaskStateService.mjs:96-124) provides the persisted-state read/write pattern
  • Orchestrator.stateFile (ai/daemons/orchestrator/Orchestrator.mjs:439-453) is the canonical orchestrator-local state file
  • trioWakeCooldown.mjs (ai/scripts/lifecycle/trioWakeCooldown.mjs:20-92) demonstrates the wake-cooldown-in-.neo-ai-data/wake-daemon/ precedent
  • add_message's task parameter already exists per A2A Task Schema; structured envelope field is the parser surface

Contract Ledger

Target Surface Source of Authority Proposed Behavior Fallback / Edge Case Docs Evidence
3-signal derivation pure function Epic #11993 + Discussion #11992 cycle-3 (identity, current-time, A2A-graph-state, readiness-sentinel-state, orchestrator-state-backoff) → wake-decision Missing or stale state inputs default to safe-no-wake (skip pulse target) JSDoc on derivation function Unit tests cover 4 known pathological cases (drop-active wakes departed; drop-idle interrupts work; drop-ready loops on blocked; operator manual-prompt grace)
Orchestrator-local backoff state Epic #11993 + Discussion #11992 OQ3 (cycle-2) Persisted in orchestrator-state.json per-task block OR .neo-ai-data/wake-daemon/backoff.json (decide per V-B-A) Corrupt-state file → ignored gracefully with logged warning, not crash-loop JSDoc on backoff write/read paths Unit tests: restart-persistence, corrupt-state fallback, TTL expiry, per-identity isolation
[wake-readiness] sentinel parser Epic #11993 + Discussion #11992 OQ4+OQ5 (cycle-2) Parses structured task envelope ({type: 'wake-readiness', ready, reason, expiresAt}); subject-only spoofing rejected; operator/self most-restrictive-wins on expiresAt If structured envelope missing or malformed → no sentinel applied (default ready: true) JSDoc on parser + structured envelope schema Unit tests: structured envelope parsing, subject-only spoofing rejected, operator-override most-restrictive-wins composition

Acceptance Criteria

  • AC1 — 3-signal derivation pure function implemented; signature: (identity, current-time, A2A-graph-state, readiness-sentinel-state, orchestrator-state-backoff) → wake-decision
  • AC2 — Orchestrator-local backoff state location chosen between orchestrator-state.json per-task block vs .neo-ai-data/wake-daemon/backoff.json per V-B-A of restart-resilience + corruptibility; decision documented in PR body
  • AC3 — Backoff state tests: restart-persistence (state survives orchestrator restart), corrupt-state graceful fallback (malformed file ignored, not crash-loop), TTL expiry (expired backoff windows auto-cleared on read), per-identity isolation (backoff for one identity does not affect others)
  • AC4 — [wake-readiness] sentinel parser reads structured task envelope ({type: 'wake-readiness', ready, reason, expiresAt})
  • AC5 — Subject-only spoofing rejected by parser (subject without matching structured task envelope → sentinel NOT applied)
  • AC6 — Operator/self sentinels compose most-restrictive-wins on expiresAt; multi-sentinel merge tested
  • AC7 — Unit tests cover all 4 known pathological cases: drop-active (wakes departed); drop-idle (interrupts work); drop-ready (loops on blocked); operator manual-prompt grace (received-activity protects 15m)
  • AC8 — Cross-family review per pull-request §6.1

Out of Scope

  • emitHeartbeatPulse mechanism — Sub-i of Epic #11993
  • SwarmHeartbeatService.pulse() Step 7 replacement — Sub-iii of Epic #11993 (consumes this sub's 3-signal-decision function)
  • tmux-inject + Shape A heartbeat-mailbox-path removal — Sub-iii of Epic #11993
  • New MCP tool for wake-readiness reporting — explicitly excluded by Epic #11993 (MCP tool cap constraint)

Avoided Traps

  • ❌ Putting backoff state on WAKE_SUBSCRIPTION properties — pollutes client-deployment graph substrate with operator-local harness health
  • ❌ Adding a new graph node type for backoff — substrate-additive cost not justified per Discussion #11992 OQ3 resolution
  • ❌ Subject-pattern-only sentinel parsing (e.g., subject.startsWith('[wake-readiness]')) — fragile, spoofable by accidental subject patterns
  • ❌ Implementing the active/idle signal as "sent within window" only (excluding received side) — falsifies the operator's manual-prompt edge case (received-activity at t=0 is the grace anchor)
  • ❌ Boot-marker primitive for fresh signal — explicitly rejected cycle-3 by collapsing to 3-signal model

Related

  • Parent Epic: #11993
  • Graduation Discussion: #11992
  • Backoff-state precedent: TaskStateService.mjs:96-124, Orchestrator.stateFile at Orchestrator.mjs:439-453, trioWakeCooldown.mjs:20-92
  • Sub-i (consumes nothing from Sub-ii): #11994 — Shape B emitHeartbeatPulse mechanism
  • Sub-iii (consumes Sub-ii's 3-signal-decision function): to be filed

Handoff Retrieval Hints

  • Semantic: query_raw_memories({query: "3-signal wake derivation backoff readiness sentinel Sub-ii"})
  • Empirical: git log --all --oneline -- ai/daemons/orchestrator/Orchestrator.mjs ai/daemons/orchestrator/services/TaskStateService.mjs ai/scripts/lifecycle/trioWakeCooldown.mjs
tobiu referenced in commit f047598 - "feat(orchestrator): WakeDecisionService 3-signal + readiness parser + backoff (#11995) (#11997) on May 25, 2026, 11:55 PM
tobiu closed this issue on May 25, 2026, 11:55 PM
tobiu referenced in commit 68ffafd - "feat(orchestrator): active-a2a-participants target source for activity-derived pulse candidate discovery (#12003) (#12009) on May 26, 2026, 3:35 AM