Skip to content

feat(usage): show Codex and Claude subscription limits on a Limits tab - #9507

Merged
juliusmarminge merged 7 commits into
mainfrom
feat/provider-usage-limits
Sep 4, 2026
Merged

juliusmarminge merged 7 commits into
mainfrom
feat/provider-usage-limits

perf(usage): skip the publish when a runtime rate-limit event changes…

20594ca
Select commit
Loading
Failed to load commit list.
MacroscopeApp / Macroscope - Effect Service Conventions succeeded Sep 4, 2026 in 5m 34s

Effect Service Conventions: no issues found

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 in-scope TypeScript (apps/**/*.ts, packages/**/*.ts, excluding tests) at head 20594ca: apps/server/src/usage/UsageLimitSources.ts, usage/cliproxyUsageLimits.ts, provider/Layers/claudeUsageLimits.ts, provider/Layers/codexUsageLimits.ts, provider/Layers/ProviderUsageLimitsIngestion.ts, provider/providerUsageLimits.ts, provider/makeManagedServerProvider.ts, provider/Layers/{ClaudeProvider,CodexProvider,ClaudeAdapter,CodexAdapter}.ts, serverSettings.ts, ws.ts, server.ts, plus the contracts, shared, and client-runtime modules.

Findings from earlier runs are addressed on this head:

  • codexRateLimitsFailureMessage now takes CodexErrors.CodexAppServerError and narrows on _tag into bounded, client-safe messages, with the raw error logged as cause.
  • The hub read failure text is produced by readFailureMessage from the statically known typed failure channel (HttpClientError / SchemaError / TimeoutError / InvalidUrl), and the structured failure is logged at debug instead of being copied onto the published snapshot.
  • InvalidUrl is a tagged error carrying url and the underlying cause.
  • The untargeted serverRefreshProviders handler now awaits usageLimitSources.refresh rather than forking it into the per-request scope.

No new violations of the check's conventions on changed lines: Effect modules are imported as subpath namespaces (with effect/unstable/http named imports matching the repo-wide pattern), UsageLimitSources follows the single-module Context.Service + inline interface + make + layer shape and acquires HttpClient, ServerSettingsService, and BackgroundPolicy from the environment, no ManagedRuntime/runPromise was introduced into service construction, and no lint or type-checker suppression directives were added in the new modules.


🔍 Agent activity — 9 tool calls

💭 I'm checking the current head commit to see which flagged issues remain unresolved. The InvalidUrl thread was fixed using Data.TaggedError instead of my suggested Schema.TaggedErrorClass convention, but since the author already responded and resolved it, I'm weighing whether re-flagging that stylistic difference is worth being duplicative. Now I'm scanning UsageLimitSources.ts for remaining concerns — checking whether HttpClient imports from the http subpath follow the repo's namespace import convention, and looking at whether Semaphore is being used correctly with withPermits, plus verifying import ordering across the Ref and other effect module imports. I also notice codexPlanLabel is exported from CodexProvider and consumed cross-module by cliproxyUsageLimits, and that the diff dropped the account.planType satisfies never exhaustiveness check — a behavior change worth flagging even if it's not strictly an Effect convention violation. Let me pull up the actual files to confirm these before writing anything up.

1. browse_code — apps/server/src/usage/UsageLimitSources.ts

2. browse_code — apps/server/src/provider/Layers/codexUsageLimits.ts

💭 Looking at the failure handling pattern, using Effect.result with a tag switch instead of Effect.catchTags seems like a reasonable alternative approach, and the convention doesn't explicitly flag this pattern as problematic. For InvalidUrl, the switch from Schema.TaggedErrorClass to Data.TaggedError was already addressed in a prior comment, and since it's an internal error that never escapes the service, this seems acceptable—no need to relitigate. The cause: unknown field also looks fine given it comes from an Effect.try catch handler. On module ordering, I'm noting that readFailureMessage and the InvalidUrl error class both appear after the tag declaration, with InvalidUrl defined after the function that references it—this was already part of my earlier feedback about keeping the imports → errors → tag → make ordering convention intact. I'm trying to figure out what's changed between my last review (c4059af) and the current head (20594ca): semaphore refresh work, the InvalidUrl error type switch, a patch-merge approach for usageLimitSources settings, an awaited refresh in ws.ts, and a new doc comment on resolveUsageLimitsAfterProbe. I want to focus specifically on the new code—the pure merge function in serverSettings.ts, the patch schema in contracts/settings.ts, and the secret handling logic in apps/server's serverSettings.ts—to see if anything there needs flagging. Checking apps/server's secret-reading error handling, it follows the existing ServerSettingsError convention, so that's fine. Moving to the ProviderUsageLimitsIngestion.ts file, its use of Effect.ignoreCause and Layer.effectDiscard looks consistent with patterns elsewhere. In claudeUsageLimits.ts, the exported Ref constructor and the function that records usage responses by taking a Ref directly rather than pulling it from a service environment seem acceptable, though I'm noting that ClaudeProvider's status-check function also takes a Ref as an explicit parameter instead of resolving it from context — worth keeping an eye on whether that's an intentional pattern or something to flag. I also notice the usage probe swallows a failure from a promise chain without logging it, which doesn't clearly violate any explicit convention but is worth double-checking against how the surrounding async function handles errors elsewhere. I want to look more closely at the ClaudeProvider file to see if this Promise-based probe follows established conventions for error handling within otherwise Effect-based code.

3. browse_code — apps/server/src/provider/Layers/ClaudeProvider.ts

4. browse_code — apps/server/src/provider/Layers/ClaudeProvider.ts

💭 I don't see obvious convention violations in the CodexProvider changes — the error handling with Effect.catch looks fine since it covers the whole error channel. I'm double-checking whether the typed error assumption could cause a type mismatch if the request fails differently, though the check passed, and I'm also noting the removal of the exhaustiveness check on planType. The function's signature now accepts a looser type, so it loses the compile-time guarantee that new plan types get handled — that's a real regression, but it's not something the "Effect Service Conventions" checklist covers directly, so I'm inclined to treat it as out of scope rather than flag it here.

5. browse_code — apps/server/src/provider/Layers/CodexProvider.ts

6. browse_code — apps/server/src/provider/Layers/CodexProvider.ts

💭 I'm checking that logging the failure cause as a debug annotation is fine even though the error message might embed a URL—since logs aren't wire payloads, preserving the exact cause value there is acceptable. Now I'm scoping which files are actually in play: the include patterns only cover .ts files, so .tsx files across web and mobile are excluded, meaning several dialog components are out of scope. That leaves me focused on server, contracts, shared, client-runtime, and a couple of state/timestamp files, and I want to confirm ServerProvider.ts follows the convention of defining its interface inline in Context.Service rather than a separate shape type. That ServerProviderShape interface is pre-existing rather than newly introduced by the diff, so it's not something to flag. I want to look more closely at makeManagedServerProvider.ts's new code, note that providerUsageLimits.ts is a new helper file without a module doc comment (though that's not an enforced convention), and check whether the semaphore usage and the import of HttpClient/HttpClientResponse as named imports from effect/unstable/http actually matches this repo's pattern of importing Effect modules as namespaces — I should grep to verify.

7. browse_code — How do other server files import HttpClient / HttpClientResponse from effect (e.g. import { HttpClient } from "effect/unstable/http" vs import * as HttpClient from ...)? Show a few examples of the exact import lines in apps/server/src and packages.

💭 That matches the repo convention, so I'll move on to checking the new code in makeManagedServerProvider.ts and ProviderUsageLimitsIngestion.ts more carefully, particularly how the ingestion logic forks a scoped effect inside Layer.effectDiscard and whether that pattern actually has access to a proper scope.

8. browse_code — apps/server/src/provider/makeManagedServerProvider.ts

💭 Now I'm checking the new error handling in serverSettings.ts, which uses ServerSettingsError with operation values like "read-secret", "write-secret", "remove-secret", and "remove-stale-secret" — that multi-value pattern is consistent with the existing convention, though I want to verify whether "remove-stale-secret" is actually part of the operation schema union. I'm also weighing whether previously flagged issues, like the placement of readFailureMessage relative to InvalidUrl, are worth mentioning again — they aren't, since that would just repeat prior feedback. I'm scanning claudeUsageLimits.ts and ClaudeAdapterLiveOptions for anything genuinely new, but the makeClaudeScopedLimitNames Ref constant and the optional scopedLimitNames dependency both look like reasonable patterns rather than actual issues. Now I'm checking providerUsageLimits.ts and the contracts/shared modules for missing doc headers or convention violations, but nothing stands out there either. The more interesting question is in UsageLimitSources.ts, where they use Effect.result with a manual _tag switch instead of Effect.catchTags — this bypasses the recommended combinator and creates a fragility risk if a fifth error type is ever added to the union, since the switch has no default case to catch it. Actually since the switch is exhaustive over a declared TypeScript union with no default, the compiler would flag any unhandled case at the call site, so this pattern is type-safe despite not being the idiomatic combinator. I'll treat this as acceptable rather than a violation and move on. Now I'm looking at cliproxyUsageLimits.ts, which imports a named function directly from a large provider layer module rather than importing that module as a namespace — this seems to break the convention around importing local service modules as namespaces at service boundaries. That interface pre-exists, so adding a method to it isn't creating a new standalone shape — skip that flag. I'm also checking the UsageLimitSources service, which uses a getter returning a fresh stream each access, similar to existing layer patterns, and its refresh function swallows failures via ignoreCause while logging, keeping the typed Effect signature intact. Now I'm looking at whether a settings read failure could silently wipe published sources — if getSettings fails and falls back to null via orElseSucceed without logging, that produces an empty entries object, which then publishes an empty snapshot list and clears all source rows. That feels like a real behavior bug rather than just a style nit, since a transient settings failure shouldn't erase already-published data, and it's worth flagging against the convention that failures should stay typed and visible rather than being silently swallowed into a fallback that has side effects. I'm leaning toward "all clear" since no new listed-convention violation stands out beyond what was already addressed, but I want to double-check the diff for any suppressed lint directives or suspicious type assertions added without justification—scanning for disable comments or unsafe casts in the non-test, in-scope code.

9. browse_code — In the files apps/server/src/usage/UsageLimitSources.ts, apps/server/src/usage/cliproxyUsageLimits.ts, apps/server/src/provider/Layers/claudeUsageLimits.ts, apps/server/src/provider/Layers/codexUsageLimits.ts, apps/server/src/provider/providerUsageLimits.ts, apps/server/src/provider/Layers/ProviderUsageLimitsIngestion.ts — are there any lint/type-checker suppression directives (oxlint-disable, eslint-disable, @ts-expect-error, @ts-ignore, biome-ignore)? Quote each occurrence with line numbers, or say none.

💭 Everything looks resolved with no new findings against the conventions, so I'll report "All clear" as the summary in my complete_check call, satisfying the minimum length requirement without needing any additional comments.


Agent Credits: 218 credits

Token usage: 28 uncached input · 819,088 cache-read input · 199,697 cache-write input · 16,463 output
Agent Credits may also include non-token charges from external tools such as web research.