# Orchestrator (Phase 2) — tasks

Decomposition in dependency order, grouped by subsystem. Each group notes its
deploy surface. Decisions referenced are in
[artifacts/implementation-decision-log.md](artifacts/implementation-decision-log.md);
the HOW is in [design.md](design.md).

Deploy surfaces:
- **coder** = `sudo systemctl restart boocoder`
- **web/contracts** = `pnpm -C packages/contracts build && docker compose up --build -d boocode`

---

## 1. Re-home conductor defs + DispatchFn seam + model param (coder) — [D-1]

- [ ] Copy `spine.ts`, `flows/*`, `contracts.ts`, `types.ts`, `render.ts` into
  `apps/coder/src/conductor/`. Do NOT copy `flow.ts` or `dispatch.ts`.
- [ ] Copy the 23 personas (`conductor/agents/*.md`) into the coder tree.
- [ ] Add a `DispatchFn` field to `StepContext` (`types.ts`); replace
  `flows/code-review.ts:10` (`import { dispatchAgent } from '../dispatch.js'`) and
  its call site (`:62`) with a call through `ctx.dispatch`.
- [ ] Parameterize `spine.ts:122` `process.env.CONDUCTOR_MODEL` → the run's `model`
  (threaded through the spine factory / step context).
- [ ] Confirm the Phase-1 CLI (`conductor/`) still builds + runs unchanged
  (regression oracle).
- [ ] Coder build passes (`pnpm -C apps/coder build`).

## 2. Schema: `flow_runs` / `flow_steps` (coder) — [D-5], [D-10]

- [ ] Add `flow_runs` to `apps/coder/src/schema.sql` (project_id no FK; band CHECK
  small|medium|large; status CHECK-named running|completed|failed; input JSONB
  CHECK `input ? 'question'`; report TEXT nullable; timestamps via
  `clock_timestamp()`).
- [ ] Add `flow_steps` (run_id FK→flow_runs CASCADE; step_id; kind CHECK agent|code;
  status CHECK-named pending|running|completed|failed|skipped — NO `queued`;
  task_id→tasks SET NULL nullable; chat_id→chats SET NULL; output TEXT FULL;
  UNIQUE(run_id, step_id)).
- [ ] Indexes `flow_steps(run_id, status)`, `flow_runs(project_id, created_at DESC)`.
- [ ] Explicit CHECK names + DROP-IF-EXISTS → guarded-ADD migration discipline.
- [ ] Verify idempotent re-apply (schema applies clean twice).

## 3. WS frames in all three registries (contracts + server + web) — [D-6]

- [ ] Add `flow_run_started` (`run_id, flow_name, band, steps[{step_id, agent,
  kind, chat_id, label}]`) + `flow_run_step_updated` (`run_id, step_id, status,
  run_status?, report?`) to `packages/contracts/src/ws-frames.ts` `WsFrameSchema`;
  rebuild (`pnpm -C packages/contracts build`).
- [ ] Add both to the server loose `InferenceFrame` union
  (`apps/server/src/services/inference/turn.ts`).
- [ ] Add both to the web strict `WsFrame` union (`apps/web/src/api/types.ts`) — the
  wire-format gate.
- [ ] Confirm the per-agent stream reuses existing `delta`/`tool_call`/
  `message_complete` by `chat_id` (no new stream frames).

## 4. Flow-runner + onTaskTerminal hook + plan mode + per-step chats (coder) — [D-2], [D-3], [D-4]

- [ ] Add `onTaskTerminal(taskId, state)` callback to `createDispatcher`, invoked at
  the external terminal transitions (`dispatcher.ts:642-646`, `:659-661`). No
  change to internal run functions.
- [ ] Create `apps/coder/src/services/flow-runner.ts`: load flow def, derive ready
  wave, INSERT `flow_runs`/`flow_steps`, build prompts via `step.run(ctx)`
  in-process (contracts injected), INSERT a synthetic `chats` row per agent step,
  INSERT each ready agent step as a `tasks` row with `state='pending'`,
  `mode_id='plan'` (hardcoded, never user-overridable), `chat_id=<synthetic>`.
- [ ] Run `code` steps inline (`flow_steps.task_id` NULL).
- [ ] On `onTaskTerminal`: read FULL task output → `flow_steps.output`, mark step,
  derive + INSERT next wave; on last wave render report → `flow_runs.report`,
  `status='completed'`.
- [ ] Publish `flow_run_started` on launch and `flow_run_step_updated` on each step
  transition (report on completion).

## 5. Resume on startup (coder) — [D-9]

- [ ] Add `initResume` (coder startup) over `flow_runs WHERE status='running'`:
  task completed → mark step done + advance; task lost/failed → re-dispatch (fresh
  task, `mode_id='plan'`); keep completed steps.
- [ ] Verify a coder restart mid-run reconciles and advances (manual smoke).

## 6. Routes: create / list / reopen (coder) — [D-2], [D-7]

- [ ] `POST /api/runs` `{project_id, flow_name, band, input:{question}, model?}` →
  create run, start flow-runner, return `run_id`.
- [ ] `GET /api/runs?project_id=` → runs history.
- [ ] `GET /api/runs/:id` → run + steps + report (reopen).

## 7. `orchestrator` pane kind + OrchestratorPane (web) — [D-7]

- [ ] Add `orchestrator` to `WorkspacePaneKind` (`api/types.ts`); thread through
  `useWorkspacePanes`, `Workspace`, `NewPaneMenu`, `ChatTabBar`,
  `PaneHeaderActions` (follow the `markdown_artifact`/`html_artifact` precedent).
- [ ] `OrchestratorPane.tsx`: run header, report-at-top on completion, collapsed
  roster reusing `AgentStatusDot` (`AgentComposerBar.tsx:204`),
  expand-one-at-a-time well reusing CoderPane stream rendering keyed by step
  `chat_id`, mobile single-column inline expand, auto-expand-follows-active.
- [ ] Subscribe to `flow_run_started` (build roster) + `flow_run_step_updated`
  (status/report) + reused stream frames by `chat_id`; handlers idempotent
  (event-dedup discipline).

## 8. Workflow toolbar button + slash launch wiring, parity (web) — [D-8]

- [ ] Add the `Workflow` (lucide) button to `ChatInput`'s controls row between
  `SquareSlash` and `Globe` (`ChatInput.tsx:648-732`); "Flows" desktop, icon-only
  mobile. Confirm the row stays one line (no scroll/wrap) on mobile.
- [ ] Slash (`/flow <focus>`): launch instantly with defaults (band small, current
  pane's project, text-after = focus), open an Orchestrator pane.
- [ ] Verify parity: the button + slash both appear in BooChat (ChatPane) AND
  BooCoder (CoderPane) since `ChatInput` is shared.

## 9. FlowLauncherDialog (web) — [D-8]

- [ ] `FlowLauncherDialog.tsx`: 5 category tabs (Analysis/Discovery/Planning/
  Authoring/Review) filtering the flow list (`flows/index.ts`), + size + focus +
  fast toggle; defaults Analysis/Small/off. On launch → `POST /api/runs`, open the
  Orchestrator pane.

## 10. Runs history + export (web) — [D-7]

- [ ] Runs history entry point in `NewPaneMenu` (backed by `GET /api/runs`);
  reopening opens an Orchestrator pane via `GET /api/runs/:id`.
- [ ] Export in the pane header `…`: copy / save-to-file / send-to-chat (existing
  `sendToChat`, `lib/events.ts`), conditional on a completed report.

## 11. Tests — [D-2], [D-9]

- [ ] Coder vitest for the scheduler: ready-wave derivation from a flow def,
  `onTaskTerminal` advancement, last-wave report render. Extract a pure helper
  (e.g. `flow-runner-decisions.ts`) for the wave/advance logic (repo pattern:
  `turn-guard.ts`/`lifecycle-decisions.ts`).
- [ ] Coder vitest for resume: completed step kept, lost/failed step re-dispatched
  (pure reconcile helper).
- [ ] Parity check: assert the `Workflow` button + slash are wired through the
  shared `ChatInput` (so both panes get them) — code-level assertion, no web test
  harness exists.

---

## Sequencing notes

- Groups 1–6 are coder-only (one `systemctl restart boocoder` after 1–6 land
  together, or incrementally).
- Group 3 (contracts) must build before groups 7–10 consume the frame types
  (`pnpm -C packages/contracts build` first).
- Groups 7–10 are web; ship with `docker compose up --build -d boocode`.
- Group 11 lands alongside the code it tests (coder tests after group 4/5).
