Submatter/.agents/skills/convoy-lifecycle/SKILL.md
Randall Stillwell 3fc7d01585 feat(worker): OpenHands session bootstrap + 5 Agent Skills
Phase 1: Add 5 SKILL.md files to .agents/skills/
  - backlog-sync: plans/ markdown ↔ DB contract
  - convoy-lifecycle: status machine, gates, MCP tools, checkpoints
  - model-routing: role→model via OmniRoute profiles + registry
  - code-review-two-axis: Standards vs Spec parallel sub-agent review
  - tdd-red-green: TDD loop with seams

Phase 2: OmniRoute operational docs
  - docs/omniroute-role-mapping.md: profile→role matrix, convoy examples
  - docs/omniroute-setup.md: step-by-step operator guide

Phase 3: Worker OpenHands integration
  - apps/worker/src/session/load-skills.ts: loads .agents/skills/
  - apps/worker/src/session/openhands-session.ts: agent-server REST adapter
  - apps/worker/src/executor/index.ts: createOpenHandsExecutor() factory
  - apps/worker/src/env.ts: WORKER_SESSION_TYPE, AGENT_SERVER_URL, SESSION_API_KEY
  - apps/worker/src/handle-job.ts: routes to OpenHands when configured
  - apps/worker/src/session/minimal-session.ts: injects skills into system prompt
  - packages/shared/src/worker/index.ts: re-export WorkerExecutorOptions
2026-09-07 15:10:02 -05:00

14 KiB

name description
convoy-lifecycle 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):

// 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:

---
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:

// 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:

-- 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:

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:

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:

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:

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:

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:

// 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):

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:

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:

---
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

## 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

---
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
  • 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