Skip to content

feat(mcp): run scheduled tasks immediately

MacroscopeApp / Macroscope - Effect Service Conventions failed Aug 30, 2026 in 2m 25s

Effect Service Conventions: 2 issues found

Two convention violations found in the changed Effect service code.

  • apps/server/src/scheduledTasks/ScheduledTaskService.ts (lines 96-119): the five new ScheduledTaskManualRun* error classes declare message: Schema.String as free-form data instead of deriving message from structured attributes (repo convention is fields + override get message()), so the caller-visible text is stored rather than modeled.
  • apps/server/src/scheduledTasks/ScheduledTaskService.test.ts (lines 38-43): import { ..., layer as scheduledTaskLayer } from "./ScheduledTaskService.ts" aliases away the service module namespace; the file already namespaces every other sibling service module.

Both findings were posted as inline review comments.

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 examined in scope: apps/server/src/scheduledTasks/ScheduledTaskService.ts, apps/server/src/scheduledTasks/ScheduledTaskService.test.ts, apps/server/src/orchestration-v2/Orchestrator.ts, ThreadLaunchService.ts, ThreadManagementService.ts, apps/server/src/mcp/OrchestratorMcpService.ts, apps/server/src/mcp/toolkits/orchestrator/{tools,handlers}.ts, apps/server/src/mcp/OrchestratorMcpToolkit.integration.test.ts, apps/server/src/provider/T3OrchestrationInstructions.ts, packages/contracts/src/{orchestrationV2,orchestratorMcp}.ts, packages/shared/src/t3McpToolPresentation.ts.

Checked and found acceptable:

  • Effect modules are imported as subpath namespaces throughout the new code; no consolidated from "effect" imports were added.
  • makeKeyedSerialExecutor is a pure helper factory (no tag/layer), and the named import matches existing usage in Orchestrator.ts and LegacyV1ThreadImporter.ts.
  • Layer.succeed occurrences (layerUnavailable, test harness stubs) are an existing unavailable-runtime placeholder and test seams, not hidden runtimes; no ManagedRuntime.make, runPromise, or runPromiseExit was introduced.
  • New service methods (runNowIdempotent, getCommandReceipt) acquire dependencies from the environment; no service instances are passed as constructor parameters in production code.
  • OrchestratorV2Shape / ThreadManagementServiceShape are pre-existing standalone shapes only extended by one member each; flagging them would require unrelated refactoring outside this PR's scope.
  • ThreadManagementServiceShape.getCommandReceipt correctly references the inferred interface via OrchestratorV2["Service"]["getCommandReceipt"].
  • New backend behavior is covered by focused tests in ScheduledTaskService.test.ts using real layers with test seams only for external services.

🔍 Agent activity — 9 tool calls

💭 I'm reviewing the diff for Effect service conventions, checking key files like ScheduledTaskService, Orchestrator, ThreadLaunchService, ThreadManagementService, and OrchestratorMcpService along with their contracts. I'm tracing through the imports in ScheduledTaskService to verify how it pulls in the command receipt types and the keyed serial executor. I'm checking whether OrchestratorV2Shape follows the convention of defining service interfaces inline in Context.Service rather than as standalone shape types, though it looks like this is a pre-existing pattern with ThreadManagementServiceShape correctly referencing OrchestratorV2["Service"]["getCommandReceipt"]. I'm also examining the error classes like ScheduledTaskManualRunScopeError which appear to store an unstructured message as data instead of deriving the message from structured attributes, which violates the intended convention. I want to actually read the current state of the ScheduledTaskService file to verify whether these error classes and their free-form message fields are genuinely a violation or match existing patterns elsewhere in the codebase.

1. browse_code — apps/server/src/scheduledTasks/ScheduledTaskService.ts

2. browse_code — apps/server/src/orchestration-v2/KeyedSerialExecutor.ts

💭 I want to check whether KeyedSerialExecutor is a service module and how other consumers import it. I'm also looking at ScheduledTaskError's existing shape—it takes message, taskId, and cause—so the codebase convention already stores an unstructured message rather than deriving it purely from attributes, which is worth reconciling with the check's guidance. Looking closer, cases like ScheduledTaskManualRunScopeError have five distinct free-form messages that really seem like they should come from a structured attribute (something like reason/role/stage) with the message derived from it, rather than embedding raw text. The ceiling errors have a similar issue where runtime mode is baked into the message string instead of being a separate field. This looks like a genuine violation of the errors convention, so I want to verify how consistent this pattern is by checking the existing ScheduledTaskError in contracts.

3. browse_code — apps/server/src/orchestration-v2/KeyedSerialExecutor.ts

4. git_tools — ScheduledTaskError

💭 I'm checking that KeyedSerialExecutor's named import style is consistent with existing conventions, since it's a plain helper rather than a tagged service. I'm also noting that the new error classes use a free-form string message field, similar to the legacy ScheduledTaskError pattern already in the contracts. Looking at ScheduledTaskManualRunScopeError specifically, it stores taskId plus an arbitrary message string that varies across roughly five call sites—this seems like a genuine violation of the rule to derive messages from structured attributes rather than storing unstructured text as the sole payload. The ceiling errors do properly split into separate classes by failure kind, but they still embed the runtimeMode value inside a free-form message rather than exposing it as a structured field. I'm also checking OrchestratorMcpService.ts, where the import style mixes a type import with the service import—this looks like a pre-existing named-import pattern rather than something new introduced by the PR, so it may not be worth flagging on its own. But the new ScheduledTaskService.test.ts file imports the layer with an alias (layer as scheduledTaskLayer), which is exactly the kind of namespace-erasing pattern the rule calls out, so that seems like a clear violation worth flagging even though it's in a test file. Since this fix touches multiple usage sites throughout the file rather than a single self-contained spot, I don't think an inline suggestion fits—I'll provide a diff for the import change and note that usages need updating accordingly. The test file falls within scope of the include patterns, so this issue counts, and I'm moving on to check the next item. Looking at ThreadManagementServiceShape and OrchestratorV2Shape, these are pre-existing standalone shape interfaces that the PR only extends with a new member—flagging them for inlining would demand an unrelated repo-wide refactor, so I'll skip that. Similarly, layerUnavailable in Orchestrator.ts uses a pre-existing pattern the PR isn't really touching beyond adding a member, so that's fine too. Now checking OrchestratorMcpService.ts next. In scheduledTaskManualRunFailure, errors get mapped via Effect.mapError with a switch on _tag, which is a translation boundary mapper rather than a catch, so that's acceptable per the rules. The switch constructs a failure using the domain error's own message—technically the rule prefers deriving wrapper messages from structural attributes rather than cause.message, but since this is presenting the domain error's message at the MCP boundary, it seems like an acceptable use case rather than a violation. Looking at runNowIdempotent, it uses a pre-existing taskError helper that just constructs a ScheduledTaskError—since it's pre-existing rather than newly added, it doesn't trigger the rule against trivial error-constructor helpers. I also want to check the poll loop where Effect.catchCause replaces a simpler catch, examining lines around 940-970 to see if it's handling interruption correctly, especially given the convention about startup reconciliation needing to preserve interruption semantics.

5. browse_code — apps/server/src/scheduledTasks/ScheduledTaskService.ts

6. git_tools — makeKeyedSerialExecutor|KeyedSerialExecutor

💭 The KeyedSerialExecutor import matches existing usage, so that checks out. I'm now scanning the test file's imports and confirming patterns like using Layer.succeed with service instances and ServerSettings.layerTest() are consistent with how other tests set up their provider registry. The type assertion casting the adapter object as ProviderAdapterV2Shape is a pre-existing convention, not a new concern. Let me pin down the exact line numbers in the test file's header so I can reference them precisely in the inline comments.

7. browse_code — apps/server/src/scheduledTasks/ScheduledTaskService.test.ts

💭 I've confirmed that first finding, where a namespaced import gets aliased in a way that erases the module namespace, standing out against sibling files that keep namespace imports intact. Now I'm checking the second finding: whether new error classes using a free-form string message actually violate convention, so I'm searching for other server error classes that derive their message via a getter instead.

8. git_tools — get message()

💭 I'm noticing the five new error classes in ScheduledTaskService.ts store a free-form message field instead of deriving it from structured fields via a getter, breaking the repo-wide convention (aside from the legacy ScheduledTaskError exception). I'll draft one inline comment covering that block, lines 96-119. Since fixing this properly means restructuring the classes and updating every construction site across multiple hunks, a self-contained diff isn't feasible, so I'll go with "no diff" but include a short illustrative example showing the intended shape for one of the classes. For the second finding on the test import alias, converting to a namespace import means renaming usages elsewhere in the file, which spans other hunks too — so I'll give a unified diff just for the import line itself and add a note that references like ScheduledTaskService need to become namespaced accordingly. This aliasing pattern is exactly what the rule calls out, so it's worth flagging. I'm also considering the catchCause swap in the poll loop, which unlike catch also swallows defects and interruption signals. That's borderline since the guidance about preserving interruption really targets startup reconciliation, not a per-task poll loop with isolation — so I'll likely leave this one out to avoid unnecessary noise, though the shutdown-interrupt-as-warning behavior gives me some pause. Since interruption is sticky in Effect, the fiber stays marked interrupted regardless, so this is mostly benign and I'll skip flagging it, settling on two solid findings. Given the review has actual findings, the check state should be failure rather than "All clear," so I need to make sure my output reflects that properly in the JSON response. Now I'm drafting the two review comments: one for the free-form message: Schema.String field in ScheduledTaskService.ts around lines 96-119, and another for the test file around lines 38-43 targeting the layer as scheduledTaskLayer line. I'm setting the line ranges to match the diff hunks so the comments anchor correctly, then writing them out concisely.

9. modify_pr — Reviewed the changed Effect service code (ScheduledTaskService, Orchestrator, ThreadLaunchService, ThreadManagementService, MCP toolkit/handlers, contracts) against the service conventions. Two convention violations found, both in apps/server/src/scheduledTasks/. The new policyCeiling plumbing, getCommandReceipt exposure, and makeKeyedSerialExecutor usage all follow existing module/layer conventions.


Agent Credits: 192 credits

Token usage: 14 uncached input · 530,920 cache-read input · 210,470 cache-write input · 9,766 output
Agent Credits may also include non-token charges from external tools such as web research.