Skip to content

fix(server): http dispatch honors turn.start bootstrap

MacroscopeApp / Macroscope - Effect Service Conventions completed Aug 23, 2026 in 4m 2s

Effect Service Conventions: 1 issue found

Finding

  • apps/server/src/orchestration/TurnStartBootstrap.ts (lines 63-74): the moved projectSetupScriptCompatibilityDetail helper loses the exhaustiveness invariant the ws.ts original had (default: return unexpectedCompatibilityError(error) typed as error: never). The new default branch stringifies the error into detail, which is copied into a user-visible activity payload, and a future ProjectSetupScriptRunnerError tag would no longer be caught at compile time. Suggested fix posted inline (retain a never assertion and return a static message).

No other convention violations found: the new service uses subpath namespace imports, an inline Context.Service interface, exported make/layer, and acquires every dependency from the Effect environment (OrchestrationEngineService, GitWorkflowService, ProjectSetupScriptRunner, VcsStatusBroadcaster, Crypto). No ManagedRuntime.make, runPromise, or service-instance parameters were introduced; the Layer.succeed(TurnStartBootstrap.TurnStartBootstrap, turnStartBootstrap) in ws.ts only re-provides an already-constructed server-lifetime instance into the per-connection layer, matching the existing pattern for ServerSelfUpdate and PullRequestService.

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

  • apps/server/src/orchestration/TurnStartBootstrap.ts (new module)
  • apps/server/src/orchestration/http.ts (new consumer of the service)
  • apps/server/src/ws.ts (bootstrap logic removed, service consumed, per-connection layer wiring)
  • apps/server/src/server.ts (Layer.provide(TurnStartBootstrap.layer) in makeRoutesLayer)
  • apps/server/src/bin.test.ts, apps/server/src/server.test.ts (test seams and new HTTP bootstrap coverage)

Notes considered but not flagged

  • toDispatchCommandError / toBootstrapDispatchCommandCauseError derive message from cause.message. This is preserved pre-existing behavior for a caller-visible RPC/HTTP error message, so changing it during a structural move would alter behavior; conventions call for preserving those messages.
  • http.ts imports the tag as import { TurnStartBootstrap } rather than a module namespace. This matches the file's existing style for OrchestrationEngineService and ProjectionSnapshotQuery and only the tag is used, so it is treated as a style preference rather than a violation.
  • The new server.test.ts cases cover genuinely new backend behavior (HTTP dispatch bootstrap plus rollback), which the conventions require rather than discourage.

🔍 Agent activity — 9 tool calls

💭 I'm reviewing the new TurnStartBootstrap.ts file against our conventions—checking imports, the Context.Service pattern with its tag/make/layer ordering, and confirming the exported options interface is a standalone type rather than a "FooShape" pattern. Continuing through: dependencies are pulled via yield* from services, the layer is built with Layer.effect, and I'm double-checking whether the private isOrchestrationDispatchCommandError constant should instead be exported from contracts since it was moved from ws.ts. The toDispatchCommandError helper looks fine since it's just a pass-through classification allowed by our mapper conventions. I notice toDispatchCommandError gets reused for both the randomUUID mapError and toBootstrapDispatchCommandCauseError, and it seems duplicated from ws.ts rather than shared — not a strict violation but worth flagging. I want to cross-check the actual repo files for http.ts and ws.ts to confirm these details, and I'm also verifying that bin.test.ts and server.ts use proper namespace imports for TurnStartBootstrap. However, http.ts imports TurnStartBootstrap as a named import rather than as a namespace, which breaks the service-boundary convention that says to import the local service module as a namespace and reference its public shape. Looking closer, http.ts already uses named imports for other services like ProjectionSnapshotQuery and OrchestrationEngineService, so this appears consistent with the file's existing style rather than a new deviation. Since these are only used as tags and the rule allows flexibility for modules serving as simple type/tag references, I'm leaning toward not flagging this as a violation, though it's genuinely borderline given the explicit service-boundary wording. Let me actually pull up the files to check for more substantive issues, including how OrchestrationDispatchCommandError's bootstrapThreadDisposition field fits in, and look at the new file alongside the ws.ts region.

1. browse_code — apps/server/src/orchestration/TurnStartBootstrap.ts

2. browse_code — apps/server/src/orchestration/http.ts

💭 I'm noticing the new ws.ts drops the exhaustiveness-check helper in favor of a generic default error message, which loses compile-time exhaustiveness guarantees—a quality regression, though not explicitly called out in the convention list. Now I want to verify whether unexpectedCompatibilityError is even still referenced elsewhere in ws.ts, since an unused function would trigger a lint error.

3. git_tools — unexpectedCompatibilityError|nowIso|isOrchestrationDispatchCommandError|toDispatchCommandError|legacySetupFailureDescription

4. browse_code — In apps/server/src, is there an exported schema predicate for OrchestrationDispatchCommandError (e.g. isOrchestrationDispatchCommandError) in @t3tools/contracts (packages/contracts)? Show where OrchestrationDispatchCommandError is defined and whether a predicate is exported.

💭 I notice the predicate isOrchestrationDispatchCommandError now exists in both ws.ts and TurnStartBootstrap.ts, duplicating logic rather than reusing the exported version—I'm weighing whether this counts as a genuine convention violation given the codebase's pattern of exporting schema predicates directly. On the error handling side, toDispatchCommandError deriving its message from cause.message seems to violate the rule against sourcing wrapper messages from cause, but since this is pre-existing behavior carried over during the move rather than newly introduced logic, I'm inclined not to flag it hard given the guidance to avoid demanding unrelated cleanup on mechanically relocated code. I'm also noticing that the exhaustiveness check present in the original ws.ts file—unexpectedCompatibilityError(error)—got replaced with a simple stringified fallback in the new file. That does look like a real regression in the exhaustiveness invariant introduced during this move, which the refactor discipline explicitly warns against losing. I'm also checking http.ts's named import of TurnStartBootstrap rather than a namespace import, per the service-boundary convention. It's technically inconsistent with how the new module is used elsewhere in the PR (server.ts, ws.ts, bin.test.ts all use namespace imports), but there's pre-existing legacy code in the same file doing the same thing, so this leans toward an optional-style nitpick rather than a clear violation worth blocking on. Dependency acquisition all flows through yield* correctly, and the Layer.effect construction for TurnStartBootstrap looks fine. I'm now checking server.ts and ws.ts, where the already-constructed TurnStartBootstrap instance gets re-provided into per-connection or per-route layers via Layer.succeed/Layer.provide. This matches a legitimate context-propagation pattern already used for other services in that section, not a case of hiding dependencies in module globals. I want to double-check that the pattern is applied consistently around ws.ts's per-connection layer setup, and confirm the service file follows the expected order — tag, helpers, make, layer — which it does, with make correctly returning TurnStartBootstrap.of({...}).

5. browse_code — apps/server/src/ws.ts

6. browse_code — apps/server/src/ws.ts

💭 The Layer.succeed pattern matches existing conventions. The real finding is that the moved code dropped the exhaustiveness check in unexpectedCompatibilityError, replacing it with a soft stringification instead of preserving the invariant — worth flagging as a small but legitimate issue. I should also verify the removed ProjectSetupScriptRunner import in ws.ts isn't still referenced elsewhere, check for duplicated error-mapping helpers between ws.ts and TurnStartBootstrap.ts, and confirm the new server.test.ts additions are justified since this PR changes actual backend behavior (bootstrapping now happens on the HTTP path) rather than just being a mechanical refactor. The strongest finding I keep coming back to is that the moved projectSetupScriptCompatibilityDetail function dropped its exhaustive never check — that's a concrete, single-line loss of an invariant during the move, which does fall under preserving invariants when relocating code. I'll flag it as a non-blocking suggestion rather than a hard failure, since it's minor enough that outright failing the check feels excessive. Before finalizing though, I want to check whether dispatchFromClient in ws.ts did anything beyond what the new shared dispatch service replicates — things like normalization or permission checks that might have been silently dropped when the origin-aware options were threaded through orchestrationEngine.dispatch. I should look around line 740-770 in ws.ts to confirm the behavior wasn't quietly lost in the move.

7. browse_code — apps/server/src/ws.ts

8. browse_code — apps/server/src/ws.ts

💭 I'm reconsidering whether the lost exhaustiveness check is really the only reportable issue, and re-checking the error conventions against this new module—specifically whether the detail field duplicating cause.message counts as a genuine violation given it's fresh code. The default branch stringifies the error into an activity payload's detail field shown in the UI, which directly violates the rule against leaking arbitrary defect text there—unlike the old code, which threw instead. That makes this line worth flagging both for losing exhaustiveness and for potentially exposing raw error text to users, so I'll leave an inline comment on the new file's default branch suggesting a never-typed exhaustiveness guard that returns a safe static string rather than reintroducing a throw, since throwing inside an Effect service is itself problematic. Since the file is new, the diff's line numbers map directly to file line numbers, so an inline suggestion at that location is feasible. As for overall verdict, given the customer's framing that ambiguity defaults to failure but a clean diff should be "All clear," I'm weighing whether this single, fairly modest finding warrants a failing verdict or should be treated as neutral instead. Rechecking whether this finding is real: the original file used compile-time exhaustiveness checking with unexpectedCompatibilityError(error), and the moved copy dropped that invariant, which directly violates the guidance to preserve invariants when moving code. I'm confident enough in this to proceed with posting the review.

9. modify_pr — Reviewed the new TurnStartBootstrap service and its call sites against the Effect service conventions. Service definition (namespace imports, inline Context.Service interface, make + layer, dependencies acquired via yield*), layer wiring in server.ts/ws.ts, and the HTTP dispatch path all look correct. One change-discipline issue on the moved helper is noted inline.


Agent Credits: 295 credits