Repository navigation
Effect Service Conventions: 4 issues found
Four findings, all in the shared ACP adapter extraction. The new OMP modules themselves (Drivers/OmpDriver.ts, Layers/OmpAdapter.ts, Layers/OmpProvider.ts, Services/OmpAdapter.ts, acp/OmpAcpSupport.ts, acp/AcpElicitation.ts, textGeneration/OmpTextGeneration.ts) follow the repository's Effect service conventions.
apps/server/src/provider/Layers/CursorAdapter.ts(~1120): thepromptsInFlightdecrement was moved out ofEffect.ensuringinto manual statements, so the counter leaks on failure or interruption ofsendTurn, permanently marking the session as steering and suppressingturn.completed. The comments documenting that invariant were also deleted.apps/server/src/provider/Layers/CursorAdapter.ts(1099): steered turns reuse the activeturnId, but the turn record is now always pushed instead of merged, producing duplicatectx.turnsentries and changingrollbackThreadsemantics.apps/server/src/provider/Layers/CursorAdapter.ts(1275-1283): MCP provider-session wiring (McpProviderSession.readMcpProviderSessionplus themcpServerspayload) was dropped, andAcpProviderRuntimeFactoryInputhas nothreadId, so Cursor sessions can no longer receive the t3-code MCP server.apps/server/src/provider/Layers/OmpAdapter.ts(15): the provider-neutralmakeAcpProviderAdapterfactory lives in the Cursor implementation module, so OMP depends onLayers/CursorAdapter.ts; it should move to a canonical provider-neutral module.
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.
Methodology: compared the diff against the pre-refactor Layers/CursorAdapter.ts, the sibling ACP adapters (GrokAdapter.ts, GenericAcpAdapter.ts, OpenCodeAdapter.ts), acp/AcpSessionRuntime.ts, acp/CursorAcpSupport.ts, Drivers/OpenCodeDriver.ts, Services/CursorAdapter.ts, and textGeneration/CursorTextGeneration.ts.
Checked and found conforming:
- Effect library modules imported from subpaths (namespace or module-level named imports matching existing sibling files); no consolidated imports from
"effect". - New adapter shape (
OmpAdapterShape extends ProviderAdapterShape<ProviderAdapterError>) matches the documented driver-bundle pattern; no strayContext.Servicetag or shadow shape. makeOmpTextGeneration/makeOmpAdapteracquireCrypto,ChildProcessSpawner,FileSystem,Path,HttpClient,ServerSettingsService, andProviderEventLoggersfrom the Effect environment; noManagedRuntime.make,runPromise, orrunPromiseExitintroduced.- Error handling uses
Effect.catchTags, existing tagged error classes with structural fields plus preservedcause, andconst isTextGenerationError = Schema.is(TextGenerationError). Layer.succeedoccurrences are the pre-existing ACP spawner seam used bymakeCursorAcpRuntime, mirrored bymakeOmpAcpRuntime.
Additional non-blocking observation (not commented inline): the case "EventStreamBarrier": yield* Deferred.succeed(event.acknowledge, undefined) branch was removed from the notification stream handler. No current Cursor/OMP code path calls AcpSessionRuntime.drainEvents, so it is latent, but drainEvents would now block forever for these adapters.
🔍 Agent activity — 1 tool call
💭 I'm looking at the diff to check for Effect service convention violations, focusing on the new files being added for the Omp provider implementation. For
Services/OmpAdapter.ts, there's a standaloneOmpAdapterShapeinterface, which technically violates the convention about not retaining separate shape interfaces. However, this follows the existing repo pattern with similar adapters likeCursorAdapter.ts, and it's part of the driver architecture that uses shapes rather than service tags, so it's probably consistent with how the codebase is structured. InOmpAcpSupport.ts, I'm seeingmakeOmpAcpRuntimeacceptingchildProcessSpawneras a dependency injection parameter from a service, which I need to examine more closely to determine if there's a violation. named imports from the subpath"effect/unstable/process"rather than importing the namespace directly. Looking at the actual imports across the files—OmpProvider uses named imports from the subpath, while OmpDriver imports as a namespace—there's inconsistency in how the convention is being applied, though it's not clear if the named import approach violates the stated rule about consolidating imports from"effect". In OmpDriver.ts, there's a detail field that interpolates the cause message directly, which violates the pattern of deriving wrapper messages from stable attributes rather than copying from the cause. This is newly introduced code, so it counts as a violation—though I should check if existing drivers like CursorDriver or OpenCodeDriver follow the same pattern to determine if it's a retained issue or a new problem. For OmpProvider.ts, thecheckOmpProviderStatusfunction extracts the failure message from the version probe and includes it in user-facing strings, which mirrors what existing providers do, so that's likely following established convention.… I'm noticing that the OMP code follows established patterns from other parts of the codebase—like how OpenCodeDriver handles ProviderDriverError with cause.message, or how CursorAcpSupport injects childProcessSpawner. The question is whether these are just standard repository conventions or actual violations I should flag. The instructions say to treat the guidelines as authoritative when older code differs, but also to only report violations that are actually introduced or changed in this pull request's scope. Looking at the OmpDriver's detail field more closely—it's constructing a message with a prefix plus cause.message, which does mirror the pattern the guidelines warn against, but it's also preserving the cause object itself. Since every driver seems to follow this same pattern, flagging all of them would be noise. I should dig deeper into the actual behavioral changes, especially in OmpTextGeneration.ts, TextGeneration.ts, and the AcpSessionRuntime to see if there are clearer issues around elicitation support or other concrete problems.… The OmpTextGeneration and Services/OmpAdapter follow established patterns without issues, but I'm noticing thatmakeAcpProviderAdapterwas extracted into CursorAdapter.ts and then imported back into OmpAdapter.ts—I need to check if this cross-file import violates the project's file organization conventions. The conventions emphasize keeping a service's schemas, errors, Context tag, construction, and layer together in one canonical module, and they warn against leaving compatibility re-exports. A Cursor-named module now owns a generic factory used by OMP, which feels like a module-ownership problem, though it's not explicitly listed as a convention violation. There's also the issue ofdefinition.cursorExtensions?: booleanappearing in what should be a provider-neutral factory—that's a design smell rather than a convention breach. I should focus on flagging actual convention violations rather than design concerns. Looking at the new code,OmpAcpSupport.tsmirrors the Cursor pattern withmapErrorcontexts andmakeOmpAcpRuntimeusingLayer.buildandLayer.succeed. The real issue is thatmakeOmpAcpRuntimeacceptsChildProcessSpawner.ChildProcessSpawner["Service"]as a parameter and re-provides it viaLayer.succeed, when it should instead yield that dependency from the environment—this mirrors what Cursor does, but the check instructions say to treat the stated conventions as authoritative even when older code differs, so this is a legitimate finding. However, I'm reconsidering:OmpTextGenerationandOmpAdapteralso acquire the service from the environment and pass it down, following the same pattern as the establishedCursorAcpSupport. Given the guidance not to demand unrelated repository-wide cleanup and that the new module mirrors an established sibling, a reasonable reviewer probably wouldn't fail on this for parity reasons. Let me dig deeper into the elicitation handling inAcpSessionRuntimeto see if there's something more specific to flag. The diff also removes the EventStreamBarrier case that was acknowledging events, which could leave barriers hanging. More critically, the refactor drops themcpServersconfiguration when creating the Cursor ACP runtime — previously the adapter passed MCP server details through, but now the generic factory doesn't include them, which is a functional regression that would break MCP integration. — and that's a critical safety issue. The refactored code manually decrementspromptsInFlightbefore each early return and after the prompt resolves, but if the prompt itself fails, or if the fiber gets interrupted, that decrement never happens, leaving the counter leaked. The originalEffect.ensuringpattern guaranteed the decrement would run regardless of how the operation exited.… I'm wondering ifdrainEventscould hang indefinitely if CursorAdapter stops handling the EventStreamBarrier event and never acknowledges it. Let me check if CursorAdapter even callsdrainEventsanywhere. Also, I notice the switch statement no longer has a case for EventStreamBarrier, but TypeScript's union type forevent._tagstill includes it, so the switch is now incomplete.… CursorAdapter skips the drainEvents call, so removing the barrier case wouldn't break anything since barriers are never actually used there anyway. That said, dropping the case means if drainEvents ever gets called in the future, it could hang. The TypeScript switch doesn't have exhaustiveness checking here since there's no default clause and the function returns void, so it compiles fine. But there are bigger issues to address. Looking… I'm noticing that the CursorAdapter in the Layers directory has stopped referencing McpProviderSession, which means it's no longer injecting the MCP server for Cursor sessions—a regression since other adapters like GenericAcpAdapter and GrokAdapter still do this. I need to figure out whether the Layers/CursorAdapter is still actively used or if it's been replaced by the orchestration-v2 system. Looking at the driver setup, Layers/CursorAdapter is used by Drivers/CursorDriver, which is in BUILT_IN_DRIVERS, so it's definitely live. This behavior change falls under the Effect Service Conventions check since the rules cover preserving invariants and documenting specification changes—dropping MCP wiring is a behavioral change during a service refactor that should be reported. The check's scope includes violations of the listed conventions, and removing documented invariants like the promptsInFlight comments and the ensuring-based decrement counts as a legitimate finding. I'm identifying four key findings: the Effect.ensuring decrement replaced by manual decrements creates a counter leak on failure or interruption with deleted invariant comments; the MCP provider session wiring dropped entirely with no test coverage; the ctx.turns merge for steered turns removed causing duplicate turn records; and the EventStreamBarrier case removal that would cause drainEvents to hang, though that's lower severity since drainEvents isn't currently reachable. I'll include inline comments for the first three violations and mention the fourth in the summary to avoid noise. Now I'm checking the new files for any genuine Effect-service-convention violations. OmpProvider exports utility functions without a service, and its ChildProcess imports match the pattern used elsewhere. AcpElicitation is pure helpers, so that's fine. For OmpDriver's use of cause.message, it aligns with the repo-wide pattern across other drivers, so I won't flag it as a separate violation.… I'm seeing two structural issues here. The rollback mechanism splices by the turns array length, so duplicating an entry for a steered turn means rolling back "1 turn" only removes the second prompt of a merged turn—that's a behavioral shift. And themakeRuntimefactory doesn't include threadId in its input type, so the Cursor runtime can't retrieve the MCP session per thread anymore since that context got dropped from the abstraction. The model selection error mapping changed too—previouslyapplyCursorAcpModelSelectionusedinput.mapErrorwith "session/set_config_option", but nowdefinition.applyModelSelectionhandles the mapping including the "session/set_model" distinction, which is actually cleaner. Thecursor/ask_questionhandler got moved under acursorExtensionscheck and there's a newhandleElicitationregistered for all providers, but for Cursor specifically both handlers are now active, which affects howrespondToUserInputroutes the method label. I'm debating whether to flag the shared ACP adapter factory living in CursorAdapter.ts as a module ownership issue — it should probably live in a provider-neutral location like acp/AcpProviderAdapter.ts rather than in an implementation-specific layer file. It's a low-severity concern but actionable and worth including as an inline comment on the OmpAdapter import line, though I need to be careful not to pile on too many findings. Now I'm verifying that the line numbers I want to comment on actually appear in the diff hunks — the GitHub API requires inline comments to reference lines that are part of the changed code. Let me check the exact positions for CursorAdapter.ts and OmpAdapter.ts against the head diff to make sure they're all valid. I'm targeting line 1099 for the turns reuse issue, line 1120 for the promptsInFlight logic, and the range around 1275-1283 for the makeCursorAcpRuntime call. Now I'll draft the actual review comments, starting with the turns duplication problem on line 1099. ThepromptsInFlightcounter isn't being decremented properly when prompts fail or get interrupted—it stays incremented, leaving the session stuck in a "steering" state where the turn never completes. I need to wrap the prompt handling inEffect.ensuringto guarantee the decrement happens. There's also a missingthreadIdfield in the MCP runtime factory input, which means the MCP servers aren't being wired up for Cursor sessions. And there's an issue with the OmpAdapter import that needs addressing.…
1. modify_pr — Reviewed the OMP provider addition and the extraction of the shared ACP adapter factory. The new OMP driver/adapter/provider/text-generation modules follow the repository's Effect service conventions (subpath namespace imports, Foo["Service"] shapes, Effect.fn constructors, dependencies acquired from the environment, Effect.catchTags, Schema.is predicate). The findings below are in apps/server/src/provider/Layers/CursorAdapter.ts, where the generalization into makeAcpProviderAdapter dropped invariants and documented behavior that the previous Cursor implementation guaranteed.
Agent Credits: 190 credits