Repository navigation
Effect Service Conventions: 1 issue found
Resolved since the previous run
apps/server/src/provider/droid/DroidRpcClient.ts— transport failures are now defined withSchema.TaggedErrorClassand structured attributes, withmessagederived from those attributes.apps/server/src/provider/Layers/DroidAdapter.ts— the rewind-result decode now recovers withEffect.catchTags({ SchemaError: ... })instead ofEffect.catchTag.
Outstanding finding
apps/server/src/provider/Drivers/DroidDriver.ts:170—ProviderDriverError.detailis built as`Failed to build Droid snapshot: ${cause.message ?? String(cause)}`, so the wrapper's caller-visible message is derived from the underlying cause rather than from its own stable structural attributes. Use a staticdetailand letcausecarry the underlying failure. Inline comment 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
New and modified TypeScript in the check's include patterns:
apps/server/src/provider/Drivers/DroidDriver.tsapps/server/src/provider/Layers/DroidAdapter.ts,Layers/DroidProvider.tsapps/server/src/provider/Services/DroidAdapter.tsapps/server/src/provider/droid/DroidProtocol.ts,droid/DroidRpcClient.tsapps/server/src/textGeneration/DroidTextGeneration.ts,textGeneration/TextGeneration.tsapps/server/src/provider/Layers/ProviderService.ts,provider/builtInDrivers.tspackages/client-runtime/src/approvalRequests.ts,src/providerDisplayName.tspackages/contracts/src/settings.ts,src/model.ts,src/providerRuntime.ts- Web/mobile client touch points (
providerIconUtils.ts,session-logic.ts,contextWindow.ts,modelOptions.ts,threadActivity.ts)
Checks performed and outcomes
- Imports / namespaces — all new Effect imports use subpath namespaces (
import * as Effect from "effect/Effect"); no consolidated named imports from"effect".import { ChildProcessSpawner } from "effect/unstable/process"accesses a service module namespace from a package subpath, which is permitted and matches sibling drivers. - Dependency acquisition —
makeDroidAdapter,makeDroidTextGeneration, andDroidDriver.createacquireFileSystem,Path,ChildProcessSpawner,Crypto,HttpClient,ServerConfig,ServerSettingsService, andProviderEventLoggersfrom the environment withyield*. Only pure configuration (DroidSettings,NodeJS.ProcessEnv) and a test-onlyregisterDebugStateReaderseam are passed as parameters. ThehttpClientparameter onenrichDroidSnapshotmirrors the existingCursorProvider/GrokProvidersnapshot-enrichment contract and is not a new pattern. - Runtime boundaries — no
ManagedRuntime.make,runPromise,runPromiseExit, orLayer.succeed-wrapped imperative APIs in the new service code. - Errors — no
Data.TaggedError; all new errors useSchema.TaggedErrorClass.DroidRpcError'skinddiscriminator with structured per-kind attributes matches the shape agreed in the prior review and is used for genuine caller control flow. - Error recovery — no
Effect.catchTagor predicate-basedcatchIffor known tags;Effect.catchTagsand whole-channelEffect.catch/Effect.catchCauseare used appropriately. - File layout —
Services/DroidAdapter.ts+Layers/DroidAdapter.tsfollows the existing per-provider adapter layout (Codex, Claude, Cursor, Grok, OpenCode) rather than introducing a new split; no compatibility shims or dead re-exports were added. - Tests — behavior changes are covered by focused tests (
DroidAdapter.test.ts,DroidProvider.test.ts,DroidProtocol.test.ts,DroidRpcClient.test.ts,DroidTextGeneration.test.ts, plus the newProviderService.tssnapshot-persistence cases) using a mock CLI process rather than mocking core business logic.
🔍 Agent activity — 19 tool calls
💭 Checking the diff, I can see the two prior comments were both addressed: DroidRpcClient.ts now uses a tagged error class with structured attributes, and DroidAdapter.ts now catches SchemaError tags explicitly. Now I want to scan the rest of the changes, starting with the Droid provider service files, for any remaining or new violations. I notice DroidAdapter.ts defines a standalone
DroidAdapterShapeinterface, which conflicts with the convention against retaining standalone shape interfaces outside aContext.Servicedeclaration. But since this file has noContext.Servicetag at all and just bundles adapters as closures, that rule may not strictly apply here, and it also matches an existing pattern used by other adapters like CodexAdapter. I'm weighing whether this is a genuine violation or just following established repo conventions that predate these rules. Now I'm looking at the DroidRpcClient errors, whereDroidRpcErroruses akinddiscriminator with a switch in the message getter — this seems to directly violate the guidance against modeling distinct failure types with a switch instead of separate error classes, since the kind values include things like encode and write failures that likely warrant their own error classes. I'm noticingmakeDroidTextGenerationreadsprocess.envdirectly via a default parameter rather than going through a config service, though other providers likeDroidProvider.tsdo the same thing so it's likely consistent with convention. Then inDroidAdapter.ts, I spotdetail: cause.messageused when constructingProviderAdapterProcessErrorin multiple places, which conflicts with the guidance against adding adetailfield that just duplicatescause.message. However,detailis a pre-existing field onProviderAdapterRequestError, not something newly introduced here, and tests actually assert on its contents, so I want to check whether other adapters like the Grok one follow the same pattern before flagging this. I also noticeDroidDriver.tsderives its detail string fromcause.messagesimilarly, so I want to see if other drivers do the same before treating it as an issue. Meanwhile files likeapprovalRequests.tslook like pure helpers with no concerns, andmakeDroidAdaptertakes pure config, so that seems fine too as I keep scanning for runtime/layer patterns in the touched files. The driver'screatefunction acquires FileSystem, Path, ChildProcessSpawner, HttpClient, and other services from the environment, which looks consistent with the rest of the codebase, though it also provides some of these services directly when checking the provider — I want to confirm that's a shared pattern elsewhere. The text generation and snapshot enrichment functions take config and services as plain parameters, which seems like reasonable dependency passing rather than something to flag. I should compare this against how CodexProvider and GrokProvider handle the same setup, so let me search the repo for those patterns.
1. browse_code — apps/server/src/provider/**/*.ts
2. git_tools — httpClient: HttpClient.HttpClient
💭 The git_grep hit on DevinProvider.ts is likely from the default branch HEAD, but that's fine since existing providers like Cursor and Grok already use the same httpClient parameter pattern, so the Droid code isn't introducing a new violation—it mirrors existing conventions. Now I want to dig into the actual new files, specifically DroidRpcClient.ts and DroidAdapter, to check for any convention issues. I'm comparing error construction patterns against GrokAdapter, and I want to check Errors.ts to see how ProviderAdapterRequestError is defined.
3. browse_code — apps/server/src/provider/Errors.ts
4. browse_code — apps/server/src/provider/droid/DroidRpcClient.ts
💭 I'm noticing that DroidRpcError uses a kind discriminator with a large switch statement to pick messages, and callers actually branch on that kind value for control flow—which is exactly the pattern the guidelines say should be split into separate error classes rather than modeled with a switch in the message getter. But I recall my earlier review comment suggested keeping
kindas the discriminator, so I want to pull the exact prior comment text via the GitHub API to confirm before deciding.
5. github_api_read_only — repos/{owner}/{repo}/pulls/{pull_number}/comments?per_page=100
💭 Let me narrow down my focus.
6. github_api_read_only — repos/{owner}/{repo}/pulls/{pull_number}/comments?per_page=10&page=1&sort=created&direction=desc
7. github_api_read_only — repos/{owner}/{repo}/pulls/{pull_number}/comments?per_page=2&page=1&sort=created&direction=desc
💭 This is from a different check, and my prior comment about DroidRpcClient already confirmed keeping
kindas the discriminator, so the current shape matches my guidance and doesn't need re-flagging. I want to verify the two prior findings are actually resolved at head and check for new violations, so I'm diffing between the prior review commit bfbd221 and head 3830327.
8. git_tools — bfbd221..3830327
💭 Since the commit seems gone, I'll review the head files directly instead, checking DroidAdapter.ts's error handling and the other Droid files for convention issues. I'm noting how DroidDriver.ts wraps failures into a ProviderDriverError with details about the snapshot build failure. Both DroidDriver.ts and DroidTextGeneration.ts seem to violate the rule against deriving error messages from
cause.messageor adding trivial wrapper helpers around error constructors. I should grep other drivers and text generation modules to see if this pattern is widespread before flagging it.
9. git_tools — Failed to build .* snapshot
10. git_tools — const failWith =
💭 I'm noticing that the "Failed to build snapshot" error pattern with cause.message is repo-wide and established across older drivers, but newer ones have moved away from that pattern toward static details. Since the instructions say to treat newer conventions as authoritative over older code, DroidDriver replicating the older pattern does count as a violation under that guidance. That message construction embeds cause.message into the wrapper, which the conventions explicitly forbid — the wrapper message should derive only from stable structural attributes. Since newer drivers like CopilotDriver, OpenCode2Driver, and PiDriver already handle this correctly with static details, this is a legitimate, actionable finding worth flagging as an inline comment. Now I want to check whether
failWithin DroidTextGeneration qualifies as a trivial wrapper helper, or whether its closure-bound operation and cause normalization make it distinct — I should compare it against GrokTextGeneration and CodexTextGeneration to understand the convention.
11. browse_code — apps/server/src/textGeneration/*.ts
12. browse_code — apps/server/src/provider/Drivers/DroidDriver.ts
💭 I should check whether GrokTextGeneration has something similar to
failWith.
13. git_tools — new TextGenerationError({
💭 I'm weighing whether the
failWithhelper violates the convention against constructing errors inline—it's used repeatedly but only adds a thin closure aroundoperationwhile the actual error attributes remain visible at each call site. This feels like a borderline style issue rather than a clear violation, so I'm inclined to treat it as minor rather than something worth failing over. Now I want to shift attention to DroidProvider.ts and DroidAdapter.ts to check for stronger findings—specifically searching forEffect.catchTag,catchIf,Data.TaggedError, or named imports from "effect" that might indicate real issues worth flagging.
14. git_tools — catchTag(|catchIf(|from "effect"|Data.TaggedError
15. git_tools — catchTag(|catchIf(|from "effect";|Data.TaggedError
16. git_tools — Schema.is|isDroid
💭 Good, the new droid files are clean of catchTag, catchIf, named-effect imports, and Data.TaggedError. Now I want to check DroidTextGeneration.ts and DroidProvider.ts for their imports and any lingering catchTag usage.
17. git_tools — catchTag(|catchIf(|^import {[^}]*} from "effect"
18. browse_code — apps/server/src/textGeneration/DroidTextGeneration.ts
💭 I'm checking that the ChildProcessSpawner import from effect/unstable/process follows the convention correctly, since it's accessed as a namespace when the barrel module exposes a service/tag member like this. Looking at DroidDriver.ts:170, I'm weighing whether embedding cause.message is a genuine convention violation worth failing the check on, given that it's introduced in the changed scope but is also consistent with several existing drivers in the codebase that do the same thing. Since prior review runs on this PR didn't flag this issue, raising it now would be inconsistent, so I'm shifting focus to verifying that only genuinely new violations since the last run get surfaced. The two earlier findings — the catchTags fix and the TaggedErrorClass fix — both check out as resolved in the current diff. I'm sweeping through the remaining changed files: ProviderService.ts, approvalRequests.ts, providerDisplayName.ts, and contextWindow.ts all look clean, with proper named exports and imports. Now I'm checking DroidProvider.ts for similar patterns around error handling. DroidProvider's snapshot enrichment and auth detection both use standard Effect patterns that look fine, and DroidAdapter.ts's error channel handling is allowed as-is. The test file uses a debug state reader as a test seam, which is acceptable. I'm now confirming that makeDroidAdapter's return type properly satisfies the DroidAdapterShape interface declared in the Services module. Now I'm reconsidering the earlier flagged issue about DroidDriver constructing a detail field directly from cause.message and using it to build the wrapper message — this seems to match the exact pattern the instructions warned against, so I need to weigh whether this actually violates the rule or aligns with existing conventions elsewhere in the codebase. That "All clear" instruction seems to conflict with the JSON schema requirement, but since I do have a genuine finding, I'll go with reporting it and failing the check. I still need to pin down the exact diff line number for the fix in DroidDriver.ts. Since it's a new file, every line sits on the RIGHT side of the diff, so line 170 maps directly. Before finalizing the comment, I want to check whether ProviderDriverError's detail message actually surfaces to users somewhere, like in unavailableProviderSnapshot.ts—if so, dropping cause.message could hurt diagnosability, though the cause itself stays preserved so I'll frame it as a suggestion rather than a hard requirement. I'm also weighing whether flagging this is worthwhile given that other sibling drivers share the same pattern. Since this instance is within the changed scope of the PR, flagging it is appropriate per the review guidance, even if similar code exists elsewhere. I'll go ahead and post the review with this single inline comment.
19. modify_pr — Reviewed the new Droid provider modules against the Effect service conventions. The two findings from the previous run are resolved: DroidRpcError/DroidRpcSpawnError are now Schema.TaggedErrorClass with structured attributes and derived message getters, and the rewind decode uses Effect.catchTags({ SchemaError: ... }). One remaining item on the new driver: the wrapper error's detail embeds cause.message. Everything else (namespace imports, dependency acquisition via yield*, error definitions, absence of ManagedRuntime/runPromise in service code) matches the conventions.
Agent Credits: 341 credits