Add decompose + orchestrate skills with operational improvements
New skills for container agent task orchestration: - /decompose: break tasks into subtasks with dependency graph, write .agent-tasks.json - /orchestrate: check task state, launch container agents in git worktrees, poll for completion Updated with learnings from first real run (agent-runtimes M3, 9 tasks): - Prompts must end with "run pytest and fix failures" - State import conventions explicitly in prompts - Warn about uncommitted files before decomposing (worktrees need committed content) - Copy dependency outputs into downstream worktrees before launching Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
128
skills/decompose/SKILL.md
Normal file
128
skills/decompose/SKILL.md
Normal file
@@ -0,0 +1,128 @@
|
||||
---
|
||||
name: decompose
|
||||
description: >
|
||||
Decompose a task into subtasks with dependencies. Writes .agent-tasks.json for orchestration
|
||||
by container agents. Use /decompose followed by a task description or invoke mid-conversation.
|
||||
user_invocable: true
|
||||
allowed-tools: Read, Write, Edit, Glob, Grep, Bash(date *), Bash(cat *), Bash(hostname *), Bash(jq *), Bash(ls *), Bash(head *)
|
||||
---
|
||||
|
||||
# /decompose Skill
|
||||
|
||||
<command-name>decompose</command-name>
|
||||
|
||||
You are decomposing a task into subtasks that can be executed by container agents in parallel.
|
||||
|
||||
## Pre-gathered context
|
||||
|
||||
### Current date
|
||||
!`date +%Y-%m-%dT%H:%M:%S`
|
||||
|
||||
### Hostname
|
||||
!`hostname`
|
||||
|
||||
### Existing task state
|
||||
!`cat .agent-tasks.json 2>/dev/null || echo "No task state file yet"`
|
||||
|
||||
### Project context
|
||||
!`cat CLAUDE.md 2>/dev/null | head -100 || echo "No CLAUDE.md"`
|
||||
!`cat SPEC.md 2>/dev/null | head -50 || echo "No SPEC.md"`
|
||||
!`ls spec/ 2>/dev/null || echo "No spec directory"`
|
||||
|
||||
## Instructions
|
||||
|
||||
### Step 1: Understand the task
|
||||
|
||||
Read `$ARGUMENTS` for the task description. If empty, ask the user what task to decompose.
|
||||
|
||||
Also read the current conversation context — the user may have been discussing the task before invoking this skill.
|
||||
|
||||
Read any relevant project files (PLAN.md, SPEC.md, spec/, CLAUDE.md) to understand the project structure and what work is needed.
|
||||
|
||||
### Step 2: Decompose into subtasks
|
||||
|
||||
Break the task into **independently executable subtasks**. Each subtask must be:
|
||||
|
||||
- **Self-contained**: A container agent with access to the project can complete it without human input
|
||||
- **Scoped**: One clear deliverable (a spec file, a test file, an implementation file)
|
||||
- **Testable**: Success can be verified (file exists, tests pass, etc.)
|
||||
|
||||
For each subtask, determine:
|
||||
- A short unique ID (e.g., `spec-harness`, `test-composition`, `impl-resolver`)
|
||||
- A human-readable name
|
||||
- Which other subtasks it depends on (by ID)
|
||||
- The full prompt that a container agent would receive
|
||||
- Which project files it needs to read
|
||||
- Which files it will create or modify
|
||||
|
||||
**Guidelines:**
|
||||
- Prefer many small tasks over few large tasks
|
||||
- Tasks that can run in parallel SHOULD NOT depend on each other
|
||||
- Include "read these files first" in each task's prompt
|
||||
- Be explicit about what output is expected (file paths, test names)
|
||||
- **End every prompt with:** "Run `pytest tests/ -v --tb=short` and fix any failures before finishing. Write a session log to memory/log/ when done."
|
||||
- **State import conventions explicitly** in prompts — e.g., "Use `from module import X`, not `from .module import X`" when source dirs aren't packages
|
||||
- **Before writing .agent-tasks.json, warn the user to commit WIP** if there are untracked/uncommitted files that agents will need. Worktrees only see committed content.
|
||||
|
||||
### Step 3: Present the task graph
|
||||
|
||||
Show the user the decomposition as a dependency graph:
|
||||
|
||||
```
|
||||
task-a (no deps) ─┐
|
||||
task-b (no deps) ─┼─► task-d (depends: a, b)
|
||||
task-c (no deps) ─┘ │
|
||||
▼
|
||||
task-e (depends: d)
|
||||
```
|
||||
|
||||
Also show a table:
|
||||
|
||||
| ID | Name | Depends On | Writes | Est. Complexity |
|
||||
|----|------|-----------|--------|-----------------|
|
||||
| ... | ... | ... | ... | low/medium/high |
|
||||
|
||||
### Step 4: Get user approval
|
||||
|
||||
Ask the user:
|
||||
1. Does the decomposition look right? They may want to merge, split, or reorder tasks.
|
||||
2. **How many concurrent agents should run?** Suggest a number based on the task graph width (max parallel tasks at any level). Note that each agent uses subscription tokens — more agents = faster but uses allocation quicker.
|
||||
|
||||
### Step 5: Write the task state file
|
||||
|
||||
Once approved, write `.agent-tasks.json` in the project root:
|
||||
|
||||
```json
|
||||
{
|
||||
"created_at": "<ISO timestamp>",
|
||||
"project": "<project name from directory>",
|
||||
"description": "<original task description>",
|
||||
"max_concurrent": <user-approved number>,
|
||||
"tasks": {
|
||||
"<task-id>": {
|
||||
"name": "<human readable name>",
|
||||
"prompt": "<full prompt for container agent>",
|
||||
"depends_on": ["<task-id>", ...],
|
||||
"reads": ["<file paths the agent should read>"],
|
||||
"writes": ["<file paths the agent will create/modify>"],
|
||||
"status": "pending",
|
||||
"branch": null,
|
||||
"worktree": null,
|
||||
"host": null,
|
||||
"container_id": null,
|
||||
"started_at": null,
|
||||
"completed_at": null,
|
||||
"exit_code": null,
|
||||
"error": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Status values:** `pending`, `running`, `completed`, `failed`, `blocked`
|
||||
|
||||
**Worktree fields:**
|
||||
- `branch`: The git branch name for this task (e.g., `agent/<task-id>`)
|
||||
- `worktree`: The worktree path (e.g., `.worktrees/<task-id>`)
|
||||
|
||||
Tell the user: "Task state written to `.agent-tasks.json`. Run `/orchestrate` to start executing tasks, or `/loop 2m /orchestrate` to auto-poll."
|
||||
173
skills/orchestrate/SKILL.md
Normal file
173
skills/orchestrate/SKILL.md
Normal file
@@ -0,0 +1,173 @@
|
||||
---
|
||||
name: orchestrate
|
||||
description: >
|
||||
Check agent task state and launch container agents for ready tasks. Designed for use
|
||||
with /loop (e.g., /loop 2m /orchestrate) to auto-dispatch work to container agents.
|
||||
user_invocable: true
|
||||
allowed-tools: Read, Write, Edit, Bash(date *), Bash(cat *), Bash(hostname *), Bash(docker *), Bash(jq *), Bash(git *)
|
||||
---
|
||||
|
||||
# /orchestrate Skill
|
||||
|
||||
<command-name>orchestrate</command-name>
|
||||
|
||||
You are the task orchestrator. Check task state, update statuses, and launch container agents for tasks that are ready to run. Each task runs in its own git worktree for isolation.
|
||||
|
||||
## Pre-gathered context
|
||||
|
||||
### Current date
|
||||
!`date +%Y-%m-%dT%H:%M:%S`
|
||||
|
||||
### Hostname
|
||||
!`hostname`
|
||||
|
||||
### Task state
|
||||
!`cat .agent-tasks.json 2>/dev/null || echo "NO_TASK_FILE"`
|
||||
|
||||
## Instructions
|
||||
|
||||
If the task state file doesn't exist, say "No .agent-tasks.json found. Run /decompose first." and stop.
|
||||
|
||||
### Step 1: Check running containers
|
||||
|
||||
For each task with `status: "running"`, check if the container is still alive:
|
||||
|
||||
```bash
|
||||
docker inspect --format '{{.State.Status}}' <container_id> 2>/dev/null
|
||||
```
|
||||
|
||||
- If container status is `exited`: get exit code with `docker inspect --format '{{.State.ExitCode}}'`
|
||||
- Exit code 0 → set task status to `completed`, record `completed_at` and `exit_code`
|
||||
- Exit code non-zero → set task status to `failed`, record `exit_code` and capture last 20 lines of logs with `docker logs --tail 20 <container_id>`, store in `error`
|
||||
- If container not found (removed or wrong host): mark as `failed` with error "Container not found"
|
||||
- If container still running: leave as `running`
|
||||
|
||||
**On task completion (success or failure):**
|
||||
- Do NOT remove the worktree yet — the user may want to review changes on the branch
|
||||
- For successful tasks, note that `branch` contains the changes
|
||||
|
||||
### Step 2: Identify ready tasks
|
||||
|
||||
A task is **ready** when:
|
||||
- Its status is `pending`
|
||||
- ALL tasks in its `depends_on` list have status `completed`
|
||||
|
||||
If a dependency has status `failed`, mark the dependent task as `blocked` (it cannot proceed).
|
||||
|
||||
### Step 3: Launch ready tasks
|
||||
|
||||
Respect `max_concurrent` from `.agent-tasks.json`. Count tasks with `status: "running"` — if at the limit, skip launching and wait for the next cycle.
|
||||
|
||||
For each ready task:
|
||||
|
||||
#### 3a. Create a git worktree
|
||||
|
||||
Determine the branch point:
|
||||
- If the task has no dependencies, branch from the current HEAD: `git worktree add .worktrees/<task-id> -b agent/<task-id>`
|
||||
- If the task depends on one completed task, branch from that task's branch: `git worktree add .worktrees/<task-id> -b agent/<task-id> agent/<dep-task-id>`
|
||||
- If the task depends on multiple completed tasks, create a merge base first:
|
||||
```bash
|
||||
git branch agent/<task-id>-base
|
||||
git checkout agent/<task-id>-base
|
||||
git merge --no-edit agent/<dep-1> agent/<dep-2> ...
|
||||
git checkout - # back to original branch
|
||||
git worktree add .worktrees/<task-id> -b agent/<task-id> agent/<task-id>-base
|
||||
```
|
||||
|
||||
Record the `branch` and `worktree` path in the task state.
|
||||
|
||||
#### 3b. Copy dependency outputs into the worktree
|
||||
|
||||
For tasks with dependencies, the dependency's output files (listed in `writes`) are likely untracked in git — they exist only in the dependency's worktree. Copy them into the new worktree:
|
||||
|
||||
```bash
|
||||
# For each completed dependency:
|
||||
for dep_task_id in <depends_on>; do
|
||||
# Copy the files that dep_task listed in its "writes" field
|
||||
for file in <dep_task.writes>; do
|
||||
cp .worktrees/<dep_task_id>/$file .worktrees/<task-id>/$file
|
||||
done
|
||||
done
|
||||
```
|
||||
|
||||
Also copy any untracked files from the main tree that the task needs (listed in `reads`):
|
||||
- Spec files, plan files, etc. that may not be committed
|
||||
- Check with `git status` whether files exist in the worktree; copy from main tree if missing
|
||||
|
||||
This step is critical — worktrees only contain committed content. Without it, agents cannot read dependency outputs or untracked project files.
|
||||
|
||||
#### 3c. Launch the container
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
-e CLAUDE_CODE_OAUTH_TOKEN="<token>" \
|
||||
-e ENFORCE_SUBSCRIPTION_PRICING=true \
|
||||
-v <absolute_worktree_path>:/project \
|
||||
-w /project \
|
||||
--entrypoint uid-wrapper.sh \
|
||||
agent-claude:latest \
|
||||
claude --print --dangerously-skip-permissions --model claude-sonnet-4-20250514 "<task prompt>"
|
||||
```
|
||||
|
||||
**Important:**
|
||||
- Use `-d` (detached) so the container runs in the background — do NOT use `--rm` so we can inspect logs after exit
|
||||
- Mount the **worktree path** (not the main project) as `/project`
|
||||
- Capture the container ID from docker run output
|
||||
- The CLAUDE_CODE_OAUTH_TOKEN must be available in the current environment. If not set, read it from `~/dev/claude/secrets/claude/long_lived_oauth_token` (extract the value after `value: `)
|
||||
- If the task's `reads` list references paths outside the project (e.g., `/foundations` for best practices), mount those as additional read-only volumes
|
||||
|
||||
After launching, update the task in `.agent-tasks.json`:
|
||||
- Set `status` to `running`
|
||||
- Set `host` to the current hostname
|
||||
- Set `container_id` to the docker container ID (full ID, not short)
|
||||
- Set `started_at` to current ISO timestamp
|
||||
- Set `branch` and `worktree` to the values from step 3a
|
||||
|
||||
### Step 4: Report status
|
||||
|
||||
Print a concise status summary:
|
||||
|
||||
```
|
||||
Agent Tasks — <project name>
|
||||
─────────────────────────────
|
||||
completed task-a: Write payload spec ✓ (branch: agent/task-a)
|
||||
completed task-b: Write entrypoint spec ✓ (branch: agent/task-b)
|
||||
running task-c: Write harness spec (container abc123..., 3m elapsed)
|
||||
pending task-d: Implement resolver (waiting on: task-c)
|
||||
blocked task-e: Integration tests (blocked by failed: task-f)
|
||||
failed task-f: Build images (exit code 1)
|
||||
─────────────────────────────
|
||||
3/6 complete | 1 running | 1 pending | 1 blocked
|
||||
Concurrency: 1/3 slots used
|
||||
```
|
||||
|
||||
### Step 5: Update task state file
|
||||
|
||||
Write the updated `.agent-tasks.json` with all status changes.
|
||||
|
||||
### Step 6: Merge completed branches (when all done)
|
||||
|
||||
When ALL tasks are resolved (no `pending` or `running` remaining):
|
||||
|
||||
1. Print "All tasks resolved."
|
||||
2. List completed task branches with a summary of what each contains
|
||||
3. Suggest the user review and merge: `git merge agent/<task-id>` for each, or `git merge agent/<task-1> agent/<task-2> ...` for an octopus merge
|
||||
4. Note that worktrees can be cleaned up with: `git worktree remove .worktrees/<task-id>`
|
||||
5. Suggest cancelling the loop if running via `/loop`
|
||||
|
||||
Do NOT auto-merge — the user should review and decide.
|
||||
|
||||
### Automation Notes
|
||||
|
||||
- This skill is designed to be called repeatedly via `/loop 2m /orchestrate`
|
||||
- Each invocation is stateless — it reads .agent-tasks.json, checks containers, updates, and exits
|
||||
- Keep output concise when called in a loop — just the status table unless something changed
|
||||
- On first run, if `CLAUDE_CODE_OAUTH_TOKEN` is not in the environment, read it once and export it for subsequent runs
|
||||
|
||||
### Error Recovery
|
||||
|
||||
- If a task fails, its dependents are marked `blocked`. The user can:
|
||||
- Fix the issue in the worktree and re-run: set status back to `pending` in .agent-tasks.json
|
||||
- Skip the task: manually mark dependents as `pending` and adjust their branch points
|
||||
- If a worktree creation fails (e.g., branch already exists): `git branch -D agent/<task-id>` and retry
|
||||
- Stale containers (running > 30 minutes with no output): flag in status report but don't kill automatically
|
||||
Reference in New Issue
Block a user