Skip to content

fix: preserve automatic resume after reporting failures

MacroscopeApp / Macroscope - Effect Service Conventions failed Aug 28, 2026 in 1m 52s

Effect Service Conventions: 1 issue found

1 finding

  • apps/server/src/orchestration/Layers/ProviderCommandReactor.ts (startup recovery of pending usage-limit resumes, lines ~1774-1798): the per-thread repair dispatch is not retried and its Effect.catchCause swallows interruption into a warning. Every other usage-limit dispatch in this file goes through retryUsageLimitResumeDispatch, and the comparable startup reconciliation in serverRuntimeStartup.ts retries once and re-fails on interrupt. Suggested fix posted inline (retry once, propagate interrupts, keep per-thread isolation), plus tests for the retry-success and persistent-failure-continuation paths.

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: all changed apps/** and packages/** TypeScript/TSX files.

Checked and found clean:

  • Imports: new modules use namespaced Effect subpath imports (effect/DateTime, effect/Fiber, effect/Schedule, effect/unstable/sql/SqlClient); no consolidated from "effect" imports were introduced. @t3tools/contracts keeps named imports as expected, and the new pure helpers (apps/server/src/provider/usageLimits.ts, packages/client-runtime/src/state/usageLimitResume.ts) are correctly consumed with named imports.
  • Service definition/layout: no new Context.Service tags, shapes, make/layer exports, or service file moves; nothing requires the canonical single-file ordering rules.
  • Dependency acquisition: no new ManagedRuntime.make, runPromise/runPromiseExit, Layer.succeed, or service-instance parameters in production code. The changed Effect.runPromise β†’ harness.runEffect call sites are test-harness only. The new usageLimitResumeFibers map is scoped inside make, not a module global.
  • Errors and predicates: no new error classes, Schema.Defect usage, discriminator fields, or catchIf/catchTag recoveries were introduced; error metadata added to the session projection (lastErrorClass, retryAt) is normalized/bounded rather than raw payload data.
  • Change discipline: new tests accompany the behavior change (decider, projector, reactor, adapters, provider usage-limit helpers) rather than proving a mechanical refactor.

Note (not flagged): retryUsageLimitResumeDispatch retries indefinitely with a 30s-capped exponential schedule. That is acceptable on the fiber/worker paths, but reusing it verbatim on the startup path would block readiness, which is why the inline suggestion uses a bounded retry there.


πŸ” Agent activity β€” 6 tool calls

πŸ’­ I'm reviewing the diff for Effect service convention violations, checking the new usage limits helper files, migration, and tests to confirm imports follow the expected patterns. The reactor introduces a fiber map scoped within make, which seems consistent with existing patterns, and I'm now examining whether the startup reconciliation logic properly preserves interruption for persistent failures rather than just logging a warning, since it retries transient per-entity repairs before catching the cause broadly for pending resumes. I notice the recovery dispatch for usage-limit-resume lacks the retry wrapper other dispatches use, unlike retryUsageLimitResumeDispatch elsewhere β€” a plausible gap where startup recovery isn't retried and swallows interruption. I also spot a similar pattern in getCommandReadModel's Effect.catchCause, which logs a warning and falls back to an empty array, again potentially swallowing interruption at startup. I want to check the actual file to confirm line numbers, and also note the odd IIFE-based field-stripping in setThreadSessionErrorOnTurnStartFailure as a possible style concern. Checking retryUsageLimitResumeDispatch more closely, it uses a capped exponential schedule with infinite retries, but since Effect.retry doesn't retry interruptions, that should be fine. I'll also glance at whether providerUsageLimitFromError mixes null and undefined inconsistently, though that seems minor, and then pull up the startup block in ProviderCommandReactor to verify everything.

1. git_tools β€” usage-limit-resume-recover

2. git_tools β€” hasInterruptsOnly

πŸ’­ I notice the startup recovery block swallows interrupts into a mere warning without retrying the recovery dispatch, unlike other usage-limit dispatches that use retryUsageLimitResumeDispatch. This points to a convention: startup reconciliation repairing multiple independent entities should preserve interruption signals rather than downgrading them, retry transient per-entity repairs before declaring readiness, and isolate persistent failures so one bad entity doesn't block the rest. I'm also noting a related spot where Effect.catchCause on getCommandReadModel() similarly reduces interruption to a warning during layer startup, though it's a single global query rather than per-entity. Checking the existing test suite, I see it covers recovery success but not the transient-retry or persistent-failure-isolation paths, which confirms this is a real gap worth flagging alongside the primary finding. I'm also weighing whether retryUsageLimitResumeDispatch's unbounded retry loop in the event-processing worker counts as a violation of the isolate-persistent-failure convention, but since that's the event path rather than startup, flagging it feels riskier and less clear-cut. I've also scanned imports and exports in ChatView.tsx and the client-runtime package.json and found nothing else worth noting there. The tests replacing Effect.runPromise calls with harness.runEffect look consistent with allowed test-harness patterns, and no new service definitions appear in the diff. That leaves one main finding to write up, so I want to pull up the exact surrounding lines for retryUsageLimitResumeDispatch and the related block further down to phrase the suggestion accurately.

3. browse_code β€” apps/server/src/orchestration/Layers/ProviderCommandReactor.ts

4. browse_code β€” apps/server/src/orchestration/Layers/ProviderCommandReactor.ts

πŸ’­ I'm drafting the review comment noting the startup recovery flow swallows interruption into a warning instead of using the retry wrapper that every other usage-limit dispatch relies on, and I'm suggesting wrapping the dispatch call to preserve interruption handling consistently. But I realize retryUsageLimitResumeDispatch retries infinitely, and since this call sits inside a forEach that's awaited before startup completes, an unbounded retry could stall readiness β€” so I should suggest a bounded retry schedule instead of a naive swap, though that complicates a simple inline suggestion. Still writing retry logic...

5. git_tools β€” Effect.retry({

πŸ’­ I found a matching precedent in serverRuntimeStartup.ts that retries once then catches the cause, preserving interrupts but logging a warning otherwise. I'm mirroring that same pattern for the inline suggestion at lines 1789-1796. I'm double-checking indentation levels against the surrounding lines so the suggestion fits cleanly into the diff, and confirming these lines are on the added side so they're commentable. I'll also briefly flag the similar catchCause swallowing interrupts at line 1765 as a secondary note in the review body rather than adding another inline suggestion. I'm deciding not to raise the unbounded retry loop as a separate finding since it's tangential to the startup reconciliation convention, and I'll note the missing retry-success/persistent-failure test coverage as a supplementary point. Now I'm assembling the review comment text and preparing the suggestion block with exact matching code lines before posting the review with a failing status.

6. modify_pr β€” One finding in the reactor's startup reconciliation of pending usage-limit resumes. Details inline.


Agent Credits: 106 credits

Token usage: 10 uncached input Β· 371,018 cache-read input Β· 101,332 cache-write input Β· 7,370 output
Agent Credits may also include non-token charges from external tools such as web research.