Skip to content

fix(server): address review findings on worktree toolkit

MacroscopeApp / Macroscope - Effect Service Conventions failed Jul 7, 2026 in 2m 29s

Effect Service Conventions: 1 issue found

Found 1 convention violation in the changed scope.

  • packages/contracts/src/worktree.ts (WorktreeOperationError): the error stores a detail: Schema.String field populated from the wrapped error's message (errorDetail(error) in apps/server/src/mcp/toolkits/worktree/handlers.ts returns error.message) and derives its message getter from this.detail. The error conventions prohibit a detail field that merely copies cause.message and is then used to build the wrapper message. The underlying failure should be preserved as cause (e.g. cause: Schema.Defect()), the message derived solely from the structural operation discriminator, and asOperationError should pass cause: error rather than a stringified detail. The sibling previewAutomation.ts errors already follow this pattern (cause: Schema.Defect() plus bounded diagnostics such as remoteMessageLength).

An inline review comment with a suggested fix was posted on packages/contracts/src/worktree.ts.

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: new worktree MCP toolkit (apps/server/src/mcp/toolkits/worktree/tools.ts, handlers.ts, tests), contracts (packages/contracts/src/worktree.ts), and touched registration/context files.

Checked and found compliant:

  • Namespace imports: handlers.ts uses import * as Effect from "effect/Effect" etc.; import { Schema } from "effect" in worktree.ts and import { Tool, Toolkit } from "effect/unstable/ai" in tools.ts match the established sibling modules (previewAutomation.ts, preview/tools.ts) and are allowed for schema/whole-package imports.
  • Error modeling: WorktreeOperationError correctly uses a multi-value operation discriminator with a generic message (a valid single-service-error model). WorktreeCapabilityUnavailableError, WorktreeThreadNotFoundError, WorktreeHandoffAlreadyInWorktreeError, and WorktreeHandoffInvalidRequestError are distinct tagged errors with structured attributes and messages derived from them, consistent with previewAutomation.ts.
  • Effect.catch on the setup-script step intentionally handles the whole error channel (setup failure must not fail the handoff), which is appropriate.
  • Tests use test/mock layers for external services rather than mocking core business logic.

The only violation is the detail/message handling in WorktreeOperationError combined with errorDetail/asOperationError, which discards the underlying cause and reintroduces a raw-message detail field the conventions call out explicitly.


🔍 Agent activity — 1 tool call

💭 I'm examining the diff to check for Effect service convention violations in the new worktree-related files, starting with the imports and module namespacing in the tools file. Looking at the handlers and contracts files, I see namespace imports from Effect subpaths like effect/Crypto and effect/Effect, which follow the convention. But in the contracts file there's a consolidated named import from "effect" for Schema, which the convention flags as a violation in touched Effect service code — I need to determine if this actually qualifies as a violation or if it's acceptable in this context. Looking at the WorktreeOperationError class, it's using a multi-value operation discriminator with a generic detail message, which aligns with the convention for service-level errors. But I'm noticing the detail: Schema.String field and need to check if that violates the guideline about not using generic string fields. The asOperationError function is extracting the error message and storing it as a string detail, which is exactly what the convention warns against — it's discarding the actual error object and replacing it with just the message. This violates the principle of preserving the immediate underlying error as cause. Additionally, asOperationError is a curried helper that's used multiple times, but it's just wrapping the error constructor without performing any real normalization or domain-specific logic, which the convention says to avoid. The real issue is that WorktreeOperationError constructs its message from the stringified detail field, which came from cause.message. The convention is clear: preserve the underlying error as cause and derive the message only from the operation itself — keep it generic. The fix would be to add a cause field to store the original error and change the message to depend only on the operation, not on the stringified cause. Before finalizing this as a violation, I should check how similar errors are defined elsewhere in the codebase — particularly in previewAutomation.ts and the preview handlers — to see if this detail pattern is an established idiom or a genuine deviation from the conventions. There's a formatting issue with how the repo parameter is being handled. The Schema import pattern is consistent with the codebase conventions, so that's not an issue. The real focus is on how WorktreeOperationError gets populated with the error detail from the cause message. Looking at the preview pattern, it deliberately avoids putting raw messages into the detail field—instead it captures structured diagnostics like lengths and categories while preserving the original error as cause. The worktree code violates this by stringifying the message and storing it in detail without keeping the underlying error, which loses important context. The asOperationError helper does add reusable context by wrapping the operation name, but it's still part of the same problem: it's normalizing in a way that discards the cause rather than preserving it alongside the structured information. The real issue is the message getter itself—it derives the message from ${this.detail} where detail is just the stringified cause message, which directly violates the convention that messages should come only from stable structural attributes like the operation discriminator. The fix is to replace the detail field with a proper cause field, update the message to derive only from operation, and have the helper pass through the original error as cause instead of stringifying it. Since the error definition lives in packages/contracts/src/worktree.ts and that's where the detail field and message getter are defined, I should place an inline comment there to flag this violation. I'm also checking whether Schema.Defect() is available—it's already used in previewAutomation.ts, so it's accessible here. The WorktreeCapabilityUnavailableError looks fine by comparison with the preview equivalent. And for the Effect.catch usage in handlers.ts, the convention allows it when the entire error channel is intentionally handled, which appears to be the case with the script runner failures. Now I'm looking at the imports in tools.ts to see if they match the pattern used in preview. The import pattern checks out against the established convention in preview/tools.ts. Looking at the core issue: WorktreeOperationError stores a raw detail: Schema.String that copies error.message, while the preview approach is more careful—it captures remoteMessageLength and uses cause: Schema.Defect() instead. The pattern seems to be avoiding that direct message copy in the detail field. I need to pinpoint where this violation occurs. There are two key locations: the WorktreeOperationError definition itself in worktree.ts (the detail field and message getter), and then handlers.ts where errorDetail gets assigned and asOperationError wraps the error. The real fix spans both files, but I should focus the comment on the root cause in worktree.ts's detail field, explaining how the message should derive from structural attributes rather than the underlying cause. Let me check the exact line numbers in the actual worktree.ts file. Looking at the handlers.ts file to find the exact line numbers where I can add another comment about the errorDetail/asOperationError conversion. I'm identifying the core issue: WorktreeOperationError is storing a stringified version of the underlying error's message in a detail field, then using that same field to construct its own message. This violates the principle of preserving the original error as the cause rather than extracting and reusing its message content. The fix requires updating both the error contract in worktree.ts (lines 135–140) to use cause: Schema.Defect() instead, and adjusting the handler mapper in handlers.ts to stop discarding the cause when it calls errorDetail. I'll place a single focused comment on the contract error that explains the full correction, including how the handler needs to change to support it. I'm setting the review state to failure since there's a clear violation, then submitting it with an inline comment spanning lines 135-140 in worktree.ts that includes a diff showing the necessary changes.

1. modify_pr — One convention issue: WorktreeOperationError copies the underlying cause.message into a detail field and builds its message from it, instead of preserving the real failure as cause and deriving the message from structural attributes. The sibling previewAutomation.ts errors already follow the expected pattern (cause: Schema.Defect() plus bounded diagnostics). See inline comment.

💭 Generating the JSON output...


Agent Credits: 88 credits