Submatter/docs/AGENT-AUTONOMOUS-CONVOYS.md

518 lines
28 KiB
Markdown
Raw Normal View History

# Autonomous Convoys with OpenHands & OmniRoute
**Status:** Draft (planning phase)
**Date:** 2026-09-07
**Goal:** Enable OpenHands + omniroute to autonomously execute Echodo convoys by mapping roles to model profiles and integrating Agent Skills format.
---
## Executive Summary
Echodo's convoy system (defined in `.convoys/*.md` with role-based `model_policy`) currently requires Cursor to execute per-role briefs. This plan replaces Cursor with OpenHands as the runtime harness, using omniroute's model profiles (`auto`, `auto/coding`, `auto/reasoning`, `auto/coding-pro`, `auto/reasoning-pro`) to route each role to the appropriate model.
The main insight: **Your convoy structure already works. We're just swapping the agent harness and making skills discoverable.**
**Key changes:**
1. Convert role-prompt rules to `.agents/skills/` in SKILL.md format (Agent Skills spec)
2. Map omniroute profiles → roles (conductor/architect/implementer/reviewer)
3. Wire OpenHands session bootstrap to inherit `agent_prompt` + skills + model policy
4. Document the flow so agents executing convoys follow consistent patterns
**Outcome:** By end of v0.1, a human can:
- Define a convoy in `.convoys/<slug>.md` with `model_policy` roles mapped to omniroute profiles
- Trigger the convoy from Mission Control
- OpenHands picks up each brief, inherits the task context, loads the right model via omniroute, and executes
- After success, worker logs role completion + model metadata back to `agent_runs` and `convoy_events`
---
## Architecture: The Convoy-to-OpenHands Flow
```
┌─── Human / Mission Control ──────────────────────────────────────┐
│ "Start convoy: brief-1-implement-feature" │
│ (.convoys/feature-x.md, brief-1-*.md exists, ready to go) │
└────────────────────────┬────────────────────────────────────────┘
│
Orchestrator
(apps/orchestrator)
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ Redis Job Queue (echodo:orchestrator:jobs) │
│ { roleId, harnessAlias, briefPath, workspaceId, ...} │
└──────────────────────┬──────────────────────────────────────┘
│
Worker dequeues
(apps/worker)
│
┌─────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
┌────────────┐ ┌──────────────┐ ┌──────────────┐
│ Resolve │ │ Resolve │ │ Load │
│ model via │ │ harness + │ │ session │
│ omniroute │ │ worktree │ │ bootstrap: │
│ profile │ │ │ │ • agent_prompt
│ (coding, │ │ (code/doc/ │ │ • skills from
│ reasoning) │ │ research │ │ .agents/ │
│ │ │ harness) │ │ • role rules │
└────────────┘ └──────────────┘ └──────────────┘
│ │ │
└────────────────┼──────────────────┘
│
OpenHands Session Start
(apps/web calls MCP claim_task)
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────────────────────────┐
│ Session context ready: │
│ • Model resolved (e.g. qwen-coder or claude-3-5-sonnet) │
│ • Task/brief context loaded (agent_prompt, file paths) │
│ • Skills available (.agents/skills/*.md files) │
│ • Tool allowlist set (code vs doc harness) │
│ • MCP tools: read_file, write_file, grep, run_command │
│ │
│ Agent now thinks: "I am role-implementer on brief-1. │
│ My success metric is: [from convoy frontmatter]. │
│ I should follow: [agent_prompt from task]. │
│ I can use these skills: [loaded from .agents/skills/]." │
└────────────────────┬─────────────────────────────────────────┘
│
Agent tool loop
(read_file, write_file,
grep, run_command)
│
┌───────────────┼───────────────┐
│ │ │
Success Failure Stall
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────────┐
│ Log │ │ Log │ │ Orchestrator │
│ complete │ │ failure │ │ detects │
│ + model │ │ + │ │ stall, │
│ metadata │ │ tokens │ │ resumes from │
│ │ │ │ │ checkpoint │
└────┬─────┘ └────┬─────┘ └──────┬───────┘
│ │ │
└─────────────┼───────────────┘
│
MCP complete_task + convoy_events.role_complete
│
Orchestrator ticks
(determines next role)
│
┌─────────────┼──────────────┐
│ │ │
Next Brief Human Gate End Convoy
│ │ │
▼ ▼ ▼
┌──────────┐ ┌─────────┐ ┌──────────┐
│ Back to │ │ Mission │ │ Released │
│ Redis │ │ Control │ │ or │
│ (loop) │ │ approve │ │ merged │
└──────────┘ └─────────┘ └──────────┘
```
---
## Four Concrete Work Phases
### Phase 1: Agent Skills Format Conversion (M estimate)
**Goal:** Make repo-integrated skills discoverable by OpenHands via SKILL.md format in `.agents/skills/`.
**Why:** Your current `.cursor/rules/*.mdc` files are Cursor-specific. OpenHands reads `.agents/skills/*/SKILL.md` (per Agent Skills spec). Converting moves you from Cursor-only to Cursor + OpenHands + any harness that supports Agent Skills.
**Tasks:**
1. **Create `.agents/skills/backlog-sync/SKILL.md`**
- **When to invoke:** When working with `plans/`, `markdown_backlog_items`, or the Echodo markdown import
- **What it teaches:** How backlog frontmatter maps to DB, how `pnpm --filter @tasks/database watch:markdown-backlog` works, the contract between `.md` and `markdown_backlog_items`
- **Reference:** `.cursor/rules/markdown-backlog.mdc` + `packages/database/src/markdown-backlog/*.ts` + `docs/Glossary.md`
- **Output format:** `SKILL.md` with frontmatter (name, description) + sections (When to Activate, Core Rules, Common Patterns, Examples)
2. **Create `.agents/skills/convoy-lifecycle/SKILL.md`**
- **When to invoke:** When working with convoys, briefs, transitions, gates, or the state machine
- **What it teaches:** Status enum (draft→ready→in_progress→pr_review→merged), gate actors (human-gate-1/2/3), MCP tools (create_convoy, transition_convoy_status, log_convoy_event), checkpoint/resume pattern
- **Reference:** `docs/convoy-lifecycle.md` + `packages/shared/src/convoy-lifecycle/*.ts` + `apps/mcp-server/src/tools/*convoy*`
- **Output format:** Same as backlog-sync
3. **Create `.agents/skills/model-routing/SKILL.md`**
- **When to invoke:** When routing models, configuring aliases, or understanding how the orchestrator selects models
- **What it teaches:** The resolution order (brief override → convoy policy → workspace routes → pipeline defaults), omniroute profile mapping, how `resolveModel(workspace, roleId)` works, how model metadata flows to `agent_runs` and `convoy_events`
- **Reference:** `docs/adr-model-routing.md` + `packages/shared/src/orchestrator/*.ts` + this doc's **Phase 2** section
- **Output format:** Same as above
4. **Create `.agents/skills/code-review-two-axis/SKILL.md`** (adapted from mattpocock/skills)
- **When to invoke:** When `role-reviewer` runs or a human asks for a PR review in Mission Control
- **What it teaches:** Two-axis review (Standards vs Spec), running both as parallel sub-agents, how to aggregate findings, the code-smell baseline
- **Reference:** https://github.com/mattpocock/skills/blob/main/skills/engineering/code-review/SKILL.md (with Echodo-specific tweaks for Standards against `AGENTS.md` + repo rules)
- **Output format:** Same as above
5. **Create `.agents/skills/tdd-red-green/SKILL.md`** (adapted from mattpocock/skills)
- **When to invoke:** When implementing features or fixing bugs in a test-first flow
- **What it teaches:** Red-green-refactor loop, seams, anti-patterns, the tracer-bullet concept
- **Reference:** https://github.com/mattpocock/skills/blob/main/skills/engineering/tdd/SKILL.md
- **Output format:** Same as above
**Acceptance criteria:**
- [ ] Each SKILL.md has frontmatter (name, description) + at least 3 sections
- [ ] `skil --validate .agents/skills/*/SKILL.md` passes (or equiv. local validation)
- [ ] Skills are referenced in `AGENTS.md` under the "Skills available" section
- [ ] Agent can invoke them via `/skill` command or model-invocation within OpenHands
---
### Phase 2: OmniRoute Profile Mapping & Model Policy Doc (S estimate)
**Goal:** Document how omniroute profiles map to roles and how the orchestrator uses `model_policy` to route.
**Why:** Your `model_policy.roles` in convoy frontmatter lists role names + model aliases. Those aliases need to resolve to omniroute **profiles** (e.g., `auto/coding`, `auto/reasoning`) so the worker can ask omniroute "give me the model for `claude-planning`" and get back the current endpoint.
**Tasks:**
1. **Create `docs/omniroute-role-mapping.md`**
- **Section 1: Profile inventory**
```
- auto (default, fastest)
- auto/coding (code-specialized)
- auto/coding-pro (complex, high-quality)
- auto/reasoning (reasoning/planning)
- auto/reasoning-pro (complex reasoning, high-cost)
```
- **Section 2: Role → Profile mapping (recommended)**
```
role-conductor → auto/reasoning (kickoff, classify, skip flags)
role-architect → auto/reasoning-pro (design, brief decomposition)
role-implementer → auto/coding (default) or auto/coding-pro (escalation)
role-reviewer → auto/coding-pro (two-axis review)
role-doc-writer → auto/coding (docs are simpler than code)
gate-copilot → auto/reasoning (summary for human gate)
```
- **Section 3: How to configure in a convoy**
```yaml
model_policy:
roles:
role-conductor: claude-planning # resolves to auto/reasoning profile
role-implementer: qwen-coder # resolves to auto/coding profile
escalate_to: claude-planning # if implementer fails 2x, re-dispatch with this
```
- **Section 4: How the orchestrator uses this**
- Reads `model_policy.roles[roleId]` from the convoy
- Calls `resolveModel(workspace, roleId)` in `packages/shared/src/orchestrator/*.ts`
- Resolution order: brief override → convoy policy → workspace routes → pipeline defaults
- Result: `{ alias, provider_type, model_id, base_url, api_key_env_var }`
- Worker receives model spec in the job payload
- **Section 5: Escalation & retry logic**
- If implementer brief fails, orchestrator can re-dispatch with `escalate_to: role-architect` (higher-tier model)
- Log tier change on `agent_runs`
2. **Create `docs/omniroute-setup.md`** (operational guide)
- **How to register omniroute in the workspace** (via Settings → AI → Providers or tRPC `model_providers.create`)
- **How to set up the 5 profiles** (assuming omniroute cloud endpoint; or local Ollama + MLX)
- **How to test** (`testProvider` MCP call, test a single model)
- **How to debug** (logs in `agent_runs`, `worker_checkpoints` metadata)
3. **Update `docs/adr-model-routing.md`** (add OmniRoute section)
- Keep the existing conceptual model
- Add subsection: "OmniRoute Cloud Integration (v0.1)"
- Explain how `resolveModel` calls omniroute's API to get the actual endpoint + auth
**Acceptance criteria:**
- [ ] `docs/omniroute-role-mapping.md` covers all 6 roles (conductor, architect, implementer, reviewer, doc-writer, gate-copilot)
- [ ] Each role has a recommended default profile + an override example
- [ ] `docs/omniroute-setup.md` has step-by-step instructions (create alias, test, configure workspace route)
- [ ] ADR updated with omniroute context
- [ ] Convoy examples in `.convoys/*.md` use the recommended aliases (no hardcoded model names)
---
### Phase 3: OpenHands Session Bootstrap & Worker Integration (L estimate)
**Goal:** Wire up OpenHands session to inherit task context, load skills, and respect model policy.
**Why:** When the worker dequeues a job and spawns an OpenHands session, the agent needs to know:
- What task it's working on (brief path, success metric, acceptance criteria)
- What role it is (role-implementer, role-reviewer, etc.)
- Which model to use (resolved from omniroute)
- Which skills are available (from `.agents/skills/`)
- Which tools are allowed (code harness: `pnpm lint`, `run_command`; doc harness: read/write/grep only)
**Tasks:**
1. **Extend `apps/worker/src/session/minimal-session.ts`** (or create `openhands-session.ts`)
- **Input:** `{ jobSpec, resolvedModel, harnessAlias, briefPath }`
- **On session init:**
- Load `brief-*.md` from `.convoys/<slug>/` (task context)
- Extract `agent_prompt` (from task → epic → plan inheritance, per AGENTS.md)
- Inherit `title`, `success_metric`, acceptance criteria from frontmatter
- Construct a **session bootstrap** (initial system + first user message)
- **Session bootstrap structure:**
```
System message (shared context):
- You are an AI assistant helping implement a software task
- Your task: [title from brief frontmatter]
- Success means: [success_metric]
- You are playing the role of: [roleId]
- Acceptance criteria: [from brief or inherited]
- Constraints:
- Use the [tool allowlist] only
- If you make a file change, it will be pushed to a branch
- After you succeed, claim_task + complete_task will be called
- Principles: [karpathy-guidelines from AGENTS.md]
First user message:
- [inherited agent_prompt from task frontmatter]
- [brief body content — the actual work description]
- "Here are the skills you have available: [list from .agents/skills/]"
- "Your model is: [resolved model name from omniroute]"
```
- **On session end:**
- Collect metrics: `{ model, tokens, duration, outcome }`
- Return to worker for logging
2. **Wire OpenHands integration into Worker job handler** (`apps/worker/src/handle-job.ts`)
- **Current flow:** `MinimalSession` → tool loop → checkpoint
- **New flow:**
```
1. Dequeue job from Redis
2. resolveHarnessForJob(job.roleId) → harness config
3. resolveModel(workspace, job.roleId) → model spec
4. Create OpenHands session with bootstrap (brief context + skills)
5. Run tool loop (agent interacts with codebase)
6. On success: push branch, call forge PR endpoint
7. Log complete_task + convoy_events.role_complete with model metadata
8. Return to step 1
```
- **Config:** Add env var `WORKER_SESSION_TYPE=openai-client | minimal | openhands` (default: minimal for backward compat, set to `openhands` to use new flow)
3. **Create `.agents/` integration test** (`apps/worker/src/openhands-integration.test.ts`)
- **Test:** Spawn a dry-run OpenHands session with a toy brief, verify:
- [ ] Brief context loads correctly
- [ ] Skills are discoverable
- [ ] agent_prompt is inherited
- [ ] Tool allowlist is respected
- [ ] Session logs metrics on exit
**Acceptance criteria:**
- [ ] `WORKER_SESSION_TYPE=openhands` env var enables the flow
- [ ] Integration test passes (dry-run with mock brief)
- [ ] Live E2E test (real brief, real repo) succeeds with model logging
- [ ] `agent_runs` row includes `{ model_alias, model_tier, tokens_in, tokens_out }`
- [ ] `convoy_events` has `role_complete` entry with `metadata.harnessAlias`
---
### Phase 4: End-to-End E2E Test & Dogfood (L estimate)
**Goal:** Prove one convoy executes autonomously from start to finish using OpenHands + omniroute.
**Why:** This is the acceptance test for the whole milestone. A human should be able to:
1. Define a convoy with roles mapped to omniroute profiles
2. Click "Start" in Mission Control
3. Watch OpenHands agents pick up and complete briefs without Cursor
4. See model metadata logged correctly
**Tasks:**
1. **Create a test convoy** (`.convoys/test-e2e-autonomous.md`)
- Classification: `server-only` (simple, no IA/UX/design)
- 2–3 briefs (implementer + reviewer)
- Roles: `role-conductor` (kickoff only), `role-implementer` (fix a small bug or add a minor feature), `role-reviewer` (review the PR)
- Model policy: use omniroute profiles (e.g., `role-implementer: auto/coding`, `role-reviewer: auto/coding-pro`)
- Success metric: "Bug is fixed, PR is drafted, review is complete"
2. **Run the full loop** (Manual walkthrough)
- [ ] Human creates convoy via MCP `create_convoy` or Mission Control UI
- [ ] Orchestrator picks up `role-conductor` (dry-run or light task)
- [ ] Orchestrator enqueues `role-implementer` to Redis
- [ ] Worker dequeues, spawns OpenHands session
- [ ] OpenHands loads brief, runs tool loop, fixes the bug, commits
- [ ] Worker logs `complete_task` + `role_complete`
- [ ] Orchestrator transitions convoy to `pr_review` (or next gate)
- [ ] Orchestrator enqueues `role-reviewer` brief
- [ ] Worker dequeues, spawns OpenHands session, runs two-axis review
- [ ] Worker logs `complete_task` + `role_complete` with review metadata
- [ ] Convoy reaches `merged` or `published` gate
- [ ] Check `agent_runs`: model aliases logged for both implementer + reviewer
- [ ] Check `convoy_events`: role_complete entries present with metadata
3. **Dogfood the pipeline** (Optional: use this convoy to close a real task)
- Pick a backlog task that fits the convoy (small feature or bug)
- Map task to a convoy brief
- Run the E2E flow on real code
- Validate that the fix/feature is correct and the PR is mergeable
**Acceptance criteria:**
- [ ] Convoy completes all briefs without Cursor
- [ ] `agent_runs` has entries for implementer + reviewer with model aliases
- [ ] `convoy_events` has `role_complete` entries
- [ ] PR is draft or merged (depending on gates)
- [ ] No manual intervention after "Start Convoy"
- [ ] Logs are clean (no auth errors, model resolution errors)
---
## Dependencies & Ordering
```
Phase 1 (Skills) ──┐
Phase 2 (Routing) ─┼──→ Phase 3 (Worker) ──→ Phase 4 (E2E)
Phase 3 unblocked │ (can start once 1 + 2 done)
as soon as 1 + 2 │
complete
```
**Critical path:**
- Phase 1 + 2 are sequential (skills first, then routing docs)
- Phase 3 can start once Phase 1 + 2 are done
- Phase 4 requires Phase 3 complete
---
## Agent Instructions for Each Phase
### For Agents Working on Phase 1 (Skills)
1. **Before writing:** Read the existing `.cursor/rules/*.mdc` files in this repo + the referenced SKILL.md files from mattpocock/skills, anthropics/skills
2. **Structure:** Follow the Agent Skills spec (frontmatter + markdown sections)
3. **Validation:** Each skill should be invokable by model (e.g., `/skill backlog-sync` or implied when needed) and have 3–4 substantive sections
4. **Test:** Run locally with OpenHands or Claude Code to verify skills load
5. **Acceptance:** PR reviewer checks that skills have frontmatter + clear When-to-Activate section
### For Agents Working on Phase 2 (Routing)
1. **Before writing:** Read `docs/adr-model-routing.md` fully + understand how `model_policy` works in `.convoys/*.md`
2. **Assumption:** OmniRoute cloud is the target (not local-only), and omniroute has 5 profiles (auto, auto/coding, auto/coding-pro, auto/reasoning, auto/reasoning-pro)
3. **Output:** Two markdown files (mapping + setup), both human-readable and copy-pasteable examples
4. **Validation:** Walk through the flow: convoy sets `model_policy.roles`, orchestrator calls `resolveModel`, worker receives model spec
5. **Test:** No automated test needed; human walkthrough in Phase 4
### For Agents Working on Phase 3 (Worker Integration)
1. **Before writing:** Read `apps/worker/src/session/minimal-session.ts` + `apps/worker/src/handle-job.ts` + understand the worker dequeue loop
2. **Pattern:** Don't replace MinimalSession; extend it or wrap it in an adapter (prefer wrapping for backward compat)
3. **Assumption:** OpenHands agent-server is running and reachable (via env var `AGENT_SERVER_URL`)
4. **Validation:**
- Type-check passes (`pnpm type-check`)
- New env var `WORKER_SESSION_TYPE` is optional (defaults to `minimal`)
- Integration test dry-runs without errors
5. **Test:** Phase 4 (E2E) will exercise this
### For Agents Working on Phase 4 (E2E)
1. **Before starting:** Phase 1 + 2 + 3 must be done
2. **Pre-flight:**
- [ ] `.agents/skills/` SKILL.md files exist + load correctly
- [ ] `docs/omniroute-role-mapping.md` + `docs/omniroute-setup.md` exist
- [ ] `WORKER_SESSION_TYPE=openhands` env var works
- [ ] Orchestrator + worker are running (or can dry-run)
3. **Test convoy:** Create `.convoys/test-e2e-autonomous.md` with 2 briefs (implementer + reviewer)
4. **Manual walkthrough:** Follow the flow in "Phase 4: Tasks" section, step by step
5. **Validation:** Check `agent_runs`, `convoy_events` tables for logs; verify PR created + reviewed
6. **Report:** Summarize the run (timeline, model aliases used, any errors, fixes applied)
---
## Success Criteria (Overall)
By end of Phase 4:
- [ ] One full convoy (2+ briefs) executes autonomously via OpenHands without Cursor
- [ ] Each role uses the model from omniroute (via model_policy)
- [ ] `agent_runs` + `convoy_events` log model metadata for observability
- [ ] No secrets or API keys leaked in logs
- [ ] Docs cover setup + usage (omniroute-role-mapping.md, omniroute-setup.md)
- [ ] Skills are discoverable + usable by agents (.agents/skills/*/SKILL.md)
- [ ] Worker integration test passes
- [ ] Code is linted + type-checked
---
## Phase 5: ACP Harness & Runtime Agnosticism (post-v0.1)
**Goal:** Make submatter runtime-agnostic via Agent Client Protocol (ACP).
**Why:** ACP is an emerging standard that allows OpenHands, Claude Code, Gemini CLI, and other runtimes to be swapped via a single protocol. This prevents vendor lock-in and lets operators choose the best runtime per task.
**Tasks:**
1. Add `HarnessType: "acp"` to shared types
2. Create `apps/worker/src/executor/acp-executor.ts` — implements ACP client (connects to any ACP-compatible server)
3. Support ACP discovery via `/.well-known/agent.json` (runtime metadata)
4. Add `acp` harness type to executor registry
5. Support sub-agents via ACP Task Tool Set
**ACP-compatible runtimes:**
- OpenHands (primary)
- Claude Code (Anthropic)
- Gemini CLI (Google)
- Cursor (experimental)
- Custom (any ACP server)
**This is NOT required for v0.1 testing.** Ships after v0.1 ships and proves out.
---
## Rollout & Scaling
Once Phase 4 succeeds:
1. **Publish to v0.1** — Mark the orchestrator + worker as production-ready for autonomous convoys
2. **Deploy OpenHands Agent Server** — Docker container on Dokploy (CT 102), worker connects via internal Docker DNS
3. **Scale briefs** — Extend to 5–6 briefs per convoy (architect → implementer → reviewer → ops-copilot → etc.)
4. **Scale roles** — Add `role-docs-writer`, `role-security-auditor`, `role-performance-reviewer` as needed
5. **Scale models** — Extend omniroute integration to support Claude/OpenAI/Gemini APIs side-by-side
6. **Migrate Cursor convoys** — For convoys that have historically run in Cursor, run them via OpenHands instead (backward compat: human-initiated, not automated)
7. **ACP harness** — After v0.1 ships, add ACP support to enable runtime-agnostic agent execution
---
## Risk Mitigations
| Risk | Mitigation |
|------|-----------|
| Model tier mismatches (expensive model for simple task) | OmniRoute profiles + explicit role mapping in docs |
| Agent hallucinations in brief context | Agent_prompt inheritance from task → epic → plan keeps context tight; karpathy-guidelines skill |
| Network issues between worker + OpenHands | Checkpoint/resume logic already in place; retry logic in orchestrator |
| Secrets leaked in logs | Review `agent_runs.metadata` + `worker_checkpoints.tool_history` before logging; never log full API responses |
| Convoy state desync (DB vs `.convoys/*.md`) | `reconcile_from_files` MCP tool keeps them in sync; convoy_events is the source of truth |
---
## References
- **Agent Skills Spec:** https://agentskills.io/specification
- **Mattpocock Skills (code-review, tdd):** https://github.com/mattpocock/skills
- **Superpowers (bootstrap pattern):** https://github.com/obra/superpowers
- **Karpathy Guidelines (4 behavioral rules):** https://github.com/multica-ai/andrej-karpathy-skills
- **Echodo Convoys:** `.convoys/README.md`
- **Model Routing ADR:** `docs/adr-model-routing.md`
- **Worker Docs:** `docs/worker.md`
- **Orchestrator Docs:** `docs/orchestrator.md`
---
## Questions for Review
Before agents start Phase 1, answer these:
1. **OmniRoute target:** Are we assuming omniroute cloud endpoint, or should we support local Ollama + MLX as a fallback?
2. **Model fallback:** If omniroute is down, should the worker use a hardcoded fallback model, or fail closed?
3. **Role coverage:** Are 6 roles (conductor, architect, implementer, reviewer, doc-writer, gate-copilot) enough, or do we need more?
4. **Cost tracking:** Should Phase 3 also wire up `cost_budget` checks from `model_providers`, or leave that for a follow-on?
5. **Backward compat:** Should Phase 3 preserve the MinimalSession adapter, or fully replace it with OpenHands?
6. **v0.1 scope:** Phase 4 tests one convoy. Should we aim for 2+ convoys running in parallel by end of Phase 4, or save that for post-v0.1?
---
**Document Version:** 1.0
**Last Updated:** 2026-09-07
**Next Review:** After Phase 1 completion