Skip to content

refactor(provider): extract ACP adapter factory

MacroscopeApp / Macroscope - Effect Service Conventions failed Aug 11, 2026 in 3m 56s

Effect Service Conventions: 1 issue found

  • apps/server/src/provider/Drivers/OmpDriver.ts: ProviderDriverError is constructed with detail: \Failed to build OMP snapshot: ${cause.message ?? String(cause)}`while also passingcause, so the wrapper's messageis derived from the cause instead of only stable structural attributes. Use a static detail and letcause` carry the underlying failure text.

No other Effect service convention violations were found in the changed scope: Effect modules are imported as subpath namespaces, service dependencies (Crypto, ChildProcessSpawner, FileSystem, Path, HttpClient, ServerSettingsService, ProviderEventLoggers) are acquired from the environment via yield*, no ManagedRuntime/runPromise/hidden Layer.succeed runtimes were introduced, errors use Schema.TaggedErrorClass with structured fields and preserved cause, and known tagged failures are handled with Effect.catchTags.

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 examined in scope:

  • apps/server/src/provider/Drivers/OmpDriver.ts
  • apps/server/src/provider/Layers/AcpAdapter.ts (provider-neutral factory extracted from CursorAdapter.ts)
  • apps/server/src/provider/Layers/CursorAdapter.ts
  • apps/server/src/provider/Layers/OmpAdapter.ts, OmpProvider.ts
  • apps/server/src/provider/Services/OmpAdapter.ts
  • apps/server/src/provider/acp/OmpAcpSupport.ts, AcpElicitation.ts, AcpAdapterSupport.ts
  • apps/server/src/textGeneration/OmpTextGeneration.ts, TextGeneration.ts
  • apps/server/src/serverSettings.ts, packages/contracts/src/settings.ts, packages/contracts/src/model.ts
  • Web/mobile touched files (Icons.tsx, providerIconUtils.ts, providerDriverMeta.ts, composerDraftStore.ts, session-logic.ts) contain no Effect service code.

Notes on items considered and not flagged:

  • Services/OmpAdapter.ts adds an empty OmpAdapterShape extends ProviderAdapterShape<ProviderAdapterError>. There is no Context.Service tag for provider adapters (they are bundled per-instance by drivers), and every sibling adapter has the same shape anchor, so this is not a retained-shape violation.
  • makeOmpAcpRuntime accepts childProcessSpawner: ChildProcessSpawner["Service"] and wires it with Layer.succeed. The spawner is acquired from the Effect environment by the adapter/text-generation constructors (yield* ChildProcessSpawner.ChildProcessSpawner) and only threaded into the ACP session layer, matching CursorAcpSupport/GrokAcpSupport; this is not fake dependency injection.
  • applyOmpAcpModelSelection takes a runtime handle object, which is a session-scoped value rather than an owned service dependency.
  • Relocated code in Layers/AcpAdapter.ts (e.g. detail: cause.message on ProviderAdapterProcessError) was moved verbatim from CursorAdapter.ts and is pre-existing legacy behavior, so it was not flagged in this mechanical extraction.

🔍 Agent activity — 1 tool call

💭 I'm checking what changed between the previous commit and the current head to see if earlier feedback was addressed, particularly around the factory location moving from CursorAdapter.ts to AcpAdapter.ts. Now I need to review the diff and verify whether other prior issues were fixed, then examine the new code for any Effect service convention violations, starting with the imports in OmpDriver.ts. Looking at the imports more carefully — HttpClient from "effect/unstable/http" is a named import from a package subpath, which aligns with how other drivers handle service modules. The same pattern appears in OmpProvider.ts with ChildProcess and ChildProcessSpawner from "effect/unstable/process". In OmpTextGeneration, makeOmpTextGeneration correctly takes ompSettings as config and yields Crypto and ChildProcessSpawner from the environment. OmpAcpSupport shows service injection through makeOmpAcpRuntime accepting ChildProcessSpawner as an input parameter, which matches the existing pattern from the Cursor implementation. Now I'm checking whether the OmpAdapter.ts service shape follows conventions — it defines OmpAdapterShape as a standalone interface extending ProviderAdapterShape, but the convention suggests defining the service interface inline within Context.Service instead of keeping a separate FooShape interface. This is a new file, so I need to see if there's an existing pattern in CursorAcpSupport or similar files that might justify this approach. Looking at the error handling and TextGenerationError construction — the detail field is static text rather than copying from cause.message, which is fine and consistent with how OmpTextGeneration handles it. Now I'm checking whether resolveOmpModelSelection special-casing the OMP driver inside the generic TextGeneration module violates architectural conventions, though it's not a direct Effect service issue. I'm also revisiting the promptsInFlight decrement logic in AcpAdapter.ts — it's manually decremented on early returns and after turn completion, which mirrors the same correctness concern flagged in the prior CursorAdapter implementation, though this is more of a behavioral issue than a convention violation.… Shape is a plain contract type following repo conventions without the Context.Service tag, so there's no violation there. Now I'm checking the new files for import patterns and error handling—O… I'm noticing there's a pattern violation in the new code where ProviderDriverError is being constructed with a detail field that directly embeds the cause message, which goes against the convention of deriving wrapper messages exclusively from stable structural attributes rather than from the underlying cause. Let me check if this is a new issue or if it's been present elsewhere.… I'm noticing that OmpDriver follows the same pattern as all the other drivers in the codebase, mirroring a legacy approach that seven siblings also use. There's one exception—OpenCode2Driver—that uses a cleaner style, which makes this a potential issue worth flagging. The detail about copying cause.message is pretty minor though, and given that this is a new file and the guideline against demanding unrelated repository-wide cleanup, I'm leaning toward not failing on this particular pattern. Now I need to dig into the truly new modules like OmpTextGeneration, OmpProvider, AcpAdapter, and OmpAcpSupport to find stronger issues. I'm particularly concerned about OmpAcpSupport.makeOmpAcpRuntime—it's accepting a ChildProcessSpawner service and then using Layer.succeed to provide it, which looks like the exact flagged pattern where a factory accepts an implementation dependency it shouldn't own. But wait, this mirrors CursorAcpSupport and GrokAcpSupport exactly, so it's a deliberate repo convention rather than a new mistake. The key insight is that the AcpAdapter acquires ChildProcessSpawner from the environment via yield*, then passes it down to makeRuntime as an internal helper—that's legitimate dependency flow, not fake DI. Same pattern in OmpTextGeneration where it acquires the spawner from the environment and passes it through. This is actually fine. Now I'm checking the full AcpAdapter.ts and OmpProvider/OmpTextGeneration files for any hidden Layer.succeed implementations or non-namespaced effect imports. The imports look properly namespaced, though I notice AcpAdapter.ts has an inline type import for AcpError. Looking at error handling patterns, OmpTextGeneration uses Effect.catchTags correctly. The isTextGenerationError constant is a private Schema.is predicate used inline rather than wrapped in a redundant function, which follows the convention. I should compare this with CursorTextGeneration to see if they're consistent.… OmpTextGeneration follows the same pattern as CursorTextGeneration, so that checks out. Now I'm verifying the imports in AcpAdapter.ts to make sure they follow namespace conventions and don't introduce any hidden runtime issues. The diff already shows the Effect imports are properly namespaced, and the Contracts imports use named imports which are allowed. I need to confirm that OmpDriver's HttpClient import from "effect/unstable/http" matches what other drivers are doing.… The imports look fine—ServerConfig is imported as a named import from config.ts, which follows the existing pattern used elsewhere like CursorAdapter, so there's no service convention violation here. I'm circling back to a previous concern about the factory being located in the Cursor-named module. Now I'm checking whether there are any genuine new violations to flag. The OmpAdapterShape interface in Services/OmpAdapter.ts is an empty extension of ProviderAdapterShape<ProviderAdapterError>, mirroring the pattern across CursorAdapter and other sibling files. The convention against standalone FooShape interfaces applies specifically to Context.Service definitions, but since there's no Context.Service tag here, that rule doesn't cleanly apply—and the pattern is already established across five similar files in the repo. The bigger issue is in OmpDriver where the detail field copies cause.message and then uses it to construct the wrapper message. That's explicitly flagged in the conventions: don't add a detail field that merely copies cause.message, and derive the wrapper's message exclusively from stable structural attributes, never from cause. This is a new occurrence in a new file, though sibling drivers follow the same pattern. I should flag this as an inline suggestion since OpenCode2Driver shows the repo is already migrating to the cleaner form. For ProviderAdapterProcessError in AcpAdapter, the detail: cause.message pattern is relocated code from CursorAdapter rather than new logic, so flagging moved lines for a legacy pattern would be noisy—I'd rather focus on concrete violations introduced in the changed scope. Let me verify there aren't other new error constructions in the… I'm looking at a newly added code pattern that violates the stated instructions—it's creating a detail field that just copies the cause message and uses it to construct the wrapper, when cause is already preserved. It's a trivial fix on a single line, but I'm weighing whether this counts as a clear convention violation that should fail the check or just a minor finding worth flagging. The guidance says findings trigger failure by default, but I'm second-guessing whether this one triviality alone is substantial enough. Let me dig deeper into the other files I haven't fully reviewed yet—checking if there are more meaningful violations in the provider status functions or elsewhere that would make this assessment stronger. I'm checking the web-side changes and the OmpAdapter type assertion — both seem fine and consistent with existing patterns. The OmpDriver detail duplication around error handling is still the strongest candidate issue, though prior runs didn't flag similar duplication, which makes me hesitant. The current prompt is specifically about Effect service conventions, so I should focus on violations of those rather than general code quality concerns. Let me reconsider what actually violates the stated conventions. the wrapper message ends up containing the cause message, which violates the convention that prohibits deriving the wrapper's message from the cause. I should post an inline comment on that line flagging this issue. Now I'm weighing whether to fail the check or mark it neutral—the instructions say violations warrant failure, but since this mirrors legacy patterns across seven sibling files, the author might reasonably argue this is existing repo style rather than new problematic code.…

1. modify_pr — Effect service conventions review of the new OMP provider modules (OmpDriver, OmpAdapter, OmpProvider, OmpAcpSupport, OmpTextGeneration) and the extracted provider-neutral Layers/AcpAdapter.ts. Imports, service acquisition (yield* Crypto.Crypto, yield* ChildProcessSpawner.ChildProcessSpawner, yield* ServerSettingsService), layer/runtime boundaries, and Effect.catchTags usage all look consistent with the conventions — no hidden runtimes, ManagedRuntime, or runPromise were introduced. One error-modeling nit below.


Agent Credits: 227 credits