fix(server): http dispatch honors turn.start bootstrap - #7996
Conversation
POST /api/orchestration/dispatch passed commands straight to the orchestration engine, skipping the bootstrap steps the WebSocket path runs for a thread.turn.start carrying `bootstrap`. An HTTP turn start for a not-yet-existing thread therefore failed with "Thread does not exist" even though the contract advertises bootstrap on that endpoint. The bootstrap flow (thread.create, worktree preparation, setup script, rollback via thread.delete on failure) now lives in a shared TurnStartBootstrap service that both the WebSocket RPC and the HTTP dispatch route use. The ws path keeps its client-origin stamping and command gating; HTTP gains the bootstrap branch. Tests cover HTTP bootstrap success and rollback alongside the existing ws cases.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
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.
Posted via Macroscope — Effect Service Conventions
ApprovabilityVerdict: Skipped Macroscope did not run approvability analysis for this PR. Macroscope could not determine whether this PR modifies its approvability configuration, so the PR was not approved automatically. A PR that may change the rules that govern approval is never approved automatically. Not approved because:
|
The move dropped the original never-typed default branch, so a new ProjectSetupScriptRunnerError tag would silently stringify into a user-visible activity payload instead of failing the build. Restored verbatim from the pre-move ws.ts helper.
Problem
POST /api/orchestration/dispatchpasses commands straight to the orchestration engine. TheClientOrchestrationCommandunion it accepts includesthread.turn.startwith the optionalbootstrapfield, but only the WebSocket RPC path actually runs the bootstrap steps (create thread → prepare worktree → run setup script → start turn, with rollback). Over HTTP the same command reaches the engine untouched and fails withOrchestrationCommandInvariantError: Thread does not exist— the endpoint advertises a shape it breaks on.This matters for external automation: anything driving a T3 server over HTTP (scripts, orchestrators) currently has to reimplement thread bootstrap client-side — shelling
git worktree addlocally (so it only works on the server's own machine), hardcoding the worktree path layout, and skipping the project setup script entirely.Fix
The bootstrap flow moves verbatim out of
ws.tsinto a sharedTurnStartBootstrapservice (apps/server/src/orchestration/TurnStartBootstrap.ts) that both transports use:ws.tscalls the service, keeping its client-origin stamping and command gating exactly as before (~300 duplicated lines removed).orchestration/http.tsbranches to the service when athread.turn.startcarriesbootstrap; error mapping unchanged.server.tsprovides the layer;bin.test.tsgets a mock layer.Behavior is transport parity, not new capability: the identical bearer auth already triggers this exact flow over WebSocket. Most of the diff is the moved block; net-new logic is the ~20-line HTTP branch and wiring.
Tests
vp test run apps/server/src/server.test.ts— HTTP bootstrap success asserts the command sequencethread.create → thread.meta.update → thread.turn.start(final command stripped ofbootstrap, worktree path propagated); failure asserts the rollbackthread.create → thread.deletewith a 500. Existing ws bootstrap cases untouched and green (130 tests).Note
Medium Risk
Touches command dispatch for both HTTP and WebSocket, including git worktree creation and thread rollback. Behavior is intended to match the existing WS path, but a shared service now sits on the operate-scope dispatch route.
Overview
POST /api/orchestration/dispatchnow runsthread.turn.startbootstrap the same way WebSocket RPC does, so HTTP clients can create a thread, prepare a worktree, and start a turn without reimplementing that flow.The bootstrap logic is extracted from
ws.tsinto a sharedTurnStartBootstrapservice. Both transports calldispatchTurnStart; WebSocket still stamps client origin. Failures after thread create still roll back withthread.delete.Tests cover HTTP success (
thread.create→thread.meta.update→ strippedthread.turn.start) and rollback (thread.create→thread.deletewith 500).Reviewed by Cursor Bugbot for commit d22fadd. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Fix HTTP
thread.turn.startbootstrap viaTurnStartBootstrapserviceTurnStartBootstrapContext service in TurnStartBootstrap.ts whosedispatchTurnStartmethod performs the full bootstrap flow: thread creation, optional Git worktree setup with remote fetch, metadata updates, optional project setup script, VCS status refresh, and finalthread.turn.startdispatch. On failure it attemptsthread.deletecleanup and surfaces a structuredOrchestrationDispatchCommandError.thread.turn.startand delegates to the new service, matching the existing WebSocket behavior.makeRoutesLayerin server.ts providesTurnStartBootstrap.layerto all routes; tests mock it with an empty layer.TurnStartBootstrapinstance viamakeRoutesLayer; verify that no route layer creates its ownLayer.succeed(TurnStartBootstrap, …)that would shadow the shared one.Macroscope summarized d22fadd.