LearnNewsExamplesServices
Frontmatter
id16323
titlehealthcheck cannot answer whether a seat is armed to receive a wake
stateClosed
labels
enhancementai
assigneesneo-opus-grace
createdAtAug 2, 2026, 2:59 AM
updatedAtAug 2, 2026, 11:31 AM
githubUrlhttps://github.com/neomjs/neo/issues/16323
authorneo-opus-grace
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 2, 2026, 11:31 AM

healthcheck cannot answer whether a seat is armed to receive a wake

Closed Backlog/active-chunk-11 enhancementai
neo-opus-grace
neo-opus-grace commented on Aug 2, 2026, 2:59 AM

Context

Split out of #16310 during review of PR #16318, on @neo-gpt's close-target audit: that PR delivers the visibility half in full while #16310's arming scope stays live, so Resolves #16310 would close a ticket the PR explicitly leaves half open. This is the fully-delivered leaf; #16310 retains the arming work under its corrected premise.

The Problem

Nothing answers "is this seat armed to receive a wake?" On 2026-08-01 six of seven peers idled out with no failing check anywhere on the plane. Every instrument agreed things were fine:

  • healthcheck reported the substrate — gate state, daemon liveness, last pulse — all of which describe the machinery, none of which describe your row.
  • A subscription with status: 'active' and no signingKey reads healthy on every surface while delivery refuses it unsigned and the manifest build throws on it. Nothing is attempted, so nothing fails, so nothing logs.

The failure is silent by construction: an unarmed seat produces no error because nothing ever tries.

The Architectural Reality

buildWakeReceiverManifest (ai/daemons/wake/buildReceiverManifest.mjs) is what actually decides whether a route exists. Its admission gate, in order:

gate unmet
status === 'active' route withdrawn, skipped
harnessTarget === 'a2a-webhook' route withdrawn, skipped
server-issued signingKey (≥32 chars) throws — aborts the whole build

That asymmetry is load-bearing and easy to get wrong: a keyless row does not cost only its own route, it makes the manifest unbuildable for every row in the set.

The health verdict must mirror this gate rather than re-derive a looser rule, or the two surfaces disagree about the same persisted row set — which is precisely the "reads healthy while deaf" condition being removed.

The Fix

buildWakeFeaturesBlock carries subscription: {armed, reason}, computed by buildSubscriptionArmingBlock in ai/services/memory-core/HealthService.mjs, RLS-scoped to the calling identity.

  • armed is tri-state; null on ignorance (unbound identity, unreadable store), never false.
  • reason names the furthest gate reached, so it points at the next repair.
  • isServerIssuedSigningKey is exported from the manifest builder and shared, so the key predicate cannot drift between the two surfaces.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
features.wake.subscription.armed buildSubscriptionArmingBlock mirroring buildWakeReceiverManifest Boolean|null; null = unanswerable, never "not armed" null + reason learn/agentos/wake-substrate/PersistentProcessManagement.md §3c spec: unbound identity yields null and does not read the graph
features.wake.subscription.reason same closed enum: deliverable | no-active-subscription | unmigrated-target | missing-signing-key | unbound-identity | unreadable unreadable same anchor spec per value
isServerIssuedSigningKey ai/daemons/wake/buildReceiverManifest.mjs (exported) shared predicate; the 32 minimum lives in one place n/a JSDoc at the export manifest suite 32/32 green after extraction
buildWakeFeaturesBlock return shape HealthService JSDoc @returns additive only; existing fields unchanged n/a inline wire-format audit found no exact-shape consumer

Decision Record impact

none — additive health field, no ADR authority touched.

Acceptance Criteria

  • healthcheck answers whether the calling identity holds a subscription the manifest build would accept.
  • armed is tri-state and reports null, never false, when the question cannot be answered.
  • reason is a closed enum, each value spec-covered.
  • The verdict agrees with buildWakeReceiverManifest across both failure classes — the skip class and the throw class — asserted as a cross-side invariant rather than pinned as strings on each side.
  • A keyless row on the deliverable path unarms the whole seat, with a passing control proving skipped rows do not.
  • Operator anchor documents the field, the reason enum, the repair for missing-signing-key, and the leg boundary.
  • buildWakeFeaturesBlock JSDoc documents the added return field.
  • Post-merge (L4): a seat with a keyless row reports missing-signing-key, and an independently-proven-delivering seat reports deliverable. Requires a plane rebuilt past 984f2023bf — see #16304.

Out of Scope

  • Arming itself — nothing invokes registration on the current transport. That is #16310, whose placement (hook / orchestrator / Fleet Manager) is deliberately undecided.
  • Claiming a wake will arrive. This reports the Memory-Core leg only; the receiver's boot-snapshotted manifest and adapter coordinates are invisible to Memory Core, so a seat can be armed: true and still unreachable.
  • The client-side tool-schema staleness layer (#16320).

Avoided Traps

  • Re-deriving the deliverability rule. The first draft did, and inverted the target check — harnessTarget !== 'a2a-webhook' read as "no key needed, therefore fine", reporting armed for exactly the seats the manifest refuses to publish.
  • some instead of every on the key check. Caught by @neo-gpt's review falsifier on PR #16318: a keyed + keyless pair reported armed: true while the builder threw. The skip/throw asymmetry is the discriminator.
  • A cross-side agreement spec that only covers one failure class. The original AGREEMENT spec used a non-deliverable target as its unarmed specimen — which the builder skips — so it proved agreement across the skip class only, and the throw class went untested. That is exactly where the two surfaces disagreed.

Related

  • #16310 — the arming half; premise corrected (the auto-bootstrap is invoked, but gated on transport === 'stdio').
  • #16300 / #16303rotate-key, the repair for the missing-signing-key state.
  • #16320 — client-side schema staleness; a different layer.
  • #16295 — freshness-label narrowing; adjacent instrument-honesty work.

Live latest-open sweep: checked latest 20 open issues at 2026-08-02T00:58:21Z; no equivalent found.

Origin Session ID: 713db0da-2239-44ea-ba5b-931be90d34fc

Retrieval Hint: query_raw_memories("wake subscription arming verdict healthcheck manifest agreement skip versus throw")

tobiu referenced in commit 4a985a7 - "feat(memory-core): report whether a seat holds a deliverable wake subscription (#16310) (#16318) on Aug 2, 2026, 11:31 AM
tobiu closed this issue on Aug 2, 2026, 11:31 AM