LearnNewsExamplesServices
Frontmatter
id15677
titleWake delivery drops silently when the OpenCode envelope goes stale
stateClosed
labels
bugai
assigneesneo-gpt
createdAtJul 22, 2026, 3:16 AM
updatedAtJul 29, 2026, 11:11 PM
githubUrlhttps://github.com/neomjs/neo/issues/15677
authorneo-kimi-phoebe
commentsCount5
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 29, 2026, 11:11 PM

Wake delivery drops silently when the OpenCode envelope goes stale

neo-kimi-phoebe
neo-kimi-phoebe commented on Jul 22, 2026, 3:16 AM

Context

Operator-reported 2026-07-22 ~01:10Z: "wake delivery to your OpenCode harness no longer works: a regression (it did before)." The wake route is the swarm's interrupt path into this seat — every A2A SENT_TO_ME in the outage window arrived only as silent mailbox entries; the operator had to relay peer messages manually (three times that session).

Amended 2026-07-22T01:5xZ per Euclid's intake (issuecomment-5040837726): needs-narrowing + needs-relinking + needs-contract-alignment. His source falsifications are accepted: the boot-hook writer already shipped (#15392 → PR #15481), so the durable work is production consumption, not authoring; the incident topology (operator-launched desktop) differs from the repo launch contract (Fleet-managed headless opencode serve); and session authority requires an owner-native binding with a no-heuristic-retarget fence (shared invariant with #15665).

The Evidence Chain (live V-B-A, 2026-07-22T01:1xZ)

  1. ~/.local/share/opencode/wake-envelope.json was dated Jul 18 18:15 — port 52371, session ses_08af21…, rotated password: all from a previous OpenCode desktop boot.
  2. The desktop restarted 2026-07-22 12:39AM; its NodeService listened on 127.0.0.1:65066 — the envelope pointed at a dead port.
  3. Daemon-side smoking gun (wake-daemon.log): 01:11:03Z [ERROR] Failed to deliver via opencode-server: fetch failed ×4 retries → 01:11:30Z [ERROR] Giving up wake delivery for WAKE_SUB:51fae59e… after 5 failed attempts; wake dropped. — the dropped wake was Emmy's CHANGES_REQUESTED on PR #15675.
  4. Failure signal existed only in wake-daemon.log — invisible to operator and seat.
  5. Manual heal (rewrite envelope → self-inject prompt_async → 204 + in-session landing) verified the adapter functional; first organic wake post-heal delivered (01:17Z, receipt in comments). Not a code regression — per-boot envelope staleness with log-only terminal failure.

Topology Choice (the intake's required decision)

This ticket takes the Fleet-managed headless topology plus the two topology-independent fences. The operator-launched desktop topology becomes a successor ticket.

  • Primary: Fleet-managed headless opencode serve (#15391/#15406 contract). All artifacts exist and wait for one production caller: generateOpenCodeSeatConfig.mjs emits the atomic 0600 wake-hook when wakeHookPath is supplied (#15481); the two-writer envelope reconciliation is merged (#15499). Missing: prepareManagedAgentWorkspace has no OpenCode artifact branch (falls through to "no workspace adapter"), FleetLifecycleService.resolveLaunch() never supplies the existing serverPassword seam, the supervisor discards child stdout and has no bound-port parser or hook invocation.
  • Fence 1 (adapter stale recovery, both topologies): deliverViaOpencodeServer may attempt at most one authority-preserving rebind — refresh boot coordinates (port/credentials) only through a named authoritative surface; connection refusal is evidence the old route is dead, not permission to select another session. No exact owner-native binding → fail closed, retain the mailbox event unread, surface degraded health.
  • Fence 2 (terminal-drop visibility, both topologies): a terminally dropped wake must project onto an independently observable operator/health surface — NOT an A2A message to the affected seat (one more silent unread behind the same broken route). fleetWakeStateAdapter currently observes subscription intent × daemon liveness but not per-subscription delivery failure; this fence closes that blindness with seat, subscription, last error class, and timestamp.
  • Successor (filed at this ticket's PR time): the operator-launched desktop topology — the actual incident path. Its boot boundary owner must be named (the desktop's launch lifecycle is not the Fleet supervisor's), and the manual-heal runbook (seat memory, session 38a38b84) covers the gap until then. Fence 2 makes the next desktop-restart outage loud even before the successor lands.

The Split-Brain Fence (session authority — the chosen, source-backed mechanism)

Boot coordinates are not session ownership. The binding is Fleet-owned session creation: the supervisor launches the headless opencode serve child with a supervisor-generated serverPassword, captures the bound port, then calls POST /api/session itself (verified live 2026-07-22 against the running server's /doc — the creation verb exists alongside GET /api/session) and binds the exact sessionId, projectId, and directory from the creation response before invoking the #15481 hook. The binding is owner-native by construction — selection never consults the server's session list, so no latest/first heuristic exists anywhere in the path.

Hook ordering (pinned): server boot → bound-port capture → POST /api/session (supervisor) → hook invocation with all four params → the seat's client connects into that session.

Falsifiers:

  • A (no sibling retarget): a restored/older session can never be selected — the binding comes from the creation response, not the list.
  • B (no child retarget): a parent-id-bearing session is never created by this path; later child sessions cannot mutate the pinned binding.
  • C (fail-closed): if POST /api/session fails or yields no id → no hook invocation, no envelope write, degraded health via Fence 2.

The desktop-successor (#15684) inherits this construction where its topology allows; its operator-owned sessions keep the discovery requirement.

Contract Ledger

Target surface Source of authority Required behavior Fallback / edge Evidence
OpenCode config/hook emission generateOpenCodeSeatConfig.mjs (#15481) existing artifact consumed, not reimplemented; explicit params preserve ADR 0019 purity no new writer generator invoked by one production caller
Managed workspace planting prepareManagedAgentWorkspace.mjs OpenCode artifact branch plants config + hook with mode/divergence policy unknown harness → current fall-through production call-site receipt (no longer spec-only)
Launch coordinates/secret deriveHarnessLaunchSpec.mjs + FleetLifecycleService.mjs supervisor creates serverPassword, captures the bound port from child stdout, invokes the hook with all four params parse failure → fail closed + degraded health launch receipt: envelope written at boot
Operator-session binding POST /api/session on the supervisor-owned server (verified live via /doc, 2026-07-22) supervisor creates the seat's top-level session at launch; sessionId/projectId/directory bound from the creation response; hook invoked after creation, before client connect creation failure → fail closed + Fence 2; never session-list inference unit witnesses A/B/C + launch receipt
Adapter stale recovery deliverViaOpencodeServer at most one authority-preserving coordinate rebind; field-level no-retarget/fail-closed connection-refused → evidence of death, not retarget permission unit witness: refusal + no binding → drop + Fence 2
Terminal-drop projection wake retry owner → independent health/operator surface drop becomes observable degraded state (seat, subscription, error class, timestamp); never solely via the failed route fleetWakeStateAdapter extended or an explicitly independent channel named at implementation drop receipt visible without reading daemon logs
Desktop boot topology successor ticket (filed at PR time) out of scope here; incident receipts carried forward manual-heal runbook until then successor ticket link

Acceptance Criteria

  • generateOpenCodeSeatConfig gains exactly one production caller via the workspace-planting path (OpenCode artifact branch in prepareManagedAgentWorkspace)
  • [L4-deferred — operator handoff needed] A Fleet-managed OpenCode seat boot follows the pinned hook ordering — server boot → bound-port capture → supervisor POST /api/session → hook invocation with all four params → client connect — and writes a correct wake envelope (receipt: restart → wake lands with no manual heal)
  • Owner-native session binding by construction (Fleet-owned creation): falsifiers A (sibling), B (child), C (creation-failure fail-closed) each pinned by a unit witness; no code path consults the server session list to choose a target
  • Adapter performs at most one authority-preserving coordinate rebind and never retargets sessions from server session-list data
  • [L4-deferred — operator handoff needed] A terminally dropped wake projects seat + subscription + error class + timestamp onto an independent health/operator surface (receipt: kill the envelope port, trigger a wake, observe the degraded state without opening wake-daemon.log)
  • Successor ticket for the desktop topology filed and linked at PR time
  • ADR 0019 config purity preserved (no env re-derivation; explicit params through the existing seams); ADR 0002 Shape-C fail-closed behavior preserved

Out of Scope

  • The kimi-server adapter's sibling route defect (#15665 — cited only for the shared no-retarget invariant)
  • The operator-launched desktop boot writer (successor ticket, above)
  • Wake coalescing semantics (#15414, landed); daemon scheduling

Related

  • Corrective successor of: #15392 (→ PR #15481, generator + hook artifact), #15391 (→ PR #15406, registry/headless launch contract), #15486 (→ PR #15499, two-writer reconciliation), #15394 (→ PR #15438, adapter/consumer)
  • #15665 (shared no-heuristic-retarget invariant; separate harness, separate lane)
  • #15666 (dev-server stale-CSS guard — unrelated failure class, same "serve-state ≠ git-state" family)

Live latest-open sweep: checked latest 20 open issues at 2026-07-22T01:16Z; #15665 adjacent-but-distinct. A2A in-flight sweep: no competing claim. Euclid's intake re-run 01:35Z; this amendment is the narrowing he requested.

Origin Session ID: 72c8c42d-f18a-408c-97c8-aeb1f82dd276

Retrieval Hint: "wake envelope stale desktop restart dropped wake opencode-server fetch failed boot boundary owner-native session binding no-retarget"

tobiu unassigned from @neo-kimi-phoebe on Jul 27, 2026, 12:26 AM
tobiu referenced in commit a490e56 - "feat(ai): bind Fleet OpenCode wake delivery (#15677) (#16141)" on Jul 29, 2026, 11:11 PM
tobiu closed this issue on Jul 29, 2026, 11:11 PM