Repository navigation
Effect Service Conventions: 1 issue found
apps/server/src/provider/Layers/ProviderInstanceRegistryLive.ts: newly addedcause: createResult.failure.causeputs an arbitrarySchema.Defect()value into a log annotation; use a normalizederrorTag(...)instead (the exact failure is already preserved asProviderDriverError.cause).
Previously flagged items appear resolved on this head: Droid transport/adapter diagnostics now go through DroidDiagnostics with errorTag/causeErrorTag and byte counts, DroidRpcError is a Schema.TaggedErrorClass with attribute-derived messages, wrapper detail values are static structural strings with cause preserved, and tagged recovery uses 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.
Scope reviewed: new Droid provider modules (provider/Drivers/DroidDriver.ts, provider/Layers/DroidAdapter.ts, provider/Layers/DroidProvider.ts, provider/droid/*, textGeneration/DroidTextGeneration.ts, provider/Services/DroidAdapter.ts), the ProviderService/ProviderAdapter rollback contract change, CheckpointReactor, provider/Errors.ts, and touched contracts/client call sites.
Checks performed:
- Imports: all touched Effect usage is namespace subpath imports; no consolidated
from "effect"imports were introduced. - Dependency acquisition:
makeDroidAdapter,makeDroidTextGeneration,makeDroidRpcClient, andDroidDriver.createall acquireFileSystem,Path,ChildProcessSpawner,Crypto,HttpClient,ServerConfig,ServerSettingsService, andProviderEventLoggersfrom the environment. NoManagedRuntime.make,runPromise, or runtime-backedLayer.succeedin production Droid code; the only injected non-service value is the plainEventNdjsonLoggerinterface, matching the existing Grok/Codex adapters. - Errors: new
ProviderAdapterSessionInvalidatedErrorandDroidRpcSpawnErroruseSchema.TaggedErrorClasswith attribute-derived messages and preservedcause; wrapperdetailvalues no longer copycause.message. Tagged recovery usesEffect.catchTags. - Logging: Droid layers route diagnostics through
logDroidWarning/logDroidError, which emiterrorTagplus boundeddetails(e.g.stderrBytes,lineBytes) rather than raw wire text. The single exception is the flaggedProviderInstanceRegistryLive.tsline. - File layout: no compatibility shims; the
validateConversationRollback->prepareConversationRollbackrename was mechanically propagated to every consumer and test harness.
🔍 Agent activity — 21 tool calls
💭 I'm checking whether the current commit still has the previously flagged issues and scanning the changed files—DroidAdapter, DroidRpcClient, DroidRpcProtocol, DroidDiagnostics, DroidTextGeneration, Errors, and DroidDriver—for anything new. Several prior issues look fixed: diagnostics now use errorTag/causeErrorTag, stderr logging captures bytes, catchTags are applied in the text generation and adapter rewind paths, the provider driver error detail is static, and DroidRpcError now uses TaggedErrorClass properly. I'm double-checking whether the
kinddiscriminator with a message getter in DroidRpcError violates the "no switch in message getter" guidance, but since the reviewer previously suggested this exact shape, I shouldn't flag it again. Now I'm examining whetherrpcMessagecarries raw wire payload versus a legitimate provider error message, and checking ProviderAdapterSessionInvalidatedError's optionalcausefield against its construction sites—since failSessionInvalidated always passes a cause, but other errors in the file also use optional cause, so it's likely consistent with existing conventions rather than a real issue. I want to look at the actual files next for patterns like ManagedRuntime usage, Layer.succeed with runtime injection, and named imports from "effect". I'm reviewing the import conventions across DroidAdapter, DroidDriver, and DroidRpcClient—namespace subpath imports like Semaphore and ChildProcess, and named imports like HttpClient and ChildProcessSpawner that seem consistent with patterns used elsewhere in the codebase. Then I'm checking makeDroidAdapter's factory signature, confirming it correctly pulls FileSystem, ChildProcessSpawner, ServerConfig, and Crypto from the environment, and noting that options.nativeEventLogger passes an EventNdjsonLogger instance as a parameter worth double-checking. I want to verify whether EventNdjsonLogger is injected as a service or just a plain object, comparing against how Grok and Codex adapters handle it. I'm also confirming that makeDroidTextGeneration properly acquires its dependencies, that checkDroidProviderStatus and the ProviderService rollback logic look fine, and now I need to pull up the actual DroidAdapter and DroidRpcClient files to check for any remaining issues.
1. git_tools — catchTag(|catchIf(|ManagedRuntime|runPromise|Layer.succeed|from "effect"
2. git_tools — catchTag(|catchIf(|ManagedRuntime|runPromise|from "effect"
💭 No droid files have catchTag or ManagedRuntime issues, so I'll dig into the droid files more closely for error conventions, starting with the current DroidRpcClient.ts.
3. browse_code — apps/server/src/provider/droid/DroidRpcClient.ts
💭 This looks fine now, prior comments addressed. I'm moving to check DroidAdapter.ts for error conventions, looking at error construction sites and detail fields—the prior run flagged the cause.message detail in ProviderAdapterRequestError, which now reads "Droid RPC request failed." I want to verify the remaining sites follow the same pattern.
4. git_tools — detail:
💭 I'm looking at line 1400's
detail:field and confirming it's fine — it's a runtime event payload for the approval flow (droidPermissionDetail), not an error object, so the guidance about not copying raw wire payloads into error details doesn't strictly apply here since the args already carry the raw params separately. Now I'm turning to DroidRpcProtocol.ts and its DroidRpcError, which uses akinddiscriminator across 11 values with a switch statement generating the message. I need to check whether this violates the convention about splitting semantically distinct failures into separate error classes when the discriminator also drives caller control flow — so I'm searching for places outside tests where code branches on.kind ===to see if it's used for both messaging and control flow.
5. git_tools — .kind === "
💭 Since only tests branch on
kindfor DroidRpcError, and the previous reviewer already suggested keeping it as the discriminator, I'll leave that alone. Now I'm scanning DroidAdapter.ts and related files to check other conventions like named imports from "effect" and service interface shapes, particularly the newDroidAdapterShapeinterface definition. The convention says service interfaces should be defined inline within theContext.Servicedeclaration rather than as a standaloneFooShapetype — but this new file introducesDroidAdapterShapeas its own type, so I want to check whether other adapter files like CodexAdapter.ts follow the same pattern before flagging this as inconsistent.
6. browse_code — apps/server/src/provider/Services/*.ts
💭 This matches the pattern already used in sibling files like GrokAdapter.ts, so it's consistent with the established driver adapter convention rather than something needing fixing. The rule about not retaining standalone shape types applies specifically to PRs that create, move, or refactor a service, and DroidAdapterShape doesn't fall into that category. Moving on, I check DroidDriver.ts and DroidProvider.ts — both look fine, properly yielding dependencies like FileSystem, Path, and ServerConfig through Effect.gen, and correctly scoping the RPC client. Then I look at DroidTextGeneration.ts's error handling, checking how the exit description gets embedded into the error detail string when the process exits early. The
failWithhelper concerns me a bit — it's a thin wrapper that just builds a TextGenerationError, but since it closes over theoperationcontext and gets reused across ten call sites, it's borderline against the "no trivial error constructor wrapper" convention rather than a clear violation. I want to compare against how ClaudeTextGeneration and GrokTextGeneration structure similar error mapping before deciding whether to flag it, especially since there's also a curriedmapRpcErrorbuilt on top of it.
7. browse_code — apps/server/src/textGeneration/*.ts
8. browse_code — apps/server/src/textGeneration/GrokTextGeneration.ts
💭 I'm comparing the two approaches to error construction—Grok builds errors inline while Droid uses helper functions like
failWith/mapRpcError. I'm weighing whether these helpers cross the line into the discouraged pattern of trivial wrapper functions that just construct an error object, even though they close over shared context like the operation name. SincefailWithandmapRpcErrordo add reusable context and control flow rather than merely wrapping a constructor, I'm leaning toward not flagging this as an issue. Now I want to check the DroidAdapter and DroidProtocol schema code for similar patterns, and also look at the orchestration contracts file. I'm also checking whetherproviderRequestKindFromRequestType, a new pure function, is used correctly, and whetherisProviderRequestKindwas already imported in session-logic.ts before being used as a Schema.is predicate.
9. git_tools — isProviderRequestKind
💭 These look like standard Schema.is predicates, nothing unusual. I'm going back through DroidAdapter.ts to check for convention violations around service definition, dependency injection, and Layer usage, focusing on the makeDroidAdapter function signature and structure. I want to trace where options.nativeEventLogger comes from—it's an EventNdjsonLogger instance passed in, so I should check whether EventNdjsonLogger is defined as a Context service by looking at its file.
10. git_tools — export (interface|const|class)
💭 EventNdjsonLogger is a plain interface rather than a Context service, so passing it through options is consistent with how Grok/Codex adapters work. I'm also confirming DroidProvider and DroidDriver don't misuse Layer.succeed since DroidDriver just returns a ProviderInstance without layers, and checking that the new error in Errors.ts follows the same pattern as other errors there, with cause being optional but always supplied.
11. browse_code — apps/server/src/provider/Errors.ts
💭 Now I'm checking DroidRpcProtocol.ts, examining how warnings log decode errors and how RPC error objects carry provider data—noting that the
datafield passes through arbitrary wire payload from the error, which is a potential exposure point worth flagging. I can't be fully sure what the prior run's truncated suggestion contained, but the current shape seems reasonable. What matters more is checking which commits landed after the last reviewed one (b8bcb17) up to head (689c640), since that's where any genuinely new issues would show up.
12. git_tools — b8bcb17..689c640
💭 I'm reviewing the diffs of recent commits for new violations, focusing on the rollback validation changes in ProviderService.ts and CheckpointReactor, and checking whether the
toValidationErrorhelper only constructs an error object or does more.
13. git_tools — toValidationError =
14. git_tools — toValidationError
💭 That helper looks like a pre-existing one, fine to leave. I'm now checking ProviderAdapter.ts for the new pure function rollbackTargetMatchesKnownHistory placed in a service interface module — seems acceptable as a pure helper there. Next I want to look at the diffs for the most recent commits, filtering to non-test TypeScript files, to check for anything relevant to Effect conventions.
15. git_tools — 316363c..689c640
💭 I'm reviewing the new code changes: DroidDiagnostics looks good, and I'm checking ProviderService's rollback preparation and validation logic, along with the CheckpointReactor change and the semaphore-based delivery stream. I notice DroidDiagnosticOptions has both cause and error fields that could create a minor tag conflict, though nothing rises to a real convention violation. I'm also checking the logging calls in DroidRpcProtocol — the timeout and unsupported-method log messages both use bounded, structural strings rather than raw wire content, so those look fine. The Semaphore usage across DroidAdapter and the backlog mutex also checks out as namespace-imported effect code. Confirming there's no lingering catchTag usage or non-namespace effect imports in the droid client files, mobile/web files aside since they aren't Effect services — the earlier grep already came back clean. Now I want to look at the change in ProviderInstanceRegistryLive.ts where
cause: createResult.failure.causewas added, so let me pull up that context.
16. browse_code — apps/server/src/provider/Layers/ProviderInstanceRegistryLive.ts
💭 I'm looking closely at the PR's addition of the failure cause into the log payload, checking it against the convention that raw error details should stay confined to a
causefield rather than leaking into general log annotations—only normalized, safe summary info should appear elsewhere. This feels significant because the same PR previously removed cause serialization from the error's detail/message per an earlier review, yet now it's being reintroduced through the log payload instead. I'm weighing whether this diagnostic need justifies the exception, since the cause itself remains preserved on the error object regardless of what the log includes.
17. git_tools — Effect.log
18. git_tools — errorTag:
💭 This confirms the PR deviates from the repo's convention of using
errorTag(failure.cause)for log annotations by instead passing the raw defect directly into the payload — a genuine issue introduced in this one-line change, while the neighboring detail field is unrelated pre-existing code. I'm now pinpointing the exact line number in the new file to attach the inline comment. I also want to scan DroidAdapter.ts more broadly for other directEffect.logcalls that might include raw data payloads after the diagnostics refactor, to see if there are additional similar violations worth flagging.
19. git_tools — Effect.log
💭 That's fine given the bounded identifiers. I'll leave one inline comment on ProviderInstanceRegistryLive.ts line 192, confirming it matches the added line in the diff hunk, and check whether the file already imports errorTag before drafting the suggestion.
20. browse_code — apps/server/src/provider/Layers/ProviderInstanceRegistryLive.ts
💭 I notice errorTag isn't imported, so the fix needs an import addition alongside the code change, which means a full inline suggestion won't work cleanly—I'll provide a small diff for the changed line and note the missing import separately before posting the review.
21. modify_pr — One finding: a newly added log annotation copies an arbitrary defect into the log payload instead of a normalized tag.
Agent Credits: 520 credits