Repository navigation
Effect Service Conventions: 1 issue found
apps/server/src/usage/UsageService.ts: therates->litellmRatesrename left a staleratesshorthand in theUsageAggregatoroptions alongside the newrates: servedRates, producing a duplicate property and an undefined identifier (module will not type-check). Inline comment posted.
No other Effect service convention violations were found in the changed scope.
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 reviewed (in-scope patterns): apps/server/src/usage/UsageService.ts, apps/server/src/usage/UsageService.test.ts, apps/server/src/usage/usagePricing.ts (+ test), apps/server/src/usage/usageScanCache.ts (+ test), apps/server/src/usage/usageTranscriptReader.ts, apps/server/src/usage/usageTranscripts.ts (+ test), apps/server/src/usage/usageAggregation.test.ts, apps/web/src/components/usage/*, apps/mobile/src/features/usage/usageProviders.ts, packages/contracts/src/usage.ts.
Convention checks performed:
- Imports: Effect modules are imported as subpath namespaces;
effect/unstable/httpnamed imports match the existing repository pattern in this module. The prior run's finding aboutimport { make as makeUsageService }has been addressed — the test now usesimport * as UsageService from "./UsageService.ts"andyield* UsageService.make. - Service definition:
Context.Servicetag with inline interface,make, thenlayerorder preserved; noFooShapereintroduced. - Dependency acquisition:
FileSystem,Path,ServerConfig,ServerSettingsService, andHttpClientare acquired from the environment inmake.HostProcessEnvironment/HostProcessPlatformareContext.Referencevalues with defaults, so acquiring them insideresolveOpenCodeDataDirdoes not leak requirements.NodeOS.homedir()matches the existingNodeOS.hostname()usage and is not covered by theno-global-process-runtimerule (platform/arch only). - Runtime boundaries: no new
ManagedRuntime.make,runPromise, orrunPromiseExit;layerTestremains a static empty-summary seam, andEffect.provideService(HttpClient.HttpClient, ...)in the new test is a legitimate test seam. - Errors: no new error classes; existing
UsageReadErrorusage unchanged, with boundeddetailand squashed cause preserved. - Tests: the new suites cover new backend behavior (models.dev overlay, WAL cache identity, OpenCode message parsing) rather than proving a mechanical refactor.
🔍 Agent activity — 1 tool call
💭 Looking at the PR changes, I see the import alias issue in UsageService.test.ts was already fixed. Now I'm checking the modified files against Effect conventions, particularly how the HTTP client imports are structured — they're using a consolidated named import from the unstable subpath rather than importing as a namespace, which seems to deviate from the convention of importing Effect library modules as namespaced imports. The prior issue is resolved, so now I'm checking other conventions. I notice the import of
HostProcessEnvironmentandHostProcessPlatformfrom the hostProcess subpath in UsageService.ts — the convention allows (but doesn't require) namespace imports for service module subpaths when accessing service/tag, make, or layer members. I should check how other files are importing hostProcess to see what pattern is already established. Also, I'm noticing the import ordering on line 39 places the@t3tools/shared/hostProcessimport after local imports, which might be worth reviewing. Now I'm looking at the HostProcessEnvironment definition to confirm it's a Context.Service tag, and checking how other modules import it to understand the established pattern. I'm checking how NodeOS.homedir() is being used in the service constructor — it's an imperative host API call that violates the convention against hiding dependencies in module globals or singleton services. There's a lint rule flagging this pattern, and I need to see if this API is already referenced elsewhere in UsageService to understand the scope of the issue. I'm noticing a potential bug whereratesis being passed to the UsageAggregator constructor, but it looks like the variable was renamed tolitellmRatesearlier, which would cause a compile error. I'm spotting a real bug in the head file where there's both arates: servedRatesproperty and a leftoverratesshorthand that references a variable that no longer exists by that name. This causes a TypeScript compile error and creates a duplicate object key, which is definitely in scope for the apps directory even though it's not strictly an Effect service convention issue. I should flag this since the addedrates: servedRatesline on 477 directly causes the stalerateson line 480 to become problematic. Now moving on to check for actual Effect service convention violations — looking atresolveOpenCodeDataDir, it's usingNodeOS.homedir()directly rather than going through an Effect service. I checked the available services and there's no HostProcessHomeDirectory option, butNodeOS.hostname()is already used elsewhere in the code, so this approach is consistent with what's already there. For the rest of the review: the type cast inreadFileRecordsisn't an Effect convention issue, the test file properly usesprovideServicefor seaming, and the HTTP imports match the existing repo pattern. But there's a potential type mismatch —UsageService.makecallsresolveOpenCodeDataDirwhich requiresHostProcessEnvironment | HostProcessPlatform, and this requirement propagates throughreadSummary, yet the service interface declaresEffect.Effect<UsageSummary, UsageReadError>with no environment requirements. This would likely surface as a type error when wrapping withLayer.effect. Actually, thinking through this more carefully —readSummarywould have typeEffect.Effect<UsageSummary, UsageReadError, HostProcessEnvironment | HostProcessPlatform>, and assigning that to the interface's expected signature (with R = never) wouldn't work. The convention here is that production service construction should acquire HostProcess dependencies at the make level usingyield*so those requirements bubble up to the layer itself rather than hiding inside the service value. Looking at the existing pattern in the file withresolveTranscriptDirs, it properly acquires its dependencies inmakeand exposes them through the layer. The HostProcess services should follow the same approach — acquiringHostProcessEnvironmentandHostProcessPlatformat the top level soreadSummarystays context-free, which aligns with the comment already in the code. This is both a type correctness issue and a convention deviation that needs fixing. But now I'm wondering if the test actually works — the test providesHostProcessEnvironmentbut notHostProcessPlatform, and ifmakerequired the platform it would fail. Since the requirement is buried insidereadSummaryand called within the same provided context, the test passes, but this suggests neitherNodeServices.layernor the default layers inhostProcess.tsprovideHostProcessPlatform. Let me trace through what's actually being provided. TheContext.Referencevalues with defaults mean R isnever, so there's no type leakage or convention issues. I'm checking whether theno-global-process-runtimelint rule coversNodeOS.homedir()sinceresolveOpenCodeDataDiruses it, though there's a more pressing issue—I need to handle that leftoverrates,comma. Looking at lines 473-481 in the head file, I can see bothrates: servedRates,andrates,are present, confirming a duplicate or undefined identifier. Let me search the file for allratesdeclarations to understand what's in scope—I know there'slitellmRatesat line 153 andopenCodeRatesat line 162, so I need to verify ifratesitself is defined somewhere. The leftoverrates,duplicate is definitely breaking the compile. Looking at the other files in the diff, usagePricing.ts has straightforward pure helpers with named exports which is fine, and usageTranscriptReader.ts contains async functions that handle node:sqlite operations and return null on errors—it's an imperative reader module invoked through Effect.promise rather than Effect service code, so the error handling conventions don't apply the same way. The test file properly usesEffect.provideServiceas a test seam, and the namespace imports follow the right pattern. Thewalfield addition is fine too. Nothing else in the PR violates the Effect conventions. TypeScript will error on duplicate object keys regardless, andratesbeing undefined compounds that. This is technically outside the convention check's scope, but it's a concrete build break in the changed code — I should report it clearly and mark the check as failing since it's a defect within the scope of what was modified. Now I need to verify the other files in the diff for convention compliance: checking whether the imported icons likeOpenCodeIconactually exist in the Icons module, and confirming the mobile file has its equivalentPROVIDER_MARKconstant. I need to post a review comment on line 477 of the UsageService file, pointing out thatrateswas renamed tolitellmRatesbut the aggregator is still using the old variable name. Since I can only suggest changes within the diff range, I'll use a unified diff format in the comment to show what needs to be removed.
1. modify_pr — One finding in apps/server/src/usage/UsageService.ts: the rate-table rename left a stale rates shorthand in the UsageAggregator construction. No Effect service convention violations found in the rest of the changed scope (namespace imports, service tag/make/layer order, dependency acquisition, and test seams all look consistent with the conventions).
Agent Credits: 97 credits