Uh oh!
There was an error while loading. Please reload this page.
feat(orchestrator): move a running thread between devices - #5544
feat(orchestrator): move a running thread between devices#5544Bil0000 wants to merge 262 commits into
Conversation
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.
One finding on the new handoff error contract; the migration, its tests, and the bundle schemas otherwise follow the Effect conventions (subpath namespace imports, no service-instance injection, no ManagedRuntime/runPromise, no Layer.succeed shortcuts).
Posted via Macroscope — Effect Service Conventions
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Effect service conventions review of the two new service modules. Three findings, all in apps/server/src/orchestration-v2/. Earlier feedback on OrchestrationV2HandoffError.message still stands and is not repeated here.
Posted via Macroscope — Effect Service Conventions
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: codex <codex@users.noreply.github.com>
- Document the target orchestration graph, IDs, lifecycles, and capability model - Add Codex app-server probe fixtures and update the probe test harness
- Introduce orchestration v2 service interfaces and error types - Add replay runtime, fixtures, and integration coverage - Update shared contracts and probe transcripts Co-authored-by: codex <codex@users.noreply.github.com>
- Add Codex adapter and replay harness wiring - Introduce in-memory orchestration projections and provider registry - Expand orchestration contracts for turn and runtime events
Co-authored-by: codex <codex@users.noreply.github.com>
- Add context transfer IDs, schemas, and projections - Support cheap fork creation and Codex native fork rollback - Cover fork idempotency and replay behavior in tests
- Track remaining projection, context transfer, rollback, capability, and subagent work - Clarify current V2 baseline and debugger-only follow-ups
- Map fork and merge-back turns into stored handoffs and transfer resolutions - Add shell snapshot projection support plus coverage tests - Update replay fixtures and web contracts for the new turn flow
Co-authored-by: codex <codex@users.noreply.github.com>
- Move Codex replay recording into `apps/server` - Add Claude Agent SDK replay fixtures and test harness - Update orchestration-v2 fixture scenarios and docs
- Move Claude provider runtime logic into its own module - Share the SDK query runner between live and replay paths - Add replay driver error wrapping for unexpected failures
- persist the selected model on run records - surface run model selection in the debug UI - update replay fixtures and contracts for the new field
- Record Claude SDK transcripts across multiple prompts and restart/query modes - Add approval and tool-call replay coverage for new orchestration fixtures - Update Claude adapter testkit to model open/prompt/permission frames
- Derive Claude SDK query options from runtime policy - Add read-only replay fixture and policy mapping tests - Reuse shared approval-policy fixtures across orchestrator tests Co-authored-by: codex <codex@users.noreply.github.com>
- add active steering and interrupt-restart replay fixtures - update Claude adapter/orchestrator turn handling for steering - refresh replay and integration test coverage
- add interrupt and mid-tool replay fixtures for Claude and Codex - log Claude Agent SDK protocol frames to native event traces - project Codex commandExecution start events into orchestration updates
- Map Cursor SDK agents and runs to V2 thread and turn lifecycles - Update MCP capability, tool, and testing guidance for SDK-based injection
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Record provider fixtures for continued forks and sibling merge-backs, exercise context survival end to end, and stop completed run listeners from consuming later provider events. Co-authored-by: codex <codex@users.noreply.github.com>
Allow a pending merge-back to be consumed while changing providers and deliver both the provider-switch history and fork delta to the selected provider. Co-authored-by: codex <codex@users.noreply.github.com>
Send full canonical target-thread context when a provider switch consumes a merge-back, and cover returning to an existing provider thread after a cross-provider fork. Co-authored-by: codex <codex@users.noreply.github.com>
CI enforces preferSchemaOverJson; the created-worktree recovery path and its test read the stored manifest with JSON.parse. Both now go through Schema.fromJsonString(OrchestrationV2HandoffBundleV1), the same codec that wrote the column.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Reviewed the new Effect service modules (ThreadHandoffGit, ThreadHandoffService) and their call sites against the service conventions. The service definitions, make/layer exports, dependency acquisition and error modelling all look consistent with the conventions; the remaining findings are service-boundary imports in the two new test files that alias layer and erase the module namespace.
Posted via Macroscope — Effect Service Conventions
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- An unreadable attachments directory now fails the prepare instead of silently shipping a conversation whose attachments never travelled; a missing directory still just means no attachments - recordHop keeps rollback metadata when the same attempt records its arrival; startup recovery finishes the stash pops for an arrived hop that stopped before handing the receiver's changes back - Import the handoff modules as namespaces in the new tests, matching every production consumer
- Keep success feedback visible in the Git action control for 10 seconds - Move the running elapsed timer into the panel menu slot
5aca742 to
77168d0Compare519c42a to
4c55679Compareceea97b to
d2f1f51Compare
Closes#5363.
Stacked on #2829 (
t3code/codex-turn-mapping).mainand the orchestrator branch share no common ancestor; every primitive this builds on exists only there.What this does
Run onpicks the machine a thread starts on and locks once it has a message — a thread is born on one device and dies there. This makes that reversible: pick another connected environment from the same control, and the conversation, the provider continuation, and the git working state move there. Pull it back later from the same control, which shows where the thread originally ran.Verified live: real threads moved repeatedly between a MacBook (desktop app) and a Linux server and continued on each side, through the actual relay-free client-brokered transfer — including round trips, pull-backs, and the failure modes listed under Verification.
The safety model
refs/handoff/<environment>/<handoff>/<branch>and stops with both machines untouched.thread.handoff.departlocks the giving side before any bundle is applied anywhere; mutations on an away thread are refused in the orchestrator (a small allowlist of local-copy lifecycle verbs excepted). An interrupted transfer releases the lock; a stranded one has aContinue hereescape hatch, offered only while the transfer never landed. A completed handoff can no longer be aborted or repointed.ThreadHandoffIdis schema-restricted to a URL- and git-ref-safe alphabet.What travels
Branch + unpushed commits (git bundle, checkpoint refs ride along so revert keeps working), tracked changes (binary patch), untracked files (null-delimited tar), the conversation (turn items + covered run ordinals), attachments (original names, which the items reference), terminal scrollback (restored on first open like after a restart), workspace shape (worktree threads land in worktrees — reuse the branch's worktree or provision one, detached when git forbids attaching). A target that lacks the repository entirely clones it from the bundle — no remote, credentials, or network needed on the receiving machine.
How it moves
Manifest inline, large parts content-addressed and fetched by digest in resumable 4 MB chunks; the receiver rejects a chunk that does not continue the staged bytes and treats an exact re-send of already-staged bytes as a no-op, so interrupted uploads resume. The client brokers the copy — the only component authenticated to both environments — and only offers targets that are connected and advertise the capability. Arrivals go through the event sink, so the thread appears in every connected client's sidebar the moment it lands.
Lineage, not direction
Each hop records the previous hop in a lineage table; a return trip is an ordinary hop toward an environment already in the chain, and the receiver revives the prior thread pair even when the client no longer remembers it — one conversation, one sidebar row, wherever it currently runs. A caller-named return target is validated against that lineage before anything is applied. Provenance survives round trips: every arrival keeps a link recording where the thread came from. Lifecycle actions (delete, archive, rename) apply to the pair once.
Version skew
threadHandoffcapability follows thethreadSettlementcontract: absent means the environment is not offered as a destination.Verification
Related
Migrations
047 (
orchestration_v2_thread_handoffslineage table), 048 (apply_cwdcolumn for projection-independent crash recovery) and 049 (root_stash_ref/root_cwdso root and worktree stashes recover independently). 047 was renumbered from 046 after the base branch inserted a migration.Note
Add thread handoff to move a running thread between devices
OrchestrationV2HandoffBundleV1manifest contract, typed part kinds (git-bundle,tracked-patch,untracked-tar, etc.), and versioned DB migrations (047–050) for theorchestration_v2_thread_handoffstable.readHandoffPart/writeHandoffPartHTTP endpoints on the environment API with offset-validated, idempotent chunked transfer and a 4 MB chunk size.prepareThreadHandoffandreceiveThreadHandoffWebSocket RPCs (requiringAuthOrchestrationOperateScope) handled in the server WS layer and wired into production startup with a handoff recovery phase.ThreadHandoffDialogfor progress-tracked moves, a banner for threads running elsewhere (with pull-back and continue-here actions), and sidebar deduplication hiding the 'away' copy when the 'here' peer is visible.ThreadHandoffBannerto surface away threads and resume them locally.Macroscope summarized 0250f02.