Skip to content

fix(server): bound OpenCode version probes

MacroscopeApp / Macroscope - Effect Service Conventions failed Aug 30, 2026 in 1m 23s

Effect Service Conventions: 1 issue found

apps/server/src/provider/Layers/OpenCodeProvider.ts — the new Effect.timeoutOrElse handler manufactures new Error("OpenCode CLI version probe timed out.") solely to populate the required cause field of OpenCodeProbeError, and that synthetic message merely restates detail. Conventions call for not inventing an Error to fill cause; make cause optional on the error class and construct this pure timeout failure with structural attributes only. The hardcoded "4 seconds" in detail also duplicates OPENCODE_VERSION_PROBE_TIMEOUT and can drift from the actual duration.

No other convention violations found: imports use effect/* subpath namespaces, the added test uses TestClock and existing test doubles, and the process-group termination added in apps/server/src/provider/opencodeRuntime.ts mirrors the existing startOpenCodeServerProcess cleanup inside the same service constructor (no hidden runtime, no ManagedRuntime.make, no runPromise).

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.

Files reviewed (in scope):

  • apps/server/src/provider/Layers/OpenCodeProvider.ts
  • apps/server/src/provider/Layers/OpenCodeProvider.test.ts
  • apps/server/src/provider/opencodeRuntime.ts

Checks performed:

  • Import style: all Effect modules imported as subpath namespaces (effect/Fiber, effect/testing/TestClock); no consolidated named imports from "effect".
  • Service definition/layout: no service tags, make, or layer exports were created, moved, or renamed; OpenCodeRuntimeShape usage in the test double is pre-existing and not introduced by this PR.
  • Dependency acquisition: makeOpenCodeRuntime continues to acquire ChildProcessSpawner, NetService, and HostProcessPlatform from the environment; no service-instance parameters, Layer.succeed hiding runtime work, ManagedRuntime.make, or runPromise/runPromiseExit were added.
  • Errors: OpenCodeProbeError is a pre-existing Data.TaggedError; only the new construction site was evaluated. The synthetic cause is the single finding.
  • Change discipline: backend behavior changed (probe now times out), and a focused TestClock-driven test was added — appropriate.

🔍 Agent activity — 6 tool calls

💭 I'm looking at the changed files, particularly OpenCodeProvider.ts, which adds a timeout and constructs an error with a synthetic cause just to populate a detail field—something that seems worth flagging as unnecessary. I should also check opencodeRuntime.ts, where process.kill is called inside Effect.sync, since detached spawn/kill logic in a runtime adapter could be fine or could warrant closer inspection depending on context. Let me pull up these files to confirm.

1. browse_code — apps/server/src/provider/Layers/OpenCodeProvider.ts

2. browse_code — apps/server/src/provider/Layers/OpenCodeProvider.ts

💭 I'm noticing the existing OpenCodeProbeError requires a cause: unknown field, but the new code fabricates an Error just to fill that slot, which violates the convention against manufacturing errors solely to populate cause. Since a timeout is a genuine domain failure without an underlying error, I need to figure out what to actually pass there instead.

3. browse_code — apps/server/src/provider/opencodeRuntime.ts

💭 I'm noticing the code calls process.kill directly instead of going through the ChildProcessSpawner service, which bypasses the dependency abstraction convention against using global Node APIs in service constructors. I want to check if there's an existing abstraction like a CurrentProcess or HostProcessPlatform service that should be used instead, and I'm also questioning how child.pid is being handled.

4. git_tools — process.kill

5. git_tools — detached

💭 I notice the new code mirrors an existing pattern elsewhere in the file, so it's consistent with established conventions rather than a new issue. The real candidate finding is the manufactured Error used for cause in OpenCodeProbeError, along with detail duplicating the message—checking whether that violates the guidance against fabricating errors. The timeout construction here doesn't wrap an actual underlying failure, so cause should really be optional rather than requiring a manufactured Error—that's a genuine issue introduced in this PR, separate from the pre-existing Data.TaggedError pattern which I shouldn't flag. I'm also noting the timeout message duplicates a constant, and that detail closely mirrors the manufactured cause's message, reinforcing that this Error is being fabricated just to satisfy the type rather than reflecting a real failure. Checking the test file, the imports and helper usage look standard, and OpenCodeRuntimeShape is pre-existing so I won't flag it under the "no standalone Shape" guidance. I want to verify Effect.timeoutOrElse has the right signature in this Effect version and confirm the timeout composition after mapError behaves as expected. For the process finalizer, I'm checking that child.pid is a valid property in this Effect version, that Windows kill handling wraps failures with Effect.ignore, and that the explicit detached flag is just a no-op restating the existing default behavior—so none of these are real convention issues, leaving just the one finding to report. Since the check defaults to a failure state whenever there are findings, I think this violation is enough to trigger that outcome rather than an "all clear" pass. I'm now tracing how OpenCodeProbeError.cause gets consumed elsewhere—confirming the squashed cause only ever surfaces through detail, meaning the manufactured cause Error I flagged is never actually read anywhere except possibly logging, confirming it's purely decorative and worth flagging. The fix would be making cause optional on the class and dropping it, though an inline suggestion can't span the class definition itself. Since this fix touches both the class definition and a separate call site, it's a multi-hunk change spanning different locations, so I'll skip a formal diff and just describe the fix in prose, pinpointing the construction call around lines 407-410 in OpenCodeProvider.ts for the inline comment. I'm confirming that cause is indeed a required field in the Data.TaggedError, so the failure state needs to include it explicitly when posting the review comment.

6. modify_pr — Effect service conventions review: one finding on the new timeout failure construction in apps/server/src/provider/Layers/OpenCodeProvider.ts. The process-group cleanup in opencodeRuntime.ts follows the existing pattern in startOpenCodeServerProcess and the added timeout test is appropriately focused.


Agent Credits: 53 credits

Token usage: 10 uncached input · 129,053 cache-read input · 50,552 cache-write input · 4,933 output
Agent Credits may also include non-token charges from external tools such as web research.