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
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 / #16303 —
rotate-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")
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 #16310would 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:
healthcheckreported the substrate — gate state, daemon liveness, last pulse — all of which describe the machinery, none of which describe your row.status: 'active'and nosigningKeyreads 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:status === 'active'harnessTarget === 'a2a-webhook'signingKey(≥32 chars)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
buildWakeFeaturesBlockcarriessubscription: {armed, reason}, computed bybuildSubscriptionArmingBlockinai/services/memory-core/HealthService.mjs, RLS-scoped to the calling identity.armedis tri-state;nullon ignorance (unbound identity, unreadable store), neverfalse.reasonnames the furthest gate reached, so it points at the next repair.isServerIssuedSigningKeyis exported from the manifest builder and shared, so the key predicate cannot drift between the two surfaces.Contract Ledger Matrix
features.wake.subscription.armedbuildSubscriptionArmingBlockmirroringbuildWakeReceiverManifestBoolean|null;null= unanswerable, never "not armed"null+ reasonlearn/agentos/wake-substrate/PersistentProcessManagement.md§3cnulland does not read the graphfeatures.wake.subscription.reasondeliverable|no-active-subscription|unmigrated-target|missing-signing-key|unbound-identity|unreadableunreadableisServerIssuedSigningKeyai/daemons/wake/buildReceiverManifest.mjs(exported)32minimum lives in one placebuildWakeFeaturesBlockreturn shapeHealthServiceJSDoc@returnsDecision Record impact
none— additive health field, no ADR authority touched.Acceptance Criteria
healthcheckanswers whether the calling identity holds a subscription the manifest build would accept.armedis tri-state and reportsnull, neverfalse, when the question cannot be answered.reasonis a closed enum, each value spec-covered.buildWakeReceiverManifestacross both failure classes — the skip class and the throw class — asserted as a cross-side invariant rather than pinned as strings on each side.missing-signing-key, and the leg boundary.buildWakeFeaturesBlockJSDoc documents the added return field.missing-signing-key, and an independently-proven-delivering seat reportsdeliverable. Requires a plane rebuilt past984f2023bf— see #16304.Out of Scope
armed: trueand still unreachable.Avoided Traps
harnessTarget !== 'a2a-webhook'read as "no key needed, therefore fine", reporting armed for exactly the seats the manifest refuses to publish.someinstead ofeveryon the key check. Caught by @neo-gpt's review falsifier on PR #16318: a keyed + keyless pair reportedarmed: truewhile the builder threw. The skip/throw asymmetry is the discriminator.Related
transport === 'stdio').rotate-key, the repair for themissing-signing-keystate.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")