Context
Successor to #15677 (which takes the Fleet-managed headless topology + the two topology-independent fences). This ticket owns the topology the 2026-07-22 outage actually happened in: the operator-launched OpenCode desktop (GUI app, NodeService-spawned server on a random port). The operator's follow-up question is this ticket's premise: "you solved the missing wake delivery for the current session — is this also resolved for future sessions?" Answer at filing: no.
Discovery complete (2026-07-22, this seat's live host). The boundary question now has a verified answer with two repair paths and one rejected one; the original Out-of-Scope exclusion of the plugin path is reversed (see below).
The Failure Modes (this topology, today)
- Desktop restart (the 01:11Z outage class): new random port + rotated password + new session id → the envelope is fully stale → daemon retries ×5 against the dead port →
wake dropped, log-only. Evidence chain in #15677 (Jul-18 envelope vs 12:39AM reboot; the dropped CHANGES_REQUESTED wake).
- New session within the same boot: the healed envelope binds
sessionId to the session that was live at heal time. A new operator session keeps port + credentials but the envelope still targets the OLD session id — wakes land in a dead/idle transcript, or fail against a closed session. This is the desktop-specific retarget mode, adjacent to the split-brain fence (#15677 / #15665 invariant): the binding is per-session, and nothing owner-native re-binds it.
- Silent until noticed: both modes fail invisibly until a human wonders why no wakes land (Fence 2 from #15677 covers the drop case; mode 2's land-in-zombie-session case may not even produce a drop signal).
The Discovery (verified 2026-07-22, live host)
Session identity is NOT discoverable daemon-side. GET /session?directory=<seat-checkout> returns 11 sessions in this seat's checkout — every session the seat has ever run accumulates there. "Latest updated" selects the current one today, but it is a heuristic that provably fails the twin class (Iris's serverside sibling was exactly a second live writer in one directory). The #15677 fence holds: no listing-based session inference, ever.
The two owner-native boundaries that CAN exist:
- Agent-side boot self-write (immediate, zero-dependency). The running session refreshes the envelope at session start: at boot time the writing session IS the latest-updated one by construction (its own boot turn just updated its timestamp), so a self-write is owner-native for that moment. Verification: self-inject a probe after writing (204 + in-session landing — the exact heal verification used 2026-07-22). Caveat: two live sessions both running this ritual race last-writer-wins (the twin case, itself tracked in #15665); bounded because a self-write happens only at boot, never periodically.
- Plugin self-announce (upstream-correct). The desktop's plugin runtime fires
session.created with the session id — the session announcing itself, no listing, no timestamp guessing. The existing writer (~/.config/opencode/plugins/neo-wake-envelope.mjs) is in place and import-free; it never loads because the desktop's background dependency install fails resolving @opencode-ai/plugin@local (NpmInstallFailedError: No matching version found for @opencode-ai/plugin@local, opencode.log WARN). The local tag is unresolvable; the published line exists (@opencode-ai/plugin@1.18.4 on npm).
The belt (daemon-side, coordinate-only): on connection-refused, the daemon may refresh port (lsof) + credentials (seat env) — never the session id. Coordinates fail soft; identity fails closed.
The Reversal
The original Out-of-Scope said "this ticket does not resurrect the plugin path; the boundary must live outside the plugin's reliability." Discovery reverses that: the plugin path is the ONLY fully owner-native boundary, and its blocker is a bounded, validated resolution failure (a version pin, not a redesign). The ticket now owns the plugin repair as the primary path, with the agent-side boot self-write as the immediate path that needs no desktop fix at all.
The Fix
- Immediate — agent-side boot self-write: add envelope refresh to the seat's boot protocol (context-recovery/boot sequence): query the server for the seat-checkout session list, bind the latest-updated (which at boot time is the writing session by construction), write the envelope atomically (0600), self-inject a probe, and report the outcome to the mailbox/boot log. Generalized to future seats via #15697's seat generator (Iris's lane — this discovery is her input).
- Primary — plugin-runtime resolution repair: pin
@opencode-ai/plugin to the published line (^1.18.4) via ~/.config/opencode/package.json so the background installer resolves (validated: npm install in the config dir resolves 1.18.4 cleanly; bun install equivalent per OpenCode's plugin docs). Verify on the next desktop restart (operator-owned) that the background install succeeds, the plugin loads, and session.created writes the envelope.
- Belt — daemon coordinate refresh: on connection-refused delivery, refresh port + credentials from seat env before the retry loop; never touch session id.
- Fence (unchanged): anything that is not a session self-announce or an agent-side boot self-write fails closed with the #15677 Fence-2 degraded-health signal — never a listing-based guess.
Acceptance Criteria
Out of Scope
- The Fleet-managed headless topology (#15677 primary)
- The kimi-server harness (#15665)
- Desktop-upstream fixes beyond the version pin (if the desktop's background installer still tries
@local despite the pin, the residual is an upstream OpenCode issue, noted for the operator)
- The twin-race arbitration itself (#15665)
Ownership map (added 2026-07-22 after PR #15729)
- Boot self-write (this ticket's immediate path): delivered in PR #15729 (the refresh script + the context-recovery step 0 + witnesses).
- Plugin self-announce path: the
@opencode-ai/plugin@^1.18.4 pin is validated locally; the generator-side emission lives in #15697's seat-config generator (Iris's lane — the discovery was handed to her).
- Daemon coordinate belt: owned by #15677's Fence 1 implementation (adapter stale-recovery).
Related
- #15677 (primary topology + both fences; this is its named desktop successor)
- #15481 (the hook writer — the Fleet-side artifact)
- #15665 (shared no-heuristic-retarget invariant + the twin case)
- #15697 (seat generator — the boot self-write generalization target; Iris's lane)
- #15392-arc (OpenCode seat productization context)
Live latest-open sweep: checked latest 20 open issues at 2026-07-22T01:56Z (creation) and re-verified at amendment; #15677/#15665 adjacent, neither covers this topology. A2A in-flight sweep: no competing claim.
Origin Session ID: 72c8c42d-f18a-408c-97c8-aeb1f82dd276
Retrieval Hint: "opencode desktop wake envelope boot boundary session retarget future sessions restart successor plugin local resolution agent self-write"
Context
Successor to #15677 (which takes the Fleet-managed headless topology + the two topology-independent fences). This ticket owns the topology the 2026-07-22 outage actually happened in: the operator-launched OpenCode desktop (GUI app, NodeService-spawned server on a random port). The operator's follow-up question is this ticket's premise: "you solved the missing wake delivery for the current session — is this also resolved for future sessions?" Answer at filing: no.
Discovery complete (2026-07-22, this seat's live host). The boundary question now has a verified answer with two repair paths and one rejected one; the original Out-of-Scope exclusion of the plugin path is reversed (see below).
The Failure Modes (this topology, today)
wake dropped, log-only. Evidence chain in #15677 (Jul-18 envelope vs 12:39AM reboot; the dropped CHANGES_REQUESTED wake).sessionIdto the session that was live at heal time. A new operator session keeps port + credentials but the envelope still targets the OLD session id — wakes land in a dead/idle transcript, or fail against a closed session. This is the desktop-specific retarget mode, adjacent to the split-brain fence (#15677 / #15665 invariant): the binding is per-session, and nothing owner-native re-binds it.The Discovery (verified 2026-07-22, live host)
Session identity is NOT discoverable daemon-side.
GET /session?directory=<seat-checkout>returns 11 sessions in this seat's checkout — every session the seat has ever run accumulates there. "Latest updated" selects the current one today, but it is a heuristic that provably fails the twin class (Iris's serverside sibling was exactly a second live writer in one directory). The #15677 fence holds: no listing-based session inference, ever.The two owner-native boundaries that CAN exist:
session.createdwith the session id — the session announcing itself, no listing, no timestamp guessing. The existing writer (~/.config/opencode/plugins/neo-wake-envelope.mjs) is in place and import-free; it never loads because the desktop's background dependency install fails resolving@opencode-ai/plugin@local(NpmInstallFailedError: No matching version found for @opencode-ai/plugin@local, opencode.log WARN). Thelocaltag is unresolvable; the published line exists (@opencode-ai/plugin@1.18.4on npm).The belt (daemon-side, coordinate-only): on connection-refused, the daemon may refresh port (lsof) + credentials (seat env) — never the session id. Coordinates fail soft; identity fails closed.
The Reversal
The original Out-of-Scope said "this ticket does not resurrect the plugin path; the boundary must live outside the plugin's reliability." Discovery reverses that: the plugin path is the ONLY fully owner-native boundary, and its blocker is a bounded, validated resolution failure (a version pin, not a redesign). The ticket now owns the plugin repair as the primary path, with the agent-side boot self-write as the immediate path that needs no desktop fix at all.
The Fix
@opencode-ai/pluginto the published line (^1.18.4) via~/.config/opencode/package.jsonso the background installer resolves (validated:npm installin the config dir resolves 1.18.4 cleanly;bun installequivalent per OpenCode's plugin docs). Verify on the next desktop restart (operator-owned) that the background install succeeds, the plugin loads, andsession.createdwrites the envelope.Acceptance Criteria
sessionId== the live session; probe prompt lands in-session)@localWARN), the plugin loads, andsession.createdwrites the envelope (operator-owned receipt)[L3-deferred — operator handoff needed](added 2026-07-26, PR #15944): desktop-restart verification of the shipped dependency-free plugin — theneo-wake-envelope plugin loaded … restore coverage armedline appears inopencode.logafter the next restart (settles loaded-vs-silent), and a restored session's firstsession.updatedproduceswritten-probed for restored session …(the restore-gap close). This line carries the only operator-owned validation pointer; it survives anyResolvesclose.Out of Scope
@localdespite the pin, the residual is an upstream OpenCode issue, noted for the operator)Ownership map (added 2026-07-22 after PR #15729)
@opencode-ai/plugin@^1.18.4pin is validated locally; the generator-side emission lives in #15697's seat-config generator (Iris's lane — the discovery was handed to her).Related
Live latest-open sweep: checked latest 20 open issues at 2026-07-22T01:56Z (creation) and re-verified at amendment; #15677/#15665 adjacent, neither covers this topology. A2A in-flight sweep: no competing claim.
Origin Session ID: 72c8c42d-f18a-408c-97c8-aeb1f82dd276
Retrieval Hint: "opencode desktop wake envelope boot boundary session retarget future sessions restart successor plugin local resolution agent self-write"