plan(automation): add mission-control foundation backlog and model-routing ADR
Documents the automation foundation plan (nine epics, Epic 1 taskified) and the accepted model-provider registry contract. Updates .env.example for backlog import workspace id and local/cloud AI provider env guidance. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
491d196384
commit
e256380671
18 changed files with 1476 additions and 2 deletions
24
.env.example
24
.env.example
|
|
@ -20,6 +20,12 @@
|
||||||
# Production (Coolify, CT 102): dedicated `echodo` user/db on shared Postgres
|
# Production (Coolify, CT 102): dedicated `echodo` user/db on shared Postgres
|
||||||
DATABASE_URL="postgresql://echodo:CHANGE_ME@192.168.68.102:5432/echodo"
|
DATABASE_URL="postgresql://echodo:CHANGE_ME@192.168.68.102:5432/echodo"
|
||||||
|
|
||||||
|
# UUID of the workspace that owns imported `plans/` rows (slug or UUID from Settings).
|
||||||
|
# For dogfooding this repo, use the `convoys-tasks` workspace id.
|
||||||
|
# Required by `pnpm --filter @tasks/database import:markdown-backlog` and
|
||||||
|
# `watch:markdown-backlog` (not loaded automatically — set in `.env`).
|
||||||
|
MARKDOWN_BACKLOG_WORKSPACE_ID="00000000-0000-0000-0000-000000000000"
|
||||||
|
|
||||||
# ----- Redis (CT 102 shared Redis) -----
|
# ----- Redis (CT 102 shared Redis) -----
|
||||||
# Use a dedicated DB index per AGENT-DEPLOY.md (ECHODO uses /11)
|
# Use a dedicated DB index per AGENT-DEPLOY.md (ECHODO uses /11)
|
||||||
# Local dev: redis://localhost:6379
|
# Local dev: redis://localhost:6379
|
||||||
|
|
@ -70,11 +76,25 @@ NEXT_PUBLIC_DIRECTUS_URL="https://directus.stillwell.cloud"
|
||||||
DIRECTUS_FEEDBACK_TOKEN=""
|
DIRECTUS_FEEDBACK_TOKEN=""
|
||||||
|
|
||||||
# ----- AI Providers -----
|
# ----- AI Providers -----
|
||||||
# Use OPENAI_BASE_URL=http://192.168.68.108:11434/v1 to route through
|
# Registry aliases (Epic 1) read these env var NAMES from the DB — values
|
||||||
# local Ollama (CT 108) instead of the OpenAI API.
|
# live here on the server/worker host, never in Postgres.
|
||||||
|
#
|
||||||
|
# Local Ollama on Mac:
|
||||||
|
# OPENAI_BASE_URL=http://<mac-lan-ip>:11434/v1
|
||||||
|
# OPENAI_API_KEY=ollama
|
||||||
|
# OPENAI_MODEL=qwen2.5-coder:32b
|
||||||
|
#
|
||||||
|
# Local MLX (mlx-openai-server on Mac — preferred for parallel implementers):
|
||||||
|
# MLX_SERVER_URL=http://<mac-lan-ip>:8000/v1
|
||||||
|
# MLX_API_KEY=local
|
||||||
|
# (Register as openai_compatible alias qwen-coder-mlx in Settings when Epic 1 ships)
|
||||||
|
#
|
||||||
|
# Cloud / free-tier (see docs/adr-model-routing.md):
|
||||||
OPENAI_API_KEY=""
|
OPENAI_API_KEY=""
|
||||||
OPENAI_BASE_URL=""
|
OPENAI_BASE_URL=""
|
||||||
ANTHROPIC_API_KEY=""
|
ANTHROPIC_API_KEY=""
|
||||||
|
GOOGLE_GENERATIVE_AI_API_KEY=""
|
||||||
|
NVIDIA_API_KEY=""
|
||||||
|
|
||||||
# ----- MCP Server -----
|
# ----- MCP Server -----
|
||||||
MCP_SERVER_PORT=3001
|
MCP_SERVER_PORT=3001
|
||||||
|
|
|
||||||
362
docs/adr-model-routing.md
Normal file
362
docs/adr-model-routing.md
Normal file
|
|
@ -0,0 +1,362 @@
|
||||||
|
# ADR: Model routing and provider registry
|
||||||
|
|
||||||
|
**Status:** Accepted (draft implementation)
|
||||||
|
**Date:** 2026-07-03
|
||||||
|
**Context:** Echodo automation foundation — orchestrator, worker, and mission-control UI without Cursor as the runtime substrate.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Echodo will own a **workspace-scoped model provider registry** and an **extensible role → model routing table**. Every automated pipeline run resolves a concrete provider configuration before inference. Resolution is deterministic, logged on every run, and configurable from Settings without redeploying orchestrator or worker code.
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
The agent-pipeline defines per-role models via:
|
||||||
|
|
||||||
|
- `model:` frontmatter in `.cursor/agents/role-*.md` (Cursor-only enforcement)
|
||||||
|
- `model_policy.roles` in convoy frontmatter (recommendation only)
|
||||||
|
- `recommended_model` on implementer briefs (optional override)
|
||||||
|
|
||||||
|
That works for manual Cursor sessions. It does **not** work when:
|
||||||
|
|
||||||
|
- An orchestrator dispatches `role-implementer` unattended
|
||||||
|
- Planning uses Claude API and implementation uses local Ollama on a Mac
|
||||||
|
- Operators add providers (OpenAI, Gemini, NVIDIA NIM, LiteLLM gateway) without code changes
|
||||||
|
- Custom roles (e.g. `gate-copilot`, `role-security-auditor`) need routing
|
||||||
|
|
||||||
|
We need one contract that web chat, orchestrator, worker, and MCP logging all share.
|
||||||
|
|
||||||
|
## Goals
|
||||||
|
|
||||||
|
1. **Per-role model selection** — conductor/architect on cloud; implementer/auditors on local coder models.
|
||||||
|
2. **Extensible providers** — Anthropic, OpenAI, Google (Gemini), OpenAI-compatible (Ollama, LiteLLM, NVIDIA NIM, Azure OpenAI).
|
||||||
|
3. **Extensible roles** — seed pipeline’s nine L2 roles; allow arbitrary `roleId` rows.
|
||||||
|
4. **Layered overrides** — workspace defaults → convoy `model_policy` → brief `recommended_model`.
|
||||||
|
5. **No secrets in DB** — store `apiKeyEnvVar` names; values live in deploy env (Coolify/Dokploy/Mac).
|
||||||
|
6. **Observable** — every `agent_runs` / `convoy_events` row records alias, provider type, model id, tier.
|
||||||
|
|
||||||
|
## Non-goals (this ADR)
|
||||||
|
|
||||||
|
- Implementing the orchestrator or worker (separate epics).
|
||||||
|
- LiteLLM as a required dependency (optional alias pointing at a gateway URL).
|
||||||
|
- Storing API keys in Postgres or committed YAML.
|
||||||
|
- Replacing `docs/model-routing-policy.md` tier guidance — this ADR **implements** it in Echodo.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ Settings → AI → Providers (ModelProviderAlias CRUD) │
|
||||||
|
│ Settings → AI → Role routing (RoleModelRoute CRUD) │
|
||||||
|
└────────────────────────────┬────────────────────────────────┘
|
||||||
|
│
|
||||||
|
resolveModel(workspaceId, roleId, ctx?)
|
||||||
|
│
|
||||||
|
┌───────────────────┼───────────────────┐
|
||||||
|
▼ ▼ ▼
|
||||||
|
apps/web apps/orchestrator apps/worker
|
||||||
|
(chat, gate (dispatch job (inference
|
||||||
|
co-pilot) with ModelRef) only)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Resolution order (first match wins)
|
||||||
|
|
||||||
|
1. **Brief override** — `recommended_model` alias on `.convoys/<slug>/brief-N-*.md`
|
||||||
|
2. **Convoy override** — `model_policy.roles[roleId]` in `.convoys/<slug>.md`
|
||||||
|
3. **Workspace route** — `role_model_routes` row for `(workspace_id, role_id)`
|
||||||
|
4. **Pipeline template** — shipped defaults (nine L2 roles + `gate-copilot`, `chat-default`)
|
||||||
|
|
||||||
|
If the alias is missing or disabled, fail closed with a structured error — do not silently fall back to a hardcoded model.
|
||||||
|
|
||||||
|
### Escalation
|
||||||
|
|
||||||
|
When `model_policy.escalate_to` is set (convoy) or workspace policy defines escalation:
|
||||||
|
|
||||||
|
- After N failed implementer attempts (default: 2), orchestrator re-dispatches the same brief with the escalation alias.
|
||||||
|
- Log `model_tier` change on the new run.
|
||||||
|
|
||||||
|
## Data model
|
||||||
|
|
||||||
|
### `model_providers` (per workspace)
|
||||||
|
|
||||||
|
| Column | Type | Notes |
|
||||||
|
|--------|------|-------|
|
||||||
|
| `id` | uuid PK | |
|
||||||
|
| `workspace_id` | uuid FK | Required; all queries filter by this |
|
||||||
|
| `alias` | varchar | Unique per workspace, e.g. `claude-planning` |
|
||||||
|
| `display_name` | varchar | UI label |
|
||||||
|
| `provider_type` | enum | See below |
|
||||||
|
| `model_id` | varchar | Provider-native model string |
|
||||||
|
| `base_url` | text nullable | Required for `openai_compatible` |
|
||||||
|
| `api_key_env_var` | varchar nullable | e.g. `ANTHROPIC_API_KEY` |
|
||||||
|
| `extra_headers` | jsonb nullable | Optional HTTP headers |
|
||||||
|
| `default_params` | jsonb nullable | `temperature`, `maxTokens`, … |
|
||||||
|
| `metadata` | jsonb nullable | `tier`, cost hints for dashboard |
|
||||||
|
| `enabled` | boolean | Default true |
|
||||||
|
| `created_at` / `updated_at` | timestamptz | |
|
||||||
|
|
||||||
|
**`provider_type` enum:**
|
||||||
|
|
||||||
|
| Value | Client | Examples |
|
||||||
|
|-------|--------|----------|
|
||||||
|
| `anthropic` | `@ai-sdk/anthropic` | Claude API |
|
||||||
|
| `openai` | `@ai-sdk/openai` | OpenAI API |
|
||||||
|
| `google` | `@ai-sdk/google` | Gemini |
|
||||||
|
| `openai_compatible` | `@ai-sdk/openai` + `baseURL` | Ollama, LiteLLM, **NVIDIA NIM**, vLLM |
|
||||||
|
| `azure_openai` | `@ai-sdk/openai` + Azure config | Azure deployments |
|
||||||
|
|
||||||
|
Future: `custom` with a registered adapter — not in v1.
|
||||||
|
|
||||||
|
### `role_model_routes` (per workspace)
|
||||||
|
|
||||||
|
| Column | Type | Notes |
|
||||||
|
|--------|------|-------|
|
||||||
|
| `id` | uuid PK | |
|
||||||
|
| `workspace_id` | uuid FK | |
|
||||||
|
| `role_id` | varchar | e.g. `role-implementer`, `gate-copilot` |
|
||||||
|
| `provider_alias_id` | uuid FK → `model_providers.id` | |
|
||||||
|
| `enabled` | boolean | |
|
||||||
|
| `sort_order` | int | UI ordering |
|
||||||
|
| `created_at` / `updated_at` | timestamptz | |
|
||||||
|
|
||||||
|
Unique constraint: `(workspace_id, role_id)`.
|
||||||
|
|
||||||
|
`role_id` is a **string**, not a DB enum — new pipeline roles do not require migrations.
|
||||||
|
|
||||||
|
### Resolved config (runtime, not persisted)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type ResolvedModel = {
|
||||||
|
alias: string;
|
||||||
|
providerType: ProviderType;
|
||||||
|
modelId: string;
|
||||||
|
baseUrl?: string;
|
||||||
|
apiKey: string; // read from process.env[apiKeyEnvVar] at call time
|
||||||
|
extraHeaders?: Record<string, string>;
|
||||||
|
defaultParams?: { temperature?: number; maxTokens?: number };
|
||||||
|
tier?: "premium" | "fast" | "local" | "auto";
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Pure function: `resolveModel(input, db)` lives in `packages/shared` (types + zod) with implementation in `packages/ai` or `packages/shared` (no React, no DB imports in shared — DB fetch in tRPC/orchestrator, pass rows into resolver).
|
||||||
|
|
||||||
|
## Provider examples
|
||||||
|
|
||||||
|
### Local Ollama (Mac)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
alias: qwen-coder
|
||||||
|
provider_type: openai_compatible
|
||||||
|
model_id: qwen2.5-coder:32b
|
||||||
|
base_url: http://192.168.1.42:11434/v1
|
||||||
|
api_key_env_var: OLLAMA_API_KEY # any non-empty string Ollama accepts
|
||||||
|
metadata: { tier: local }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Local MLX on Apple Silicon (Mac)
|
||||||
|
|
||||||
|
[MLX](https://github.com/ml-explore/mlx) is Apple's on-device ML framework. Echodo does **not** talk to MLX directly — use an **OpenAI-compatible HTTP server** in front of MLX, same `provider_type: openai_compatible` as Ollama.
|
||||||
|
|
||||||
|
| Server | Package | Default port | When to use |
|
||||||
|
|--------|---------|--------------|-------------|
|
||||||
|
| **mlx_lm.server** | `pip install mlx-lm` | 8080 | Solo dev, single implementer, simplest path |
|
||||||
|
| **mlx-openai-server** | [cubist38/mlx-openai-server](https://github.com/cubist38/mlx-openai-server) | 8000 | **Parallel implementer fan-out** — continuous batching handles concurrent requests better than the reference server |
|
||||||
|
| mlx-lm-server (community) | [0xDaizz/mlx-lm-server](https://github.com/0xDaizz/mlx-lm-server) | 8000 | Alternative if mlx-openai-server doesn't fit |
|
||||||
|
|
||||||
|
**Parallel implementers note:** When the orchestrator dispatches multiple `role-implementer` jobs at once (disjoint briefs per agent-pipeline `slice_dependencies`), a single local inference process can become the bottleneck. Prefer **mlx-openai-server** (or mlx-lm-server with batching) over the minimal `mlx_lm.server` when running 2+ implementers against the same Mac. Ollama can also serve parallel requests but MLX-native models (`mlx-community/*` on Hugging Face) often perform better on Apple Silicon unified memory. You may run **both** Ollama (`:11434`) and an MLX server (`:8000` / `:8080`) as separate registry aliases and route roles accordingly.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
alias: qwen-coder-mlx
|
||||||
|
provider_type: openai_compatible
|
||||||
|
model_id: mlx-community/Qwen2.5-Coder-32B-Instruct-4bit
|
||||||
|
base_url: http://192.168.1.42:8000/v1 # mlx-openai-server; use :8080/v1 for mlx_lm.server
|
||||||
|
api_key_env_var: MLX_API_KEY # placeholder; local servers ignore it
|
||||||
|
metadata: { tier: local, runtime: mlx }
|
||||||
|
```
|
||||||
|
|
||||||
|
**Operator setup (mlx-openai-server example):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Machine-wide install (all repos): ~/mlx-inference/mlx-serve.sh
|
||||||
|
# See ~/mlx-inference/README.md
|
||||||
|
~/mlx-inference/mlx-serve.sh
|
||||||
|
|
||||||
|
# Or manual:
|
||||||
|
pip install mlx-lm mlx-openai-server
|
||||||
|
mlx-openai-server launch \
|
||||||
|
--model-path mlx-community/Qwen2.5-Coder-32B-Instruct-4bit \
|
||||||
|
--model-type lm \
|
||||||
|
--host 0.0.0.0 \
|
||||||
|
--port 8000
|
||||||
|
```
|
||||||
|
|
||||||
|
**Networking:** Bind `0.0.0.0` when Echodo (Coolify) or `apps/worker` on another host must reach the Mac over LAN. Restrict firewall to homelab subnet. Do not expose raw local inference to the public internet.
|
||||||
|
|
||||||
|
**v1 registry:** No `provider_type: mlx` — HTTP gateway only. Native MLX SDK in `apps/worker` is out of scope.
|
||||||
|
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
alias: claude-planning
|
||||||
|
provider_type: anthropic
|
||||||
|
model_id: claude-sonnet-4-20250514
|
||||||
|
api_key_env_var: ANTHROPIC_API_KEY
|
||||||
|
metadata: { tier: premium }
|
||||||
|
```
|
||||||
|
|
||||||
|
### NVIDIA NIM
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
alias: nim-llama-70b
|
||||||
|
provider_type: openai_compatible
|
||||||
|
model_id: meta/llama-3.1-70b-instruct
|
||||||
|
base_url: https://integrate.api.nvidia.com/v1
|
||||||
|
api_key_env_var: NVIDIA_API_KEY
|
||||||
|
```
|
||||||
|
|
||||||
|
### Gemini
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
alias: gemini-flash
|
||||||
|
provider_type: google
|
||||||
|
model_id: gemini-2.0-flash
|
||||||
|
api_key_env_var: GOOGLE_GENERATIVE_AI_API_KEY
|
||||||
|
```
|
||||||
|
|
||||||
|
### LiteLLM gateway (optional)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
alias: litellm-gateway
|
||||||
|
provider_type: openai_compatible
|
||||||
|
model_id: qwen2.5-coder:32b # or per-role model_id in route table
|
||||||
|
base_url: http://host:4000/v1
|
||||||
|
api_key_env_var: LITELLM_MASTER_KEY
|
||||||
|
```
|
||||||
|
|
||||||
|
## Default role routing (seed template)
|
||||||
|
|
||||||
|
Shipped when a workspace enables pipeline integration:
|
||||||
|
|
||||||
|
| role_id | Suggested alias | tier |
|
||||||
|
|---------|-----------------|------|
|
||||||
|
| `role-conductor` | `claude-planning` | premium |
|
||||||
|
| `role-architect` | `claude-planning` | premium |
|
||||||
|
| `role-ia-architect` | `claude-planning` | premium |
|
||||||
|
| `role-ux-reviewer` | `qwen-coder` or `qwen-coder-mlx` | fast/local |
|
||||||
|
| `role-implementer` | `qwen-coder` or `qwen-coder-mlx` | fast/local |
|
||||||
|
| `role-reviewer` | `qwen-coder` or `qwen-coder-mlx` | fast/local |
|
||||||
|
| `role-design-system-auditor` | `qwen-coder` or `qwen-coder-mlx` | fast/local |
|
||||||
|
| `role-a11y-auditor` | `qwen-coder` or `qwen-coder-mlx` | fast/local |
|
||||||
|
| `role-doc-writer` | `qwen-coder` or `qwen-coder-mlx` | fast/local |
|
||||||
|
| `gate-copilot` | `claude-planning` | premium |
|
||||||
|
| `chat-default` | workspace choice | — |
|
||||||
|
|
||||||
|
Operators remap aliases in Settings; seed is not immutable.
|
||||||
|
|
||||||
|
## Convoy / brief integration
|
||||||
|
|
||||||
|
Convoy frontmatter uses **registry aliases**, not Cursor model slugs:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
model_policy:
|
||||||
|
roles:
|
||||||
|
role-conductor: claude-planning
|
||||||
|
role-architect: claude-planning
|
||||||
|
role-implementer: qwen-coder
|
||||||
|
escalate_to: claude-planning
|
||||||
|
```
|
||||||
|
|
||||||
|
Brief frontmatter:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
recommended_model: qwen-coder
|
||||||
|
model_tier: fast
|
||||||
|
```
|
||||||
|
|
||||||
|
Importer/parser for convoy files is out of scope for the first epic; orchestrator reads frontmatter when dispatching.
|
||||||
|
|
||||||
|
## API surface (tRPC)
|
||||||
|
|
||||||
|
Router: `ai.models` (or `pipeline.models`)
|
||||||
|
|
||||||
|
| Procedure | Purpose |
|
||||||
|
|-----------|---------|
|
||||||
|
| `listProviders` | Paginated; workspace-scoped |
|
||||||
|
| `createProvider` / `updateProvider` / `deleteProvider` | Admin-only |
|
||||||
|
| `testProvider` | One-token completion; returns latency + error detail |
|
||||||
|
| `listRoleRoutes` | All routes for workspace |
|
||||||
|
| `upsertRoleRoute` | Set role → alias; supports custom `roleId` |
|
||||||
|
| `deleteRoleRoute` | Remove custom role |
|
||||||
|
| `resolveForRole` | Debug: given `roleId` + optional convoy/brief context |
|
||||||
|
|
||||||
|
## UI
|
||||||
|
|
||||||
|
- `/{workspaceSlug}/settings/ai/providers` — provider table + add/edit dialog + test button
|
||||||
|
- `/{workspaceSlug}/settings/ai/role-routing` — role → alias table + add custom role
|
||||||
|
- Link from convoy/gate screens: “Model for this step: `qwen-coder` (change)”
|
||||||
|
|
||||||
|
## Logging contract
|
||||||
|
|
||||||
|
Extend `agent_runs` (and future `convoy_events`) with:
|
||||||
|
|
||||||
|
- `model_alias` varchar nullable
|
||||||
|
- `provider_type` varchar nullable
|
||||||
|
- `model_id` varchar nullable
|
||||||
|
- `model_tier` varchar nullable
|
||||||
|
|
||||||
|
`complete_task` MCP tool accepts optional `modelAlias` / `modelTier` for close-out logging.
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
- Provider CRUD: workspace admin only (same bar as `updateWorkflowPrompt`).
|
||||||
|
- `testProvider` runs server-side; rate-limit per workspace.
|
||||||
|
- Never return resolved API keys to the browser — only alias and env var **name**.
|
||||||
|
- Worker/orchestrator processes must have env vars for aliases they use.
|
||||||
|
|
||||||
|
## Relationship to other ADRs / plans
|
||||||
|
|
||||||
|
| Artifact | Relationship |
|
||||||
|
|----------|----------------|
|
||||||
|
| `docs/model-routing-policy.md` | Tier guidance; Cursor-era slugs migrate to aliases |
|
||||||
|
| `plans/Plan-agent-coordination` | `claim_task` / `complete_task` log model fields |
|
||||||
|
| `plans/Plan-automation-foundation` | Implements this ADR (model registry epic) |
|
||||||
|
| Future orchestrator epic | Calls `resolveModel` on every dispatch |
|
||||||
|
| Future worker epic | Consumes `ResolvedModel` only |
|
||||||
|
|
||||||
|
## Migration from Cursor slugs
|
||||||
|
|
||||||
|
Legacy convoys may reference `claude-4.6-opus-high-thinking` or `composer-2.5-fast`. Optional alias map in workspace metadata:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"legacy_model_aliases": {
|
||||||
|
"claude-4.6-opus-high-thinking": "claude-planning",
|
||||||
|
"composer-2.5-fast": "qwen-coder"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
**Positive**
|
||||||
|
|
||||||
|
- Single place to change Claude vs Qwen vs NIM without code deploys.
|
||||||
|
- Orchestrator and worker stay provider-agnostic.
|
||||||
|
- Extensible roles support pipeline evolution.
|
||||||
|
|
||||||
|
**Negative**
|
||||||
|
|
||||||
|
- Operators must configure providers before automation runs (mitigated: test button + clear errors).
|
||||||
|
- Two sources of truth for convoy overrides (markdown + DB) until convoy lifecycle MCP lands — resolution order is explicit.
|
||||||
|
|
||||||
|
## Open questions
|
||||||
|
|
||||||
|
1. **Repo-level `echodo.models.yml`** — mirror workspace config in git for bootstrap? Deferred; DB is v1 source.
|
||||||
|
2. **Per-user overrides** — deferred; workspace-level only for v1.
|
||||||
|
3. **Budget caps per alias** — defer to LiteLLM or Phase 4 workers.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- Agent-pipeline: `docs/model-routing-policy.md`, `docs/role-reference.md`
|
||||||
|
- Agent-pipeline v0.4 plan: convoy lifecycle + Echodo integration
|
||||||
|
- Vercel AI SDK provider packages in `packages/ai`
|
||||||
|
- MLX: [ml-explore/mlx](https://github.com/ml-explore/mlx), [mlx-lm SERVER.md](https://github.com/ml-explore/mlx-lm/blob/main/mlx_lm/SERVER.md), [mlx-openai-server](https://github.com/cubist38/mlx-openai-server)
|
||||||
|
- Free-tier discovery: [cheahjs/free-llm-api-resources](https://github.com/cheahjs/free-llm-api-resources), [mnfst/awesome-free-llm-apis](https://github.com/mnfst/awesome-free-llm-apis)
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
---
|
||||||
|
kind: epic
|
||||||
|
slug: apps-orchestrator
|
||||||
|
title: apps/orchestrator — role dispatch and gate pauses
|
||||||
|
plan_slug: automation-foundation
|
||||||
|
status: draft
|
||||||
|
priority: P1
|
||||||
|
tenant_id: global
|
||||||
|
cursor_epic_id: null
|
||||||
|
updated_at: "2026-07-03"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Epic objective
|
||||||
|
|
||||||
|
Long-running **`apps/orchestrator`** service: Redis job queue, convoy state machine driver, role dispatcher. Inspired by Symphony **concepts** (stall detection, token logging) — **not** Symphony codebase.
|
||||||
|
|
||||||
|
On **Go**: enqueue planning roles serially (ia → ux → architect), stop at `convoy_review`; after Gate 1, dispatch implementer jobs (parallel when `slice_dependencies` allow); fan-out audit jobs; stop at `pr_review` and post-merge gates.
|
||||||
|
|
||||||
|
**Parallel implementer fan-out:** When multiple implementer jobs target the same Mac inference host, orchestrator should pass the same `qwen-coder-mlx` alias to each job; operator must run a batching-capable MLX server (mlx-openai-server) — see `docs/adr-model-routing.md` § Local MLX. Cap concurrent implementers per host in orchestrator config if OOM occurs (agent-pipeline multitask playbook).
|
||||||
|
|
||||||
|
Each job calls `resolveModel(workspace, roleId, convoy, brief)` and passes `ResolvedModel` to worker.
|
||||||
|
|
||||||
|
## In scope / out of scope
|
||||||
|
|
||||||
|
**In scope**
|
||||||
|
|
||||||
|
- `apps/orchestrator` package in monorepo.
|
||||||
|
- Redis queue (existing infra).
|
||||||
|
- Deploy on Coolify beside `web`.
|
||||||
|
- Stall detection → `agent_runs` / convoy `blocked`.
|
||||||
|
|
||||||
|
**Out of scope**
|
||||||
|
|
||||||
|
- Worker implementation (Epic 7).
|
||||||
|
- Symphony / Linear integration.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Epic 1 (`resolveModel`).
|
||||||
|
- Epic 3 (repo link for clone path).
|
||||||
|
- Epic 4 (transitions).
|
||||||
|
- Epic 5 (gates — for dogfood; orchestrator can run in dry-run before UI).
|
||||||
|
|
||||||
|
## Blocks
|
||||||
|
|
||||||
|
- Epic 7 (worker consumes jobs).
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- [ ] Dry-run: orchestrator logs next role for a convoy without worker.
|
||||||
|
- [ ] Live: one implementer brief dispatched and paused correctly at gate.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
_To be decomposed when epic graduates to `ready`._
|
||||||
|
|
||||||
|
## Note
|
||||||
|
|
||||||
|
Supersedes [Epic-optional-orchestrator](../../Plan-agent-coordination/Epic-optional-orchestrator/Epic-optional-orchestrator.md) deferred stub with a concrete Echodo-native design.
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
---
|
||||||
|
kind: epic
|
||||||
|
slug: apps-worker-v1
|
||||||
|
title: apps/worker v1 — minimal Echodo coding agent
|
||||||
|
plan_slug: automation-foundation
|
||||||
|
status: draft
|
||||||
|
priority: P1
|
||||||
|
tenant_id: global
|
||||||
|
cursor_epic_id: null
|
||||||
|
updated_at: "2026-07-03"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Epic objective
|
||||||
|
|
||||||
|
First-party **`apps/worker`**: minimal agent runtime in the Echodo monorepo (not OpenHands). Docker sandbox per job, tool loop (`read_file`, `write_file`, `grep`, `run_command` with allowlist), git worktree per brief, MCP `claim_task` / `complete_task`, inference via `ResolvedModel` from orchestrator.
|
||||||
|
|
||||||
|
Role prompt = `.cursor/agents/role-*.md` + brief path. Model endpoint points at operator Mac (**Ollama or MLX server** — see ADR § Local MLX) or cloud APIs per registry.
|
||||||
|
|
||||||
|
**Parallel implementers:** When orchestrator dispatches N implementers to the same Mac inference host, prefer a batching-capable MLX server (**mlx-openai-server**) over `mlx_lm.server` alone. Document chosen server + port in workspace provider alias (`qwen-coder-mlx`).
|
||||||
|
|
||||||
|
## In scope / out of scope
|
||||||
|
|
||||||
|
**In scope**
|
||||||
|
|
||||||
|
- `WorkerSession` adapter interface (for future optional OpenHands plug-in).
|
||||||
|
- `pnpm lint && pnpm test` in allowlisted commands.
|
||||||
|
- Branch + PR draft output (forge adapter from Epic 3).
|
||||||
|
|
||||||
|
**Out of scope**
|
||||||
|
|
||||||
|
- Browser automation, multi-agent delegation.
|
||||||
|
- Feature parity with OpenHands.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Epic 1, 3, 6.
|
||||||
|
|
||||||
|
## Blocks
|
||||||
|
|
||||||
|
- Epic 8 (meaningful PR webhook dogfood).
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- [ ] One brief from `.convoys/` completes unattended on connected repo.
|
||||||
|
- [ ] `complete_task` includes model alias + token totals.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
_To be decomposed when epic graduates to `ready`._
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
---
|
||||||
|
kind: epic
|
||||||
|
slug: connected-repos
|
||||||
|
title: Connected repos — paste remote URL, forge adapters
|
||||||
|
plan_slug: automation-foundation
|
||||||
|
status: draft
|
||||||
|
priority: P1
|
||||||
|
tenant_id: global
|
||||||
|
cursor_epic_id: null
|
||||||
|
updated_at: "2026-07-03"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Epic objective
|
||||||
|
|
||||||
|
Workspace admins connect git remotes (Forgejo, GitHub, GitLab, Bitbucket, future Origin) via **paste URL** UX. Store `repo_links` with `forge_type`, API base, and credentials reference. Provide a `ForgeAdapter` interface for clone metadata, open PR, and (later) webhooks.
|
||||||
|
|
||||||
|
See automation foundation discussion: Vercel/Coolify-style “show me your repo.”
|
||||||
|
|
||||||
|
## In scope / out of scope
|
||||||
|
|
||||||
|
**In scope**
|
||||||
|
|
||||||
|
- `connected_repos` (or `repo_links`) Drizzle table, workspace-scoped.
|
||||||
|
- tRPC CRUD + “test clone” (shallow fetch).
|
||||||
|
- Settings UI: connect / disconnect / list.
|
||||||
|
- Forge adapter trait + implementations: `forgejo`, `github` (minimum two).
|
||||||
|
|
||||||
|
**Out of scope**
|
||||||
|
|
||||||
|
- Webhook receivers (Epic 8).
|
||||||
|
- Orchestrator clone logic (Epic 6 consumes this table).
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Workspace admin auth patterns (existing).
|
||||||
|
|
||||||
|
## Blocks
|
||||||
|
|
||||||
|
- Epic 6 (orchestrator), Epic 7 (worker).
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- [ ] Admin pastes Forgejo URL; Echodo stores link and detects `forge_type`.
|
||||||
|
- [ ] Worker/orchestrator can read `git_remote_url` + default branch from API.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
_To be decomposed when epic graduates to `ready`._
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
---
|
||||||
|
kind: epic
|
||||||
|
slug: convoy-lifecycle-mcp
|
||||||
|
title: Convoy lifecycle MCP — state machine and convoy_events
|
||||||
|
plan_slug: automation-foundation
|
||||||
|
status: draft
|
||||||
|
priority: P1
|
||||||
|
tenant_id: global
|
||||||
|
cursor_epic_id: null
|
||||||
|
updated_at: "2026-07-03"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Epic objective
|
||||||
|
|
||||||
|
Implement agent-pipeline **Phase 2a** convoy lifecycle in Echodo MCP: `create_convoy`, `create_brief`, `transition_convoy_status`, `log_convoy_event`, `query_manifest_status`, `reconcile_from_files`.
|
||||||
|
|
||||||
|
Server-enforced state machine (draft → convoy_review → ready → in_progress → pr_review → merged → released). Human gate actors (`human-gate-1/2/3`) only for their transitions.
|
||||||
|
|
||||||
|
Reference: agent-pipeline v0.4 plan §7; local-first `.convoys/` with `.pending-mcp-sync.jsonl` outbox.
|
||||||
|
|
||||||
|
## In scope / out of scope
|
||||||
|
|
||||||
|
**In scope**
|
||||||
|
|
||||||
|
- MCP tools in `apps/mcp-server`.
|
||||||
|
- `convoy_events` table (or extend `agent_runs` pattern).
|
||||||
|
- Map convoy → Echodo `project` object; brief → `task` child (per v0.4 object mapping).
|
||||||
|
- Extended status enum documented; UI filters updated in Epic 5.
|
||||||
|
|
||||||
|
**Out of scope**
|
||||||
|
|
||||||
|
- Orchestrator auto-dispatch (Epic 6).
|
||||||
|
- Deliverable templates (agent-pipeline Phase 2b).
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Epic 2 (convoy file conventions).
|
||||||
|
- Epic 3 (workspace `convoys-<repo>` convention).
|
||||||
|
|
||||||
|
## Blocks
|
||||||
|
|
||||||
|
- Epic 5, 6, 9.
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- [ ] Invalid transition `draft → released` rejected with structured error.
|
||||||
|
- [ ] `role-architect` cannot call `human-gate-1` transition.
|
||||||
|
- [ ] Offline convoy edit reconciles via `reconcile_from_files`.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
_To be decomposed when epic graduates to `ready`._
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
---
|
||||||
|
kind: epic
|
||||||
|
slug: fleet-dashboard
|
||||||
|
title: Fleet dashboard — all connected repos and convoys
|
||||||
|
plan_slug: automation-foundation
|
||||||
|
status: draft
|
||||||
|
priority: P2
|
||||||
|
tenant_id: global
|
||||||
|
cursor_epic_id: null
|
||||||
|
updated_at: "2026-07-03"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Epic objective
|
||||||
|
|
||||||
|
Top-level **fleet** view: all `convoys-<repo>` workspaces (or connected repos) in one screen. Roll up convoys by status, gate inbox count, active `agent_runs`, recent spend by model alias.
|
||||||
|
|
||||||
|
Answers: “What needs me across all projects?”
|
||||||
|
|
||||||
|
## In scope / out of scope
|
||||||
|
|
||||||
|
**In scope**
|
||||||
|
|
||||||
|
- Route `/fleet` or home dashboard for operator account.
|
||||||
|
- Cross-workspace tRPC (membership-scoped).
|
||||||
|
- Links into per-repo mission control (Epic 5).
|
||||||
|
|
||||||
|
**Out of scope**
|
||||||
|
|
||||||
|
- Multi-tenant fleet across unrelated customers (operator's own repos only for v1).
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Epic 4 (queryable convoy status).
|
||||||
|
- Epic 5 (gate semantics).
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- [ ] Operator with two connected repos sees combined “3 awaiting approval” badge.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
_To be decomposed when epic graduates to `ready`._
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
---
|
||||||
|
kind: epic
|
||||||
|
slug: forge-webhooks
|
||||||
|
title: Forge webhooks — PR and CI events drive convoy transitions
|
||||||
|
plan_slug: automation-foundation
|
||||||
|
status: draft
|
||||||
|
priority: P2
|
||||||
|
tenant_id: global
|
||||||
|
cursor_epic_id: null
|
||||||
|
updated_at: "2026-07-03"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Epic objective
|
||||||
|
|
||||||
|
Webhook receivers on Echodo for connected forges: PR opened → `pr_review`; PR merged → trigger doc-writer job + `merged`; CI failed → optional `blocked` or notify. Supports Forgejo and GitHub adapters minimum.
|
||||||
|
|
||||||
|
## In scope / out of scope
|
||||||
|
|
||||||
|
**In scope**
|
||||||
|
|
||||||
|
- `POST /api/webhooks/forge/:repoLinkId` with signature verification.
|
||||||
|
- Map events to `transition_convoy_status(actor: automation)`.
|
||||||
|
- Link PR branch to convoy/brief via branch naming convention or PR body marker.
|
||||||
|
|
||||||
|
**Out of scope**
|
||||||
|
|
||||||
|
- Replacing Coolify deploy webhooks for Gate 3 prod promote (manual or separate integration).
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Epic 3, 4, 7.
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- [ ] Merge on Forgejo moves convoy from `pr_review` to `merged` when Gate 2 approved.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
_To be decomposed when epic graduates to `ready`._
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
---
|
||||||
|
kind: epic
|
||||||
|
slug: mission-control-gates-ui
|
||||||
|
title: Mission control — convoy kanban, gate inbox, co-pilot
|
||||||
|
plan_slug: automation-foundation
|
||||||
|
status: draft
|
||||||
|
priority: P1
|
||||||
|
tenant_id: global
|
||||||
|
cursor_epic_id: null
|
||||||
|
updated_at: "2026-07-03"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Epic objective
|
||||||
|
|
||||||
|
Echodo UI for human gates and convoy visibility:
|
||||||
|
|
||||||
|
- **Convoy kanban** — columns map to state machine statuses (Planning, Awaiting plan approval, Building, …).
|
||||||
|
- **Gate inbox** — “needs you” queue for `convoy_review`, `pr_review`, pre-`released`.
|
||||||
|
- **Approve / deny / request changes** — calls `transition_convoy_status` with correct `human-gate-*` actor.
|
||||||
|
- **Gate co-pilot** — AI panel using `gate-copilot` role route (`resolveModel`) to summarize plan/diff and suggest action.
|
||||||
|
|
||||||
|
## In scope / out of scope
|
||||||
|
|
||||||
|
**In scope**
|
||||||
|
|
||||||
|
- Routes under `/{workspaceSlug}/convoys/` or `/mission-control/`.
|
||||||
|
- Conductor-style chat to seed convoys (can call `create_convoy` MCP or web tRPC wrapper).
|
||||||
|
|
||||||
|
**Out of scope**
|
||||||
|
|
||||||
|
- Fleet cross-repo view (Epic 9).
|
||||||
|
- Unattended orchestration (Epic 6).
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Epic 4 (lifecycle transitions and queryable convoy state).
|
||||||
|
|
||||||
|
## Blocks
|
||||||
|
|
||||||
|
- Epic 6 (orchestrator pauses until gate UI exists for dogfood).
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- [ ] Operator sees pending Gate 1 convoy and approves → status `ready`.
|
||||||
|
- [ ] Co-pilot uses `claude-planning` alias when configured.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
_To be decomposed when epic graduates to `ready`._
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
---
|
||||||
|
kind: epic
|
||||||
|
slug: model-provider-registry
|
||||||
|
title: Model provider registry and per-role routing
|
||||||
|
plan_slug: automation-foundation
|
||||||
|
status: ready
|
||||||
|
priority: P1
|
||||||
|
tenant_id: global
|
||||||
|
cursor_epic_id: null
|
||||||
|
updated_at: "2026-07-03"
|
||||||
|
agent_prompt: |
|
||||||
|
Epic: model provider registry (docs/adr-model-routing.md). Server-only change —
|
||||||
|
no UI component library inventing; reuse components/ui/. Admin-only mutations.
|
||||||
|
Filter every DB query by workspace_id. One task per session; claim_task first.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Epic objective
|
||||||
|
|
||||||
|
Deliver the workspace-scoped **model provider registry** and **role → alias routing** described in [docs/adr-model-routing.md](../../../docs/adr-model-routing.md): Drizzle schema, zod types in `packages/shared`, `resolveModel()`, tRPC CRUD + `testProvider`, Settings UI, and `agent_runs` model metadata on completion.
|
||||||
|
|
||||||
|
This epic is the first shipped piece of the automation-foundation plan and unblocks future orchestrator/worker work.
|
||||||
|
|
||||||
|
## In scope / out of scope
|
||||||
|
|
||||||
|
**In scope**
|
||||||
|
|
||||||
|
- Tables `model_providers`, `role_model_routes`.
|
||||||
|
- Pure resolver + unit tests.
|
||||||
|
- tRPC router (workspace admin).
|
||||||
|
- Settings pages: Providers, Role routing.
|
||||||
|
- Seed/default routes for nine L2 roles + `gate-copilot` + `chat-default`.
|
||||||
|
- `complete_task` optional model logging fields.
|
||||||
|
- `.env.example` env var documentation.
|
||||||
|
|
||||||
|
**Out of scope**
|
||||||
|
|
||||||
|
- Orchestrator dispatch (consumes resolver later).
|
||||||
|
- Worker inference loop.
|
||||||
|
- Parsing `model_policy` from `.convoys/*.md` files (orchestrator reads later).
|
||||||
|
- Billing/budget enforcement.
|
||||||
|
|
||||||
|
## Related tasks
|
||||||
|
|
||||||
|
| Task | Link |
|
||||||
|
|------|------|
|
||||||
|
| Shared schemas and resolveModel | `./Task-shared-schemas-resolve-model.md` |
|
||||||
|
| DB schema and migration | `./Task-db-model-providers-migration.md` |
|
||||||
|
| tRPC models router | `./Task-trpc-models-router.md` |
|
||||||
|
| Settings — providers UI | `./Task-settings-ai-providers-ui.md` |
|
||||||
|
| Settings — role routing UI | `./Task-settings-role-routing-ui.md` |
|
||||||
|
| agent_runs model fields + MCP | `./Task-agent-runs-model-logging.md` |
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Depends on: existing auth + workspace admin patterns in `apps/web`.
|
||||||
|
- Blocks: orchestrator epic (needs `resolveModel`), worker epic (needs `ResolvedModel`).
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- [ ] Admin can create `claude-planning` (anthropic) and `qwen-coder` (openai_compatible + base URL) in a workspace.
|
||||||
|
- [ ] Admin can map `role-implementer` → `qwen-coder`, `role-architect` → `claude-planning`.
|
||||||
|
- [ ] `testProvider` returns success/failure without exposing API keys.
|
||||||
|
- [ ] `resolveModel()` unit tests cover override order and missing-alias error.
|
||||||
|
- [ ] At least one task in this epic completed via `claim_task` → `complete_task` with model metadata.
|
||||||
|
|
||||||
|
## Proposed timeline
|
||||||
|
|
||||||
|
| Phase | Window | Notes |
|
||||||
|
|-------|--------|-------|
|
||||||
|
| Schema + resolver | Session 1–2 | Tasks: shared schemas, migration |
|
||||||
|
| API | Session 3 | tRPC router |
|
||||||
|
| UI | Session 4–5 | Both settings pages |
|
||||||
|
| Logging | Session 6 | agent_runs + MCP |
|
||||||
|
|
||||||
|
## Pipeline / convoy mapping
|
||||||
|
|
||||||
|
When running via agent-pipeline, architect briefs should align to tasks:
|
||||||
|
|
||||||
|
| Brief # | Task slug |
|
||||||
|
|---------|-----------|
|
||||||
|
| 1 | `shared-schemas-resolve-model` |
|
||||||
|
| 2 | `db-model-providers-migration` |
|
||||||
|
| 3 | `trpc-models-router` |
|
||||||
|
| 4 | `settings-ai-providers-ui` |
|
||||||
|
| 5 | `settings-role-routing-ui` |
|
||||||
|
| 6 | `agent-runs-model-logging` |
|
||||||
|
|
||||||
|
Briefs 1–2 serial (schema depends on types); 4–5 can fan out after brief 3 if `files:` are disjoint.
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
---
|
||||||
|
kind: task
|
||||||
|
slug: agent-runs-model-logging
|
||||||
|
title: agent_runs model metadata and complete_task logging fields
|
||||||
|
plan_slug: automation-foundation
|
||||||
|
epic_slug: model-provider-registry
|
||||||
|
status: ready
|
||||||
|
priority: P1
|
||||||
|
tenant_id: global
|
||||||
|
owner: unassigned
|
||||||
|
cursor_todo_id: null
|
||||||
|
updated_at: "2026-07-03"
|
||||||
|
agent_prompt: |
|
||||||
|
Read docs/adr-model-routing.md. Extend agent_runs schema + complete_task MCP +
|
||||||
|
runs UI columns. Migration via db:generate. claim_task before work;
|
||||||
|
complete_task when done — include modelAlias on your own completion as
|
||||||
|
dogfood proof.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Task summary
|
||||||
|
|
||||||
|
Persist model routing metadata on `agent_runs` and accept optional model fields on `complete_task` so dogfooded pipeline sessions record which alias was used.
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
### DB (`agent_runs`)
|
||||||
|
|
||||||
|
Add nullable columns:
|
||||||
|
|
||||||
|
- `model_alias` varchar
|
||||||
|
- `provider_type` varchar
|
||||||
|
- `model_id` varchar
|
||||||
|
- `model_tier` varchar
|
||||||
|
|
||||||
|
Migration via `pnpm db:generate`.
|
||||||
|
|
||||||
|
### MCP `complete_task`
|
||||||
|
|
||||||
|
Extend zod input (optional):
|
||||||
|
|
||||||
|
- `modelAlias?: string`
|
||||||
|
- `modelTier?: string`
|
||||||
|
- `providerType?: string`
|
||||||
|
- `modelId?: string`
|
||||||
|
|
||||||
|
Write to `agent_runs` on close. Document in tool description.
|
||||||
|
|
||||||
|
### Runs UI
|
||||||
|
|
||||||
|
`apps/web/app/(app)/[workspaceSlug]/settings/runs/page.tsx` — show model alias column in table and expanded row detail.
|
||||||
|
|
||||||
|
### `.env.example`
|
||||||
|
|
||||||
|
Add commented entries: `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_GENERATIVE_AI_API_KEY`, `NVIDIA_API_KEY`, `OLLAMA_API_KEY`, `OPENAI_BASE_URL` with note pointing to ADR.
|
||||||
|
|
||||||
|
## Subtasks
|
||||||
|
|
||||||
|
- [ ] Schema + migration
|
||||||
|
- [ ] complete_task schema + handler
|
||||||
|
- [ ] Runs UI column
|
||||||
|
- [ ] .env.example update
|
||||||
|
|
||||||
|
## Estimation
|
||||||
|
|
||||||
|
S
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- [ ] Completing a run with `modelAlias: "qwen-coder"` persists and displays in runs UI
|
||||||
|
- [ ] Existing complete_task callers unchanged (fields optional)
|
||||||
|
- [ ] ADR logging contract satisfied
|
||||||
|
|
||||||
|
## Links
|
||||||
|
|
||||||
|
- Epic: `./Epic-model-provider-registry.md`
|
||||||
|
- Plan: `../Plan-automation-foundation.md`
|
||||||
|
- Related: `plans/Plan-agent-coordination/`
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
---
|
||||||
|
kind: task
|
||||||
|
slug: db-model-providers-migration
|
||||||
|
title: Drizzle schema and migration for model_providers and role_model_routes
|
||||||
|
plan_slug: automation-foundation
|
||||||
|
epic_slug: model-provider-registry
|
||||||
|
status: ready
|
||||||
|
priority: P1
|
||||||
|
tenant_id: global
|
||||||
|
owner: unassigned
|
||||||
|
cursor_todo_id: null
|
||||||
|
updated_at: "2026-07-03"
|
||||||
|
agent_prompt: |
|
||||||
|
Read docs/adr-model-routing.md and packages/shared model-routing types from
|
||||||
|
Task-shared-schemas-resolve-model. packages/database only. Generate migration
|
||||||
|
with pnpm db:generate — do not hand-edit SQL. claim_task before work.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Task summary
|
||||||
|
|
||||||
|
Add `model_providers` and `role_model_routes` tables with workspace scoping and indexes per the ADR.
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
### Schema (`packages/database/src/schema/`)
|
||||||
|
|
||||||
|
- `model_providers.ts` — columns from ADR; unique `(workspace_id, alias)`
|
||||||
|
- `role_model_routes.ts` — FK to `model_providers`; unique `(workspace_id, role_id)`
|
||||||
|
- Re-export from `schema/index.ts`; add relations if needed
|
||||||
|
|
||||||
|
### Migration
|
||||||
|
|
||||||
|
- `pnpm db:generate` from repo root after schema edit
|
||||||
|
- Commit generated SQL under `packages/database/migrations/`
|
||||||
|
|
||||||
|
### Optional seed helper
|
||||||
|
|
||||||
|
- `seedDefaultRoleRoutes(workspaceId)` in database package or web server — inserts pipeline default role_ids pointing at aliases **only if** matching aliases exist (no auto-create of providers)
|
||||||
|
|
||||||
|
## Subtasks
|
||||||
|
|
||||||
|
- [ ] Define Drizzle tables
|
||||||
|
- [ ] Generate and commit migration
|
||||||
|
- [ ] Verify `workspace_id` index on both tables
|
||||||
|
|
||||||
|
## Estimation
|
||||||
|
|
||||||
|
S
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- [ ] Migration applies cleanly with `pnpm db:migrate`
|
||||||
|
- [ ] Every query example in epic doc filters by `workspace_id`
|
||||||
|
- [ ] Types align with `packages/shared` zod schemas
|
||||||
|
|
||||||
|
## Links
|
||||||
|
|
||||||
|
- Epic: `./Epic-model-provider-registry.md`
|
||||||
|
- ADR: `../../../docs/adr-model-routing.md`
|
||||||
|
- Depends on: `Task-shared-schemas-resolve-model.md`
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
---
|
||||||
|
kind: task
|
||||||
|
slug: settings-ai-providers-ui
|
||||||
|
title: Settings UI — AI model providers
|
||||||
|
plan_slug: automation-foundation
|
||||||
|
epic_slug: model-provider-registry
|
||||||
|
status: ready
|
||||||
|
priority: P1
|
||||||
|
tenant_id: global
|
||||||
|
owner: unassigned
|
||||||
|
cursor_todo_id: null
|
||||||
|
updated_at: "2026-07-03"
|
||||||
|
agent_prompt: |
|
||||||
|
Read docs/adr-model-routing.md. apps/web UI only — reuse components/ui.
|
||||||
|
Route: /[workspaceSlug]/settings/ai/providers. Do not store secrets in
|
||||||
|
forms — only api_key_env_var name field. claim_task before work.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Task summary
|
||||||
|
|
||||||
|
Build the workspace Settings page for CRUD on model provider aliases, including a “Test connection” action.
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
### Route
|
||||||
|
|
||||||
|
`apps/web/app/(app)/[workspaceSlug]/settings/ai/providers/page.tsx`
|
||||||
|
|
||||||
|
Add nav link under Settings (near runs/audit).
|
||||||
|
|
||||||
|
### UI
|
||||||
|
|
||||||
|
- Table: alias, display name, provider type, model id, base URL (truncated), enabled, actions
|
||||||
|
- Add/Edit dialog: all fields from ADR; provider type select drives whether base URL is required
|
||||||
|
- Test button → calls `ai.models.testProvider` → toast with latency or error
|
||||||
|
- Delete with confirm
|
||||||
|
|
||||||
|
### UX notes
|
||||||
|
|
||||||
|
- Show helper text: “API key is read from server env var `ANTHROPIC_API_KEY`” — not a password field
|
||||||
|
- Example presets (optional buttons): “Ollama local”, “MLX local (mlx-openai-server)”, “Anthropic”, “NVIDIA NIM”, “Gemini” — prefill form per `docs/adr-model-routing.md` § Provider examples
|
||||||
|
|
||||||
|
## Subtasks
|
||||||
|
|
||||||
|
- [ ] Page + list query
|
||||||
|
- [ ] Create/edit dialog
|
||||||
|
- [ ] Test connection + delete
|
||||||
|
- [ ] Settings nav entry
|
||||||
|
|
||||||
|
## Estimation
|
||||||
|
|
||||||
|
M
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- [ ] Admin can add `qwen-coder` with Mac Ollama base URL and test it
|
||||||
|
- [ ] Non-admin sees read-only or hidden mutations
|
||||||
|
- [ ] Matches existing settings page visual patterns (e.g. runs page)
|
||||||
|
|
||||||
|
## Links
|
||||||
|
|
||||||
|
- Epic: `./Epic-model-provider-registry.md`
|
||||||
|
- Depends on: `Task-trpc-models-router.md`
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
---
|
||||||
|
kind: task
|
||||||
|
slug: settings-ai-role-routing-ui
|
||||||
|
title: Settings UI — per-role model routing
|
||||||
|
plan_slug: automation-foundation
|
||||||
|
epic_slug: model-provider-registry
|
||||||
|
status: ready
|
||||||
|
priority: P1
|
||||||
|
tenant_id: global
|
||||||
|
owner: unassigned
|
||||||
|
cursor_todo_id: null
|
||||||
|
updated_at: "2026-07-03"
|
||||||
|
agent_prompt: |
|
||||||
|
Read docs/adr-model-routing.md. apps/web UI. Route:
|
||||||
|
/[workspaceSlug]/settings/ai/role-routing. Allow adding custom role_id
|
||||||
|
strings. claim_task before work.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Task summary
|
||||||
|
|
||||||
|
Build the Settings page that maps `role_id` → provider alias, with pipeline defaults seeded and support for custom roles.
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
### Route
|
||||||
|
|
||||||
|
`apps/web/app/(app)/[workspaceSlug]/settings/ai/role-routing/page.tsx`
|
||||||
|
|
||||||
|
### UI
|
||||||
|
|
||||||
|
- Table: role id, display label (optional friendly name), provider alias (dropdown from `listProviders`), tier badge from provider metadata, enabled toggle
|
||||||
|
- “Add role” — free-text `role_id` + alias dropdown (for `gate-copilot`, custom auditors, etc.)
|
||||||
|
- “Reset to pipeline defaults” — calls upsert for nine L2 roles + `gate-copilot` + `chat-default` **if** matching aliases exist; confirm dialog
|
||||||
|
- Link to ADR / note about convoy `model_policy` overrides
|
||||||
|
|
||||||
|
### Default labels
|
||||||
|
|
||||||
|
Map known `role_id` to human labels (Conductor, Architect, …) in a const — custom roles show raw id.
|
||||||
|
|
||||||
|
## Subtasks
|
||||||
|
|
||||||
|
- [ ] Page + list routes query
|
||||||
|
- [ ] Upsert/disable per row
|
||||||
|
- [ ] Add custom role flow
|
||||||
|
- [ ] Reset defaults action
|
||||||
|
- [ ] Settings nav entry
|
||||||
|
|
||||||
|
## Estimation
|
||||||
|
|
||||||
|
M
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- [ ] User can set `role-architect` → `claude-planning`, `role-implementer` → `qwen-coder`
|
||||||
|
- [ ] User can add `gate-copilot` role row
|
||||||
|
- [ ] Dropdown only lists enabled providers for the workspace
|
||||||
|
|
||||||
|
## Links
|
||||||
|
|
||||||
|
- Epic: `./Epic-model-provider-registry.md`
|
||||||
|
- Depends on: `Task-trpc-models-router.md`, `Task-settings-ai-providers-ui.md` (providers should exist first for dogfood; soft dependency)
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
---
|
||||||
|
kind: task
|
||||||
|
slug: shared-schemas-resolve-model
|
||||||
|
title: Shared zod schemas and resolveModel() pure function
|
||||||
|
plan_slug: automation-foundation
|
||||||
|
epic_slug: model-provider-registry
|
||||||
|
status: ready
|
||||||
|
priority: P1
|
||||||
|
tenant_id: global
|
||||||
|
owner: unassigned
|
||||||
|
cursor_todo_id: null
|
||||||
|
updated_at: "2026-07-03"
|
||||||
|
agent_prompt: |
|
||||||
|
Read docs/adr-model-routing.md first. Implement only packages/shared (and
|
||||||
|
packages/ai if needed for zero DB imports in shared). No UI, no migration.
|
||||||
|
claim_task this backlog item before coding; complete_task when done.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Task summary
|
||||||
|
|
||||||
|
Add zod schemas and types for model providers, role routes, and a pure `resolveModel()` function per the ADR.
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
### Deliverables
|
||||||
|
|
||||||
|
1. **`packages/shared`** — export:
|
||||||
|
- `providerTypeSchema` enum: `anthropic` | `openai` | `google` | `openai_compatible` | `azure_openai`
|
||||||
|
- `modelProviderSchema`, `roleModelRouteSchema`, `resolvedModelSchema`
|
||||||
|
- `resolveModel(input)` — pure function; input includes optional brief/convoy overrides, workspace routes, provider catalog
|
||||||
|
- `PIPELINE_DEFAULT_ROLE_ROUTES` constant (role_id → suggested alias names, not UUIDs)
|
||||||
|
- `LEGACY_CURSOR_MODEL_ALIASES` map for slug migration
|
||||||
|
|
||||||
|
2. **Tests** — `packages/shared/src/model-routing/*.test.ts`:
|
||||||
|
- Resolution order: brief > convoy > workspace > default
|
||||||
|
- Missing/disabled alias → structured error
|
||||||
|
- Legacy slug remapping
|
||||||
|
|
||||||
|
### Constraints
|
||||||
|
|
||||||
|
- `packages/shared` must not import DB or Node-only APIs.
|
||||||
|
- Resolver accepts plain objects (tRPC/orchestrator loads rows and passes in).
|
||||||
|
|
||||||
|
## Subtasks
|
||||||
|
|
||||||
|
- [ ] Add schemas and types
|
||||||
|
- [ ] Implement `resolveModel`
|
||||||
|
- [ ] Add vitest cases (≥6 scenarios)
|
||||||
|
- [ ] Export from `packages/shared` index
|
||||||
|
|
||||||
|
## Estimation
|
||||||
|
|
||||||
|
S (one agent session)
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- [ ] `pnpm --filter @tasks/shared test` passes with new tests
|
||||||
|
- [ ] `pnpm type-check` clean
|
||||||
|
- [ ] Resolver matches resolution order in ADR § Architecture
|
||||||
|
|
||||||
|
## Links
|
||||||
|
|
||||||
|
- Epic: `./Epic-model-provider-registry.md`
|
||||||
|
- Plan: `../Plan-automation-foundation.md`
|
||||||
|
- ADR: `../../../docs/adr-model-routing.md`
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
---
|
||||||
|
kind: task
|
||||||
|
slug: trpc-models-router
|
||||||
|
title: tRPC ai.models router — CRUD, role routes, testProvider
|
||||||
|
plan_slug: automation-foundation
|
||||||
|
epic_slug: model-provider-registry
|
||||||
|
status: ready
|
||||||
|
priority: P1
|
||||||
|
tenant_id: global
|
||||||
|
owner: unassigned
|
||||||
|
cursor_todo_id: null
|
||||||
|
updated_at: "2026-07-03"
|
||||||
|
agent_prompt: |
|
||||||
|
Read docs/adr-model-routing.md. apps/web/server only. Admin-only mutations
|
||||||
|
(match updateWorkflowPrompt bar). testProvider runs a minimal completion
|
||||||
|
server-side — never return API keys to client. claim_task before work.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Task summary
|
||||||
|
|
||||||
|
Add `ai.models` tRPC router: provider CRUD, role route upsert/delete, `testProvider`, and `resolveForRole` debug procedure.
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
### Procedures
|
||||||
|
|
||||||
|
| Procedure | Auth | Notes |
|
||||||
|
|-----------|------|-------|
|
||||||
|
| `listProviders` | member | workspace-scoped |
|
||||||
|
| `createProvider` / `updateProvider` / `deleteProvider` | admin | zod validate; unique alias |
|
||||||
|
| `testProvider` | admin | one short completion; return `{ ok, latencyMs, error? }` |
|
||||||
|
| `listRoleRoutes` | member | join provider alias for display |
|
||||||
|
| `upsertRoleRoute` / `deleteRoleRoute` | admin | custom `roleId` allowed |
|
||||||
|
| `resolveForRole` | member | calls shared `resolveModel` with DB rows |
|
||||||
|
|
||||||
|
### Model client factory
|
||||||
|
|
||||||
|
- Add `packages/ai/src/providers/resolve-client.ts` (or similar): given `ResolvedModel`, return Vercel AI SDK model instance for `generateText` in `testProvider`
|
||||||
|
- Support at least: `anthropic`, `openai`, `google`, `openai_compatible`
|
||||||
|
|
||||||
|
### Wire router
|
||||||
|
|
||||||
|
- Register in `apps/web/server/root.ts`
|
||||||
|
- Export input schemas from shared where possible
|
||||||
|
|
||||||
|
## Subtasks
|
||||||
|
|
||||||
|
- [ ] Implement provider CRUD
|
||||||
|
- [ ] Implement role route CRUD
|
||||||
|
- [ ] Implement testProvider with provider factory
|
||||||
|
- [ ] Implement resolveForRole
|
||||||
|
|
||||||
|
## Estimation
|
||||||
|
|
||||||
|
M
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- [ ] Admin can create provider via tRPC; non-admin gets FORBIDDEN
|
||||||
|
- [ ] testProvider works against env-configured Anthropic or Ollama when aliases exist
|
||||||
|
- [ ] `pnpm lint && pnpm type-check` clean
|
||||||
|
|
||||||
|
## Links
|
||||||
|
|
||||||
|
- Epic: `./Epic-model-provider-registry.md`
|
||||||
|
- Depends on: `Task-db-model-providers-migration.md`, `Task-shared-schemas-resolve-model.md`
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
---
|
||||||
|
kind: epic
|
||||||
|
slug: pipeline-convoy-scaffolding
|
||||||
|
title: Pipeline convoy scaffolding — .convoys/ and metrics in tasks repo
|
||||||
|
plan_slug: automation-foundation
|
||||||
|
status: draft
|
||||||
|
priority: P1
|
||||||
|
tenant_id: global
|
||||||
|
cursor_epic_id: null
|
||||||
|
updated_at: "2026-07-03"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Epic objective
|
||||||
|
|
||||||
|
Install L3 convoy infrastructure in the `tasks` repo so agent-pipeline dogfooding works: `.convoys/README.md`, `scripts/log-convoy-event.sh`, `.cursor/rules/convoy-planning.mdc`, `.gitignore` entry for `.convoys/.metrics.jsonl`.
|
||||||
|
|
||||||
|
Can run **in parallel** with Epic 1 (model provider registry).
|
||||||
|
|
||||||
|
## In scope / out of scope
|
||||||
|
|
||||||
|
**In scope**
|
||||||
|
|
||||||
|
- Copy/adapt from `agent-pipeline` L3 `_common` templates (manifest-aware paths).
|
||||||
|
- Document kickoff ritual in `.convoys/README.md`.
|
||||||
|
|
||||||
|
**Out of scope**
|
||||||
|
|
||||||
|
- Convoy lifecycle MCP (Epic 4).
|
||||||
|
- Creating convoy files for other consumer repos.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- None (bootstrap from agent-pipeline templates).
|
||||||
|
|
||||||
|
## Blocks
|
||||||
|
|
||||||
|
- Epic 4 (convoy lifecycle MCP) — operators need a place to write convoy files first.
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- [ ] `bash scripts/log-convoy-event.sh role=role-conductor convoy=test` appends valid JSONL.
|
||||||
|
- [ ] `role-conductor` can write `.convoys/<slug>.md` without conflicting rules.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
_To be decomposed by architect when epic graduates to `ready`._
|
||||||
228
plans/Plan-automation-foundation/Plan-automation-foundation.md
Normal file
228
plans/Plan-automation-foundation/Plan-automation-foundation.md
Normal file
|
|
@ -0,0 +1,228 @@
|
||||||
|
---
|
||||||
|
kind: plan
|
||||||
|
slug: automation-foundation
|
||||||
|
title: Automation foundation — mission control, model routing, orchestrator path
|
||||||
|
status: ready
|
||||||
|
priority: P1
|
||||||
|
tenant_id: global
|
||||||
|
cursor_plan_id: null
|
||||||
|
updated_at: "2026-07-07"
|
||||||
|
agent_prompt: |
|
||||||
|
You are implementing Echodo's automation foundation. Read docs/adr-model-routing.md
|
||||||
|
before touching model routing code. This repo uses pnpm, Drizzle, tRPC, zod in
|
||||||
|
packages/shared, and multitenancy via workspace_id on every tenant table.
|
||||||
|
|
||||||
|
Dogfooding (required for every task in this plan):
|
||||||
|
1. At session start: call MCP claim_task with this task's backlogItemId (import
|
||||||
|
plans/ if the row is missing).
|
||||||
|
2. At session end: call MCP complete_task with outcome, tokensTotal, and
|
||||||
|
modelAlias/modelTier when you used a routed model.
|
||||||
|
3. Stay within the task's acceptance criteria; do not widen scope.
|
||||||
|
4. Run pnpm lint && pnpm type-check && pnpm test before declaring done.
|
||||||
|
|
||||||
|
Stack: Next.js App Router, packages/database for schema, apps/web for UI.
|
||||||
|
Never store API keys in committed files or DB — only api_key_env_var names.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Plan overview
|
||||||
|
|
||||||
|
Echodo is evolving from a multitenant task app with MCP coordination into **mission control** for an agent pipeline: fleet view, human gates, per-role model routing, and (later) a built-in orchestrator and worker. This plan delivers the **first concrete slice** — the model provider registry and role routing system defined in [docs/adr-model-routing.md](../../docs/adr-model-routing.md).
|
||||||
|
|
||||||
|
Later epics in this plan (2–9) add convoy scaffolding, connected repos, lifecycle MCP, gates UI, orchestrator, worker, webhooks, and fleet dashboard. Epic 1 gives them a stable `resolveModel()` contract.
|
||||||
|
|
||||||
|
## Dogfooding the agent pipeline
|
||||||
|
|
||||||
|
**Yes — this plan is designed to dogfood the agent-pipeline end to end.**
|
||||||
|
|
||||||
|
| Pipeline artifact | How we use it |
|
||||||
|
|-------------------|---------------|
|
||||||
|
| L2 roles | Implementation runs as `role-implementer` per task brief (or ad hoc with implementer rules) |
|
||||||
|
| `claim_task` / `complete_task` | Every task session claims and completes via Echodo MCP |
|
||||||
|
| `agent_prompt` (plan → epic → task) | First-message context for agents; inheritance from this frontmatter |
|
||||||
|
| `docs/adr-model-routing.md` | Architect contract — agents read before coding |
|
||||||
|
| `.convoys/model-routing-registry.md` | **Created at kickoff** via `role-conductor` — tracks IA/UX/architecture for the initiative (not stored under `plans/`) |
|
||||||
|
| `model_policy` in convoy | Dogfoods alias-based routing (Claude planning, Qwen local) once registry exists |
|
||||||
|
| `scripts/log-convoy-event.sh` | Log each role completion to `.convoys/.metrics.jsonl` when L3 convoy scaffolding is present |
|
||||||
|
|
||||||
|
**Kickoff ritual (human + conductor):**
|
||||||
|
|
||||||
|
1. Prompt: *"Start a new convoy: Implement workspace-scoped model provider registry and role routing per docs/adr-model-routing.md. Success = operators can configure Anthropic + Ollama aliases and assign them to pipeline roles from Settings."*
|
||||||
|
2. Conductor writes `.convoys/model-routing-registry.md` with `classification: server-only` (skip ia, ux, design, a11y).
|
||||||
|
3. Architect decomposes into briefs that **map 1:1 to tasks in `Epic-model-provider-registry`** (below).
|
||||||
|
4. Each implementer session: `claim_task` on the matching `Task-*.md` → implement → `complete_task`.
|
||||||
|
|
||||||
|
## Getting started (operator checklist)
|
||||||
|
|
||||||
|
Do these **before or in parallel with** Epic 1 implementation. Nothing here blocks writing code; some items unblock dogfooding and `testProvider` against real endpoints.
|
||||||
|
|
||||||
|
### A. Repo and backlog (one time)
|
||||||
|
|
||||||
|
- [ ] `pnpm install` at repo root
|
||||||
|
- [ ] `.env` from `.env.example` — `DATABASE_URL`, `REDIS_URL`, `NEXTAUTH_*` at minimum
|
||||||
|
- [ ] `pnpm db:migrate` if schema is behind
|
||||||
|
- [ ] `pnpm --filter @tasks/database import:markdown-backlog` — loads `plans/Plan-automation-foundation/**` into DB for `claim_task`
|
||||||
|
- [ ] Confirm Echodo MCP in `.cursor/mcp.json` works (restart Cursor after MCP changes)
|
||||||
|
|
||||||
|
### B. Local inference on Mac (pick one or both)
|
||||||
|
|
||||||
|
See ADR § **Local MLX on Apple Silicon** and **Local Ollama**.
|
||||||
|
|
||||||
|
| Goal | Action |
|
||||||
|
|------|--------|
|
||||||
|
| Simplest local coder | Install [Ollama](https://ollama.com), `ollama pull qwen2.5-coder:32b`, listen on LAN if Coolify must reach Mac |
|
||||||
|
| Best Apple Silicon perf / MLX models | `pip install mlx-openai-server` (preferred if you expect **parallel implementers**), launch with `--host 0.0.0.0` |
|
||||||
|
| Quick MLX smoke test | `pip install mlx-lm` → `mlx_lm.server --model mlx-community/...` on port 8080 |
|
||||||
|
|
||||||
|
Note your Mac LAN IP and port for registry aliases later (`http://<ip>:8000/v1` or `:11434/v1`).
|
||||||
|
|
||||||
|
### C. Cloud / free-tier aliases (optional until Epic 1 UI exists)
|
||||||
|
|
||||||
|
Configure env vars on the machine that runs `apps/web` / worker (Coolify or local):
|
||||||
|
|
||||||
|
- `ANTHROPIC_API_KEY` — planning roles (or use NIM/Gemini free tier per ADR references)
|
||||||
|
- `GOOGLE_GENERATIVE_AI_API_KEY`, `NVIDIA_API_KEY` — optional free-tier planning/gates
|
||||||
|
|
||||||
|
Until Settings UI ships, `OPENAI_BASE_URL` + `OPENAI_API_KEY` still power the existing chat route.
|
||||||
|
|
||||||
|
### D. Agent-pipeline convoy (when you start building)
|
||||||
|
|
||||||
|
- [ ] Run **role-conductor** (Cursor): convoy for model registry per kickoff ritual above → `.convoys/model-routing-registry.md`
|
||||||
|
- [ ] **role-architect** → briefs map to Epic 1 tasks
|
||||||
|
- [ ] Epic 2 (optional parallel): install `.convoys/` scaffolding + `log-convoy-event.sh`
|
||||||
|
|
||||||
|
### E. First implementation task
|
||||||
|
|
||||||
|
Start with **`Task-shared-schemas-resolve-model`** — no DB migration, pure `packages/shared` + tests. Then migration, tRPC, UI.
|
||||||
|
|
||||||
|
### F. Not needed yet
|
||||||
|
|
||||||
|
- Forgejo / connected repos (Epic 3)
|
||||||
|
- Orchestrator or worker (Epics 6–7)
|
||||||
|
- LiteLLM (optional gateway later)
|
||||||
|
- Symphony
|
||||||
|
|
||||||
|
## Objectives and success criteria
|
||||||
|
|
||||||
|
- Workspace admins can CRUD model provider aliases (Anthropic, OpenAI, Google, OpenAI-compatible for Ollama/NIM/LiteLLM).
|
||||||
|
- Workspace admins can map any `role_id` (nine L2 defaults + custom) to a provider alias.
|
||||||
|
- `resolveModel()` implements ADR resolution order (brief → convoy → workspace → template).
|
||||||
|
- Settings UI: Providers + Role routing pages with connection test.
|
||||||
|
- `agent_runs` records `model_alias`, `provider_type`, `model_id`, `model_tier` on completion.
|
||||||
|
- Documented in ADR; `.env.example` updated with referenced env var names.
|
||||||
|
|
||||||
|
## Scope and boundaries
|
||||||
|
|
||||||
|
**In scope (full plan)**
|
||||||
|
|
||||||
|
- Nine epics — see [Epic roadmap](#epic-roadmap-complete) below.
|
||||||
|
- Epic 1 (model registry) is **ready** with six tasks; epics 2–9 are **draft** stubs for architect decomposition.
|
||||||
|
- ADR at `docs/adr-model-routing.md`.
|
||||||
|
|
||||||
|
**Out of scope (separate future plans)**
|
||||||
|
|
||||||
|
- Agent-pipeline Phase 2b deliverable templates.
|
||||||
|
- Agent-pipeline Phase 4 drift/rollup workers (replace GHA cron).
|
||||||
|
- LiteLLM deployment guide (operators use OpenAI-compatible alias).
|
||||||
|
- Symphony adoption.
|
||||||
|
|
||||||
|
## Cross-team collaborators
|
||||||
|
|
||||||
|
| Name / group | Role |
|
||||||
|
|--------------|------|
|
||||||
|
| Solo operator | Product, implement, dogfood pipeline |
|
||||||
|
|
||||||
|
## Epic roadmap (complete)
|
||||||
|
|
||||||
|
Sequential order with parallel opportunities noted. Only **Epic 1** has bead-scale tasks today; epics 2–9 are draft stubs ready for architect decomposition when the prior epic lands.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
E1[1 Model registry]
|
||||||
|
E2[2 Convoy scaffolding]
|
||||||
|
E3[3 Connected repos]
|
||||||
|
E4[4 Convoy lifecycle MCP]
|
||||||
|
E5[5 Gates UI]
|
||||||
|
E6[6 Orchestrator]
|
||||||
|
E7[7 Worker v1]
|
||||||
|
E8[8 Forge webhooks]
|
||||||
|
E9[9 Fleet dashboard]
|
||||||
|
|
||||||
|
E1 --> E6
|
||||||
|
E1 --> E7
|
||||||
|
E2 --> E4
|
||||||
|
E3 --> E6
|
||||||
|
E4 --> E5
|
||||||
|
E4 --> E6
|
||||||
|
E5 --> E6
|
||||||
|
E6 --> E7
|
||||||
|
E7 --> E8
|
||||||
|
E5 --> E9
|
||||||
|
E4 --> E9
|
||||||
|
```
|
||||||
|
|
||||||
|
| # | Epic | Status | Delivers | Depends on |
|
||||||
|
|---|------|--------|----------|------------|
|
||||||
|
| 1 | [Model provider registry](./Epic-model-provider-registry/Epic-model-provider-registry.md) | **ready** (6 tasks) | `resolveModel()`, Settings AI pages, run model logging | — |
|
||||||
|
| 2 | [Pipeline convoy scaffolding](./Epic-pipeline-convoy-scaffolding/Epic-pipeline-convoy-scaffolding.md) | draft | `.convoys/`, `log-convoy-event.sh`, `convoy-planning.mdc` — dogfood metrics | — (parallel with 1) |
|
||||||
|
| 3 | [Connected repos](./Epic-connected-repos/Epic-connected-repos.md) | draft | `repo_links` table, “paste remote URL”, forge adapter interface | — |
|
||||||
|
| 4 | [Convoy lifecycle MCP](./Epic-convoy-lifecycle-mcp/Epic-convoy-lifecycle-mcp.md) | draft | `create_convoy`, `transition_convoy_status`, state machine §7, `convoy_events` | 2, 3 |
|
||||||
|
| 5 | [Mission control gates UI](./Epic-mission-control-gates-ui/Epic-mission-control-gates-ui.md) | draft | Convoy kanban, gate inbox, approve/deny/revise, gate co-pilot chat | 4 |
|
||||||
|
| 6 | [Orchestrator](./Epic-apps-orchestrator/Epic-apps-orchestrator.md) | draft | `apps/orchestrator`, Redis queue, role dispatch, pause at gates | 1, 3, 4 |
|
||||||
|
| 7 | [Worker v1](./Epic-apps-worker-v1/Epic-apps-worker-v1.md) | draft | `apps/worker` minimal tool loop, worktree-per-brief, MCP claim/complete | 1, 3, 6 |
|
||||||
|
| 8 | [Forge webhooks](./Epic-forge-webhooks/Epic-forge-webhooks.md) | draft | PR opened/merged/CI webhooks → convoy transitions | 3, 4, 7 |
|
||||||
|
| 9 | [Fleet dashboard](./Epic-fleet-dashboard/Epic-fleet-dashboard.md) | draft | Cross-repo home, `convoys-*` workspaces, “needs you” rollup | 4, 5 |
|
||||||
|
|
||||||
|
### Overlap with existing plans
|
||||||
|
|
||||||
|
| Existing artifact | How this roadmap relates |
|
||||||
|
|-------------------|-------------------------|
|
||||||
|
| [Plan-agent-coordination](../Plan-agent-coordination/Plan-agent-coordination.md) | Epics 1 (logging) and 4–7 **extend** it; `claim_task` / `complete_task` stay canonical |
|
||||||
|
| [Epic-optional-orchestrator](../Plan-agent-coordination/Epic-optional-orchestrator/Epic-optional-orchestrator.md) | **Superseded in intent** by Epic 6 here — richer spec, Echodo-native, no Symphony dependency |
|
||||||
|
| Agent-pipeline v0.4 Phase 2a | Epic 4 implements the MCP tools described there |
|
||||||
|
| Agent-pipeline v0.4 Phase 4 workers | Out of scope until Epic 8+; drift/rollup is a future Plan, not this roadmap |
|
||||||
|
|
||||||
|
### End-state (what “done” looks like for the full plan)
|
||||||
|
|
||||||
|
Operator opens Echodo fleet view → starts convoy chat → hits **Go** → orchestrator runs pipeline roles with per-role models → worker executes briefs against connected repos → gates surface in inbox with AI co-pilot → approve → repeat until `released`.
|
||||||
|
|
||||||
|
## Epics (links)
|
||||||
|
|
||||||
|
| Epic | Link |
|
||||||
|
|------|------|
|
||||||
|
| 1 Model provider registry | `./Epic-model-provider-registry/Epic-model-provider-registry.md` |
|
||||||
|
| 2 Pipeline convoy scaffolding | `./Epic-pipeline-convoy-scaffolding/Epic-pipeline-convoy-scaffolding.md` |
|
||||||
|
| 3 Connected repos | `./Epic-connected-repos/Epic-connected-repos.md` |
|
||||||
|
| 4 Convoy lifecycle MCP | `./Epic-convoy-lifecycle-mcp/Epic-convoy-lifecycle-mcp.md` |
|
||||||
|
| 5 Mission control gates UI | `./Epic-mission-control-gates-ui/Epic-mission-control-gates-ui.md` |
|
||||||
|
| 6 Orchestrator | `./Epic-apps-orchestrator/Epic-apps-orchestrator.md` |
|
||||||
|
| 7 Worker v1 | `./Epic-apps-worker-v1/Epic-apps-worker-v1.md` |
|
||||||
|
| 8 Forge webhooks | `./Epic-forge-webhooks/Epic-forge-webhooks.md` |
|
||||||
|
| 9 Fleet dashboard | `./Epic-fleet-dashboard/Epic-fleet-dashboard.md` |
|
||||||
|
|
||||||
|
## Metrics and milestones
|
||||||
|
|
||||||
|
| Milestone | Epic | Metric |
|
||||||
|
|-----------|------|--------|
|
||||||
|
| M0 ADR | — | `docs/adr-model-routing.md` merged |
|
||||||
|
| M1 Model registry | 1 | Settings: configure + test Anthropic + Ollama aliases |
|
||||||
|
| M2 Dogfood loop | 1–2 | One task via claim_task → complete_task with model fields |
|
||||||
|
| M3 Repos connected | 3 | Paste Forge/GitHub URL; clone succeeds from Echodo |
|
||||||
|
| M4 Convoy state | 4 | `transition_convoy_status` enforces gate actors |
|
||||||
|
| M5 Human surface | 5 | Gate inbox shows pending `convoy_review` |
|
||||||
|
| M6 Automation | 6–7 | Orchestrator dispatches one implementer brief unattended |
|
||||||
|
| M7 PR loop | 8 | Merge webhook moves convoy to `merged` |
|
||||||
|
| M8 Fleet | 9 | Single view across ≥2 connected repos |
|
||||||
|
|
||||||
|
## Risks and mitigations
|
||||||
|
|
||||||
|
| Risk | Mitigation |
|
||||||
|
|------|------------|
|
||||||
|
| Mac Ollama unreachable from Coolify-hosted web | `testProvider` surfaces error; document LAN URL; dev can run web locally |
|
||||||
|
| Cursor slug drift in old convoys | Legacy alias map in ADR; conductor uses registry aliases for new convoys |
|
||||||
|
| Scope creep into orchestrator | ADR + plan explicitly defer daemon/worker |
|
||||||
|
|
||||||
|
## Links
|
||||||
|
|
||||||
|
- [ADR: Model routing](../../docs/adr-model-routing.md)
|
||||||
|
- [Plan: Agent coordination](../Plan-agent-coordination/Plan-agent-coordination.md)
|
||||||
|
- Agent-pipeline: `docs/model-routing-policy.md`, `docs/role-reference.md`
|
||||||
Loading…
Reference in a new issue