Skip to content

feat(threads): add agent-requested handoffs

MacroscopeApp / Macroscope - Effect Service Conventions failed Aug 19, 2026 in 2m 53s

Effect Service Conventions: 3 issues found

Three convention violations were found in the new MCP thread-handoff toolkit; other new Effect code (orchestration decider/handoff helpers, client-runtime commands, layer wiring) conforms.

  • apps/server/src/mcp/toolkits/handoff/tools.ts (18-20): tool failure is an unstructured Schema.Struct({ message: Schema.String }) instead of a Schema.TaggedErrorClass with structural attributes and a derived message.
  • apps/server/src/mcp/toolkits/handoff/handlers.ts (35-38): Effect.mapError collapses the structured OrchestrationDispatchError into a string derived from cause.message, discarding the cause chain and the available threadId/handoffId context.
  • apps/server/src/mcp/toolkits/handoff/tools.ts (7-8, also handlers.ts and tools.test.ts): the local service module is imported by named tag rather than as a namespace at the service boundary, unlike the adjacent McpInvocationContext/PreviewAutomationBroker imports.

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.

Methodology: reviewed every changed .ts/.tsx file in the include scope, then compared the new MCP toolkit against the existing preview toolkit (apps/server/src/mcp/toolkits/preview/tools.ts, handlers.ts) and the orchestration error module (apps/server/src/orchestration/Errors.ts), which already model failures with Schema.TaggedErrorClass and preserve cause.

Checked and found clean:

  • No ManagedRuntime.make, runPromise, runPromiseExit, or new Layer.succeed implementations were introduced in service code.
  • Service dependencies are acquired from the environment (yield* McpInvocationContext.McpInvocationContext, yield* OrchestrationEngineService, yield* Crypto.Crypto); explicit service instances appear only in tools.test.ts as test seams.
  • Effect library modules are imported via subpath namespaces (effect/Effect, effect/Schema, effect/Option, effect/Crypto, effect/DateTime); no consolidated imports from "effect".
  • No new catchTag/catchIf-with-schema-predicate recovery, no error-construction-only helpers, and no compatibility re-export shims or service file moves.

Note (not reported as a violation, outside the listed conventions): apps/server/src/orchestration/threadHandoff.ts and packages/client-runtime/src/state/threadHandoff.ts are near-identical copies of the same activity-folding logic, which will need to be kept in sync as handoff states evolve.


πŸ” Agent activity β€” 1 tool call

πŸ’­ I'm scanning the diff across the handoff handlers, tools, and orchestration files for violations of Effect service conventions, particularly around how errors get mapped into the failure schema in handlers.ts. This looks like a violation since the convention calls for Schema.TaggedErrorClass with structured attributes, and the message should be derived from those attributes rather than from the raw error or cause. I should check whether other MCP toolkits in the repo already use this same unstructured pattern, though the instructions say to treat the new conventions as authoritative over older code regardless. I'm also noting that the orchestration and client-runtime threadHandoff files look like near-duplicates, which raises a separate concern about keeping service logic in one place rather than splitting it across packages. Checking the import style in handlers.ts and tools.ts β€” the subpath imports from effect/unstable/ai and the named imports from @t3tools/contracts look consistent with existing patterns, so those seem fine. I want to compare against the preview toolkit's tools and handlers to confirm conventions, and also check how the error channel type is handled where error instanceof Error appears in mapError. Let me pull up those actual files to verify. Now I'm comparing this against the preview toolkit's handlers and tools. I'm checking whether the preview toolkit's error type follows the structured error convention, and noticing that the handoff toolkit's new failure struct with a plain message field and mapError deriving from cause.message violates the guidance to use Schema.TaggedErrorClass with structured attributes rather than an unstructured message. The mapError also collapses the underlying OrchestrationEngine dispatch error into a plain string, losing the original cause entirely. I want to look at how PreviewAutomationError is defined and what error shape the OrchestrationEngine dispatch actually produces. I notice the new handler swallows the structured OrchestrationDispatchError into a plain { message: string }, losing the tagged error class, deriving the message from cause.message, and discarding the cause entirely β€” a clear violation of error conventions. I want to check the actual definition of OrchestrationDispatchError to cite in my comment, and also see whether other MCP toolkits or server code already define a proper error class pattern for tool failures, like Preview seems to. I'm confirming that PreviewAutomationError is a union of TaggedErrorClass, so this convention already exists elsewhere in the repo. I should also check apps/server/src/orchestration/Errors.ts for existing error definitions, and note that the file layout convention about hoisting domain/Services and domain/Layers files doesn't really apply here since the new code just imports from the existing legacy orchestration path. I'm also noticing threadHandoff.ts appears duplicated between the server and client-runtime packages, though I'm not sure the server even depends on client-runtime, so that's a separate concern from the Effect-service error convention I'm focused on. I'm deciding this duplication is a genuine maintenance issue but outside the scope of what I should flag, since the task is to report concrete convention violations rather than general code smells -- I'll verify whether the server imports from client-runtime anywhere else before moving on. Checking the imports in threadHandoff.ts and related files, everything uses proper namespace imports and there's no stray ManagedRuntime.make or runPromise usage, and the McpHttpServer layer composition looks standard. Now I want to look at how errors are being mapped in the diff to see if that follows the established error-handling pattern. The new tool failure using a plain struct for the error violates the convention, so I should define a proper tagged error class with structural fields like operation, threadId, and handoffId plus a cause, deriving the message from those fields rather than a raw string. I also want to verify the McpInvocationContext import uses the namespace pattern correctly, and check whether the OrchestrationEngineService import follows the service boundary convention. I'm noticing the new files use a named import of the service rather than the namespace-style import convention used elsewhere in the repo, which seems like a minor deviation from the stated pattern rather than the more serious aliasing violation the convention explicitly flags. Since existing files like http.ts and the Layers directory already use this same named-import style, while the adjacent preview toolkit uses namespace imports, I'm weighing whether this is worth a low-severity note rather than a hard failure, and decide to flag it lightly once on handlers.ts line 7, mentioning tools.ts as well. The check criteria say a clear convention violation may fail the check, and the error modeling issue is the main one. I'm also checking whether threadHandoff.ts's duplication across server and client-runtime packages is worth flagging β€” deciding it's not really an Effect-service convention issue, so I'll mention it only as a non-blocking note rather than a violation, and I'm confirming the other Schema and React-related files look fine. I'm confirming the handler's mapError returns a plain object literal without a tag, matching the failure schema but losing the cause β€” this is a clear violation worth flagging precisely at the ThreadHandoffToolFailure definition in tools.ts and the mapError call in handlers.ts, so I want to pin down exact line numbers from the diff's right side. I'm drafting two inline review comments: one flagging that the failure schema in tools.ts should be a TaggedErrorClass with structured attributes and a derived message/cause, and another noting that handlers.ts stringifies the cause message instead of preserving the structured OrchestrationDispatchError. I'm also considering adding a brief third comment about the namespace import for OrchestrationEngineService. Writing the TaggedErrorClass definition... Actually, I realize the single-value operation literal duplicates the error tag distinction, so I should drop it and keep just threadId, handoffId, and cause, with a message referencing the thread handoff request failure. This needs a ThreadId import, so I'll present it as a unified diff rather than an inline suggestion. For the handler, I need to make sure it maps to the new error while preserving the cause and pulling threadId from the invocation context. I'm constructing the error right at the failure boundary inside requestThreadHandoff, wrapping the dispatch call so any failure gets mapped into a ThreadHandoffToolError carrying the threadId, handoffId, and original cause β€” this keeps the error's attributes and cause visible exactly where the failure occurs, and simplifies the toLayer entry to just the plain function. Now I'm noting the specific line numbers for review comments across the new handler and tool files, and planning to post the review with a clear "failure" verdict since this is a definite violation. I'll keep the import-related comment brief and suggestive, then draft the diff replacing the old failure schema struct with the new tagged error class definition.

1. modify_pr β€” Reviewed the new Effect-facing code (MCP handoff toolkit, orchestration decider/handoff helpers, client-runtime commands) against the service conventions. Service acquisition, layer wiring, and effect/* subpath namespace imports look correct; the findings below are about how the new MCP tool failure is modeled.


Agent Credits: 107 credits