Skip to content

fix(connector): recover dropped channel wakes without polling#243

Open
davidfarah2003 wants to merge 1 commit into
mainfrom
fix/foreground-durable-guard
Open

fix(connector): recover dropped channel wakes without polling#243
davidfarah2003 wants to merge 1 commit into
mainfrom
fix/foreground-durable-guard

Conversation

@davidfarah2003

Copy link
Copy Markdown
Contributor

Problem

A Claude channel nudge is fire-and-forget: await resolves when it is written to the transport, it is never acknowledged, and it is silently dropped when the host's channel handler is not yet registered (the ~1.4s boot window) or the session is not admitted. A dropped wake left an idle agent with pending inbox work and no future event to surface it — the observed zombie: unacked durable messages were redelivered by JetStream every ack_wait, and each redelivery was swallowed by the pending-duplicate dedup in MeshAgent.ingest.

Fix — event-driven, no timer

  • MeshAgent.ingest: a durable duplicate of a still-pending item now refreshes the ack handle and re-emits the existing item through the ordinary incoming policy path. JetStream redelivery becomes the retry for a lost wake — broker-driven, coupled to "work is still unacked", bounded by ack_wait. A live duplicate still dedups silently; no second inbox row is ever created.
  • Claude connector: reconcile once after the channel capability gate opens — if messages buffered while the mesh connected in parallel with the MCP handshake, send one best-effort nudge.

Policy is preserved end to end: quiet ambient stays pull-only and non-waking, dnd/focus and muted are unchanged, and each host's in-flight guard (OpenCode busy, Pi unconfirmed batch, Hermes awaitingId, Claude's working-gate) prevents double-drives.

Explicitly not in this change

  • No setInterval reconciler — redelivery replaces it with an event that already exists.
  • No per-turn history recall; live-only catch-up and durable-membership provisioning are separate designs.

Residual (documented in connect-claude.md): live-only traffic has no durable copy, hence no broker retry; a zero-gap guarantee needs an upstream handler-ready/enqueue acknowledgement from the host.

Verification

  • cross-path-dedup.smoke.ts: 52/52 — new cases for durable redelivery reannounce (live→durable and durable→durable), live-duplicate silence, freshest-ack retention, quiet remaining pull-only/non-waking.
  • pnpm typecheck green across all packages; docs bundle regenerated and consistent.
  • Design and diff independently reviewed by a designer/critic pair; converged with no blocker.

A Claude channel nudge is fire-and-forget: written to the transport, never
acknowledged, and silently dropped when the host's channel handler is not yet
registered (the boot window) or the session is not admitted. A dropped wake
left an idle agent with pending inbox work and no future event — the observed
zombie: unacked durable messages redelivered every ack_wait and were swallowed
by the pending-duplicate dedup in MeshAgent.ingest.

Two event-driven repairs, no timer:

- MeshAgent.ingest: a durable duplicate of a still-pending item now refreshes
  the ack handle AND re-emits the existing item through the ordinary incoming
  policy path, turning JetStream redelivery into the retry for a lost wake.
  A live duplicate still dedups silently; quiet ambient stays pull-only and
  non-waking; host in-flight guards (busy/unconfirmed/working) still apply.
- claude connector: reconcile once after the channel capability gate opens —
  if work buffered while the mesh connected in parallel with the MCP
  handshake, send one best-effort nudge.

Residual, documented in connect-claude.md: live-only traffic has no durable
retry, and no zero-gap proof exists without an upstream handler-ready or
enqueue acknowledgement from the host.

Covered in cross-path-dedup.smoke.ts: durable redelivery reannounces, live
duplicates stay silent, fresh durable ack retained, quiet remains pull-only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant