đ Walkthrough
OpenCode v2 recovery now distinguishes owned servers from external connections. Event requests follow their connection scope, and owned-process exit is available as a lifetime signal. Reconciliation and turn updates are coordinated with session state. Tests and provider documentation cover the updated recovery behavior.
Changes: OpenCode v2 recovery
Layer / File(s) Summary Connection lifetime and scoped event streams
packages/provider-opencode/src/server/v2/OpenCode2Client.ts,packages/provider-opencode/src/server/v2/OpenCode2Client.test.ts,packages/provider-opencode/src/server/v2/OpenCode2Server.ts,packages/provider-opencode/src/server/v2/OpenCode2Server.test.ts,packages/provider-opencode/src/server/v2/adapter.tsEvent-stream requests use a scope. Owned connections expose a process-exit signal, which the adapter races against stream following. Tests check request cancellation and process lifetime. Reconnect and reconcile active turns
packages/provider-opencode/src/server/v2/adapter.tsOwned servers retry reconciliation with capped exponential delays; external servers keep bounded retries and a timeout. Recovery mutations check the active turn under a lock. Reconciliation updates missing sessions and pending requests, and Stop handling finishes interrupted turns under the lock. Recovery test controls and scenarios
apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.testkit.ts,apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.test.ts,docs/user/providers-opencode.mdReplay controls support delayed responses and simulated process exit. Tests cover stalled and repeated recovery, request restoration, cancellation, Stop behavior, and background work. The provider guide describes reconnection behavior. Priority: âŹïž High
Estimated code review effort: 4 (Complex) | ~60 minutes
Change: Bug fix · Severity of issue fixed: High
Sequence Diagram(s)
mermaid1 sequenceDiagram 2 participant OpenCode2Adapter 3 participant OpenCode2Server 4 participant SessionState 5 OpenCode2Adapter->>OpenCode2Server: Retry event stream and read reconciliation data 6 OpenCode2Server-->>OpenCode2Adapter: Return stream or reconciliation data 7 OpenCode2Adapter->>SessionState: Lock and apply updates for the active turn 8 OpenCode2Server-->>OpenCode2Adapter: Signal owned-process exit 9 OpenCode2Adapter->>SessionState: Fail session after process exit Suggested reviewers:
maria-rcks,nexxeln
Merge Risk: đ” Low · up to 4aa8e
This change adds recovery tests for OpenCode and does not alter production behavior in the reviewed files. One test would hang rather than report a clear failure if the terminal status were wrong. This is a small test cleanup and does not block merging.
Pre-merge checks: Passed 4
Check name Status Explanation Title check Passed The title "fix(opencode): preserve owned turns during server recovery" is a conventional commit title that matches the main change. The raw summary shows recovery changes for T3-owned OpenCode servers⊠Description check Passed The description follows the repository template. It has Behavior, Why, Validation, and Recovery policy sections, and it links issue #23962. The template's Problem, Change, and Scope and approval headiâŠLinked Issues check Passed Directly linked issue [ #23962] is open. Its requirement: T3 tolerates a stall of a live shared OpenCode server, or reconnects after the server answers, and does not fail every OpenCode thread after boâŠOut of Scope Changes check Passed All changed areas connect to the recovery requirement of [ #23962] or to the behavior named in the current PR description:
-adapter.ts: owned and external recovery policies (verified in the diff), âŠFull details: Title check
Explanation
The title "fix(opencode): preserve owned turns during server recovery" is a conventional commit title that matches the main change. The raw summary shows recovery changes for T3-owned OpenCode servers: reconnect with an exponential backoff capped at 30 seconds, reconciliation without a deadline for owned processes, and turns that remain active during recovery. The title is specific and does not include file lists or vague terms.
Full details: Description check
Explanation
The description follows the repository template. It has Behavior, Why, Validation, and Recovery policy sections, and it links issue
#23962. The template's Problem, Change, and Scope and approval headings are not used as written. The Validation section lists focused tests, a changed-check command, and linked before/after harness evidence, and it names the environment and the limits of the verification. The description also gives the model and harness used. Its content covers the problem, the change, and the verification. The main gap is that it does not explicitly state the maintainer approval link or comment for scope, which the template asks for. The Behavior and Why sections give the problem and the change in substance.
Full details: Linked Issues check
Explanation
Directly linked issue [
#23962] is open. Its requirement: T3 tolerates a stall of a live shared OpenCode server, or reconnects after the server answers, and does not fail every OpenCode thread after bounded retries.Evidence at head 4aa8e263, verified in this review from the whole-PR diff against merge base 97be291 (adapter.ts and OpenCode2Server.ts):
- Owned-process recovery has no read deadline. In
reconnect,connection.processExit === undefinedselects the bounded path (reconcile.pipe(Effect.timeout(RECONCILE_TIMEOUT)),times: RECONNECT_ATTEMPTS - 1,Schedule.spaced(RECONNECT_DELAY)). Otherwise the path usesreconcile.pipe(Effect.retry({ schedule: OWNED_RECONNECT_SCHEDULE }))andtimesisundefined. The owned path therefore has no retry count or elapsed-time cutoff. - Backoff is capped.
OWNED_RECONNECT_SCHEDULEisSchedule.exponential("2 seconds")withSchedule.modifyDelayclamping each delay to 30 seconds. - The signal that marks a server as owned is
OpenCode2Server.withConnection, which now setsprocessExit: server.exitCode.pipe(Effect.asVoid). The owned-versus-external choice depends on this field. - Reconciliation does not resend prompts. It reads history and pending requests, and it settles or updates turns that are already active.
- Turn ownership is rechecked under the lock. Backfill, request reconciliation, and turn completion each run inside
lock.withPermitand checkstate.active !== turnor the request owner before they apply results. HTTP reads run outside the lock, so Stop can settle while a history read is blocked. - A session that returns
SessionNotFoundErroris markedmissing, removed from the active set, and finished as broken without blocking sibling sessions.
The issue's workaround (lowering tool priority) is not required. The issue lists longer recovery while the server stays alive as one valid direction, and the PR implements that direction.
Limits: the truncated part of the adapter.ts diff (after the reconnect hunk) was not visible. The summary states that recovery races the event stream against
processExit, and the PR tests cover process death. I did not verify that race directly. The retry options are read from the initialconnectionwhenreconnectis defined. This assumes one adapter keeps one server kind for its lifetime, as the previous review noted. CPU scheduling is unchanged, as the PR states.- Owned-process recovery has no read deadline. In
Full details: Out of Scope Changes check
Explanation
All changed areas connect to the recovery requirement of [
#23962] or to the behavior named in the current PR description:adapter.ts: owned and external recovery policies (verified in the diff), history backfill under the lock, missing-session handling for deleted sessions, request reconciliation that avoids re-asking for requests already pending, cancellation of requests absent from the latest listing, and unsettled-turn handling when interrupt times out during recovery. These implement recovery and Stop behavior described in the PR.OpenCode2Server.ts: optionalprocessExitonOpenCode2Connection, set from the owned processexitCode(verified in the diff). This is the process-death signal that recovery uses.OpenCode2Client.tsand its test:eventsrequiresScope.Scope, and/api/eventruns underHttpClient.withScope. This closes unread SSE subscriptions with their connection, as the PR states.OpenCode2Server.test.ts: checks that the spawned connection has an owned lifetime and that the process exits when the test scope closes.OpenCode2AdapterV2.testkit.ts:beforeResponseandprocessExitoptions. These support the replay tests.OpenCode2AdapterV2.test.ts: regression tests for recovery, Stop, background requests, deleted sessions, process exit, and shutdown.docs/user/providers-opencode.md: documents the new recovery and Stop behavior.
The last commit adds controlled tests for requests that arrive during reconciliation, which also fits the recovery requirement. I found no demonstrated unrelated product change. Limit: the remaining part of the adapter.ts diff and the test-file diffs were not read line by line. Their scope rests on the file summaries and the PR description.
âš Finishing Touches
đ§Ș Generate unit tests (beta)
- Create a new PR
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Comment @coderabbitai help to get the list of available commands.