Skip to content

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

Open
pablospe wants to merge 3 commits into
pingdotgg:mainfrom
pablospe:orc/http-bootstrap-parity
Open

fix(server): http dispatch honors turn.start bootstrap#7996
pablospe wants to merge 3 commits into
pingdotgg:mainfrom
pablospe:orc/http-bootstrap-parity

Conversation

@pablospe

@pablospe pablospe commented Aug 23, 2026

Copy link
Copy Markdown

Problem

POST /api/orchestration/dispatch passes commands straight to the orchestration engine. The ClientOrchestrationCommand union it accepts includes thread.turn.start with the optional bootstrap field, 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 with OrchestrationCommandInvariantError: 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 add locally (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.ts into a shared TurnStartBootstrap service (apps/server/src/orchestration/TurnStartBootstrap.ts) that both transports use:

  • ws.ts calls the service, keeping its client-origin stamping and command gating exactly as before (~300 duplicated lines removed).
  • orchestration/http.ts branches to the service when a thread.turn.start carries bootstrap; error mapping unchanged.
  • server.ts provides the layer; bin.test.ts gets 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 sequence thread.create → thread.meta.update → thread.turn.start (final command stripped of bootstrap, worktree path propagated); failure asserts the rollback thread.create → thread.delete with 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/dispatch now runs thread.turn.start bootstrap 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.ts into a shared TurnStartBootstrap service. Both transports call dispatchTurnStart; WebSocket still stamps client origin. Failures after thread create still roll back with thread.delete.

Tests cover HTTP success (thread.createthread.meta.update → stripped thread.turn.start) and rollback (thread.createthread.delete with 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.start bootstrap via TurnStartBootstrap service

  • Introduces a new TurnStartBootstrap Context service in TurnStartBootstrap.ts whose dispatchTurnStart method performs the full bootstrap flow: thread creation, optional Git worktree setup with remote fetch, metadata updates, optional project setup script, VCS status refresh, and final thread.turn.start dispatch. On failure it attempts thread.delete cleanup and surfaces a structured OrchestrationDispatchCommandError.
  • The HTTP dispatch route in http.ts now detects a bootstrap thread.turn.start and delegates to the new service, matching the existing WebSocket behavior.
  • The WebSocket path in ws.ts removes its inline bootstrap implementation and delegates to the same shared service instance, so both transports use identical logic.
  • makeRoutesLayer in server.ts provides TurnStartBootstrap.layer to all routes; tests mock it with an empty layer.
  • Risk: both transports now share one TurnStartBootstrap instance via makeRoutesLayer; verify that no route layer creates its own Layer.succeed(TurnStartBootstrap, …) that would shadow the shared one.

Macroscope summarized d22fadd.

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.
@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XL 500-999 changed lines (additions + deletions). labels Aug 23, 2026
@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0c2cd60a-d436-4e61-91a4-7abd4ed9a1ed

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread apps/server/src/orchestration/TurnStartBootstrap.ts
Comment thread apps/server/src/orchestration/TurnStartBootstrap.ts

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread apps/server/src/orchestration/TurnStartBootstrap.ts
@macroscopeapp

macroscopeapp Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: 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:

  • 2 blocking correctness issues found at or above your repo's Minimum Blocking Severity

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL 500-999 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant