Flow Work Phases
(Branch question already asked in SKILL.md before reading this file)
CRITICAL: If you are about to create:
- a markdown TODO list,
- a task list outside
.flow/, - or any plan files outside
.flow/,
STOP and instead:
- create/update tasks in
.flow/usingflowctl, - record details in the epic/task spec markdown.
Setup
CRITICAL: flowctl is BUNDLED — NOT installed globally. which flowctl will fail (expected). Always use:
FLOWCTL="${DROID_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}/scripts/flowctl"
Phase 1: Resolve Input
Detect input type in this order (first match wins):
- Flow task ID
fn-N-slug.M(e.g., fn-1-add-oauth.3) or legacyfn-N.M/fn-N-xxx.M→ SINGLE_TASK_MODE - Flow epic ID
fn-N-slug(e.g., fn-1-add-oauth) or legacyfn-N/fn-N-xxx→ EPIC_MODE - Spec file
.mdpath that exists on disk → EPIC_MODE - Idea text everything else → EPIC_MODE
Track the mode — it controls looping in Phase 3.
Flow task ID (fn-N-slug.M or legacy fn-N.M/fn-N-xxx.M) → SINGLE_TASK_MODE:
- Read task:
$FLOWCTL show <id> --json - Read spec:
$FLOWCTL cat <id> - Get epic from task data for context:
$FLOWCTL show <epic-id> --json && $FLOWCTL cat <epic-id> - This is the only task to execute — no loop to next task
Flow epic ID (fn-N-slug or legacy fn-N/fn-N-xxx) → EPIC_MODE:
- Read epic:
$FLOWCTL show <id> --json - Read spec:
$FLOWCTL cat <id> - Get first ready task:
$FLOWCTL ready --epic <id> --json
Spec file start (.md path that exists):
- Check file exists:
test -f "<path>"— if not, treat as idea text - Initialize:
$FLOWCTL init --json - Read file and extract title from first
# Headingor use filename - Create epic:
$FLOWCTL epic create --title "<extracted-title>" --json - Set spec from file:
$FLOWCTL epic set-plan <epic-id> --file <path> --json - Create single task:
$FLOWCTL task create --epic <epic-id> --title "Implement <title>" --json - Continue with epic-id
Spec-less start (idea text):
- Initialize:
$FLOWCTL init --json - Create epic:
$FLOWCTL epic create --title "<idea>" --json - Create single task:
$FLOWCTL task create --epic <epic-id> --title "Implement <idea>" --json - Continue with epic-id
Phase 2: Apply Branch Choice
Based on user's answer from setup questions:
- Worktree: use
skill: flow-next-worktree-kit - New branch:
git checkout main && git pull origin main git checkout -b <branch> - Current branch: proceed (user already confirmed)
Phase 3: Task Loop
For each task, spawn a worker subagent with fresh context.
3a. Find Next Task
$FLOWCTL ready --epic <epic-id> --json
If no ready tasks, check for completion review gate (see 3g below).
3b. Start Task
$FLOWCTL start <task-id> --json
3c. Spawn Worker
Use the Task tool to spawn a worker subagent. The worker gets fresh context and handles:
- Re-anchoring (reading spec, git status)
- Implementation
- Committing
- Review cycles (if enabled)
- Completing the task (flowctl done)
Prompt template for worker:
Pass config values only. Worker reads worker.md for phases. Do NOT paraphrase or add step-by-step instructions - worker.md has them.
Implement flow-next task.
TASK_ID: fn-X.Y
EPIC_ID: fn-X
FLOWCTL: /path/to/flowctl
REVIEW_MODE: none|rp|codex
RALPH_MODE: true|false
Follow your phases in worker.md exactly.
Worker returns: Summary of implementation, files changed, test results, review verdict.
3d. Verify Completion
After worker returns, verify the task completed:
$FLOWCTL show <task-id> --json
If status is not done, the worker failed. Check output and retry or investigate.
3e. Plan Sync (if enabled) — BOTH MODES
Runs in SINGLE_TASK_MODE and EPIC_MODE. Only the loop-back in 3f differs by mode.
Only run plan-sync if the task status is done (from step 3d). If not done, skip plan-sync and investigate/retry.
Check if plan-sync should run:
$FLOWCTL config get planSync.enabled --json
Skip unless planSync.enabled is explicitly true (null/false/missing = skip).
Get remaining tasks (todo status = not started yet):
$FLOWCTL tasks --epic <epic-id> --status todo --json
Skip if empty (no downstream tasks to update).
Extract downstream task IDs:
DOWNSTREAM=$($FLOWCTL tasks --epic <epic-id> --status todo --json | jq -r '[.[].id] | join(",")')
Note: Only sync to todo tasks. in_progress tasks are already being worked on - updating them mid-flight could cause confusion.
Use the Task tool to spawn the plan-sync subagent with this prompt:
Sync downstream tasks after implementation.
COMPLETED_TASK_ID: fn-X.Y
EPIC_ID: fn-X
FLOWCTL: /path/to/flowctl
DOWNSTREAM_TASK_IDS: fn-X.3,fn-X.4,fn-X.5
Follow your phases in plan-sync.md exactly.
Plan-sync returns summary. Log it but don't block - task updates are best-effort.
3f. Loop or Finish
IMPORTANT: Steps 3d and 3e ALWAYS run after worker returns, regardless of mode. Only the loop-back behavior differs:
SINGLE_TASK_MODE: After 3d→3e, go to Phase 4 (Quality). No loop.
EPIC_MODE: After 3d→3e, return to 3a for next task.
3g. Completion Review Gate (EPIC_MODE only)
When 3a finds no ready tasks, check if completion review is required.
Check epic's completion review status directly:
$FLOWCTL show <epic-id> --json | jq -r '.completion_review_status'
- If
ship→ review already passed, go to Phase 4 - If
unknownorneeds_work→ needs review
If review needed:
Invoke
/flow-next:epic-review <epic-id>skill- Pass
--review=<backend>matching the work review backend - Skill handles rp/codex backend dispatch
- Skill runs fix loop internally until SHIP verdict
- Pass
After skill returns with SHIP:
- Set status:
$FLOWCTL epic set-completion-review-status <epic-id> --status ship --json - Go to Phase 4 (Quality)
- Set status:
Note: The epic-review skill gets SHIP from the reviewer but does NOT set the status itself. The caller (work skill or Ralph) sets completion_review_status=ship after successful review.
Fix loop behavior: Same as impl-review. If reviewer returns NEEDS_WORK:
- Skill parses issues
- Skill fixes code inline
- Skill commits
- Skill re-reviews (same chat for rp, same session for codex)
- Repeat until SHIP
Only after SHIP does control return here. If skill outputs <promise>RETRY</promise>, there was a backend error - retry the skill invocation.
Why spawn a worker?
Context optimization. Each task gets fresh context:
- No bleed from previous task implementations
- Re-anchor info stays with implementation (not lost to compaction)
- Review cycles stay isolated
- Main conversation stays lean (just summaries)
Ralph mode: Worker inherits bypassPermissions from parent. FLOW_RALPH=1 and REVIEW_RECEIPT_PATH are passed through.
Interactive mode: Permission prompts pass through to user. Worker runs in foreground (blocking).
Phase 4: Quality
After all tasks complete (or periodically for large epics):
- Run relevant tests
- Run lint/format per repo
- If change is large/risky, run the quality auditor subagent:
- Task flow-next:quality-auditor("Review recent changes")
- Fix critical issues
Phase 5: Ship
Verify all tasks done:
$FLOWCTL show <epic-id> --json
$FLOWCTL validate --epic <epic-id> --json
Final commit (if any uncommitted changes):
git add -A
git status
git diff --staged
git commit -m "<final summary>"
Do NOT close the epic here unless the user explicitly asked. Ralph closes done epics at the end of the loop.
Then push + open PR if user wants.
Definition of Done
Confirm before ship:
- All tasks have status "done"
$FLOWCTL validate --epic <id>passes- Tests pass
- Lint/format pass
- Docs updated if needed
- Working tree is clean
Example flow
Phase 1 (resolve) → Phase 2 (branch) → Phase 3:
├─ 3a-c: find task → start → spawn worker
├─ 3d: verify done
├─ 3e: plan-sync (if enabled + downstream tasks exist)
├─ 3f: EPIC_MODE? → loop to 3a | SINGLE_TASK_MODE? → Phase 4
├─ no more tasks → 3g: check completion_review_status
│ ├─ status != ship → invoke /flow-next:epic-review → fix loop until SHIP → set status=ship
│ └─ status = ship → Phase 4
└─ Phase 4 (quality) → Phase 5 (ship)