$crank - Autonomous Epic Execution (Codex Native)
Quick Ref: Execute every open issue in an epic via wave-based workers usingspawn_agent,wait_agent,send_input, andclose_agent. Output: closed issues + final validation.
You must execute this workflow. Do not just describe it.
Architecture
Crank (lead agent)
|
+-> bd ready (current wave)
|
+-> Build a wave task packet
|
+-> spawn_agent per issue (worker or explorer role)
|
+-> wait_agent for all worker ids
|
+-> Validate results + bd update
|
+-> Loop until epic DONE
Backend Rules
- Prefer Codex session agents when
spawn_agentis available. - Use
agent_type=workerfor implementation agents andagent_type=explorerfor discovery agents when the runtime exposes roles. - Use
send_inputonly for short steering or retry prompts. - Use
close_agentfor stalled or unnecessary agents. - Never depend on legacy CSV fan-out or host-task result polling. Use
spawn_agent,wait_agent,send_input, andclose_agentinstead.
Codex Lifecycle Guard
When this skill runs in Codex hookless mode (CODEX_THREAD_ID is set or CODEX_INTERNAL_ORIGINATOR_OVERRIDE is Codex Desktop), ensure startup context before the first wave:
ao codex ensure-start 2>/dev/null || true
ao codex ensure-start is the single startup guard for Codex skills. It records startup once per thread and skips duplicate startup automatically. Leave ao codex ensure-stop to closeout skills after the implementation wave ends.
Flags
| Flag | Default | Description | |------|---------|-------------| | --test-first | off | SPEC -> TEST -> IMPL wave sequence. Workers classify tests by pyramid level (L0-L3) per the test pyramid standard (test-pyramid.md in the standards skill). When $plan includes test_levels metadata, carry it into metadata.validation.test_levels. |
Global Limits
MAX_EPIC_WAVES = 50 (hard limit). Typical epics use 5-10 waves.
Completion Enforcement (Sisyphus Rule)
After each wave, output one of:
<promise>DONE</promise>- epic complete, all issues closed<promise>BLOCKED</promise>- cannot proceed, with reason<promise>PARTIAL</promise>- incomplete, with remaining items
Never claim completion without the marker.
Node Repair Operator
When a task fails during wave execution, classify as RETRY (transient — re-add with adjustment, max 2), DECOMPOSE (too complex — split into sub-issues, terminal), or PRUNE (blocked — escalate immediately). Budget: 2 per task.
Mutation logging on failure: DECOMPOSE logs task_removed + task_added per sub-task. PRUNE logs task_removed. RETRY logs nothing (task identity unchanged).
Execution Steps
Given $crank [epic-id | .agents/rpi/execution-packet.json | plan-file.md | "description"]:
Step 0: Load Knowledge Context
if command -v ao &>/dev/null; then
ao lookup --query "<epic-title>" --limit 5 2>/dev/null || true
ao ratchet status 2>/dev/null || true
fi
Apply retrieved knowledge: If learnings are returned, check each for applicability to this epic. For applicable learnings, treat as implementation constraints and cite by filename. Record citations with the correct type: ao metrics cite "<path>" --type applied when the learning influenced a decision, or --type retrieved when loaded but not referenced.
Section evidence: When lookup results include section_heading, matched_snippet, or match_confidence fields, prefer the matched section over the whole file — it pinpoints the relevant portion. Higher match_confidence (>0.7) means the section is a strong match; lower values (<0.4) are weaker signals. Use the matched_snippet as the primary context rather than reading the full file.
Step 0.5: Detect Tracking Mode
if bd ready --json >/dev/null 2>&1 && bd list --type epic --status open --json >/dev/null 2>&1; then
TRACKING_MODE="beads"
else
TRACKING_MODE="tasklist"
fi
Step 0.6: Initialize Shared Task Notes
Create the shared notes file for cross-wave context persistence. See references/shared-task-notes.md for the full pattern.
mkdir -p .agents/crank
cat > .agents/crank/SHARED_TASK_NOTES.md <<EOF
# Shared Task Notes — Epic ${EPIC_ID:-unknown}
> Cross-wave context for workers. Read before starting. Report discoveries in task output.
> Maintained by the crank orchestrator — workers do NOT write to this file directly.
EOF
Step 0.7: Initialize Plan Mutation Audit Trail
Create the JSONL file that tracks every plan mutation during execution. See references/plan-mutations.md for the full schema and mutation budget.
mkdir -p .agents/rpi
: > .agents/rpi/plan-mutations.jsonl
# Budget counters
MUTATION_TASK_ADDED=0
MUTATION_TASK_ADDED_LIMIT=5
MUTATION_TASK_REORDERED=0
MUTATION_TASK_REORDERED_LIMIT=3
Helper function:
log_plan_mutation() {
local mutation_type="$1" task_id="$2" before="$3" after="$4"
local ts
ts=$(date -Iseconds)
if [[ "$mutation_type" == "task_added" ]]; then
MUTATION_TASK_ADDED=$((MUTATION_TASK_ADDED + 1))
if [[ $MUTATION_TASK_ADDED -gt $MUTATION_TASK_ADDED_LIMIT ]]; then
echo "WARN: task_added budget exceeded ($MUTATION_TASK_ADDED/$MUTATION_TASK_ADDED_LIMIT). Consider re-running $plan."
fi
elif [[ "$mutation_type" == "task_reordered" ]]; then
MUTATION_TASK_REORDERED=$((MUTATION_TASK_REORDERED + 1))
if [[ $MUTATION_TASK_REORDERED -gt $MUTATION_TASK_REORDERED_LIMIT ]]; then
echo "WARN: task_reordered budget exceeded ($MUTATION_TASK_REORDERED/$MUTATION_TASK_REORDERED_LIMIT)."
fi
fi
echo "{\"timestamp\":\"$ts\",\"wave\":$wave,\"task_id\":\"$task_id\",\"mutation_type\":\"$mutation_type\",\"before\":$before,\"after\":$after}" \
>> .agents/rpi/plan-mutations.jsonl
}
Mutation types: task_added, task_removed, task_reordered, scope_changed, dependency_changed.
Step 1: Identify the Execution Target
Beads mode:
- If epic ID provided: use it directly
- If no epic ID:
bd list --type epic --status open 2>/dev/null | head -5
Execution-packet/file mode:
- If the input is
.agents/rpi/execution-packet.json, readobjective,epic_id,tracker_mode,done_criteria, andvalidation_commands - If
epic_idexists inside the execution packet, keep that epic as the execution spine - If
epic_idis absent, keep the packetobjectiveas the execution spine and continue in file-backed mode instead of inventing an epic ID - For other plan files, read the plan file and extract tasks
Step 1.5: Branch Isolation Gate
Before wave-1 commit, refuse to crank on main/master. Cut crank/<epic-id> to prevent parallel-session reset clobbers. See [references/branch-isolation.md](references/branch-isolation.md) for the gate script and override flag.
Step 2: Load Execution Details
Beads mode:
bd show <epic-id> 2>/dev/null
Execution-packet/file mode:
- Read the packet or plan file into local state for the current objective
- Preserve the same objective across retries; do not narrow to one slice from
bd ready
Step 3: List Ready Work for the Current Wave
Beads mode:
bd ready 2>/dev/null
bd ready returns all unblocked issues - these can run in parallel.
Execution-packet/file mode:
- Read remaining tasks from
.agents/rpi/execution-packet.jsonor the plan file - Execute against the packet objective until the plan-backed work is done, blocked, or the retry budget is exhausted
Step 3a: Pre-flight Checks
- Verify there are ready issues. Empty list is an error unless the epic is already complete.
- If 3+ issues are ready, check
.agents/council/for pre-mortem evidence. - If tracking mode is
beadsandscripts/bd-audit.shexists, run the backlog audit before spawning workers. - If bd-audit flags ba