Submatter/.agents/skills/convoy-lifecycle/SKILL.md

440 lines
14 KiB
Markdown
Raw Permalink Normal View History

---
name: convoy-lifecycle
description: Work with the convoy system - understand status transitions, gate actors, MCP tools, and checkpoint/resume. Use when creating/managing convoys, transitioning status, logging events, or implementing the worker harness.
---
# Convoy Lifecycle
Convoys are the pipeline execution model for Echodo. A convoy groups related briefs into a cohesive unit with a success metric, model policy, and human gates. This skill teaches you the status machine, actor rules, MCP tools, and how the orchestrator drives the flow.
## When to Activate
Use this skill when:
- Creating or managing `.convoys/*.md` files
- Transitioning convoy status (draft → ready → merged, etc.)
- Understanding gate actors and what they can/cannot do
- Implementing the worker harness (Epic 7)
- Logging events to `convoy_events` or `agent_runs`
- Checking checkpoint/resume behavior
- Debugging convoy state issues
## Core Rules
### 1. Status Machine (The Contract)
Convoys have a strict status lifecycle. Transitions are enforced by the server.
**Code flow (from → to):**
```
draft ──→ convoy_review ──→ ready ──→ in_progress ──→ pr_review ──→ merged ──→ released
↑ │ ↑
│ │ │
│ └──→ blocked └──→ blocked
│ ↑
└──→ cancelled └──→ cancelled
```
**Non-code variant (docs/SOPs):**
```
draft ──→ convoy_review ──→ ready ──→ in_progress ──→ doc_review ──→ published ──→ released
↑ │ ↑
│ └──→ blocked └──→ blocked
│
└──→ cancelled
```
**Terminal states:** `released`, `cancelled`
**Exceptional state:** `blocked` (can resume from most states)
### 2. Actor Rules (Who Can Do What)
The `ALLOWED_TRANSITIONS` table in `packages/shared/src/convoy-lifecycle/state-machine.ts` defines explicit actor permissions. **Pipeline roles cannot perform gate transitions.**
| Actor | Can Do |
|-------|--------|
| `role-architect` | Submit draft to convoy_review |
| `role-conductor` | Cancel convoy, unblock from blocked |
| `role-implementer` | Submit ready to in_progress, move pr_review back to in_progress, block/unblock |
| `human-gate-1` | Approve/reject convoy_review → ready, block/unblock, cancel |
| `human-gate-2` | Approve pr_review → merged, doc_review → published, block/unblock, cancel |
| `human-gate-3` | Approve merged → released |
| `doc-approver` | Alias for human-gate-2 on doc flows |
| `automation` | Orchestrator-driven transitions (checkpoint resume, stall recovery) |
**Key principle:** `role-architect` ≠ human-gate-1. The conductor/architect roles plan and kick off; humans approve and release.
### 3. Gate Actors are Not Pipeline Roles
This is a common mistake. Do NOT let pipeline roles perform gates:
- ❌ `role-implementer` cannot approve PR merge (must be `human-gate-2`)
- ❌ `role-architect` cannot release to production (must be `human-gate-3`)
- ❌ `role-conductor` cannot approve convoy_review (must be `human-gate-1`)
**Exception:** The orchestrator runs as `automation` actor for internal transitions (checkpoint resume, stall recovery).
### 4. Reason Requirement
Some transitions require a `reason` string (e.g., blocking, cancelling, unblocking):
```typescript
// From state-machine.ts
{ from: "blocked", to: "ready", actors: ["human-gate-1", "role-conductor"], requiresReason: true }
```
When calling MCP `transition_convoy_status` for these, pass `reason: "blocking for redesign"`.
### 5. File-Based Convoys (`.convoys/*.md`)
Convoys are defined in markdown with YAML frontmatter. Location: `.convoys/<slug>.md`.
**Frontmatter structure:**
```yaml
---
name: <slug>
classification: server-only | web | full-stack
success_metric: One sentence explaining what "done" looks like
skip:
- ia # skip IA review
- ux # skip UX review
- design
- a11y
- visual
- smoke
status: draft | in_progress | merged | shipped | abandoned
created: YYYY-MM-DD
model_policy:
roles:
role-conductor: claude-planning
role-architect: claude-planning
role-implementer: qwen-coder
role-reviewer: qwen-coder
escalate_to: claude-planning
---
```
**Body sections:**
- **Why:** Problem statement and motivation
- **Scope:** What's in/out
- **Roles invoked:** Who runs which phases
- **Todos:** Brief mapping
- **Architecture:** Technical notes
**Briefs live in:** `.convoys/<slug>/brief-<N>-<title>.md`
### 6. Checkpoint/Resume (Worker Feature)
When a worker job runs, it can checkpoint state to `worker_checkpoints`:
```typescript
// Checkpoint payload (example from docs/worker.md)
{
toolHistory: [{ tool: "read_file", input: { path: "..." }, output: "..." }],
status: "in_progress",
lastCheckpointAt: "2026-08-12T10:00:00Z"
}
```
**Resume behavior:**
- Worker kills → orchestrator detects stall (`ORCHESTRATOR_STALL_MINUTES` default 120)
- Next dequeue reads checkpoint for same job → resumes from where it left off
- `toolHistory` enables context replay without re-running everything
### 7. Events Table (`convoy_events`)
Every transition and role completion is logged to `convoy_events`:
```sql
-- Example log entries
| id | convoy_slug | event_type | actor | metadata |
|----|-------------|-----------------|---------------|-------------------------------|
| 1 | feature-x | status_change | role-architect| { from: "draft", to: "ready" }|
| 2 | feature-x | role_complete | role-architect| { brief: "brief-1", ... } |
| 3 | feature-x | role_complete | role-implementer| { brief: "brief-2", model: ...}|
| 4 | feature-x | status_change | human-gate-2 | { from: "pr_review", to: "merged" } |
```
**Event types:**
- `status_change` — transition from one convoy status to another
- `role_complete` — a brief finished with metrics (model, tokens, duration)
- `stall_recovered` — orchestrator resumed from checkpoint
- `gate_action` — human approved/requested changes
## MCP Tools
### create_convoy
**Purpose:** Create a new convoy at `draft` status with metadata.
**Signature:**
```typescript
create_convoy({
workspace: string, // Workspace UUID or slug
slug: string, // Convoy slug (kebab-case)
title: string,
successMetric: string, // Frontmatter success_metric
classification: string, // server-only | web | full-stack
skip?: string[], // Optional: ["ia", "ux", "design"]
modelPolicy?: { // Optional: set model routes
roles: Record<string, string>, // role -> alias
escalateTo?: string,
}
})
```
**Returns:** Convoy object with ID, slug, current status (`draft`), created_at.
**Example usage:**
```
"Create a convoy called 'test-omniroute-integration' with success metric 'OmniRoute routes all 5 roles successfully'. Classification: server-only. Skip IA, UX, design."
```
### transition_convoy_status
**Purpose:** Move convoy between statuses. Enforces actor rules + reason requirements.
**Signature:**
```typescript
transition_convoy_status({
workspace: string,
convoySlug: string,
fromStatus: string,
toStatus: string,
actor: string, // role-architect, human-gate-1, etc.
reason?: string, // Required for block/unblock/cancel
})
```
**Returns:** Updated convoy status, event log entry.
**Example usage:**
```
"Transition convoy 'test-e2e-autonomous' from 'ready' to 'in_progress'. Actor: role-implementer."
```
### log_convoy_event
**Purpose:** Append a role completion or custom event to `convoy_events`.
**Signature:**
```typescript
log_convoy_event({
workspace: string,
convoySlug: string,
eventType: string, // role_complete, gate_action, etc.
actor: string, // Who performed the event
metadata: Record<string, unknown>, // Additional context
})
```
**Example usage:**
```
"Log role_complete for convoy 'test-e2e-autonomous', actor: role-implementer, brief: brief-2-implement-feature, model: qwen-coder, tokens: 12345"
```
### query_manifest_status
**Purpose:** Get convoy status, briefs, and recent events for Mission Control UI.
**Signature:**
```typescript
query_manifest_status({
workspace: string,
convoySlug?: string, // Optional: filter to one convoy
})
```
**Returns:** Convoy object with status, briefs (with status), recent events.
### reconcile_from_files
**Purpose:** Import `.convoys/*.md` into the DB and report pending sync outbox.
**Signature:**
```typescript
reconcile_from_files({
repoPath?: string, // Optional: scan specific path
})
```
**Returns:** Summary of created/updated convoys, any sync errors.
## Common Patterns
### Creating a Convoy
1. **Define the convoy file:** `.convoys/feature-x.md`
2. **Write frontmatter:** name, classification, success_metric, model_policy
3. **Write body:** Why, Scope, Roles, Todos
4. **Create briefs:** `.convoys/feature-x/brief-1-*.md` for each task
5. **Import to DB:** `reconcile_from_files` or `create_convoy` MCP
6. **Start work:** `transition_convoy_status` to `convoy_review`
### Running Through Gates
1. **Draft → Convoy Review:** `role-architect` submits (or `create_convoy` with status)
2. **Convoy Review:** `human-gate-1` approves (→ ready) or rejects (→ draft)
3. **Ready → In Progress:** `role-implementer` starts work
4. **In Progress → PR Review:** `role-implementer` submits code
5. **PR Review:** `human-gate-2` approves (→ merged) or requests changes (→ in_progress)
6. **Merged → Released:** `human-gate-3` promotes to production
### Logging Role Completion
When an agent finishes a brief:
```typescript
// In worker, after complete_task MCP
await log_convoy_event({
workspace: workspaceId,
convoySlug: convoy.slug,
eventType: "role_complete",
actor: roleId, // e.g., "role-implementer"
metadata: {
briefSlug: brief.slug,
model: modelSpec.alias, // e.g., "qwen-coder"
modelTier: tier, // "coding" or "coding-pro"
tokensIn: tokens.input,
tokensOut: tokens.output,
durationMs: duration,
outcome: "success" | "failure",
}
});
```
### Handling Blocks
To block a convoy (any gate actor or role-implementer):
```typescript
await transition_convoy_status({
workspace: workspaceId,
convoySlug: "feature-x",
fromStatus: "in_progress",
toStatus: "blocked",
actor: "human-gate-2", // or role-implementer
reason: "Design changed - need API redesign before continuing"
});
```
To unblock:
```typescript
await transition_convoy_status({
workspace: workspaceId,
convoySlug: "feature-x",
fromStatus: "blocked",
toStatus: "in_progress",
actor: "human-gate-2", // or role-conductor
reason: "API redesign complete"
});
```
## Examples
### Example 1: Simple Code Convoy
**`.convoys/add-auth-endpoint.md`:**
```yaml
---
name: add-auth-endpoint
classification: server-only
success_metric: POST /api/auth/register returns 201 and creates user row
skip:
- ia
- ux
- design
status: draft
created: 2026-08-12
model_policy:
roles:
role-architect: claude-planning
role-implementer: qwen-coder
role-reviewer: qwen-coder
---
```
**Brief:** `.convoys/add-auth-endpoint/brief-1-implement.md`
```markdown
## Description
Implement the POST /api/auth/register endpoint.
## Scope
- Create route handler at apps/web/app/api/auth/register/route.ts
- Add validation with zod
- Call database insert
- Return 201 on success, 400 on validation error
## Acceptance Criteria
- [ ] Route file exists at correct path
- [ ] zod schema validates email/password
- [ ] Database insert creates user row
- [ ] pnpm lint && pnpm type-check pass
- [ ] Brief is done
```
### Example 2: Doc-Only Convoy
```yaml
---
name: write-sops
classification: server-only
success_metric: 5 SOP documents are published under docs/sops/
skip:
- ia
- ux
- design
- visual
- smoke
status: draft
created: 2026-08-12
model_policy:
roles:
role-doc-writer: auto/coding
---
Use the template at docs/templates/doc-sop.md to write SOPs for:
- How to run a conveyor
- How to add a new model provider
- How to configure OmniRoute
- How to add a new harness
```
### Example 3: Status Transition Flow
```
1. Architect submits: transition(ready → convoy_review) by role-architect
2. Gate approves: transition(convoy_review → ready) by human-gate-1
3. Implementer starts: transition(ready → in_progress) by role-implementer
4. Implementer submits: transition(in_progress → pr_review) by role-implementer
5. Gate merges: transition(pr_review → merged) by human-gate-2
6. Gate releases: transition(merged → released) by human-gate-3
```
## References
- **State machine:** `packages/shared/src/convoy-lifecycle/state-machine.ts`
- **Schemas:** `packages/shared/src/convoy-lifecycle/schemas.ts`
- **Kanban view:** `packages/shared/src/convoy-lifecycle/kanban.ts`
- **Tests:** `packages/shared/src/convoy-lifecycle/state-machine.test.ts`
- **MCP tools:** `apps/mcp-server/src/tools/create-convoy.ts`, `transition_convoy_status.ts`, `log_convoy_event.ts`
- **Worker docs:** `docs/worker.md`
- **Orchestrator docs:** `docs/orchestrator.md`
- **Convoy lifecycle doc:** `docs/convoy-lifecycle.md`
- **Example convoys:** `.convoys/second-brain-os-v01.md`, `.convoys/polish-dag-tiers-cost.md`
## Related Skills
- `backlog-sync` — when briefs map to markdown_backlog_items
- `model-routing` — how model_policy aliases resolve via omniroute
- `openhands-worker` — worker implementation that uses this lifecycle
- `code-review-two-axis` — what role-reviewer does during pr_review