Skip to content

fix(provider): deliver opencode steers immediately at the next tool b…

MacroscopeApp / Macroscope - Effect Service Conventions failed Aug 13, 2026 in 1m 24s

Effect Service Conventions: 1 issue found

apps/server/src/provider/Layers/OpenCodeAdapter.ts (lines 1490-1518) — the new steer dispatch assigns an already-invoked SDK Promise to sendPrompt and passes () => sendPrompt to runOpenCodeSdk. The request is therefore started eagerly outside the Effect, and a synchronous throw from the SDK call escapes Effect.tryPromise's catch, becoming a defect rather than a typed OpenCodeRuntimeError. That bypasses toRequestError and the tapError handler that resets session state and emits turn.aborted. Fix: make sendPrompt a thunk (const sendPrompt = () => ...) and pass it directly to runOpenCodeSdk.

An inline review comment with the suggested fix has been posted.

Details

Note

Your check run agent prompt is: .macroscope/check-run-agents/effect-service-conventions.md
More information about how Check Run Agents work can be found in our Help Center.

Scope examined: apps/server/src/provider/Layers/OpenCodeAdapter.ts and apps/server/src/provider/Layers/OpenCodeAdapter.test.ts (the only in-scope TypeScript files in the diff), plus apps/server/src/provider/opencodeRuntime.ts for the runOpenCodeSdk/OpenCodeRuntimeError contract.

No other convention violations were introduced: no new effect consolidated imports, no new service tags/layers, no ManagedRuntime.make, Layer.succeed, runPromise, or service-instance parameters, and no new error classes or discriminators. Pre-existing OpenCodeRuntimeShape/OpenCodeAdapterShape usages are untouched legacy code and were not flagged. The test additions extend the existing runtime test double and assert the new steer delivery behavior, which is appropriate for a backend behavior change.


🔍 Agent activity — 1 tool call

💭 I'm examining the changes to OpenCodeAdapter.ts, particularly around the prompt-sending logic. There's a potential issue with how sendPrompt eagerly creates a promise by calling context.client.v2.session.prompt(...) outside the Effect thunk, which violates Effect service conventions. The real problem is that sendPrompt creates the SDK promise immediately when the function is evaluated, rather than deferring it until the effect actually runs—this breaks Effect's laziness guarantees and messes up interruption and retry logic. I'm wondering if this violates the conventions around runtime boundaries and dependency hiding, or if it's strictly an Effect correctness issue. I'm realizing that using Effect.tryPromise with an eager promise creates a timing issue—the promise starts before the Effect actually runs, which means rejections can happen synchronously and escape as unhandled if the effect gets interrupted, plus the request fires regardless of whether the surrounding effect ever executes. The core problem is that this breaks laziness; any synchronous errors during promise construction would leak out as defects rather than being properly typed errors. This actually violates the conventions around runtime boundaries and error typing—the failure should stay typed within Effect so callers can handle it, not bypass the recovery path and leave the session in a "running" state. I should also scan the rest of the diff for other convention violations, like whether the test file is retaining interfaces it shouldn't. I'm settling on posting a focused inline comment about the eager promise breaking Effect laziness semantics, since it's genuinely introduced in this PR and violates the core Effect discipline that effects must remain lazy and errors typed in the Effect channel. I'll suggest wrapping the calls inside a thunk function to defer execution and preserve the error type. For the actual fix, I need to make two changes: convert const sendPrompt = to const sendPrompt = () => on line 1490, and then update the call site at line 1518 from () => sendPrompt to just sendPrompt. Since these are in separate locations, I'll use a unified diff format in a single comment to show both edits clearly rather than splitting across multiple suggestions.

1. modify_pr — Reviewed the OpenCode adapter change against the Effect service conventions. One finding on the new steer/prompt dispatch: the SDK call is now evaluated eagerly outside the runOpenCodeSdk thunk, which breaks the lazy-effect boundary and can drop the failure out of the typed error channel.


Agent Credits: 44 credits