Repository navigation
fix(usage): price new models without waiting a day for the rate table - #9202
Effect Service Conventions: All clear
All clear
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.
Reviewed the changed TypeScript in scope (apps/server/src/usage/UsageService.ts, apps/server/src/usage/UsageService.test.ts, apps/server/src/usage/usagePricing.ts, apps/server/src/ws.ts, apps/server/src/auth/RpcAuthorization.ts, apps/web/src/state/usage.ts, apps/mobile/src/state/usage.ts, packages/client-runtime/src/state/server.ts, packages/contracts/src/rpc.ts) against the Effect service conventions.
Checked and found conforming:
- Namespace subpath imports for Effect modules (
effect/Semaphore,effect/Duration,effect/testing/TestClock), matching existing repo usage; named imports retained for@t3tools/contractstypes and the purerunAtomCommandhelper. UsageServicekeeps the canonical module order (imports, schemas,Context.Servicetag with inline interface,make,layer);refreshRatesis added to the inline interface with no standalone shape type.makeacquires all dependencies from the environment (FileSystem,Path,ServerConfig,ServerSettingsService,HttpClient,HostProcessEnvironment,Semaphore.make); no service-instance parameters, module-global runtimes,ManagedRuntime.make, orrunPromiseintroduced in service code.layerTest'sLayer.succeedremains a pure test seam.- Client-side
runAtomCommanduse stays at the React hook boundary. - RPC surface follows existing patterns (
Schema.Struct({})payload,EnvironmentAuthorizationErrorchannel, registered inWsRpcGroupandRPC_REQUIRED_SCOPES); no new error types, predicates, or lint suppressions were added. - Behavior change is covered by a focused server test that stubs only the external LiteLLM HTTP response.
🔍 Agent activity — 10 tool calls
💭 I'm checking the changed files for Effect service convention violations, specifically noting the semaphore and test clock imports which suggest this repo uses a newer Effect version with different module paths. That seems fine, so I'll move on to examine the actual files.
1. browse_code — apps/server/src/usage/UsageService.ts
2. git_tools — 70cd258..d378d2e
💭 I'm checking whether the Semaphore import path is valid for this effect version, and reviewing other conventions like Effect.fn, withSpan usage, and inline service interfaces to make sure everything matches existing repo patterns.
3. git_tools — effect/Semaphore
4. git_tools — makeSemaphore|Semaphore
💭 The semaphore usage looks idiomatic. I'm checking the ws.ts handler and RPC error handling, which seem fine since refreshRates never fails. Now I'm looking at whether combining
it.livewith aTestClock.layer()causes conflicts, since running under the live clock while also providing a TestClock and callingTestClock.adjustseems like it could be a real issue. Tracing through the timing math though, it actually checks out: starting at time 0, then adjusting by two minutes puts currentTimeMillis at 120000, which correctly triggers a refresh past the 60-second floor while staying well under the 24-hour staleness threshold. This seems more like a test-correctness question than an Effect service convention issue, so it might be out of scope for what I'm checking. The main convention items to verify: refreshRates matches the Effect.Effect pattern seen elsewhere, the semaphore properly serializes and re-checks timestamps after waiting (addressing the earlier coalescing concern), and the disk-load path correctly uses maxAgeMs now. On the client side, I want to confirm whether runAtomCommand returns a Promise that supports .finally, and whether using runPromise at a React hook boundary is an acceptable pattern here. Now let me check the client-runtime state and runtime setup.
5. git_tools — export function runAtomCommand|export const runAtomCommand
6. git_tools — runAtomCommand
7. browse_code — packages/client-runtime/src/state/runtime.ts
💭 The runAtomCommand promise resolves rather than rejecting, so
.finallyshould work fine here. I want to verify the shape passed to createEnvironmentRpcCommand matches other commands, and check how empty payload schemas are typically defined elsewhere, like in serverRetryRes.
8. git_tools — WsServerRetryResourceTelemetryRpc = Rpc.make
9. git_tools — mode: "singleFlight"
💭 I'm checking for actual Effect service convention violations now, verifying the ws.ts handler's error channel typing and confirming the module ordering in the service definition file—imports, schema, tag, constants, layerTest, make, layer—all looks correctly structured, with layerTest's pure implementation being an acceptable pattern. Looking closer at
refreshRates, I noticeEffect.map(pricing)runs after the lock releases, so it could read state mutated by a concurrent fetch—minor but not a real convention violation, and the spans and test coverage look properly set up. I'm now wondering whether combiningit.livewith a provided TestClock is actually a valid pattern in @effect/vitest, sinceit.effectnormally supplies TestClock automatically. That combination seems intentional here since the service touches real filesystem/timing, though I want to double check thatUsagePricingis exported from the contracts module as both a schema value and usable as a type, since it's imported astype UsagePricingin the usage service.
10. git_tools — UsagePricing
💭 No violations found so far. I'm double-checking the mobile/web useUsage import placement and the ws.ts handler naming, both of which seem fine against the conventions, and now looking at how readSummary handles its scope. The refreshRates path isn't deduped through the deferred cache key, but the semaphore already guards concurrent access, and the pricing map returned outside the lock still matches the interface's error-free contract. Module-level mutable state follows an existing pattern, so nothing stands out there either. I'm now weighing how to phrase my final response—there's tension between an instruction to reply with a fixed phrase when nothing is found and the requirement to output valid JSON, so I'll fold that phrase into the JSON summary field instead. Before finalizing, I want to make sure a test is warranted here since this is a behavior change rather than a pure mechanical refactor, so adding one seems appropriate. I'm double-checking the TestClock usage pattern with the live test runner and confirming the semaphore setup inside the generator doesn't need scoping—both look fine, so I'll conclude there are no findings and mark this as a success with a clean summary.
Agent Credits: 84 credits
Token usage: 16 uncached input · 398,075 cache-read input · 72,957 cache-write input · 5,619 output
Agent Credits may also include non-token charges from external tools such as web research.