Skip to content

refactor(server): distinguish manual run failures

MacroscopeApp / Macroscope - Effect Service Conventions failed Aug 30, 2026 in 3m 0s

Effect Service Conventions: 1 issue found

1 finding

  • apps/server/src/orchestration-v2/Orchestrator.ts (line 179): the new getCommandReceipt member on OrchestratorV2Shape is typed via the standalone CommandReceiptStoreV2Shape["getByCommandId"] (and adds a type CommandReceiptStoreV2Shape import) instead of the inferred service interface CommandReceiptStoreV2["Service"]["getByCommandId"], which is the form used by the adjacent members and by the mirrored member added in ThreadManagementService.ts.

No other convention violations were found in the changed Effect service code.

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

  • apps/server/src/scheduledTasks/ScheduledTaskService.ts — new manual-run errors are now distinct Schema.TaggedErrorClass declarations with structured attributes and messages derived from those attributes (prior findings on message: Schema.String payloads and reason discriminators are resolved). Module order (imports, errors, tag, layer) is unchanged from the existing canonical layout; makeKeyedSerialExecutor is a pure helper, so the named import is appropriate. Effect.catchCause in the poll loop intentionally handles the whole channel while re-failing interrupts, which is allowed.
  • apps/server/src/scheduledTasks/ScheduledTaskService.test.ts — uses import * as ScheduledTasks from "./ScheduledTaskService.ts" and Foo["Service"][...] types; service instances are passed only through test harness seams (mapThreadManagement, getProject), which is a legitimate test seam rather than production injection. Backend behavior changed, so the added coverage is expected.
  • apps/server/src/mcp/OrchestratorMcpService.ts — scheduledTaskManualRunFailure mirrors the existing threadManagementFailure translation boundary and performs real classification across many tags, so it is not a redundant one-line error factory.
  • apps/server/src/orchestration-v2/ThreadManagementService.ts, ThreadLaunchService.ts, apps/server/src/mcp/toolkits/orchestrator/*, packages/contracts/src/*, packages/shared/src/t3McpToolPresentation.ts — no service-definition, dependency-acquisition, or runtime-boundary violations (no new ManagedRuntime.make, runPromise, or fake Layer.succeed implementations).

Not flagged

  • Pre-existing standalone shapes (OrchestratorMcpServiceShape, ThreadManagementServiceShape, CommandReceiptStoreV2Shape itself) — legacy, outside this PR's scope apart from the new reference noted above.
  • New manual-run errors without a cause field: the translated upstream error (ThreadManagementThreadNotFoundError) carries no context beyond the threadId/projectId already captured on the new errors.

🔍 Agent activity — 10 tool calls

💭 I'm reviewing the diff for Effect service convention violations, checking ScheduledTaskService, Orchestrator, ThreadManagementService, the MCP toolkit, and contracts. The previously flagged issues with generic error messages and reason discriminators appear fixed now with distinct tagged error classes, so I'm moving on to check the new error classes in ScheduledTaskService.ts for remaining problems. Those new error classes lack a cause field, but since they're mostly validation/domain errors that's acceptable per convention. I'm also examining the named import style in OrchestratorMcpService.ts, the test file's namespace import (which already got addressed), and whether the standalone OrchestratorMcpServiceShape interface is pre-existing or newly introduced by this diff. Now I'm checking CommandReceiptStoreV2Shape["getByCommandId"] in Orchestrator.ts against the preferred Service["getCommandReceipt"] pattern used elsewhere, and verifying whether CommandReceiptStoreV2 is defined as a Context.Service to see if this usage should be flagged. I'm also confirming the test harness setup and the static error message for ScheduledTaskManualRunTaskScopeError look fine as-is. Moving on, taskError remains a pre-existing helper so it's not new debt, and the Effect.catchCause interrupt-preservation logic in ScheduledTaskService is intentional and consistent with convention. Now I'm looking at scheduledTaskManualRunFailure in OrchestratorMcpService, a switch-based error-to-error mapper, and weighing whether this translation logic should instead live as a static factory on the target error class per convention. I conclude it's one-off usage, so an inline switch is acceptable rather than forcing a static method, though I want to check if similar mapper patterns already exist elsewhere in that file. The domain errors lacking a cause field, like ScheduledTaskManualRunNotFoundError, look fine since they're pure domain errors. I want to actually inspect the files directly now, starting with CommandReceiptStoreV2.

1. git_tools — CommandReceiptStoreV2Shape

2. git_tools — OrchestratorMcpFailure|function failure

💭 The scheduled task manual run failure pattern matches the existing thread management failure convention, which is consistent. I'm checking that the pre-existing CommandReceiptStoreV2Shape interface is being referenced correctly, following the convention of using the inferred service interface's typed method signature rather than a separate type. Comparing against ThreadManagementService.ts, which correctly uses the canonical Foo["Service"] form, I notice the new Orchestrator.ts line is inconsistent — it introduces a fresh import of the deprecated standalone Shape type instead. This looks like a legitimate, if minor, style violation introduced by the PR, since the convention explicitly calls for the canonical form even in mechanically updated code. I want to look more closely at ScheduledTaskService.ts's imports and structure next.

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

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

💭 I'm checking whether KeyedSerialExecutor is a pure helper module, which would justify keeping it as a named import rather than following the service-module convention. I also want to look at errors missing a cause field, particularly around ScheduledTaskManager, to see if they're wrapping an underlying failure that should be preserved.

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

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

💭 KeyedSerialExecutor being a pure helper makes the named import fine. But I'm noticing loadScopedThread's mapError does a structural check on cause._tag rather than using Effect.catchTags — and since conventions specifically discourage using catchIf-style predicates just to recover known tag variants, this pattern might need reconsidering, though it's technically mapError rather than catch. Actually this is a common translation pattern using a ternary on _tag, so it's arguably fine rather than a clear violation. What stands out more is that CallerScopeError and TargetScopeError don't appear to define a cause field, yet they're being used to translate a real underlying failure (ThreadManagementThreadNotFoundError) — which conflicts with the convention that translated errors should preserve the immediate underlying cause alongside their structural fields. I'm leaning toward flagging this lightly, though it risks being seen as a minor style nit rather than a hard violation. Let me move on to check the rest of the diff for stronger findings, including the test file — imports look properly consolidated from "effect" submodules and from "@effect/vitest", and the test harness setup appears reasonable so far. Looking at ThreadManagementService.ts, the shape interface change is pre-existing and fine, as are the MCP toolkit handlers. In orchestratorMcp.ts I notice a manual unicode well-formedness loop that duplicates functionality now available via a native string method, and there's apparent schema duplication between two client-request-id schemas where only one applies the well-formed check — a design inconsistency, but not something covered by the Effect service conventions I'm checking against, so I'll set it aside as out of scope. Error conventions in contracts look fine with the pre-existing OrchestratorMcpFailure. Now I want to check how OrchestratorV2Shape members reference other services' types in Orchestrator.ts, particularly around the CommandReceiptStoreV2Shape getByCommandId finding, so I'm searching for similar ["Service"] references to establish whether there's a consistent pattern in this file.

7. git_tools — ["Service"][

💭 I'm noticing most of the codebase references services as Foo["Service"]["member"], but the new Orchestrator.ts line imports a standalone CommandReceiptStoreV2Shape type and indexes into it directly instead. It's a mild inconsistency worth flagging, though I'm weighing whether it truly counts as a convention violation since the shape type is explicitly exported for this purpose. I keep the bar in mind: only clear convention violations should fail, not optional style preferences. I check other candidates — the test harness's Layer usage is fine since it's test code, ScheduledTaskService's inline construction without a make function is pre-existing rather than newly introduced, and the manual run result structure looks fine too. I'm now considering whether a small inline error-construction helper counts as a disallowed trivial wrapper. I'm also looking at scheduledTaskManualRunFailure, which classifies error tags into MCP failure codes, mirroring an existing pattern elsewhere in the codebase, so I don't think it's worth flagging given the precedent. I'm checking whether any new errors duplicate a distinction between a specific tag and a separate reason field, but I don't see that here. The strongest remaining candidate is that several new manual-run errors skip a cause field even though some wrap an underlying not-found error from thread management — I want to trace through loadScopedThread to see exactly how that mapping happens before deciding whether to flag it. Losing that cause chain does seem like a real, if minor, violation of the stated convention, but fixing it would mean touching two error classes and their construction sites, which isn't a small inline diff. I'm second-guessing whether flagging a third error-shape nit is too pedantic given prior rounds already caught similar issues, so I want to check if there are more substantive problems first, like the effect on cause handling elsewhere. I'm scanning for other convention violations — checking tagged failure handling, catchIf usage, and result/exit patterns, all of which look fine. One thing that stands out is an import line in OrchestratorMcpService.ts that pulls in a type and service directly rather than importing the local service module as a namespace, which the convention explicitly calls for at service boundaries. Looking closer, I see other imports in the same file also use named rather than namespace imports, so this pattern is mixed across the repo — probably not worth flagging on its own. Now I'm weighing whether to raise both this import issue and the cause-preservation finding, or stay quiet, since the check defaults to reporting "all clear" if nothing's flagged and two prior review rounds have already driven meaningful fixes. Verifying that CommandReceiptStoreV2["Service"]["getByCommandId"] is a valid pattern here, since it mirrors how ProjectionStoreV2 is used in the same file — it checks out. But acting on it would leave an unused import, requiring edits across two separate hunks, and since cross-hunk changes in one file mean I can't produce a single clean diff, I'll just describe the two edits in prose instead of trying to force a unified diff. I'm also weighing whether losing the underlying error's cause in the loadScopedThread error-mapping counts as a real violation — since it's translating a genuine failure into a "not found" error, I think it does, and I'll raise it as a suggestion to preserve the cause. Still, I want to avoid piling on too many minor comments at once, so I'm considering consolidating into a single focused comment. Given the borderline nature, I'm leaning toward not failing the check unless I find something clearly wrong — so I want to do one more pass over the diff, especially checking the test file's use of the non-null assertion on the options object and whether the added tests are proportionate to the refactor or feel excessive. Reasoning through the tests confirms they're warranted since backend behavior actually changed. I'm noticing a minor import ordering inconsistency in the scheduled task test file, along with a named import that deviates slightly from the usual namespace convention, but neither seems worth flagging. In the contracts file I'm also spotting a helper function that duplicates existing built-in string functionality. Deciding I should just settle on posting two comments: one about the CommandReceiptStoreV2Shape since it clearly violates the explicit convention of defining the service interface inline rather than keeping a standalone shape type, and one about cause-preservation, since that guideline is also unambiguous and applies to newly introduced code. I'm torn on the severity to assign — treating a type-alias nit as an automatic CI failure feels heavy-handed, but the guidance leans toward defaulting to failure unless everything is clear. Since the check explicitly defaults to failure rather than success, I'll go with failure. I'll keep both findings since they're distinct and actionable rather than trimming to just one, and now I want to double-check whether the error class in this codebase actually supports a cause field by looking at how ScheduledTaskError is defined in contracts.

8. git_tools — class ThreadManagementThreadNotFoundError

9. git_tools — class ScheduledTaskError

💭 I'm deciding to skip the not-found error finding since it carries no extra context beyond what's already replicated. Now I'm turning to evaluate whether the CommandReceiptStoreV2Shape type reference is a genuine deviation worth flagging. Reconsidering, the convention clearly intends for standalone shape types to be replaced by the inferred service interface, so referencing this shape type in new code does violate that rule's letter, even if minor. I'll go ahead and post the comment on it, treating it as a real, concrete finding rather than a stylistic quibble. Since fixing line 179 also requires removing the now-unused import on line 44, that's two hunks in one file, so per the rules I should skip the inline diff suggestion and just describe the change instead. I've confirmed the correct line number in the PR diff (line 179 of Orchestrator.ts) via the diff hunk and a grep check, so I'm ready to post the review.

10. modify_pr — One convention finding in the Effect service changes: new code in Orchestrator.ts references the standalone CommandReceiptStoreV2Shape type instead of the inferred Foo["Service"] interface. Everything else (tagged manual-run errors with derived messages, namespace imports, keyed serial executor as a pure helper, test-only service seams) looks consistent with the conventions.


Agent Credits: 182 credits

Token usage: 14 uncached input · 646,900 cache-read input · 175,271 cache-write input · 12,194 output
Agent Credits may also include non-token charges from external tools such as web research.