From ywc-agent-toolkit
Runs multiple independent tasks concurrently using Git Worktree isolation for faster execution. Delegates worktree lifecycle to ywc-worktrees. Waives until all Wave N tasks succeed before starting Wave N+1.
How this skill is triggered — by the user, by Claude, or both
Slash command
/ywc-agent-toolkit:ywc-parallel-executorThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Announce at start:** "I'm using the ywc-parallel-executor skill to run independent tasks concurrently in isolated worktrees."
Announce at start: "I'm using the ywc-parallel-executor skill to run independent tasks concurrently in isolated worktrees."
Worktree lifecycle is owned by the dedicated
ywc-worktreesskill (priority resolution +--mode create/audit/prune/resolve). This skill delegates worktree creation, audit, and cleanup toywc-worktreesinstead of inlining the procedures. Seeywc-worktrees/SKILL.mdfor the four-mode contract and the priority chain (.worktrees/> CLAUDE.mdworktree_root> caller-specified > legacy fallback). The bundledaudit-worktrees.sh/cleanup-worktree.shscripts have moved to that skill.
Executes tasks generated by task-generator in parallel using agents.
When tempted to skip a step, check this table first:
| Excuse | Reality |
|---|---|
| "Tasks look independent, run all in parallel" | Run only tasks whose Ownership and Shared Surfaces do not overlap. Dependency-graph.md is the source of truth. |
| "Wave 1 had a partial failure, start Wave 2 anyway" | Wave boundary is a hard gate. All Wave N tasks must succeed before Wave N+1 starts. |
| "Worktree cleanup is manual hassle, skip it" | Stale worktrees corrupt subsequent runs. Always cleanup after each task completes via ywc-worktrees --mode prune (see ywc-worktrees). |
| "Conflicts With is just a hint" | If two tasks declare Conflicts With each other, they cannot run in the same wave even if dependencies allow. |
| "Task category does not match any specialized agent, skip the assignment" | Default to executor agent. Never silently drop the task. |
| "Mid-wave one task is slow, kill it and continue" | A killed task leaves a dirty branch. Stop, report, and let the user decide. |
| "--draft creates PR at the end, bot review can wait until un-draft" | Bot review bots (CodeRabbit, Codex Review, Claude Review) post on draft PRs immediately. After creating the draft PR in Step 5, poll for reviews via ../references/pr-bot-polling.md and invoke ywc-handle-pr-reviews if BOT_COUNT > 0. The PR stays draft — responding now avoids a round-trip after un-drafting. |
| "The aggregate draft PR passed CI before bot reviews — re-checking CI after bot fixes is extra work" | Bot review fixes push new commits that trigger a fresh CI run. The pre-bot CI result is stale and does not cover the updated code. Always re-verify CI after pushing bot review fixes on the aggregate draft PR (Step 5). |
| "Last task in the last wave has no downstream wave, skip delivery for it" | Step 4e is unconditional — every task in every wave, including the last task in the last wave, must complete (a) and (b). The absence of a downstream wave is not a reason to skip delivery. Without delivery (ywc-finish-branch for --local-merge/--draft, or gh pr merge + inline Mark Complete for --per-task-pr), the implementation code is stranded on an orphaned feature branch, tasks/completed/ is wrong, and the base branch is missing the work. |
"All waves done in --draft mode — go straight to Completion Report" | For --draft, before the Completion Report the aggregate draft branch must be created and pushed (Step 5 pre-report). The run does not end at the last wave's 4i; the Step 5 pre-report steps are required. |
"--per-task-pr: the PR is created, move to the next task" | --per-task-pr is now a full-lifecycle mode. Creating the PR is not delivery — you must wait for CI to pass, handle bot reviews, merge the PR (gh pr merge --delete-branch), sync local base, and commit + push the completion marker, all within the task's slot in the wave (Step 4e (a)+(b)). A created-but-unmerged PR is an incomplete task. |
"--per-task-pr: CI is green, merge the PR" | A sibling task in the same wave may have merged first, leaving this PR CONFLICTING or BEHIND. Refresh the branch against the latest base (Step 4e (a) step 4, merge-not-rebase per references/pr-conflict-resolution.md) and re-verify CI before gh pr merge. A real textual conflict on refresh marks the task BLOCKED — never force-merge. |
"--aggregate-pr: the single PR is created, the group is done" | --aggregate-pr is full-lifecycle, not draft. Creating the aggregate PR is not delivery — you must mark it ready, wait for CI, handle bot reviews, pass the merge-readiness gate, merge it (gh pr merge --delete-branch), and sync local base. A created-but-unmerged aggregate PR is an incomplete group. See references/aggregate-pr.md §B. |
"--aggregate-pr: re-Mark-Complete the tasks after the PR merges" | Each task's chore: mark … as completed commit already landed on the aggregate branch during its wave (--defer-push local merge, same as --draft). The aggregate PR merge carries those marker commits into base. Running Mark Complete again would double-move directories. Do not re-mark. |
"Run several --aggregate-pr groups in parallel in this one clone" | They share the local <base> branch ref, which the --draft-style accumulation mutates (git reset --hard origin/<base> + per-task merges) — concurrent groups corrupt each other's base accumulation. Parallel groups need one clone per group (or run them back-to-back). See references/aggregate-pr.md §C. |
| "Use git worktrees to split the parallel groups inside one clone" | Worktrees separate the working tree and the untracked .ywc-run-state.json, but share .git refs — the local <base> branch is shared and can be checked out in only one worktree, so the accumulation still collides at the ref layer. Worktrees are not an isolation boundary for this mode; use separate clones. See references/aggregate-pr.md §C. |
| "Each wave has only one task — worktrees add nothing, just work on the aggregate branch" | Worktree-per-task (Step 4a) is unconditional, independent of wave width and delivery mode. A fully linear chain (every wave = 1 task) is the wrong input for this skill — stop and route to ywc-sequential-executor per the Step 2 Linear-chain guard. Never invent an aggregate-branch-serial path. |
"--aggregate-pr accumulates on the aggregate branch, so check it out and commit there" | --aggregate-pr changes only end-of-run delivery (Step 5 / §B). Per-task execution (Step 4a–4e) is identical to every mode: isolated worktree + feature branch + --defer-push local merge. The aggregate branch is carved at Step 5, never used as the per-task work surface. |
| "Implement the whole wave, then verify everything at the wave end" | That is outrunning your headlights — behavior-changing tasks crash at runtime when test feedback comes only after a big batch. Tests gate implementation: a behavior-changing worker authors/confirms its failing test before its implementation (Step 4b directive). See ../references/tdd-deep-module-gray-box.md §2. |
| "Same-wave tasks share an API/DTO, they'll each just edit it" | A shared public surface edited by two parallel workers is a merge/design conflict waiting to happen. Define the contract before worker dispatch (or serialize the tasks across waves); design the interface before the body. See ../references/tdd-deep-module-gray-box.md §3 and the Shared Surfaces guard. |
Violating the letter of these rules is violating the spirit. Parallel execution is faster only when wave isolation is honored.
| Parameter | Format | Example | Description |
|---|---|---|---|
| Task specifier | <name> or <start>..<end> | 000001-010..000002-040 | Single task or range. Both 001010 (legacy) and 000001-010 (new 6-digit PHASE) formats are accepted; range matching uses lexical order. |
--all | flag | Execute all tasks | |
--tasks-dir | --tasks-dir <path> | --tasks-dir tasks/ | Tasks directory (default: tasks/) |
--pr-lang | --pr-lang <lang> | --pr-lang ko | PR title/description language passed to PR creation (used by --aggregate-pr's final ywc-create-pr call). Default: auto-detect from CLAUDE.md or AGENTS.md, fallback to the project's dominant language |
--review | flag | Auto-run /ywc-impl-review on each task's worktree branch before the wave merge (Step 4d). Applies the recurring-defects catalog to catch bot-flagged defect classes before the PR opens | |
--local-merge | flag | No PR, merge and push to base-branch directly | |
--draft | flag | Create draft PR after all tasks complete | |
--per-task-pr | flag | Per task: create the PR, wait for CI, handle bot reviews, then merge the PR (gh pr merge --delete-branch), sync base, and mark complete — the full lifecycle, mirroring ywc-sequential-executor's default normal-pr mode | |
--aggregate-pr | flag | Whole invocation → one branch + one PR. Tasks still run in parallel (waves) and accumulate onto a single aggregate branch (same per-wave merge as --draft); the end-of-run PR is marked ready, CI-verified, bot-reviewed, and merged. The full-lifecycle twin of --draft. See references/aggregate-pr.md | |
--group-name | --group-name <name> | --group-name payments | Names the aggregate branch (aggregate/<name>) and disambiguates concurrent groups. --aggregate-pr only; defaults to aggregate/<base-branch>-<timestamp> when omitted |
--terse | flag | Compact Completion Report: task table + Completion Status only — no prose reminders, no worktree audit lines, no mode explanations |
--review can be combined with other flags.
Flag conflicts: --local-merge, --draft, --per-task-pr, and --aggregate-pr are mutually exclusive. If multiple are specified, ask for clarification before execution. --group-name is valid only with --aggregate-pr.
Default behavior: When none of --local-merge, --draft, --per-task-pr, or --aggregate-pr is specified, ask the user which mode to use before execution. Do not silently default to any mode — the user must explicitly choose how completed tasks are delivered. Present the four options:
--local-merge — No PR, merge and push to base-branch directly--draft — Create a single draft PR after all tasks complete (left open for human merge)--per-task-pr — Create, CI-verify, bot-review, and merge an individual PR per task (full lifecycle, like ywc-sequential-executor's default)--aggregate-pr — One branch + one PR for the whole invocation, CI-verified, bot-reviewed, and merged (the full-lifecycle twin of --draft)A task is done only when all of the following have happened, in this order. A task that is "merged but not marked" is not done — downstream waves resolve dependencies by reading <tasks-dir>/completed/, so a missing move silently breaks the dependency graph for the next wave.
--local-merge and --draft, ywc-finish-branch returned DONE — that status enforces the merge with post-merge verification and the move of the task directory to <tasks-dir>/completed/<task-name> with a chore: mark <task-name> as completed commit. For --per-task-pr, the PR passed CI, was merged via gh pr merge --delete-branch, local base was synced, and the same completion-marker move-commit was made and pushed.--local-merge and --per-task-pr: every merge / PR-merge and completion-marker commit was pushed immediately during the wave. For --draft: the deferred local merge commits accumulate locally and are pushed once at the end of all waves before the Completion Report.--keep-branch, which is mandatory in this skill because the worktree owns the branch checkout. Step 4g is therefore the source of truth for branch deletion in parallel execution.If any of the four is missing, the task is incomplete regardless of how git log --oneline base-branch looks. The Wave Delivery + Mark Complete step (Step 4e — ywc-finish-branch for --local-merge/--draft/--aggregate-pr, inline gh pr merge + Mark Complete for --per-task-pr) writes the code and the contract; Step 4g releases the worktree and the branch.
--aggregate-pr group-level done: in addition to every task satisfying the four conditions above (delivered via the same --draft-style per-wave local merge with --defer-push), the group is done only when the single aggregate PR has been marked ready, passed CI, cleared bot review, passed the merge-readiness gate, and been merged with local base synced (Step 5 + references/aggregate-pr.md §B). A run whose aggregate PR is created but unmerged is DONE_WITH_CONCERNS at best, never DONE.
Action required before creating any worktree: Read
../references/non-stop-execution.mdnow. It defines the exhaustive allowed-stop list, the force-continue rule, tool permission prompt handling, and the Tool Error Recovery policy (how to recover fromEdit/Bashfailures without entering extended thinking). Do not defer this read to mid-execution.
Execute all waves without interruption. Do not ask the user for confirmation between waves. The shared rule, full stop list, force-continue rule, zero-output rule, and --local-merge rationale live in ../references/non-stop-execution.md.
The unit for this skill is wave. The Allowed Stop Reasons that are parallel-specific are:
git push rejected by remoteThe "Zero output between transitions" rule applies to the gap between Step 4g/4h of one wave and Step 4a of the next. Everything else — Force Continue Rule, what not to stop for, tool-permission-prompts-are-not-stop-conditions, and the --local-merge rationale — is in the reference and applies verbatim.
Resume check first: Before running the checks below, look for
.ywc-run-state.jsonin the project root. If it exists, follow the Resume Detection procedure in Checkpoint and Resume. If the user confirms resume, skip Pre-flight and jump to the saved wave and pending tasks. If the user declines or there is no state file, proceed with the checks below. Intent-match guard (do not skip): if this invocation specifies an explicit range/task that does not match the saved run's tasks, never silently resume — surface the divergence and require an explicit choice (resume the saved run vs discard it and start the new range), per the guard in the Resume Detection procedure. A new range hijacked by a stale interrupted run is the failure this guard prevents.
Verify the following conditions before starting:
Clean working tree — git status --porcelain must be empty
On base branch — Must be on main/develop/master
Tasks directory exists — dependency-graph.md and task subdirectories must be present
gh CLI authenticated (PR creation modes only) — gh auth status must succeed
No stale worktrees from previous runs — Run the bundled audit (prunes stale metadata first, then reports any remaining worktree-* paths):
bash claude-code/skills/ywc-worktrees/scripts/audit-worktrees.sh --prune
# exit 0 = clean; exit 1 = stale worktrees detected — details on stdout
If exit 1, resolve per worktree listed in the output:
git worktree remove --force <path> && git branch -D feature/<task-name>Pre-flight must end with exit 0 before Step 1 begins. Starting a new run on top of stale metadata is the most common reason worktrees "leak" — the new run reuses paths and branches the cleanup logic no longer recognises. Docker-isolated projects: after the worktree audit, also run bash claude-code/skills/ywc-docker-isolate/scripts/audit-docker-stacks.sh --expect <t1,t2,...> (comma-separated, no spaces — every expected task name) — non-empty stdout means a prior run's ywc-<task> stack still holds the deterministic port, so abort the run, surface the printed stacks + the --prune remediation, and do not start Step 1 (§A3.W/AC10).
State Init (non-resume runs only): Initialize .ywc-run-state.json from the computed wave plan, and add it to .gitignore if absent:
grep -qxF '.ywc-run-state.json' .gitignore 2>/dev/null || echo '.ywc-run-state.json' >> .gitignore
bash claude-code/skills/scripts/update-state.py init-parallel \
--mode <local-merge|draft|per-task-pr|aggregate-pr> --tasks-dir <tasks-dir> \
--waves '[{"wave":1,"tasks":["t-a","t-b"]},{"wave":2,"tasks":["t-c"]}]'
The --waves array is the wave plan from Step 3 — one entry per wave, tasks listing that wave's task-directory names. See the schema in Checkpoint and Resume.
The Non-Stop Execution Principle above governs LLM-level pausing, but it cannot override Claude Code's tool permission layer — every unprompted git/gh/mv command can independently block on user confirmation. In multi-wave execution this is the single most common reason waves stop mid-run: each worktree creation, merge, push, and cleanup across multiple tasks triggers an individual prompt.
Required for multi-wave execution: Each wave runs multiple
gitandghcommands across multiple worktrees. Without pre-authorization, each command pauses for approval — multiplied across all tasks and waves, this makes unattended execution impossible. Complete this setup before starting.
The fix is a one-time .claude/settings.local.json setup that pre-authorizes a small set of read-only and task-scoped Bash patterns. The same permissions are used by both ywc-parallel-executor and ywc-sequential-executor.
For the full pattern list, narrow fallback for strict policies, privacy notes, and diagnosis steps when pre-authorization is not enough, see ../references/local-merge-permissions.md.
The executor writes .ywc-run-state.json in the project root after each major wave event, so an interrupted multi-wave run can resume from the last checkpoint (completed waves skipped, the in-progress wave restarting with only its remaining pending tasks). The full procedure — Resume Detection (executor / age / worktree validation / intent-match guard), the State File Format, the Checkpoint Summary event table, and Manual Inspection commands — is in references/checkpoint-resume.md. Run Resume Detection before the Pre-flight checks.
Read dependency-graph.md to extract the TaskInfo list. Parse the following from each task:
db, api, ui, test, etc.)Separate tasks into waves based on dependency relationships:
Linear-chain guard: if every planned wave contains exactly one task, the input is a strictly sequential chain — there is no concurrency to exploit and worktree isolation is pure overhead. Stop and tell the user that ywc-sequential-executor (with --aggregate-pr for single-PR delivery) is the correct tool; proceed in parallel only on explicit user confirmation. Never silently work directly on the base or aggregate branch.
Granularity cross-ref: routing a linear chain to
ywc-sequential-executor --worktreeis not a violation of this guard — sequential's--worktreeis a run-level worktree wrapping the whole run in one tree (tasks still run sequentially inside it), a different granularity from the task-level, one-per-task worktrees this guard governs.
Wave Planning is the critical upfront decision in this skill — a wrong wave boundary cascades into unnecessary serialization (waste) or unsafe parallelism (merge conflicts and broken dependencies) across every subsequent wave. Because the damage is expensive to undo once worktrees and feature branches exist, this is the right place to apply Pattern C from advisor-pattern.md: a single upfront Opus advisor call before worktree creation begins.
When to invoke:
Conflicts With declarations exist across the task set; Shared Surfaces overlap across candidate waves; or the first-pass topological sort produced a wave with mixed categories (e.g., db + api + ui in the same wave).How to invoke: Use the Task tool with model: opus. Payload (≤200 lines total):
Depends On + Conflicts With + Shared Surfaces only; do not forward full task READMEs.Ask the advisor for three things:
refactor task that is actually a domain logic rewrite in disguise)?Budget: exactly 1 Opus call per invocation. Mid-wave escalation is explicitly disallowed — per-wave task agents run in isolated worktrees and handle their own decisions. If the initial wave plan proves wrong during Step 4, stop execution, report to the user, and re-run the skill with refined input rather than calling Opus mid-flight.
Advisor output format (≤300 words):
After the verdict, either continue to Step 3 with the adjusted wave plan, or surface the "reconsider" verdict to the user for plan refinement before proceeding.
| Category | subagent_type | Description |
|---|---|---|
db, api, domain, lib, worker | ywc-backend-coder | Server-side code generation/modification (Tier-1 named worker; persona at claude-code/agents/ywc-backend-coder.md) |
ui | ywc-frontend-coder | UI component, page generation/modification (ywc-frontend-coder.md) |
test | ywc-qa-engineer | Test strategy + test code generation (ywc-qa-engineer.md) |
infra | (inline implementer) | CI/CD, deployment configuration — no named Tier-1 agent yet; dispatch via general-purpose with focused prompt |
refactor | (inline implementer) | Code structure improvement — no named Tier-1 agent yet; dispatch via general-purpose with focused prompt |
If an Agent Hint is specified in the task's README.md, it overrides the mapping above. Category coverage: 5 of 7 categories map to named Tier-1 worker agents; infra and refactor follow up in a later PR (Iteration 1 §B3 deferred coverage).
Repeat the following for each wave. Each task must have its own independent worktree and branch — never execute multiple tasks on a single branch. The worktree model structurally guarantees this, but ensure agents do not work outside their assigned worktree.
4a. Create Worktrees — For each task in the wave:
git worktree add ../worktree-<task-name> -b feature/<task-name> <base-branch>
4a-verify (mechanical gate — do not skip): before Step 4b, confirm each task's worktree physically exists. Implementing a task on the base/aggregate branch working tree (the aggregate-branch-serial deviation) violates the isolation contract regardless of wave width. Re-run Step 4a on failure — never reach 4b without one isolated worktree per task:
for t in <wave-task-names>; do git worktree list --porcelain | grep -q "/worktree-$t$" || { echo "GATE FAIL: worktree-$t missing"; exit 1; }; done
4a-isolate (Docker-isolated projects only): immediately after 4a-verify passes, isolate each task's Docker host ports — bash claude-code/skills/ywc-docker-isolate/scripts/setup-docker-ports.sh --task-name <t> --worktree-path ../worktree-<t>. Exit 1 (hardcoded port / squatter / corrupt persist) → mark the task BLOCKED and preserve its worktree (skip Step 4g for it), wave continues (§A1.2). This hook is delivery-mode-independent — it fires for every per-task worktree.
Checkpoint: bash claude-code/skills/scripts/update-state.py wave-start <N> — flips wave <N> to in_progress, populates pending from its task list, and stamps last_checkpoint. (Hand-editing the JSON risks malformed state and stale timestamps; the script does the mutation deterministically.)
4b. Spawn Agents — Use Claude Code's Agent tool to spawn one subagent per task in parallel. Pass each agent:
The task's task.md (implementation checklist)
The task's README.md (scope, ownership, spec reference)
The worktree path (working directory)
The canonical term table from docs/ubiquitous-language.md if it exists in the project root (include the "Synonyms to Avoid" column — identifiers matching those entries are naming violations)
Question-First directive (append verbatim):
Before any code change: read
task.mdand the Spec Reference, then enumerate genuinely ambiguous decisions whose wrong answer would force a rewrite (interface shape, data model, naming that conflicts with existing code, library choice when more than one is installed). If the list is non-empty, returnNEEDS_CONTEXTwith the questions enumerated — do not infer from neighboring tasks. Inferring silently compounds error and is the most expensive failure mode. See ../references/question-first-gate.md for what counts as genuine ambiguity and the question format.
Completeness directive (append verbatim to every subagent prompt):
This implementation will be merged directly into the base branch — treat it as production code. Before returning output: (1) every function/method must have a complete implementation body — no
// TODO, no// rest of code, no placeholder stubs; (2) all imports must be used and all referenced symbols must be defined; (3) tests must contain real assertions, not emptyit()blocks; (4) if token budget is approaching and generation is incomplete, stop at a clean function boundary and write[PAUSED — X of Y files complete. Continue: <file-list>]— never truncate mid-function. A stub is a compile error; a truncated function is worse.
Tool Error Recovery directive (append verbatim to every subagent prompt):
When a tool call returns an error, do not enter extended thinking — apply the recovery action immediately. For
Edit/Update→ "Error editing file": (1) re-read the full file withRead, (2) retry the edit withold_stringfrom the fresh content. ForBashnon-zero exit: inspect the error, fix the root cause (wrong flag, path, binary), re-run. Maximum 2 fix attempts for any tool error before returningBLOCKEDwith the file path, attempted change, and exact error text.
Simplicity + Surgical Changes directive (append verbatim to every subagent prompt):
Implement the minimum code that satisfies this task — no speculative features, no unsolicited abstractions, no "flexibility" that wasn't asked for. When editing existing code: touch only files listed in your declared Ownership; do not improve adjacent code, comments, or formatting unless they are the direct subject of this task. If you notice unrelated issues, mention them in the PR description — do not fix them. Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify before committing.
Interface-first (deep module) directive (append verbatim to every subagent prompt):
Before writing the body, design the public interface this task exposes or changes — function signature, endpoint, event payload, DTO, component props, CLI flag — and keep the implementation behind it. Do not split cohesive behavior into shallow single-use wrappers, and add an interface only for a real boundary (no speculative generality). A shallow-module maze is what the next reader, human or AI, gets lost in. See ../references/tdd-deep-module-gray-box.md §3.
Test-first-where-feasible directive (append verbatim to every subagent prompt):
If this task changes observable behavior, author the test first and confirm it fails (RED) for the intended reason before implementing, then make it pass (GREEN) — do not weaken or delete a test to go green. Don't outrun your headlights: feedback speed is your speed limit. Docs/config/mechanical tasks may skip the RED state but must state the reason; never fabricate an empty/passing test for an untestable change. See ../references/tdd-deep-module-gray-box.md §2.
Handling each subagent's status return: each subagent ends with DONE, DONE_WITH_CONCERNS, BLOCKED, or NEEDS_CONTEXT. The orchestrator's response is defined by ../references/subagent-status-actions.md. In particular: NEEDS_CONTEXT → provide the missing context and re-dispatch the same subagent at the same model class (context is the cheapest fix); BLOCKED → run the four-step triage (context → reasoning → scope → plan) before surfacing to the user; DONE_WITH_CONCERNS → read the concerns and decide whether they are correctness-level (fix and re-dispatch) or observation-level (carry forward to the Completion Report). Do not silently retry the same subagent on the same input — change the input or the model class between attempts.
The named worker subagents return payloads per ../references/subagent-status-actions.md §3.5. Apply the following routing table to every per-task subagent return inside the wave loop:
| Returned status | Caller action |
|---|---|
DONE | Proceed to Step 4c (Task Verify), then Step 4e (Wave Delivery + Mark Complete) for this task; continue with remaining tasks in the wave |
DONE_WITH_CONCERNS | Observation-level concerns → carry forward to the Completion Report; correctness-level concerns → fix and re-dispatch the same subagent before merging |
BLOCKED | Run the four-step triage (context → reasoning → scope → plan), preserve the task's branch and worktree (skip Step 4g for this task), record for the Completion Report — see Wave-specific amplification below |
NEEDS_CONTEXT | Provide the missing context and re-dispatch the same subagent at the same model class — do not silently infer from neighboring tasks |
| Status absent or unparseable | Treat as implicit BLOCKED; preserve the worktree, surface the raw payload to the user without retry |
BLOCKED returns in this skill have additional wave-level semantics defined in the Wave Failure Handling section: a single failed task preserves its branch and worktree (do not run Step 4g cleanup for it), the remaining successful tasks merge normally, and subsequent wave tasks that depend on the failed task are skipped. Merge conflicts and Step 4g cleanup failures have their own handling rules in that section — read both this table and the Wave Failure Handling section together when the loop returns non-DONE for any task.
4c. Task Verify — After each agent completes, run the Task Verify commands from task.md. Then run two gates before the task is eligible for 4e Wave Delivery:
ywc-sequential-executor Step 4 layer 3). Task Verify alone proves the task's own behavior, not that it left shared code intact. After Task Verify passes, run the full project test suite (or, when a full run is impractical inside the worktree, the impacted-scope subset — and document why the scope was narrowed). A wave task can pass its own Task Verify and still regress shared state / types / schema / runtime wiring; without this layer that regression reaches the base branch (and in --local-merge is never caught at all). Long suites may run in the background.git -C "$WT" diff --name-only and confirm every changed path is within the task's declared Ownership. An out-of-Ownership file is a scope-creep signal — a missed dependency (return BLOCKED) or a drive-by edit (revert it), never a silent merge.4d. Review (optional + forced for critical paths) — If --review is set, auto-invoke /ywc-impl-review on the task's worktree branch after Task Verify (4c) passes and before the Wave Delivery (4e). Running the review while the code is still isolated in its worktree means any issue it surfaces is fixed before the change reaches the base branch. For --local-merge and --draft, this is the last quality gate where no remote bot review has run yet. For --per-task-pr, a remote bot review also runs after PR creation (Step 4e (a)), so here --review acts as a pre-PR gate that reduces the number of bot round-trips rather than being the only gate.
Critical-path auto-escalation (forced, even without --review). Gray-box review is the default, but it is insufficient for critical modules. When a task's declared Ownership matches a critical path (auth / authz / session / token / password / secret / crypto / payment / billing / finance / PII / external-input boundaries — full list and CLAUDE.md critical_paths override in ../references/tdd-deep-module-gray-box.md §4), invoke /ywc-impl-review and /ywc-security-audit on that task's worktree branch before its 4e delivery, regardless of --review. Detection is on the task's Ownership (known at wave planning). Non-critical tasks keep the default gray-box verification.
The review applies the recurring real-world defects catalog — the defect classes (data-layer access-boundary / ownership isolation, data-integrity / NULL handling, error-swallow, external-call resilience, validation, HTTP status, test fidelity) that PR-review bots flag most. Catching them here reduces the bot-review round-trips that would otherwise land on the aggregate/per-task PR.
Invoke against the worktree so the review reads the right tree. --spec is required by ywc-impl-review; source <task-spec-path> from the task README.md's Spec Reference (the same spec the implementer read in 4a):
/ywc-impl-review --spec <task-spec-path> --git-range <base-branch>..feature/<task-name>
Handling the review's status return: /ywc-impl-review emits DONE, DONE_WITH_CONCERNS, BLOCKED, or NEEDS_CONTEXT. Apply ../references/subagent-status-actions.md: correctness-level concerns (Critical/High) are fixed on the worktree branch and re-reviewed before 4e merges the task; observation-level concerns carry forward to the Completion Report. A BLOCKED review preserves the task's worktree (skip 4g for it) exactly like a BLOCKED implementation.
4e. Wave Delivery + Mark Complete — After all tasks in the wave complete their implementation, deliver successful tasks into the base branch sequentially and mark each one complete. Delivery into the base branch is required for every mode because downstream waves branch from it. For --local-merge and --draft, the per-task git merge --no-ff + post-merge verification + Mark Task Complete + push (or defer) is delegated to ywc-finish-branch. For --per-task-pr, this skill runs the full PR lifecycle (create → CI → bot → gh pr merge → base sync) and an inline Mark Complete, because the worktree model is incompatible with finish-branch's normal-pr mode (which assumes the feature branch is the current checkout and runs a local git checkout <base> that cannot execute from a worktree).
⚠️ DO NOT SKIP DELIVERY FOR THE LAST TASK IN THE LAST WAVE. There is no exception. Even when there are no remaining waves and no downstream task waiting, Step 4e (a)+(b) must run for every task. For --local-merge and --draft, ywc-finish-branch performs the local merge, completion-marker commit, and (for local-merge) the push. For --per-task-pr, (a) merges the PR via gh pr merge --delete-branch and (b) commits and pushes the completion marker. Skipping delivery leaves implementation code on an orphaned branch and tasks/completed/ out of sync.
For each task in the wave sequentially (topological order within the wave) — every task in every wave, including the last task in the last wave, must complete steps (a) and (b); no task is exempt because there is no downstream task waiting on it:
(a) Per-task PR lifecycle — applies only to --per-task-pr (runs unconditionally for every task in every wave, including the last task in the last wave). Skip entirely for --local-merge and --draft. All commands are branch-explicit so they are safe to run from the main checkout while the feature branch lives in its worktree:
git push origin feature/<task-name>
gh pr create --base <base-branch> --head feature/<task-name> --title "<task-name>" --body "..."
gh pr checks <pr-number> --watch
Categorize failures as lint/format (run the project's auto-fix command first), type, test, or build — fix on the worktree branch, commit, git push origin feature/<task-name>, then re-poll. After 2 failed fix cycles, mark the task BLOCKED, preserve its worktree and branch (skip Step 4g for it), and record it for the Completion Report.../references/pr-bot-polling.md. If BOT_COUNT > 0, invoke ywc-handle-pr-reviews for this task's PR, then re-verify CI (step 2) — bot fixes push new commits that trigger a fresh CI run — then re-poll. Repeat until CI is green and no new comments arrive within the polling window. Bot reviews across tasks in the same wave are processed one task at a time so each task's review commits land before its merge.../references/pr-conflict-resolution.md. This matters when branch protection requires PR branches to be up to date, or when a previous task in the same wave already merged and pushed its completion marker:
git fetch origin <base-branch>
if ! git merge-base --is-ancestor origin/<base-branch> feature/<task-name>; then
git -C ../worktree-<task-name> merge --no-ff origin/<base-branch> -m "Merge origin/<base-branch> into feature/<task-name>"
git -C ../worktree-<task-name> push origin feature/<task-name>
# The branch now has a new commit; return to step 2 and re-verify CI.
fi
If the base refresh conflicts, mark the task BLOCKED, preserve its worktree and branch, and record the conflict for the Completion Report. Do not force-push and do not merge a PR whose checks correspond to an older head SHA.gh pr merge <pr-number> --merge --delete-branch
--delete-branch removes only the remote branch; the local branch (checked out in the worktree) is deleted in Step 4g.git pull origin <base-branch>
The main checkout is already on <base-branch>; this fast-forwards it to include the PR merge.(b) Mark Complete — move the task directory to completed/ and commit the marker. The mechanism differs by mode because --per-task-pr already merged via gh pr merge in (a), while --local-merge and --draft still need the local merge that ywc-finish-branch performs.
For --local-merge and --draft — delegate the local merge + Mark Complete to ywc-finish-branch. Mode mapping:
parallel-executor --mode | finish-branch invocation |
|---|---|
--local-merge | --mode local-merge --keep-branch (push every task immediately) |
--draft | --mode local-merge --keep-branch --defer-push (push deferred to end of all waves; a single draft PR is created in Step 5) |
--aggregate-pr | --mode local-merge --keep-branch --defer-push (identical to --draft per wave; Step 5 lifts the accumulated state onto one branch and runs the full merge lifecycle per references/aggregate-pr.md §B) |
/ywc-finish-branch \
--mode local-merge \
--branch feature/<task-name> \
--base-branch <base-branch> \
--task-name <task-name> \
--tasks-dir <tasks-dir> \
--keep-branch \
[--defer-push only when parallel mode is --draft — controls only the Mark-Complete commit push, never the merge or any earlier step]
--keep-branch is required: the branch is checked out in ../worktree-<task-name>, so git branch -d would fail until Step 4g releases the worktree.
For --per-task-pr — the merge already happened in (a) step 5, so do not call finish-branch (its local-merge would attempt a redundant merge, and its normal-pr assumes the feature branch is the current checkout, which it is not under the worktree model). Instead, run the same Mark Complete that finish-branch would, inline, then push immediately:
bash claude-code/skills/scripts/mark-complete.sh <tasks-dir> <task-name> --push
This is the same shared marker script ywc-finish-branch Step 7 uses. It moves the task into completed/, writes the mandatory chore: mark <task-name> as completed commit (handling the gitignored-<tasks-dir> case with a plain mv + --allow-empty commit), pushes the current branch — which under this flow is the synced base — and verifies the move (destination exists, source gone, marker at HEAD). A non-zero exit means the move or verification failed: mark the task BLOCKED, do not treat it as delivered. A --per-task-pr task is delivered only when its PR is merged (a step 5), local base is synced (a step 6), and this marker commit is pushed.
Status return handling for --local-merge and --draft (per task within the wave loop): ywc-finish-branch ends with DONE, DONE_WITH_CONCERNS, BLOCKED, or NEEDS_CONTEXT. Apply ../references/subagent-status-actions.md:
DONE → proceed to the next task in the wave.DONE_WITH_CONCERNS → if observation-level, carry into the Completion Report; if correctness-level, fix and re-dispatch before continuing.BLOCKED → preserve the task's branch and worktree (Step 4g must skip it for recovery), record for the Completion Report, continue with the remaining wave tasks. Common BLOCKED causes here: merge conflict, post-merge verification mismatch, post-move verification mismatch.NEEDS_CONTEXT → provide the missing context and re-dispatch.Status handling for --per-task-pr (no finish-branch delegation): the inline path has three failure gates — a CI failure exhausted after 2 fix cycles (a step 2), a latest-base refresh conflict (a step 4), and a post-move verification mismatch (b). Any one marks the task BLOCKED: preserve its worktree and branch (Step 4g must skip it), record it for the Completion Report, and continue with the remaining wave tasks. A merge conflict reported by gh pr merge (a step 5) is likewise BLOCKED — never force-merge; surface the conflict to the user.
Checkpoint (after each successful task delivery in the inner loop): bash claude-code/skills/scripts/update-state.py task-merged <N> <task-name> — moves the task from pending to merged in wave <N> and stamps last_checkpoint.
Wave-end push for --draft mode: After every task in the wave has been processed, the --defer-push completion-marker commits remain local. They are pushed once at the end of all waves (not per-wave), as part of the Completion Report transition. For --local-merge and --per-task-pr, no wave-end push is needed — every task already pushed individually (local-merge via finish-branch, per-task-pr via the (b) inline push after gh pr merge).
Failed (BLOCKED) tasks remain in <tasks-dir>/<task-name>; finish-branch never moves them. Record those tasks for the Completion Report.
Checkpoint (after the entire wave loop finishes): bash claude-code/skills/scripts/update-state.py wave-complete <N> — flips wave <N> to completed (it refuses if any task is still pending, a built-in guard against marking an incomplete wave done) and stamps last_checkpoint.
4g. Clean Up Worktrees — Delete worktrees and branches for merged-and-marked tasks. This step is mandatory and verified, not best-effort. A leaked worktree pollutes Pre-flight on the next run, blocks reuse of the task name, and leaves the feature branch alive long after the work is on the base branch.
For each task whose Step 4e delivery completed (DONE), first tear down its Docker stack (Docker-isolated projects only) — bash claude-code/skills/ywc-docker-isolate/scripts/teardown-docker.sh --task-name <task-name> --worktree-path ../worktree-<task-name> — then remove the worktree. Because this runs inside the DONE-only loop, a BLOCKED-preserved task (whose 4g is skipped) is never torn down (AC6):
bash claude-code/skills/ywc-worktrees/scripts/cleanup-worktree.sh <task-name>
# exit 0 = PASS (worktree removed, branch deleted, prune done, verified)
# exit 1 = FAIL — fix hints on stdout; read them before taking any action
The script encodes all four failure paths (modified files, locked worktree, not-fully-merged branch, directory persists) and runs post-cleanup verification automatically. If exit 1, read the stdout output — it names the step that failed and includes the exact commands to investigate or resolve.
Preserve worktrees and branches of failed tasks for recovery. Do not run this step for any task whose Step 4e delivery did not complete (DONE). Record every task whose cleanup succeeded, was force-cleaned, or was deliberately preserved — Step 5 reports each category.
4h. Wave Cleanup Audit — Before transitioning to the next wave, audit the worktree state for the wave as a whole. Per-task verification in 4g catches single-task drift; this step catches wave-level drift (e.g. an agent created a sibling worktree the executor did not track).
bash claude-code/skills/ywc-worktrees/scripts/audit-worktrees.sh \
--expect <comma-separated preserved-failure task names, or omit if none>
# exit 0 = clean (or only expected preserved failures); exit 1 = DRIFT — investigate before next wave
If exit 1 (DRIFT), investigate before starting the next wave. Do not auto-delete unknown worktrees — they may belong to a concurrent operator or to a task whose state was not tracked.
4i. Transition to Next Wave — Per the Non-Stop Execution Principle, proceed to the next wave immediately and silently. Do not ask the user for confirmation. Do not emit any text between waves — no "Wave N complete", no status summary, no acknowledgment. The next output after Step 4h is the first tool call (creating worktrees) of Step 4a for the next wave. Before transitioning, run the executable terminal-state audit below — every task in the current wave must be in exactly one of two terminal states.
# Every task whose Step 4e delivery completed (DONE) must have its directory under completed/.
for task in <wave-success-tasks>; do
test -d <tasks-dir>/completed/$task || { echo "LEAKED: $task missing from completed/"; exit 1; }
done
# Every task whose Step 4e returned BLOCKED (preserved failure) must still be in <tasks-dir>/<task-name>.
for task in <wave-blocked-tasks>; do
test -d <tasks-dir>/$task || { echo "DRIFT: $task missing from <tasks-dir>/ (and not in completed/)"; exit 1; }
done
<tasks-dir>/completed/, worktree removed, branch deleted<tasks-dir>/<task-name>, worktree and branch retained for recoveryNo task should be in an in-between state (e.g. moved to completed/ but branch still alive, or worktree removed but directory not moved). If the audit reports LEAKED or DRIFT, do not transition — surface the offending task name to the user; transitioning forward with a missing Mark-Complete silently corrupts dependency resolution for every downstream wave.
--draft and --aggregate-pr modes: Aggregate PR (execute before the report below)
Both modes accumulate all task changes locally on base-branch via wave merges with
--defer-push, then lift that state onto a single branch and open one PR. The full
command sequences live in references/aggregate-pr.md:
--draft → §A: create draft/<base>-<timestamp>, push, reset local base, open a
draft PR, poll bots, re-verify CI, and stop (left open for a human to merge).
Capture the PR URL for the report.--aggregate-pr → §B: create aggregate/<group-name> (or timestamped), push, reset
local base, open the PR, mark it ready, CI-verify, bot-review, pass the
merge-readiness gate, merge (gh pr merge --delete-branch), and sync local base. The
completion-marker commits ride into base through this single merge — do not re-Mark
Complete. Capture the merged PR URL for the report.Action required: Read references/aggregate-pr.md before executing either path — it carries the exact branch names, the CI/bot/merge-readiness gates (reusing
pr-bot-polling.mdandpr-conflict-resolution.md), and the §C multi-group concurrency rules.
--per-task-pr mode: no end-of-run push — each task's PR was already merged via gh pr merge --delete-branch and its completion-marker commit was already pushed during the task's slot in the wave (Step 4e (a) step 5 and (b)). There is nothing deferred to flush here, and the individual PRs are merged and closed on the remote — not left open. Proceed directly to the report below.
Display the following after all waves are complete:
Total tasks executed, total waves
Each task: name, status (success/failed/skipped), merge commit SHA in --local-merge mode, or the merged PR URL in --per-task-pr mode
Failed/skipped tasks with reasons
Per-wave changed public contracts (interfaces/DTOs altered) and any critical-path tasks that triggered /ywc-security-audit
In --local-merge mode: remind that no PR was created, no remote CI ran, only local verification was performed
In --per-task-pr mode: each task's PR was created, CI-verified, bot-reviewed, and merged (gh pr merge); list each PR URL with its merged status
Worktree cleanup status — one line per task, in one of these categories (omitted when --terse is set):
clean — worktree removed and branch deleted via Step 4g (the expected outcome for every successful task)force-cleaned — git worktree remove --force was required; note the reason (lock, disposable artifacts, etc.)preserved (failure) — intentionally retained for recovery; include the worktree path and branch name so the user can resume workLEAKED — Step 4g did not produce a clean result and the cause is not a recorded failure; this is a bug, surface it explicitly with the path and branch so the user can clean up manuallyRun a final audit before printing the report:
git worktree list --porcelain | awk '/^worktree /{print $2}' | grep -E '/worktree-' || echo "OK: no parallel-executor worktrees remain"
git branch --list 'feature/*' | sed 's/^[* ] //' || true
Any worktree-* path or feature/* branch that does not map to a preserved (failure) task in the report is a leak and must be reported as LEAKED, not silently omitted.
When --terse is set, omit all prose reminders, worktree audit lines, and mode-specific explanations. Emit only:
Completion Status lineThis is the preferred format for CI scripts or automation that parse the report output.
Reporting Symbols: Use the shared vocabulary in symbols.md for the per-task status column, the worktree cleanup status column, and the wave-level summary line. Parallel-specific addition: 🚨 for LEAKED worktree or branch detected by the final audit (Step 5) — surface explicitly, never reduce to ❌. Leaks are a distinct severity category the user must act on regardless of overall run status. For multi-step worktree lifecycle traces, use the flow operator » (e.g. worktree ✅ » impl ✅ » verify ✅ » merge ✅ » cleanup ✅).
Completion Status: End every report with one of these four declarations on its own line. This is the final line of the report — nothing follows it.
| Status | When to use |
|---|---|
DONE | All tasks completed, all worktrees clean, no open issues |
DONE_WITH_CONCERNS | Run completed but with caveats — failed tasks, LEAKED worktrees, advisor budget exceeded, or non-critical warnings |
BLOCKED | Run stopped and cannot continue — merge conflict, circular dependency, push rejected, or a decision requiring human input |
NEEDS_CONTEXT | Task list or arguments are ambiguous; the run cannot start without clarification |
Operational Self-Improvement: Before deleting the checkpoint file, append any genuinely new project-specific operational findings to .ywc-learnings.jsonl (one JSON object per line, format {"ts":"<ISO-8601>","skill":"ywc-parallel-executor","project":"<basename>","learning":"<one sentence>"}). Add the file to .gitignore if absent. Record only project-specific facts (package manager quirks, worktree isolation issues, build-step ordering, wave sizing observations); skip generic programming facts and anything already in CLAUDE.md. If nothing new in this run, skip the write entirely; empty entries are noise.
State cleanup: After displaying the report, delete the checkpoint file — the run is complete:
rm -f .ywc-run-state.json
LEAKED in Step 5 with the exact git worktree remove / git branch -d commands the user should run, so cleanup is one paste away.git push rejectedGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub yongwoon/ywc-agent-toolkit