Context
First implementation slice of #16741 (D#16720's S7 — wake delivery over the ingress). The parent's design thread carries design v1 (fork + contract ledger), v1.1 (the ingress-liveness falsifier folded: honest-degraded as AC), the F1 closure (all four digest event classes are GraphLog-persisted), and the recon-grounded implementation plan. This ticket exists because the slice is one-PR-deliverable while the parent's AC2 (the remote-only journey's working wake story) is journey-level, landing with the C1/C2 client consumption — and a draft PR cannot collect review, so the slice carries its own close-target.
Live latest-open sweep: checked latest 20 open issues at 2026-08-09T15:05Z — no equivalent beyond the parent. A2A in-flight sweep: mailbox drained 15:07Z, no overlapping claims.
The Problem
Pull-bridge-class seats and no-checkout cockpit machines have no wake-delivery channel that crosses the authenticated ingress: push terminates on host-side listeners a remote client cannot be. The verb-shaped delivery half: a client-held-watermark poll that derives the caller's wake digest at read time, over the same authenticated MCP surface the client already holds.
The Architectural Reality
ai/services/memory-core/WakeSubscriptionService.mjs — resync({subscriptionId, sinceLogId}) already replays GraphLog deltas above a client-tracked watermark; the shared match() evaluator (heartbeatPulseEvaluator.mjs) already reconciles CURRENT read state per delivery shape (a wake for an already-read message never matches); ai/daemons/wake/wakeDigestBuilder.mjs's buildWakeDigest is pure over {messages, tasks, permissions, heartbeats} and already imported into the service tree (precedent: buildReceiverManifest import).
- The fleet MCP convention is action dispatch on
manage_wake_subscription (resync's sibling), registered at toolService.mjs + Server.mjs + openapi.yaml (parity-linted).
- Caller binding via
RequestContextService.getAgentIdentityNodeId() + per-subscription ownership check (resync's guard).
The Fix
WakeSubscriptionService.pollDigest({subscriptionId, sinceLogId}) — ownership guard verbatim; the resync delta walk extracted verbatim into _collectSubscriptionEvents (both consume it — cannot drift); survivors bucketed into the daemon's own buildWakeDigest. Empty answer = closed {pending: 0, reason, watermark} — distinguishable from transport failure, never a verdict. Watermark echoed, never server-persisted.
manage() gains the poll-digest case; openapi.yaml action enum + descriptions updated.
- Five falsifier tests in
WakeSubscriptionService.spec.mjs: derivation above watermark; closed empty-with-reason; client-held watermark semantics both directions (advancing empties the next poll; replay at the old watermark still sees the events); ownership rejection; manage routing.
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
WakeSubscriptionService.pollDigest (new method) |
this ticket + parent design thread |
derive-at-read digest over the caller's subscription events |
closed empty-with-reason state |
method docblock |
spec: 5 falsifier tests |
manage() action switch + JSDoc |
this ticket |
poll-digest case; error string lists it |
— |
— |
spec: routing test |
openapi.yaml /wake-subscriptions/manage |
this ticket |
action enum + subscriptionId/sinceLogId descriptions |
— |
operation description |
parity lint |
resync public contract |
pre-existing |
unchanged (walk extracted verbatim) |
— |
— |
110 pre-existing spec tests green |
Acceptance Criteria
Out of Scope
- The parent's AC2 (remote-only journey wake story) — C1/C2 consumption slice.
- The
ingress-pull route-class metadata (the verb does not need it; the journey slice decides).
- Fleet-surface alias placement (the design's F4 — MC surface now, S1 alias later).
- Any AiConfig touch (none — the gate is considered, not triggered).
Related
#16741 · PR #16796 · #16710 (SDK vocabulary) · ADR 0038 §2.5.1 row 6 names the target ("S7 moves it onto the authenticated ingress").
Origin Session ID: 6df9925c-e527-496d-9fbf-0a277c175d59
Retrieval Hint: "wake poll-digest derive-at-read ingress pull-bridge client watermark"
Context
First implementation slice of #16741 (D#16720's S7 — wake delivery over the ingress). The parent's design thread carries design v1 (fork + contract ledger), v1.1 (the ingress-liveness falsifier folded: honest-degraded as AC), the F1 closure (all four digest event classes are GraphLog-persisted), and the recon-grounded implementation plan. This ticket exists because the slice is one-PR-deliverable while the parent's AC2 (the remote-only journey's working wake story) is journey-level, landing with the C1/C2 client consumption — and a draft PR cannot collect review, so the slice carries its own close-target.
Live latest-open sweep: checked latest 20 open issues at 2026-08-09T15:05Z — no equivalent beyond the parent. A2A in-flight sweep: mailbox drained 15:07Z, no overlapping claims.
The Problem
Pull-bridge-class seats and no-checkout cockpit machines have no wake-delivery channel that crosses the authenticated ingress: push terminates on host-side listeners a remote client cannot be. The verb-shaped delivery half: a client-held-watermark poll that derives the caller's wake digest at read time, over the same authenticated MCP surface the client already holds.
The Architectural Reality
ai/services/memory-core/WakeSubscriptionService.mjs—resync({subscriptionId, sinceLogId})already replays GraphLog deltas above a client-tracked watermark; the sharedmatch()evaluator (heartbeatPulseEvaluator.mjs) already reconciles CURRENT read state per delivery shape (a wake for an already-read message never matches);ai/daemons/wake/wakeDigestBuilder.mjs'sbuildWakeDigestis pure over{messages, tasks, permissions, heartbeats}and already imported into the service tree (precedent:buildReceiverManifestimport).manage_wake_subscription(resync's sibling), registered attoolService.mjs+Server.mjs+openapi.yaml(parity-linted).RequestContextService.getAgentIdentityNodeId()+ per-subscription ownership check (resync's guard).The Fix
WakeSubscriptionService.pollDigest({subscriptionId, sinceLogId})— ownership guard verbatim; the resync delta walk extracted verbatim into_collectSubscriptionEvents(both consume it — cannot drift); survivors bucketed into the daemon's ownbuildWakeDigest. Empty answer = closed{pending: 0, reason, watermark}— distinguishable from transport failure, never a verdict. Watermark echoed, never server-persisted.manage()gains thepoll-digestcase;openapi.yamlaction enum + descriptions updated.WakeSubscriptionService.spec.mjs: derivation above watermark; closed empty-with-reason; client-held watermark semantics both directions (advancing empties the next poll; replay at the old watermark still sees the events); ownership rejection; manage routing.Contract Ledger
WakeSubscriptionService.pollDigest(new method)manage()action switch + JSDocpoll-digestcase; error string lists itopenapi.yaml/wake-subscriptions/managesubscriptionId/sinceLogIddescriptionsresyncpublic contractAcceptance Criteria
poll-digestreturns a digest derived from events above the client watermark, withdigestPriorityand the echoed watermark.wakeDigestBuilder.spec).Out of Scope
ingress-pullroute-class metadata (the verb does not need it; the journey slice decides).Related
#16741 · PR #16796 ·
#16710(SDK vocabulary) · ADR 0038 §2.5.1 row 6 names the target ("S7 moves it onto the authenticated ingress").Origin Session ID: 6df9925c-e527-496d-9fbf-0a277c175d59
Retrieval Hint: "wake poll-digest derive-at-read ingress pull-bridge client watermark"