Repository navigation
Effect Service Conventions: 2 issues found
Two findings posted as inline review comments:
apps/server/src/usage/usageOpenCodeDb.ts(line 160): new// eslint-disable-next-line no-await-in-loopdirective is not the repo's oxlint directive form, targets a rule already disabled invite.config.ts, and carries no adjacent rationale — suggest removing it.apps/server/src/provider/Layers/OpenCodeAdapter.ts(lines 1026-1030): the new token accumulator sums everymessage.updatedpayload without keying on message id, so cumulative per-messagetokens/costare double counted intousage,totalCostUsd, andmodelUsage; the added accumulation test only covers distinct message ids, so this path is untested.
Previously flagged items are resolved on this head: the file-level @effect-diagnostics nodeBuiltinImport:off now carries a rationale, resolveOpenCodeDbPath takes the env from the service, node:path in UsageService.ts was replaced with the injected Effect Path, the unused tryExtractOpenCodeTokensFromRecord helper is gone, the as never payload cast was replaced with typed spreads, and focused parser/adapter tests were added.
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: apps/server/src/provider/Layers/OpenCodeAdapter.ts, apps/server/src/usage/{UsageService.ts,usageOpenCodeDb.ts,usageTranscripts.ts,usageScanCache.ts}, packages/contracts/src/usage.ts, the web/mobile provider presentation constants, and the added tests.
Convention checks performed with no violations found: Effect modules are imported from subpaths as namespaces in all touched files; no new Layer.succeed, ManagedRuntime.make, runPromise/runPromiseExit, or service-instance parameters were introduced; the Node-built-in SQLite access is isolated in a single reader module invoked via Effect.promise, mirroring the existing usageTranscriptReader.ts precedent, and its env dependency is now threaded in from HostProcessEnvironment; no new error classes, predicates, or catchIf/catchTag usages were added, so the error-modeling conventions are unaffected; contract/version and provider-kind additions are mechanical and their existing tests were updated rather than duplicated.
🔍 Agent activity — 14 tool calls
💭 I want to verify the current state of the changed files against prior review findings—checking whether the unused helper, missing tests, diagnostic suppression, process.env usage, node:path in UsageService, and the
as nevercast have actually been addressed.
1. git_tools — node:path|NodePath|from "effect"|tryExtractOpenCodeTokensFromRecord
2. git_tools — as never|Record<string, unknown> = {
💭 The prior issues seem resolved, but I want to double-check the UsageService diff since it still references path.join and NodeOS.homedir() — I should verify the imports in UsageService.ts.
3. browse_code — apps/server/src/usage/UsageService.ts
4. browse_code — apps/server/src/usage/UsageService.ts
💭 With prior findings addressed, I'm now checking the diff between 52f99f6 and b34f72e to see what's changed since the last reviewed commit.
5. git_tools — 52f99f6..b34f72e
💭 Since the prior commit isn't available due to a force-push or rebase, I'll just review the current diff instead. The main additions are tests in OpenCodeAdapter.test.ts and usageTranscripts.test.ts along with the adapter code, and I'm now checking the diff for convention violations—starting with some heavy type casts in OpenCodeAdapter.ts that aren't really an Effect-specific issue. I'm checking usageOpenCodeDb.ts, where the file-wide diagnostic suppression now includes a proper reason, which looks good. Then I notice an eslint-disable comment for no-await-in-loop inside readOpenCodeDbRecords, with an adjacent comment explaining that the code yields to the event loop every 100 rows to avoid blocking cold scans — that seems like sufficient justification for the disabled rule, though I want to confirm whether the repo actually uses eslint or something like oxlint instead. I also notice
let isSessionMessageis never reassigned, which is a prefer-const lint issue rather than an Effect convention concern, so I'm weighing whether it's worth flagging given the low value. I'm also considering whether resolveOpenCodeDbPath's dependency on Node APIs isolated in a module is acceptable per the docstring rationale, and whether the error handling in usageOpenCodeDb.ts that swallows errors and returns null mirrors the pattern in usageTranscriptReader.ts, which I should check. The empty catch block on db.close() seems fine as-is, and now I want to verify the actual lint configuration to settle these questions.
6. browse_code — */{.eslintrc,eslint.config.,biome.json,oxlint,.oxlintrc*}
7. browse_code — apps/server/src/usage/usageTranscriptReader.ts
💭 I notice there's no eslint config in the repo itself, only in vendored dependencies, which makes that disable directive effectively dead code for a linter this project doesn't run. I'm weighing whether to flag it for removal since it's not actually suppressing anything active, even though it's adjacent to an explanatory comment. Now I'm scanning the adapter's actual diff for real Effect-convention violations rather than style nitpicks. The
emitOpenCodeTokenUsagefunction takes several positional unknown-typed parameters, and there are numerousas unknown as Record<string, unknown>casts scattered through it — more a type-safety concern than a strict convention breach, so I'm checking whether the existing context objects already have mutable fields as a precedent before flagging anything here. I'm also worried the generic default-case token extraction could double-count usage since OpenCode'smessage.updatedevent likely fires repeatedly with cumulative totals for the same assistant message as it streams, which combined with the part-updated accumulation could inflate token counts significantly — that seems like the real bug worth flagging rather than the untested speculative code path. So multiple events could carry the same token snapshot, causing double counting on repeated updates for one message id. This is a real correctness concern, but it's borderline for the Effect-conventions scope I'm checking — I might flag it lightly as an untested behavior change rather than a hard violation, since the review instructions focus on Effect patterns rather than general logic bugs.
8. browse_code — apps/server/src/provider/Layers/OpenCodeAdapter.ts
9. browse_code — apps/server/src/provider/Layers/OpenCodeAdapter.ts
💭 I'm confirming that token accumulation triggers on every message.updated event without deduping by message ID, and since OpenCode repeatedly emits updates for the same assistant message across part/step boundaries, this could cause double counting—a real correctness concern I need to figure out how to frame in my review. Now I'm evaluating candidate findings against Effect Service Conventions: the eslint-disable directive in usageOpenCodeDb.ts stands out since the repo has no eslint configured, so I should verify that by checking devDependencies, and also grep the diff for other suppression comments like ts-expect-error. The error-swallowing pattern in usageOpenCodeDb matches its sibling reader's precedent, so that's likely fine and not worth flagging.
10. git_tools — eslint
11. git_tools — ts-expect-error|@effect-diagnostics|biome-ignore|oxlint-disable
💭 My grep filter for eslint might have been malformed, causing an empty result even though there's an eslint-disable comment in usageOpenCodeDb.ts. Let me re-run the search without the path exclusion to double-check.
12. git_tools — eslint
💭 This suppression comment doesn't match the repo's oxlint-based directive format and targets a rule that's already disabled globally, plus it lacks the
-- reasonconvention used elsewhere like in http.ts—so it's an unnecessary and inconsistently-styled addition. I'm also checking other Effect convention issues:Effect.promiseusage looks fine since the wrapped functions catch internally, and I don't see newLayer.succeed/ManagedRuntime.make/runPromise calls introduced. The mobile/web diffs are just pure constants. In UsageService.ts, the cache typing for opencode records looks consistent with the scan cache update. TheUSAGE_CONTRACT_VERSIONbump to 6 and the corresponding test adjustment from-2to-3still produces a consistent incompatible-version check againstUSAGE_MERGE_COMPATIBLE_SINCE = 4. The bigger concern is the default-case generic extraction path handling event shapes with heavy type casting, which risks double-counting tokens ifmessage.updatedfires more than once for the same message without id-based dedupe -- and the existing tests only cover distinct message ids, so this gap isn't exercised. I'm weighing whether to flag this: it's not strictly a listed convention violation, but it does fit under the "backend behavior changed requires focused tests" rule, so I think it's defensible to raise precisely on that basis rather than as a generic bug report. Before writing it up though, I want to verify the premise -- I should check whether OpenCode actually re-emitsmessage.updatedrepeatedly for the same message with updated token counts, by grepping the adapter's test fixtures and any existing code comments referencing this behavior, since the existing pattern of updatingmessageRoleByIdon everymessage.updatedcall suggests repeats are indeed expected as normal behavior. For the OpenCodeAdapter.ts accumulation block, I'll comment on lines 1026-1030. For the eslint-disable line, I'm figuring out how to suggest its removal cleanly—probably need a diff-style suggestion rather than just deleting the line, so I'm checking the exact surrounding lines 150-168 in usageOpenCodeDb.ts to construct it properly.
13. browse_code — apps/server/src/usage/usageOpenCodeDb.ts
💭 I'm drafting a review comment flagging that the ESLint-style directive at line 160 in usageOpenCodeDb.ts is ineffective since the project lints with oxlint, and suggesting it be removed since the preceding comment already justifies the intentional await. For the second comment, on OpenCodeAdapter.ts, I'm noting that
message.updatedfires repeatedly for the same assistant message with cumulative token/cost data, so summing across every event double-counts usage onturn.completed. I'm suggesting the fix should key the accumulator by message id, keeping only the latest snapshot per id and summing across distinct ids, plus adding a test with duplicatemessage.updatedevents to verify this. I'll leave this comment without an inline diff since it spans multiple locations, then finalize and post the review with a title reflecting two issues found.
14. modify_pr — </antml import="">
Agent Credits: 96 credits
Token usage: 20 uncached input · 511,459 cache-read input · 65,762 cache-write input · 9,574 output
Agent Credits may also include non-token charges from external tools such as web research.