Repository navigation
Effect Service Conventions: 1 issue found
apps/server/src/project/AgentSessionImporter.ts — Import-time validation failures reuse OrchestrationCommandInvariantError with a fabricated commandType (agentSessions.import is an RPC method, not an orchestration command) and push all real context into a free-form detail string, collapsing two semantically distinct failures (unresumable Claude session id, thread owned by another project) into one generic tag. Suggested fix: declare dedicated Schema.TaggedErrorClass errors with structured attributes (source/providerSessionId, threadId/projectId) and derive each message from them.
One inline review comment was 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.
Checked areas that were clean:
AgentSessionImporter.tsacquires every dependency from the environment (yield* AgentSessionScanner.AgentSessionScanner,OrchestrationEngineService,ProjectionSnapshotQuery,ProviderSessionDirectory,Crypto.Crypto); no service-instance parameters, noManagedRuntime.make, norunPromisein domain code. The earlierEffect.serviceOptionoptional-dependency pattern flagged on a prior run has been replaced with real environment acquisition, andws.tsnow provides the service at the RPC boundary.- Effect modules are imported as subpath namespaces (
effect/Effect,effect/Option,effect/Schema,effect/Crypto); local service modules use namespace imports and their publicService/layershape. Named imports from@t3tools/contractsand the purefindProjectByPathhelper are within the allowed exceptions. AgentSessionScanner.tskeeps the canonical order (schemas,Context.Servicetag with inline interface,make,layer); the newrecentThreadsmember is added inline rather than via a standalone shape type, and callers useAgentSessionScanner["Service"][...]types.- New contract errors (
AgentSessionImportProjectNotFoundError) useSchema.TaggedErrorClasswith a message derived from structured attributes; theAgentSessionScanErrorwrapper preserves the realcause. Layer.mock/Service.of(...)/Effect.provideServiceoccurrences in the new tests are legitimate test seams, not production dependency injection.- Behavior changes (history import decider path, revert retention, awareness relay suppression) come with focused tests, so no additional test coverage is required.
🔍 Agent activity — 7 tool calls
💭 I'm reviewing the new AgentSessionImporter.ts file for adherence to Effect service conventions—checking that catching the whole error channel with Effect.catch is justified, and looking at how it reuses OrchestrationCommandInvariantError to signal conditions where an import should be skipped. I want to check whether this is a valid reuse of a command error type or an error-modeling mismatch, since the "commandType" field passed in looks like an RPC method name rather than an actual command type. I'm also examining how AgentSessionScanError gets constructed with an "operation" field during a project scan failure, comparing it against the earlier flagged issue in ws.ts about a fabricated cause, and checking whether the message wording still makes sense in this new context. I'm also noticing there's a module-scoped mutable cache for candidates inside the service's Effect.gen closure with no invalidation logic, which could lead to stale results in recentThreads even though it's not strictly a hidden-dependency violation under the stated conventions — more of a correctness concern I'll note separately from the Effect service convention check. I should pull up AgentSessionScanner.ts and the contracts error definitions to verify these details directly.
1. git_tools — AgentSessionScanError
2. git_tools — class OrchestrationCommandInvariantError
💭 I'm checking AgentSessionImporter.ts, where it reuses OrchestrationCommandInvariantError with commandType set to "agentSessions.import" for skip conditions that aren't actually orchestration commands—this looks like a potential misuse of that error type, since it's meant for commands, not RPC methods. Meanwhile, the AgentSessionScanError mapper at the boundary preserves the cause properly, though its "read-projects" operation label is a bit mismatched for an import context—minor but acceptable given the error union structure. I also want to confirm the previous ws.ts serviceOption issue is truly resolved by looking at the current region where providerSessionDirectory is pulled from context, alongside the head of the importer file.
3. browse_code — apps/server/src/project/AgentSessionImporter.ts
4. browse_code — apps/server/src/ws.ts
💭 I'm wondering why the handler explicitly re-provides services via Effect.provideService when they're already available in the enclosing context — likely because the handler's own type signature requires R = never, so explicit provision is needed even though it seems redundant. I want to check whether other handlers in ws.ts follow the same pattern to confirm this is the file's convention.
5. git_tools — Effect.provideService
💭 This seems to be the only spot in ws.ts using provideService directly, so I'm checking how other handlers with dependencies are typically structured—they usually acquire services from the enclosing context via closures, similar to how importRecentAgentThreads does it. Providing services acquired from the enclosing layer is legitimate as long as the handler's requirement type is never, so this pattern is acceptable even if a bit verbose. Now I'm looking at the AgentSessionScanner.ts changes, where the service caches candidates in a closure and recentThreads relies on that cache, with providerInstanceIds arrays being mutated via push. This hidden mutable cache isn't really a smuggled dependency, but it could cause recentThreads to return stale results if the scan already ran and new sessions were created afterward—more of a behavioral correctness concern than a strict convention violation, so I'm weighing whether it's worth flagging given the review seems to expect concrete convention breaches. I'm also examining error handling in AgentSessionImporter, where
OrchestrationCommandInvariantErrorgets reused for non-orchestration-command failures with a free-formdetailstring and a misleadingcommandTypefield. This collapses two semantically distinct failures—an unresumable Claude session versus a thread owned by another project—into one generic error, which conflicts with the convention of splitting semantically distinct failures into separate tagged error classes when the failure reason determines the user-facing message. Continuing through the rest of the checklist: the catch block also handles AgentSessionScanError types appropriately, log annotations properly preserve cause without leaking secrets, and the named imports throughout (threadReducer, projectImport.logic, test files) all fall under allowed patterns for whole-package or pure-helper imports. Test files using Layer.mock, Service.of, and Effect.provideService are legitimate test seams, and I'm now checking the AgentSessionImporter test's use of provideService for correctness. I'm now looking closer at the scanner service's cachedCandidates closure to determine whether it violates the rule against hiding dependencies in module globals or singleton closures — but since this cache is scoped to a per-instance service rather than a true module-level global, it seems more like a potential staleness bug than an actual convention violation, so I'd treat it as out of scope for this narrowly-defined check. Checking the indentation confirmscachedCandidatesis scoped inside themakefunction, not a module-level global, so that's fine. I want to double check the interface signature forrecentThreadsto make sure it's returning the expected effect type with the proper error channel. Now I'm checking a couple of other error definitions—theAgentSessionImportProjectNotFoundErrorlooks correctly built with a tagged schema and derived message, and the reuse ofAgentSessionScanErrorwith a "read-projects" operation tag in the importer's shell-reading path seems acceptable since that literal covers the case. So my main finding remains the potential misuse ofOrchestrationCommandInvariantError, and I want to see if other non-orchestration code in the repo does something similar. I should search for other instances ofnew OrchestrationCommandInvariantErroroutside the orchestration module to see if there's already precedent for this pattern.
6. git_tools — new OrchestrationCommandInvariantError(
💭 AgentSessionImporter turns out to be the only real production usage, and it's misusing an RPC method name as a fake commandType with a free-form detail string covering two distinct validation failures. Given the convention of using Schema.TaggedErrorClass with structured attributes and stable domain context instead of interpolated strings, I should flag this with an inline comment and a diff suggestion splitting it into separate error classes. I check the other files quickly — the projection/decider changes and threadReducer are pure functions, so those are fine, and the imports in AgentSessionImporter and its tests all use standard effect module paths without anything unusual. I confirm the dependency acquisition pattern there is correct since it's a use-case function rather than a service module, so no issue there. Since fixing the error class problem requires adding new classes across files rather than a single-hunk change, I decide not to include a diff and instead post it as an inline comment describing the needed refactor. These errors are caught and logged rather than escaping, so they're really diagnostics, but their unstructured message still ends up in logs, so it's still worth flagging. I need to pin down the exact diff line numbers for the new AgentSessionImporter.ts file—the invariant error construction spans roughly lines 63-78, so I'll comment on that range before posting the review.
7. modify_pr — Reviewed the new/changed Effect service code (AgentSessionImporter.ts, AgentSessionScanner.ts, ws.ts wiring, contracts errors). Dependency acquisition now happens through the environment (yield* Foo.Foo) and the RPC handler provides the services at the boundary, which matches the conventions. One error-modeling issue below.
Agent Credits: 101 credits
Token usage: 12 uncached input · 410,738 cache-read input · 89,959 cache-write input · 7,660 output
Agent Credits may also include non-token charges from external tools such as web research.