Skip to content

fix(provider): serialize ACP turn setup

MacroscopeApp / Macroscope - Effect Service Conventions failed Aug 12, 2026 in 4m 52s

Effect Service Conventions: 1 issue found

Finding

  • apps/server/src/provider/Layers/AcpAdapter.ts (rollbackThread, L1163-1169): the shared ACP factory replaces Cursor's previous in-memory turn trimming with an unconditional ProviderAdapterRequestError. This is a caller-visible behavior change (ProviderService.rollbackConversation and the checkpoint-revert reactor now fail for Cursor threads) introduced by a structural refactor with no focused test. Suggested fix: make rollback support part of AcpProviderAdapterDefinition, or add a Cursor adapter test pinning the new failure.

Verified clean

  • Effect module namespace imports and @t3tools/contracts named imports in all new files.
  • OmpDriver, makeOmpAdapter, makeOmpTextGeneration, makeOmpAcpRuntime: dependencies acquired via yield* Service; no ManagedRuntime/runPromise in service code; spawner injection matches the existing CursorAcpSupport runtime-building pattern.
  • Error modeling: TextGenerationError/ProviderAdapterError/ProviderCommandNotFoundError carry structural attributes plus cause; Effect.catchTags used for known tags; no new detail: cause.message wrappers (the previously flagged OmpDriver case is fixed).
  • File layout follows the existing Services/*Adapter.ts shape anchor + Layers/*Adapter.ts implementation split; no compatibility shims left behind by the Cursor extraction.
  • Previously flagged issues (event-stream barrier acknowledgement, promptsInFlight ensuring accounting, steered-turn record merge, Cursor MCP server wiring, provider-neutral factory module placement) are all resolved at this head.

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/Layers/AcpAdapter.ts, Layers/OmpAdapter.ts, Layers/OmpProvider.ts, Layers/CursorAdapter.ts, Services/OmpAdapter.ts, Drivers/OmpDriver.ts, provider/acp/OmpAcpSupport.ts, provider/acp/AcpElicitation.ts, provider/acp/AcpAdapterSupport.ts, provider/builtInDrivers.ts, textGeneration/OmpTextGeneration.ts, textGeneration/TextGeneration.ts, serverSettings.ts, packages/contracts/src/settings.ts, packages/contracts/src/model.ts, plus the touched web/mobile UI files and tests.

Comparisons used as convention baselines: Drivers/CursorDriver.ts, acp/CursorAcpSupport.ts, textGeneration/CursorTextGeneration.ts, Services/CursorAdapter.ts, providerSnapshot.ts, and the sibling GrokAdapter/HermesAdapter/ClaudeAdapter rollback implementations.


🔍 Agent activity — 1 tool call

💭 I'm checking the current PR state against previous findings to see what's been addressed—the EventStreamBarrier case in AcpAdapter looks fixed, the error message in OmpDriver has been updated, and CursorAdapter's Effect.ensuring appears to be restored. Looking at the new changes, I notice the factory has been moved to Layers/AcpAdapter.ts rather than the provider-specific path mentioned before, which is arguably a cleaner approach since it's now provider-neutral. I'm also examining the service injection patterns—OmpAcpSupport accepts ChildProcessSpawner as a service instance parameter, which mirrors the existing Cursor pattern, and OmpDriver uses Effect.provideService for dependency injection, consistent with other drivers. For the OmpAdapterShape interface in Services/OmpAdapter.ts, the conventions say to avoid standalone FooShape interfaces, but this follows the existing repo pattern where CursorAdapterShape exists similarly. Since there's no Context.Service declaration here—these are per-instance adapter shapes—it's consistent with prior conventions and shouldn't be flagged as a violation. Now I'm checking the actual error handling in files like OmpTextGeneration.ts to verify TextGenerationError construction is correct, particularly around cause handling. In OmpProvider.ts, I'm seeing message derivation from the version probe failure and command output being included in detail messages. This looks like a pre-existing pattern across providers, so it's likely not a new violation.… The OmpAcpSupport implementation mirrors the CursorAcpSupport pattern, which is good. I'm noticing a convention issue in AcpAdapter.ts where ProviderAdapterProcessError copies cause.message into the detail field, but the conventions explicitly forbid this pattern. This is existing behavior from CursorAdapter that's been retained, though it seems to conflict with what the conventions specify about reporting only relevant context. The prior run flagged this same pattern in OmpDriver.ts and the author fixed it there, so I'm weighing whether to flag it here as a moved line or let it pass since it's unchanged code being relocated. I'm leaning toward not flagging it since it's a verbatim move and other adapters follow the same approach, but it's worth noting the inconsistency. Now I'm checking whether the new code in this PR introduces any error convention violations, starting with OmpTextGeneration.ts which uses proper error tag handling with Schema validation. h a detail string containing the instanceId — that's fine. Now I'm going through the convention checklist more systemat… I'm noticing that detailFromResult is exported from providerSnapshot.ts but appears to only be used by OmpProvider now. Let me check its definition and see if other providers have similar helper functions. A grep search shows it's only referenced by OmpProvider, so I should look at the file around line 100 to understand what's happening. I'm also noting something odd about the merge base being f5fce74 while the git log shows commits dated 2026-08-03, which suggests this is a large monorepo with multiple providers like PiAgent and Hermes.… The OmpProvider takes a different approach by implementing its own command execution logic instead of reusing the shared utility, which duplicates some functionality like the Windows command-not-found error check but with a different regex pattern. I'm now examining the new code areas that interact with Effect service conventions more closely, starting with the Acp layer. Looking at the new OmpAdapter files, they follow the existing repo pattern of splitting Services and Layers directories, similar to how CursorAdapter is structured. But the conventions suggest consolidating service definitions into a single canonical module — the question is whether creating new split files for OmpAdapter violates that principle, since Services/OmpAdapter only contains an interface alias while the actual implementation lives in Layers/OmpAdapter.… Looking at the OmpAdapter Services file—it follows the existing pattern, so that's fine. Now I'm examining the TextGeneration.ts changes more carefully, particularly the TextGenerationProvider type assignment and the OMP-specific handling, plus how resolveOmpModelSelection uses instance.snapshot.refresh in the router, which isn't in the convention list. I need to check the full AcpAdapter.ts body for patterns like Effect.forkI. The ProviderAdapterProcessError detail that copies cause.message is actually moved from CursorAdapter rather than new, so I'll skip flagging it. Let me scan the rest of AcpAdapter for convention issues like the duplicated selectAutoApprovedPermissionOption helper and Effect.catch usage. There's also a critical behavior change in CursorAdapter where rollbackThread now fails instead of trimming—that's a caller-visible error message change that violates the convention about preserving error messages across structural refactors.… I'm noticing that Cursor's rollbackThread behavior changed from trimming to failing, which is a visible change for callers—specifically ProviderService.rollbackConversation will now fail for Cursor threads. This is a behavioral change introduced in the refactor without corresponding tests, and according to the guidelines, error messages exposed through HTTP/RPC responses count as behavior that needs to be preserved. If backend behavior is changing, I need focused tests to cover it. I'm wondering if this change was… The Cursor rollback behavior changed without a corresponding test—it now errors instead of trimming turns and returning a snapshot. I need to check if there's an existing test in CursorAdapter.test.ts that covers rollback behavior or assertions about turn trimming. It's a change-discipline violation: the refactor silently altered Cursor's rollback behavior without a focused test. I should flag… OmpDriver mirrors CursorDriver's structure with its own imports for child process spawning and HTTP client functionality. It also lists BackgroundPolicy in the environment type without yielding it, which appears to be a transitive requirement from the managed server provider setup. Now I'm examining the OmpProvider implementation...… I'm checking whether instance.snapshot.refresh actually fails by looking at the snapshot service type in ProviderDriver—if refresh has an error channel, it would need to match TextGenerationError, but it's probably an Effect<ServerProvider> with never as the error type, which is fine. Then I need to examine how OmpTextGeneration handles error catching with Schema.decodeE and the catchTags pattern.… OmpTextGeneration matches CursorTextGeneration, which is good. The serverSettings.ts update changes the fallback text generation provider to use the selected model instead of a default constant, with a test covering this behavior change. The elicitation module contains pure functions without service dependencies. Now I need to verify that all callers of the AcpAdapterSupport.acpPermissionOutcome signature change have been updated, checking places like GrokAdapter and Herm.… Only AcpAdapter relies on it, so GrokAdapter needs a different approach. The AcpAdapterSupport error mapping function stays the same. The wiring for ProviderInstanceRegistryLive and built-in drivers looks good. The web app changes are minimal. I'm thinking about the convention around error classification — static factories and mappers can handle this when the logic gets reused across multiple places. The key principle is avoiding trivial helpers that just wrap arguments into a new error. In OmpAdapter, the mapError call to mapAcpToAdapterError does real normalization work, so that's justified. For AcpAdapter's placement in provider/Layers/ — it's a shared factory sitting alongside other adapters, which makes sense even though there's already an AcpAdapterV2 in orchestration-v2 and ACP helpers in the provider/acp directory. The convention allows implementation modules to stay in implementation-oriented directories as long as they're genuinely separate. The error construction with ProviderAdapterProcessError({ detail: cause.message }) and similar patterns at line 1020 in readFile mapping are moved code that fit this pattern. Now I'm weighing whether to flag the detail: cause.message instances in AcpAdapter. The rule says to report violations introduced or retained in the pull request's changed scope — these lines are literally in the new file, so technically they're in scope. But the rules also say to preserve error messages exposed through HTTP/RPC responses or caller-visible boundaries during structural refactors, and changing detail: cause.message would alter user-visible messages. That contradicts the message-preservation principle, so flagging it would be inconsistent with the broader refactoring guidance. Moving on to more substantive issues: I'm noticing a behavior change in findModeByAliases where the matching logic shifted from .includes(alias) to .split(" ").includes(alias) — this is stricter word-boundary matching. It's probably intentional to avoid false positives, but it could cause regressions. For example, a mode named "Architect (planning)" with alias "plan" would no longer match, though "architect" would. There's no test covering this behavior change, and it's a subtle logic shift bundled into what looks like a straightforward refactor. Looking at the permission handling changes: the auto-approve logic now accepts kebab and snake case id aliases, which is a reasonable extension. The fallback behavior when no matching option is found remains the same — it asks the user. On decline, the outcome changed slightly: if reject options aren't available, it returns cancelled instead of always returning "reject-once", but that's tested and seems fine. I'm also noticing the ModeChanged event case was removed from the switch statement — previously it had an explicit return, and now it's gone entirely. The notification fiber is now using Effect.forkIn(sessionScope) instead of forkChild, tying it to the session scope and wrapping it in an Effect.scoped block. There are also behavior changes around rollback handling and the user input error method that I'm tracking through. For the elicitation handler, the shared factory now always registers handleElicitation whereas the Cursor adapter previously didn't, though this is likely harmless since Cursor's capabilities may not advertise elicitation support anyway. I'm evaluating which of these changes warrant flagging under Effect Service Conventions — the focus is on preserving useful comments, avoiding unnecessary tests, and requiring focused tests when backend behavior actually changes. The most significant finding is that Cursor's rollbackThread behavior shifted from locally trimming and returning the thread to throwing a hard ProviderAdapterRequestError, which is a visible behavior change for callers with no test coverage. The mode alias matching change from exact-match to partial-match is more minor since Cursor's actual mode IDs would already be caught by exact matching. I need to decide if these rise to the level of convention violations worth failing the check, keeping in mind the guidance about clear violations versus optional style preferences.… The rollbackConversation method used to silently trim the Cursor thread, but now it's surfacing an error to the client—that's a meaningful behavior shift from the refactor. I should either document this as an intentional change with a focused test or revert to the previous trimming approach, and I'm weighing whether this warrants failing the check given the discipline around backend behavior changes.… I'm tracing through the checkpoint revert flow in Cursor to see where the rollbackConversation error would surface — it's likely being caught at a higher level with an Effect handler, but I need to verify whether that error handling exists for all the other agents like Grok, Hermes, and PiAgent. Looking at the broader picture, if Grok and the others already tolerate missing rollback functionality in their revert flows, then Cursor's new behavior isn't actually a regression — it's just converging with how those agents already work. But this does represent a real behavior shift for Cursor specifically: revert used to complete successfully with trimmed threads, and now it might not complete at all. The key issue is that Cursor's trimming logic was intentional and tested elsewhere (ClaudeAdapter has a dedicated test for this pattern), so removing it silently during the refactor is a discipline problem worth flagging — either keep the local trim or add a focused test to document the change. Let me verify how the reactor handles failures in the revert handler and check if there's proper error wrapping, then scan through the rest of AcpAdapter for any other significant issues I haven't caught yet — particularly around handler registration ordering and the factory input interfaces. Now I'm settling on the key finding: the Cursor rollbackThread behavior changed from trimming local turns to throwing a hard error, which is caller-visible through ProviderService.rollbackConversation but has no test coverage. I should flag this with an inline comment at the rollbackThread implementation, noting that Cursor previously matched ClaudeAdapter's trim behavior and suggesting either making rollback configurable per provider or adding a focused test for this change. For the second finding about findModeByAliases narrowing from substring matching to word-boundary matching—it's also a silent behavior change in moved code, but it's marginal and might add noise. I'm leaning toward skipping it to keep the findings focused and precise. Looking at the other silent behavior changes: selectAutoApprovedPermissionOption now accepts kebab IDs (deliberate), respondToUserInput preserves its method label, and sendTurn now acquires the thread lock during setup (an intentional improvement). The configuration application running outside the lock while the counter increments inside is fine as-is.… I'm planning to add an inline comment to the rollbackThread method in AcpAdapter.ts, targeting line 1168 in the new file. Since this is a newly added file, I need to use the right-side line numbers for the GitHub PR review, positioning the comment between lines 1163 and 1169.…

1. modify_pr — Reviewed the new OMP provider plumbing and the shared ACP adapter extraction against the Effect service conventions. Imports, service/definition layout (Services/OmpAdapter.ts + Layers/OmpAdapter.ts mirrors the existing per-instance adapter pattern), dependency acquisition in OmpDriver/makeOmpTextGeneration, and error modeling (TextGenerationError, ProviderCommandNotFoundError, catchTags) all look consistent with the repo conventions. Earlier findings (event-stream barrier, promptsInFlight accounting, steered-turn merge, MCP wiring, OmpDriver detail copying cause.message, provider-neutral factory module) are resolved. One remaining item: the extraction silently changes Cursor's rollbackThread contract without a focused test.


Agent Credits: 336 credits