# VibePod Orchestrate — Design Spec

**Date:** 2026-03-22
**Branch:** a2a
**Status:** Draft

---

## Overview

`vp orchestrate` is a new subcommand group that enables Agent-to-Agent (A2A) orchestration via VibePod. A human-triggered controller agent breaks a task into sub-tasks, assigns each to a persona (preferred agent + fallback), runs workers in isolated git worktrees in parallel, and synthesizes the results.

The workflow has three explicit phases:

```
vp orchestrate plan <task>    # controller agent generates PLAN.md
vp orchestrate execute        # spawn workers per PLAN.md, monitor to completion
vp orchestrate merge          # controller agent synthesizes worktree results
```

Each phase is independently runnable so the user can review and edit between steps.

---

## CLI

### Commands

| Command | Description |
|---|---|
| `vp orchestrate plan <task>` | Trigger controller agent to produce a PLAN.md |
| `vp orchestrate execute` | Read PLAN.md, spawn worker agents, monitor progress |
| `vp orchestrate merge` | Synthesize all worktree results into main branch |

### Shared Flags

| Flag | Default | Description |
|---|---|---|
| `-w / --workspace` | `.` | Workspace directory (same as `vp run`) |
| `--plan-file` | `.vibepod/PLAN.md` | Override plan file location |

### `vp orchestrate plan` Flags

| Flag | Description |
|---|---|
| `--force` | Overwrite existing PLAN.md without prompting |

### `vp orchestrate merge` Flags

| Flag | Description |
|---|---|
| `--force` | Skip the pre-merge warning when tasks are `failed`/`blocked` |

### New and Existing `vp run` Flags Used by Orchestrate

| Flag | Status | Description |
|---|---|---|
| `--prompt-file <path>` | New | Mount a prompt file read-only into the container at `/vibepod-prompt.md`; sets `VP_PROMPT_FILE` env var inside the container via `docker run -e` (same mechanism as `spec.extra_env`). Used internally by `vp orchestrate execute` and available to users directly. |
| `--detach / -d` | Existing | Run container in background without attaching terminal. Already implemented. VibePod obtains the container ID from the returned container object for subsequent status monitoring. |

---

## Configuration

Personas and orchestration settings live in the existing `config.yaml`:

```yaml
orchestrate:
  controller: claude          # agent used for plan and merge phases
  merge_strategy: auto        # auto | review
  cleanup: true               # remove worktrees/branches after successful merge
  max_parallel: 4             # max worker containers running simultaneously (default: 4)

  personas:
    frontend:
      preferred: claude
      fallback: codex
      description: "Builds UI components, handles styling, and client-side logic"
      capabilities:
        - React / Vue / Svelte components
        - CSS and styling systems
        - Accessibility (WCAG)
        - Client-side state management
    backend:
      preferred: codex
      fallback: claude
      description: "Implements APIs, database models, and server-side business logic"
      capabilities:
        - REST / GraphQL APIs
        - Database design and migrations
        - Authentication and authorization
        - Performance optimization
    security:
      preferred: gemini
      fallback: claude
      description: "Reviews and implements security controls, audits dependencies"
      capabilities:
        - Threat modelling
        - Dependency vulnerability scanning
        - Auth and session security
        - Input validation and sanitization
    testing:
      preferred: claude
      fallback: gemini
      description: "Writes and maintains automated tests at all levels"
      capabilities:
        - Unit and integration tests
        - End-to-end test suites
        - Test coverage analysis
        - Mocking and fixtures
```

Rules:
- Only personas listed under `orchestrate.personas` are eligible for dispatch.
- The controller agent reads the full persona list (including `description` and `capabilities`) during `plan` and uses them to assign each sub-task to the best-fitting persona.
- `description` and `capabilities` are injected verbatim into the controller's planning prompt so it can make informed assignments.
- At `execute` time, VibePod tries `preferred` first; on start failure or worker-reported failure, it retries with `fallback`.
- A persona without a `fallback` fails hard — no retry.
- `controller` defaults to `default_agent` if not set.
- `max_parallel` limits how many worker containers run simultaneously. Tasks beyond the limit queue until a slot opens.

---

## PLAN.md Format

Human-readable and editable markdown. Generated by `vp orchestrate plan`, consumed by `vp orchestrate execute`. If `.vibepod/PLAN.md` already exists, `plan` prompts the user to confirm overwrite; `--force` skips the prompt.

```markdown
# VibePod Orchestration Plan

## Meta
- **run-id**: a1b2c3d4
- **created**: 2026-03-22T10:00:00Z

## Task
<original task description>

## Sub-tasks

### [TASK-1] Set up authentication module
- **persona**: backend
- **agent**: codex (fallback: claude)
- **worktree**: .vibepod/worktrees/task-1
- **prompt-file**: .vibepod/tasks/task-1.md
- **depends-on**: none
- **status**: pending

### [TASK-2] Build login UI components
- **persona**: frontend
- **agent**: claude (fallback: codex)
- **worktree**: .vibepod/worktrees/task-2
- **prompt-file**: .vibepod/tasks/task-2.md
- **depends-on**: none
- **status**: pending

### [TASK-3] Write integration tests
- **persona**: testing
- **agent**: claude (fallback: gemini)
- **worktree**: .vibepod/worktrees/task-3
- **prompt-file**: .vibepod/tasks/task-3.md
- **depends-on**: TASK-1, TASK-2
- **status**: pending
```

Valid `status` values written to PLAN.md: `pending`, `waiting` (queued behind unmet dependencies), `running`, `done`, `failed`, `blocked` (dependency failed). `waiting` is a live PLAN.md value, not just an internal state — VibePod writes it to the file when a task is held behind unsatisfied dependencies.

Each sub-task also has a detailed prompt file at `.vibepod/tasks/task-N.md` containing full agent instructions. PLAN.md is the human-editable overview; task files are the per-agent prompts.

The `status` field is updated live by VibePod during `execute`.

### `.vibepod/` Layout

```
.vibepod/
  config.yaml                # existing project config (unchanged)
  PLAN.md                    # orchestration plan (human-editable)
  tasks/
    task-1.md                # agent prompt for TASK-1
    task-2.md                # agent prompt for TASK-2
    task-3.md                # agent prompt for TASK-3
  worktrees/
    task-1/                  # git worktree for TASK-1
    task-2/                  # git worktree for TASK-2
    task-3/                  # git worktree for TASK-3
  status/
    task-1.json              # completion signal written by worker
    task-2.json              # completion signal written by worker
    task-3.json              # completion signal written by worker
  merge-prompt.md            # generated prompt for merge phase
```

---

## Task Prompt Files

Each `.vibepod/tasks/task-N.md` is generated by the controller agent during `plan` and contains:

1. **Task description** — what needs to be done
2. **Context** — relevant files, dependencies, constraints
3. **Completion instruction** — explicit instruction to write a completion signal:

```
When you have finished, write the following JSON to the file at the path
stored in the VP_STATUS_FILE environment variable:

  {"status": "done", "summary": "<one sentence summary>", "files_changed": ["..."]}

If you cannot complete the task, write:

  {"status": "failed", "reason": "<why it failed>"}
```

VibePod validates the JSON on read. If the file is missing or malformed after the container exits, the task is marked `failed` and the fallback agent is tried.

---

## Worker Agent Lifecycle

### Spawn

For each ready task (all dependencies met, within `max_parallel` limit), VibePod:

1. Creates a git worktree at `.vibepod/worktrees/task-N` on branch `orchestrate/task-N-<run-id>`, where `<run-id>` is read from the `## Meta` block of PLAN.md (generated at `plan` time). The worktree base commit is `HEAD` of the current branch at the moment `execute` is run.
2. Runs `vp run <agent> --ikwid --detach --prompt-file .vibepod/tasks/task-N.md -w .vibepod/worktrees/task-N` with two additional mounts and env vars injected via `docker run -e` (same mechanism as `spec.extra_env`):
   - `.vibepod/status/` is bind-mounted read-write into the container at `/vibepod-status/`
   - `VP_STATUS_FILE` is set to `/vibepod-status/task-N.json` (the in-container path)
   This ensures the worker can write its completion signal to a path that the host reads back from `.vibepod/status/task-N.json`.
3. Updates PLAN.md `status` to `running`.

### Completion Signal

Workers write to `VP_STATUS_FILE` when done:

```json
{
  "status": "done",
  "summary": "Implemented JWT authentication with refresh tokens",
  "files_changed": ["src/auth/jwt.ts", "src/auth/refresh.ts"]
}
```

`status` is either `"done"` or `"failed"`. On `failed`, VibePod resets the worktree to its base commit (`git checkout .` + `git clean -fd`) before retrying with the fallback agent, ensuring the fallback starts from a clean state.

### Status Flow

```
pending → waiting (unmet deps) → running → done
                                         ↘ failed → reset worktree → (retry with fallback) → done / failed
                                                   → blocked (if a dependency failed)
```

### Monitoring

VibePod polls `VP_STATUS_FILE` and container status during `execute`. PLAN.md is updated in real time. When all tasks reach a terminal state (`done`, `failed`, or `blocked`), `execute` exits and reports a summary, then prompts the user to run `vp orchestrate merge`.

### Re-entrancy

If `vp orchestrate execute` is re-run against an existing PLAN.md:

- Tasks with `status: done` are skipped entirely.
- Tasks with `status: running` are checked: if their container is still alive, they continue monitoring; if the container is gone and no valid status file exists, they are reset to `pending` and re-queued.
- Tasks with `status: failed` are eligible for re-run (retried from scratch on the reset worktree).
- Tasks with `status: blocked` are re-evaluated: VibePod checks whether the blocking dependency is now `done`. If yes, the task is promoted to `pending` and queued. If the dependency is still `failed`, the task remains `blocked`. The user must manually reset the dependency's status in PLAN.md (e.g., back to `pending`) before the dependency itself can be re-run.
- Tasks with `status: pending` or `waiting` are processed normally.

---

## Merge Phase

`vp orchestrate merge`:

1. **Pre-merge report** — lists all tasks with their status (`done`, `failed`, `blocked`). If any tasks are `failed` or `blocked`, VibePod warns the user and asks whether to proceed with only the completed tasks or abort. `--force` skips this prompt and proceeds with completed tasks only.
2. **Review gate** — lists completed worktrees with summaries from status files. User can inspect any worktree before proceeding (`git diff main orchestrate/task-N-<run-id>`).
3. **Merge prompt** — VibePod generates `.vibepod/merge-prompt.md` containing:
   - The original task
   - Each completed sub-task summary and changed files
   - Names of the `orchestrate/task-N-<run-id>` branches to merge
   - Instruction to merge branches one at a time using `git merge`, resolve any conflicts, and produce a coherent result
4. **Controller agent** — spawned against the main workspace with `--prompt-file .vibepod/merge-prompt.md --ikwid`. The agent performs `git merge` operations and resolves conflicts. If it cannot resolve a conflict, it leaves conflict markers in place, writes a note to `.vibepod/merge-conflicts.md`, and VibePod reports the conflict to the user for manual resolution.
5. **Merge strategy**:
   - `auto` — controller agent merges all branches sequentially without pausing; conflicts are resolved by the agent or flagged as above
   - `review` — after each branch is merged (or flagged with a conflict), VibePod stops and presents an interactive prompt asking the user to confirm (`y`) or abort (`n`) before continuing to the next branch
6. **Cleanup** — if `orchestrate.cleanup: true`, VibePod removes `.vibepod/worktrees/`, `.vibepod/status/`, and all `orchestrate/task-N-<run-id>` branches once `merge` completes without any unresolved conflicts. If conflicts were flagged, cleanup is skipped and VibePod informs the user to resolve them and run `vp orchestrate merge` again. PLAN.md and task files are always retained for reference.

---

## Dependency Handling

- Tasks with `depends-on` start in `waiting` state during `execute`.
- VibePod promotes `waiting` → `running` (subject to `max_parallel`) once all listed dependencies are `done`.
- If a dependency reaches `failed`, dependent tasks are marked `blocked` and skipped.
- To retry a blocked task: edit PLAN.md to reset the failed dependency back to `pending`, then re-run `vp orchestrate execute`.

---

## Error Handling

| Scenario | Behavior |
|---|---|
| Worker signals `failed` | Reset worktree, retry with fallback agent |
| Fallback also fails | Task marked `failed`, dependents marked `blocked` |
| Container fails to start | Same retry-with-fallback logic |
| No fallback defined | Immediate `failed`, no retry |
| Dependency failed | Dependent tasks marked `blocked`, skipped |
| Status file missing after container exits | Treated as `failed`, retry with fallback |
| Status file malformed JSON | Treated as `failed`, retry with fallback |
| Branch name collision | Run-id suffix generated at `plan` time prevents collision |
| PLAN.md already exists on `plan` | Prompt to confirm overwrite; `--force` skips prompt |
| Merge conflict during `auto` merge | Agent resolves; if unresolvable, flags in `merge-conflicts.md`, cleanup skipped |
| `blocked` task re-run | User resets dependency status in PLAN.md to `pending`, then re-runs `execute` |

---

## Out of Scope (First Iteration)

- Streaming / real-time log forwarding from worker containers
- Dynamic re-planning mid-execute (the plan is fixed once `execute` starts)
- Remote / multi-machine distribution (all containers run on the local Docker daemon)
- More than one level of dependency nesting (dependencies are flat task IDs)
