Orchestrator-Managed Branch
March 20, 2026 · View on GitHub
Status: Ready for implementation
Created: 2026-03-18
GitHub Issue: #24
Related: resilience-architecture.md, architecture
1. Problem Summary
Three issues with the current orchestrator branch model:
-
Protected branch blindness. If
/orchstarts onmain, tasks execute for hours, then merge fails because branch protection blocks the push. Wasted time, wasted tokens. -
HEAD disruption. The merge step fast-forwards the user's current branch in the main checkout (
git merge --ff-only tempBranchinrepoRoot). This changes files under VS Code unexpectedly. If the user switches branches during a long run, merge fails or causes conflicts. -
Worktree directory collisions. Two simultaneous batches by the same operator use the same worktree paths (
{prefix}-{opId}-{N}), becausebatchIdis only in branch names, not directory names.
2. Solution: Orchestrator-Managed Branch
Core change
The orchestrator creates an ephemeral branch orch/{opId}-{batchId} at batch start and does ALL work relative to it:
- Lane worktrees branch from
orch/{opId}-{batchId}(not the user's branch) - Wave merges merge into
orch/{opId}-{batchId}in a merge worktree (not the main checkout) - User's HEAD is never touched — VS Code stays on whatever branch the user was working on
After the batch completes, the user integrates the orch branch manually (or via /orch-integrate).
Flow diagram
User's branch (e.g., feat/foo) ── A ── B ← NEVER TOUCHED by orch
│
orch starts
│
orch/henry-20260318T140000 ─────────── B ← created from user's HEAD
│
Wave 1 starts
┌─────┼─────┐
wt-1 wt-2 wt-3
│ │ │
(tasks execute)
│ │ │
└─────┼─────┘
│
Wave 1 merge (into orch branch, in worktree)
│
orch/henry-20260318T140000 ─── B ── C ── D ── E
│
Wave 2 starts
(worktrees reset to E)
┌─────┼─────┐
wt-1 wt-2
│ │
(tasks see Wave 1 results)
└─────┘
│
Wave 2 merge
│
orch/henry-20260318T140000 ─── B ── ... ── F ── G
│
Batch complete.
User runs /orch-integrate
3. Batch-Scoped Worktree Containers
Current naming (collides)
.worktrees/taskplane-wt-henry-1/ ← batch 1 and batch 2 both use this
.worktrees/taskplane-wt-henry-2/
New naming (batch-scoped)
.worktrees/{opId}-{batchId}/
├── lane-1/
├── lane-2/
└── merge/
Example:
.worktrees/henry-20260318T140000/
├── lane-1/
├── lane-2/
├── lane-3/
└── merge/
Benefits:
- No collisions between concurrent batches
- Clean per-batch cleanup (remove entire directory)
- Merge worktree is inside the container (no separate
merge-workspace-{opId}naming) - Obvious which worktrees belong to which batch
4. /orch-integrate Command
After batch completion, the user integrates the orch branch:
/orch-integrate Fast-forward current branch to orch branch
/orch-integrate --merge Real merge if branches diverged
/orch-integrate --pr Push orch branch and open PR on GitHub
Branch safety check
/orch-integrate reads baseBranch from the persisted batch state and compares to the user's current branch:
- ✅ Same branch → proceed
- ⚠️ Different branch → warn and require
--force
⚠ Batch was started from feat/foo, but you're on feat/bar.
Switch to feat/foo first, or use --force to merge into feat/bar.
Finding the orch branch
The command reads .pi/batch-state.json for:
orchBranch— the orch branch namebaseBranch— the branch the user was on when/orchstartedphase— must be"completed"for integration
If no batch state exists or phase isn't completed, show an appropriate message.
Auto-integration (opt-in config)
{
"orchestrator": {
"integration": "manual" // default
}
}
"manual"— batch completes, user runs/orch-integrate"auto"— batch auto-fast-forwards on completion (fails gracefully if diverged, falls back to manual)
5. State Schema Changes
New fields in OrchBatchRuntimeState
interface OrchBatchRuntimeState {
// Existing:
baseBranch: string; // branch user was on at /orch start
// New:
orchBranch: string; // "orch/{opId}-{batchId}" — the managed branch
}
New fields in PersistedBatchState
interface PersistedBatchState {
// Existing:
baseBranch: string;
// New:
orchBranch: string;
}
Config schema addition
interface OrchestratorConfig {
orchestrator: {
// ... existing fields ...
integration: "manual" | "auto"; // default: "manual"
};
}
6. Code Change Map
6.1 types.ts — Schema + defaults
- Add
orchBranch: stringtoOrchBatchRuntimeStateandPersistedBatchState - Add
integration: "manual" | "auto"toOrchestratorConfig.orchestrator - Default
integrationto"manual" - Default
orchBranchto""infreshOrchBatchState()
6.2 worktree.ts — Batch-scoped containers
generateWorktreePath()— Change signature: acceptbatchId,laneNumber. Output:{basePath}/{opId}-{batchId}/lane-{N}(batch-scoped container)generateMergeWorktreePath()— New function. Output:{basePath}/{opId}-{batchId}/mergelistWorktrees()— Update pattern matching for new nested structureremoveAllWorktrees()— Remove entire batch container directorygenerateBranchName()— Unchanged (branch names already include batchId)resolveWorktreeBasePath()— Unchanged (still handles sibling vs subdirectory)
6.3 engine.ts — Orch branch lifecycle
- Batch start (Phase 1): After capturing
baseBranch:- Generate
orchBranch = "orch/{opId}-{batchId}" git branch {orchBranch} {baseBranch}— create orch branch from user's HEAD- Store
orchBranchinbatchState
- Generate
- Pass
orchBranch(notbaseBranch) toexecuteWave()andmergeWaveByRepo()as the base for worktrees and merge target - Post-merge worktree reset: Reset worktrees to
orchBranchHEAD (notbaseBranch) - Cleanup (Phase 3): Remove orch branch only if auto-integration succeeded, OR preserve it for manual integration
- Auto-integration: If
config.orchestrator.integration === "auto":- Fast-forward
baseBranchtoorchBranch - If ff fails (diverged), log warning and preserve orch branch for manual integration
- If ff succeeds, delete orch branch
- Fast-forward
- Post-batch notification: Show orch branch name and integration instructions
6.4 merge.ts — Merge into orch branch
mergeWave()— ThebaseBranchparameter now receives the orch branch name. The temp branch and merge worktree logic stays largely the same, but:targetBranch = orchBranch(wasbaseBranch)- Fast-forward step changes: ff the orch branch to temp branch (no longer touches
user's branch in main checkout). Since orch branch isn't checked out in the
main repo, use
git update-ref refs/heads/{orchBranch} {tempBranchHead}instead ofgit merge --ff-only. - Remove the stash/pop logic (no longer needed — we're not touching the main checkout's working tree)
- Merge worktree path: Use
generateMergeWorktreePath()for batch-scoped container
6.5 waves.ts — Base branch resolution
allocateLanes()— receivesorchBranchas base. Creates worktrees branching from orch branch.resolveBaseBranch()— In repo mode, useorchBranchdirectly. In workspace mode, the per-repo branch detection still applies (each repo has its own orch branch or falls back toorchBranch).
6.6 extension.ts — /orch-integrate command
- Register new
/orch-integratecommand - Arguments:
--merge,--pr,--force - Implementation:
- Load batch state from
.pi/batch-state.json - Verify batch phase is
"completed" - Extract
orchBranchandbaseBranch - Check current branch matches
baseBranch(unless--force) - Default behavior:
git merge --ff-only {orchBranch} --merge:git merge {orchBranch}(real merge)--pr:git push origin {orchBranch}+gh pr create- On success: delete orch branch + clean up batch state
- Show summary
- Load batch state from
6.7 persistence.ts — Serialize orchBranch
- Add
orchBranchto serialization/deserialization inpersistRuntimeState()andloadPersistedState() - Backward compatibility: default
orchBranchto""when loading older state files
6.8 config-loader.ts / config-schema.ts — Integration config
- Add
integrationfield to orchestrator config schema - Default to
"manual" - Add to camelCase↔snake_case mapping
6.9 settings-tui.ts — Integration setting in TUI
- Add "Integration" toggle field in Orchestrator section:
manual/auto
6.10 Documentation
- Update
docs/reference/commands.md— add/orch-integrate - Update
docs/reference/configuration/taskplane-settings.md— add Integration setting - Update
README.mdcommand table
7. What Does NOT Change
- Lane branch naming (
task/{opId}-lane-{N}-{batchId}) — unchanged - Task execution flow — workers run the same way, just in worktrees that branch from orch branch instead of user branch
- Merge agent behavior — same merge request/result protocol, just targeting orch branch
- Dashboard — works the same (reads STATUS.md from worktrees and state files)
/orch-pause,/orch-resume,/orch-abort— same semantics- Discovery, waves, dependencies — unchanged
8. Edge Cases
Detached HEAD at /orch start
Already handled — getCurrentBranch() returns null, batch fails with clear message.
Protected base branch
No longer matters. Orch never pushes to the user's branch. The user can /orch-integrate --pr to create a PR against the protected branch.
User switches branches during batch
No impact. Orch branch and worktrees are independent of the main checkout's HEAD.
Concurrent batches
Batch-scoped containers prevent directory collisions. Orch branch names include batchId, so no branch collisions either.
Resume after crash
Persisted state includes orchBranch. Resume picks up from the orch branch. The user's branch is still untouched.
Workspace/polyrepo mode
Each repo gets its own lane worktrees. The orch branch is per-repo (or per-batch depending on workspace config). The merge worktree is per-repo too. resolveBaseBranch() adapts.
9. Task Decomposition
Task 1: Schema + Types + Config (TP-020)
- Add
orchBranchtoOrchBatchRuntimeState,PersistedBatchState,freshOrchBatchState() - Add
integrationtoOrchestratorConfigwith default"manual" - Update
config-schema.tswith new field - Update
config-loader.tsfor camelCase↔snake_case - Add Integration toggle to
settings-tui.ts - Tests for schema, defaults, serialization round-trip
Dependencies: None
Size: M
File scope: types.ts, config-schema.ts, config-loader.ts, settings-tui.ts, tests
Task 2: Batch-Scoped Worktree Containers (TP-021)
- Refactor
generateWorktreePath()to use{basePath}/{opId}-{batchId}/lane-{N} - New
generateMergeWorktreePath()→{basePath}/{opId}-{batchId}/merge - Update
listWorktrees()for nested structure - Update
removeAllWorktrees()to remove batch container - Update all callers
- Tests for path generation, listing, cleanup
Dependencies: TP-020 (needs batchId in worktree functions)
Size: M
File scope: worktree.ts, waves.ts, execution.ts, engine.ts, merge.ts, tests
Task 3: Orch Branch Lifecycle + Merge Redirect (TP-022)
- Create
orch/{opId}-{batchId}branch at batch start inengine.ts - Pass
orchBranchas base for worktrees and merge target - Replace
git merge --ff-onlyinmerge.tswithgit update-ref - Remove stash/pop logic from merge
- Post-merge worktree reset to orch branch HEAD
- Auto-integration logic (config-driven)
- Preserve orch branch for manual integration
- Update post-batch notification
- Tests for branch creation, merge redirect, ff, auto-integration
Dependencies: TP-020 (schema), TP-021 (worktree paths)
Size: L
File scope: engine.ts, merge.ts, persistence.ts, tests
Task 4: /orch-integrate Command (TP-023)
- Register
/orch-integrateinextension.ts - Implement ff, merge, PR modes
- Branch safety check (current branch vs baseBranch)
--forceoverride- Cleanup on success (delete orch branch, clean batch state)
- Tests for command parsing, branch safety, integration modes
Dependencies: TP-022 (orch branch must exist to integrate)
Size: M
File scope: extension.ts, tests
Task 5: Documentation + Settings (TP-024)
- Update
docs/reference/commands.mdwith/orch-integrate - Update
docs/reference/configuration/taskplane-settings.mdwith Integration - Update
README.mdcommand table - Update
docs/explanation/architecture.mdif needed
Dependencies: TP-023 (command must be implemented first)
Size: S
File scope: docs/**, README.md
10. Wave Plan
Wave 1: TP-020 (schema/types/config)
Wave 2: TP-021 (worktree containers) — depends on TP-020
Wave 3: TP-022 (orch branch + merge redirect) — depends on TP-020, TP-021
Wave 4: TP-023 (/orch-integrate command) — depends on TP-022
TP-024 (docs) — depends on TP-023
Note: TP-023 and TP-024 can potentially run in parallel in Wave 4 if docs are written from the spec rather than from the implementation. But safer to run TP-024 after TP-023.
11. Risk Assessment
| Risk | Impact | Mitigation |
|---|---|---|
| Worktree path refactor breaks existing batches | High | Comprehensive tests, backward-compat for listWorktrees |
git update-ref edge cases (concurrent writes) | Medium | Lock files, atomic operations |
| Workspace/polyrepo: per-repo orch branches needed | Medium | resolveBaseBranch() already handles per-repo, extend pattern |
Resume from pre-#24 state files missing orchBranch | Low | Default to "", fall back to legacy baseBranch merge |