From ywc-agent-toolkit
Delivers a completed feature branch through PR readiness, CI with bot review polling, merging, post-merge verification, task completion, and local branch cleanup.
How this skill is triggered — by the user, by Claude, or both
Slash command
/ywc-agent-toolkit:ywc-finish-branchThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Announce at start:** "I'm using the ywc-finish-branch skill to deliver this feature branch."
Announce at start: "I'm using the ywc-finish-branch skill to deliver this feature branch."
Encapsulates the post-implementation delivery flow that ywc-sequential-executor and ywc-parallel-executor previously inlined separately. One feature branch, one task — given a verified branch with passing local tests, this skill takes it the rest of the way to "done": optionally creates the PR, marks it ready, waits for CI, handles automated bot reviews, merges (PR or local), runs the post-merge hard gate, moves the task directory to completed/, and deletes the local feature branch. Worktree-specific cleanup is intentionally not in scope — that stays with ywc-parallel-executor because the lifecycle differs.
When tempted to skip a step, check this table first:
| Excuse | Reality |
|---|---|
| "Step 4 (Merge) succeeded, that means we are done" | The Definition of Done has 5 conditions. Merge is condition 2 of 5. Mark Task Complete (Step 7) and branch deletion (Step 8) are equally required — skipping them silently breaks dependency resolution for downstream tasks. |
"Post-merge verification is just running git log — skip it for speed" | The hard gate exists because git merge can silently no-op (already merged, nothing to do, fast-forward elided) and the caller cannot tell from exit code alone. Always verify. |
| "Bot review polling found 0 comments, skip the wait window" | Polling the full window is the contract. Bots can post asynchronously after CI completes; cutting short the window misses real findings. |
"Branch was already deleted by gh pr merge --delete-branch, skip the local cleanup" | --delete-branch only removes the remote branch. The local copy persists and the next task's branch creation will conflict. Always run git branch -d. |
| "Mode flag conflict was already validated upstream, skip the check here" | This skill is a callable unit and may be invoked outside the executors. Validate flag conflicts at every entry point, not just the upstream caller. |
| "Mark Task Complete is bookkeeping — defer it for after all wave tasks merge" | tasks/completed/ is the contract that downstream tasks read for dependency resolution. Move-then-merge ordering is forbidden by the Definition of Done; merge-then-move within the same task cycle is required. |
"If git merge --no-ff reports a conflict, abort and try again" | An aborted merge loses the working state needed to debug the conflict. Never auto-abort — surface the conflict to the user so they can inspect it. |
"build-pr-title.py exited 1 — I'll use just the slug as the title" | Exit 1 means TASK_NUMBER was not detected. Stop and return NEEDS_CONTEXT — do not construct a title without [TASK_NUMBER]. A title without the task number cannot be traced back to the task in the audit trail. |
"ywc-create-pr generates its own title from git log, so --title is optional" | ywc-create-pr's internal title generation does not know the task-number format. It silently produces a title without [TASK_NUM]. Always pass --title explicitly with [TASK_NUMBER] already included. |
| "CI failed twice — return BLOCKED and let the user fix it" | Before returning BLOCKED, categorize the failure type. Lint/format failures are auto-fixable without judgment. Type errors and build errors usually have a clear mechanical fix visible in the log. Only return BLOCKED when the root cause requires a design decision or all fix attempts are genuinely exhausted. |
"CI is green, so gh pr merge will succeed" | A PR can be CONFLICTING or BEHIND against the base even with green CI — a sibling task or another PR merged first. Check gh pr view --json mergeable,mergeStateStatus before merging instead of letting gh pr merge fail. If the branch is merely behind, merge the base into it (never rebase — it orphans review threads) and re-verify CI; if it is a real textual conflict, surface it and return BLOCKED. See references/pr-conflict-resolution.md. |
| "The UL update in Step 1.5 is optional bookkeeping — skip it to save time" | The glossary is the shared vocabulary LLMs use in every subsequent prompt on this project. A branch that introduces new domain terms without updating the glossary silently pollutes downstream code generation and spec reviews. If docs/ubiquitous-language.md does not exist, Step 1.5 skips automatically — there is no overhead in that case. |
Violating the letter of these rules is violating the spirit. Delivery is the step where the user trusts the executor most — earning that trust requires honesty about partial completion.
| Parameter | Format | Example | Description |
|---|---|---|---|
--mode | <mode> | --mode local-merge | One of normal-pr, local-merge, draft, skip-ci-wait, per-task-pr. See Modes. |
--branch | feature/<task-name> | --branch feature/000001-010-db-create-users | The feature branch to deliver. Must already exist locally. |
--base-branch | <branch-name> | --base-branch develop | Target branch. Default: auto-detect (develop > main > master). |
--task-name | <task-name> | --task-name 000001-010-db-create-users | Task directory name under <tasks-dir>/, used for the Mark Complete commit. |
--tasks-dir | <path> | --tasks-dir tasks/ | Tasks directory root. Default: tasks/. |
--pr-lang | <lang> | --pr-lang ja | PR title/description language. Default: auto-detect from CLAUDE.md / AGENTS.md / recent PRs. |
--bot-action | sequential | parallel | --bot-action sequential | Post-bot polling behavior. Default: sequential (re-run CI after bot fixes). Use parallel when called from a wave loop where CI does not re-gate between bot iterations. |
--defer-push | flag | Skip the push of the Mark Complete commit. Used by range-mode callers that batch pushes at the end of the range. | |
--keep-branch | flag | Skip the local feature branch deletion (git branch -d). Used by ywc-parallel-executor, where the branch is checked out in a worktree at the time of merge — git branch -d would fail until the worktree is removed. Caller takes responsibility for the eventual git worktree remove + git branch -d. | |
--worktree-path | <path> | --worktree-path /abs/run-000024-010-000024-020 | Run every git operation in Steps 1 and 5–8 as git -C <path> … so the delivery acts on the run worktree's HEAD instead of the main checkout. When unset, all git commands run against the repo root (current behavior — 100% backward compatible). Passed by ywc-sequential-executor --worktree; worktree creation and removal stay the caller's responsibility. See Worktree-path mode. |
Mode-flag mutual exclusivity: --mode is a single value; conflicts are impossible by construction. The caller is responsible for resolving ambiguity (e.g., the upstream executor's --draft and --local-merge flags must be collapsed into one --mode value before invoking this skill).
When --worktree-path <path> is set, every git command this skill issues — the Step 1 git branch --list / git rev-parse validations, the Step 5 merge/checkout/pull/push/branch-delete sequence, the Step 6 post-merge git log, the Step 7 git check-ignore / git mv / git commit / post-move check, and the Step 8 checkout/pull/branch-delete — runs as git -C <path> …. The delivery semantics are unchanged: CI wait, bot-review polling, the merge-readiness gate, and Mark Task Complete behave identically; only the working directory the git commands act on differs (the run worktree instead of the main checkout). gh commands (gh pr ready / gh pr checks / gh pr merge / gh pr view) operate on the remote PR, not a working tree, so they are unaffected by --worktree-path.
HEAD invariant (per-task feature branch deletion is safe). At the moment Step 5 runs, the run worktree's HEAD is on the integration branch (the worktree's permanent checkout). finish-branch creates/merges feature/<task-name> and then checks out the integration branch before deleting feature/<task-name>, so the per-task branch is never the worktree's current checkout when git branch -d runs. The integration branch (and the trunk base branch) is never a deletion target here — finish-branch only deletes the per-task feature/<task-name>. This is why per-task branch deletion succeeds inside a worktree without needing --keep-branch: only a branch that is checked out in a worktree is undeletable, and the per-task branch is not.
| Mode | PR? | CI wait? | Merge? | Mark Complete? | Cleanup? | Use case |
|---|---|---|---|---|---|---|
normal-pr | yes (delegates to ywc-create-pr) | yes | yes (gh pr merge --delete-branch) | yes | yes (git branch -d) | Default upstream flow |
local-merge | no | no | yes (git merge --no-ff + push) | yes | yes | Personal repos, fast iteration |
draft | yes | no | no | no (task stays open until manually merged) | no | User wants PR for review without auto-merge |
skip-ci-wait | yes (mark ready) | no | no | no | no | User merges manually after offline review |
per-task-pr | yes | no (caller handles per-wave CI) | no (caller merges locally for wave progression) | no (caller marks complete in wave loop) | no | ywc-parallel-executor per-task PR delivery |
per-task-pr is the special mode used inside ywc-parallel-executor's wave loop — this skill creates and pushes the PR but stops short of merging or marking complete, because wave-level orchestration handles those steps. For all other modes, this skill executes the full lifecycle.
A --mode normal-pr or --mode local-merge invocation is done only when all of the following have happened, in this order:
gh pr merge --delete-branch (normal-pr) or git merge --no-ff succeeded locally and was pushed (local-merge).git log -1 --format="%s" on the base branch begins with Merge branch 'feature/.git mv <tasks-dir>/<task-name> <tasks-dir>/completed/<task-name> and the chore: mark <task-name> as completed commit was created.git branch -d feature/<task-name> succeeded (refuses to delete an unmerged branch — that is the safety check).For --mode draft and --mode skip-ci-wait, conditions 2–5 are intentionally not performed; the skill's responsibility ends at PR creation (and ready-marking for skip-ci-wait). For --mode per-task-pr, only the PR creation portion runs; the caller handles the rest within the wave loop.
--branch exists locally (git branch --list <branch> returns the branch).--base-branch is reachable (git rev-parse --verify <base-branch> succeeds).--task-name matches an entry under <tasks-dir>/ (not under <tasks-dir>/completed/).gh auth status succeeds.When --worktree-path <path> is set, run the git branch --list and git rev-parse validations as git -C <path> … (see Worktree-path mode).
If any check fails, return NEEDS_CONTEXT with the specific missing input. Do not proceed with partial information.
Check whether docs/ubiquitous-language.md exists in the project root:
test -f docs/ubiquitous-language.md
ywc-ubiquitous-language --mode update. This runs before PR creation so that any new domain terms introduced by this branch are captured in the glossary before the PR description is written.ywc-create-pr is called in Step 2 with --skip-ubiquitous-update to prevent a second invocation of the same update.
For --mode ∈ {normal-pr, draft, skip-ci-wait, per-task-pr}: construct the PR title per the format below, then invoke ywc-create-pr passing:
--title "<constructed-title>" — the [task-number] description string built from --task-name and --pr-lang--lang <pr-lang> — so the description is written in the correct language--base-branch <base-branch> — the resolved target branch--skip-post-ci-check — prevents ywc-create-pr from running its own post-PR CI + bot check; this skill handles CI verification in Step 4--skip-ubiquitous-update — prevents ywc-create-pr from running the UL update a second time; Step 1.5 already ran itywc-create-pr handles security check, CI pre-push validation, push, and PR creation as a draft. Because --title is provided explicitly, ywc-create-pr will use it verbatim and skip its own title-generation step.
PR title format: [<task-number>] <human-readable description in --pr-lang>
Run the bundled script to extract the task number and English slug without regex parsing:
python claude-code/skills/ywc-finish-branch/scripts/build-pr-title.py <task-name>
# TASK_NUMBER=000001-010
# SLUG_EN=Db Create Users Table
If the script exits 1 (TASK_NUMBER is empty): stop immediately and return NEEDS_CONTEXT — report the task name that failed to parse. Do not construct a title with an empty task number.
For English PRs (--pr-lang en), use --format title to get the complete title directly:
python claude-code/skills/ywc-finish-branch/scripts/build-pr-title.py <task-name> --format title
# [000001-010] Db Create Users Table
For other languages, translate only SLUG_EN to --pr-lang, then compose: [<TASK_NUMBER>] <translated-slug>.
The script supports both task-name formats (new 000001-010-slug and legacy 001010-slug). Examples of final titles: [000001-010] Create users table (en) · [000001-010] ユーザーテーブル作成 (ja) · [001010] DB 사용자 테이블 생성 (ko).
Before calling ywc-create-pr: verify the constructed title string starts with [. If it does not, stop — the [TASK_NUMBER] prefix was lost somewhere in the construction. Do not call ywc-create-pr without a valid title.
For --mode local-merge: skip this step entirely. Local-merge does not produce a PR.
Invoke gh pr ready <pr-number> to convert the draft into an open PR. Skip for --mode draft, --mode skip-ci-wait, --mode per-task-pr (those modes leave the PR as draft or hand control back to the caller).
Action required: Read
../references/pr-bot-polling.mdnow before proceeding. The 60-second initial wait, the 300-second polling window, theBOT_COUNTjq query, and the merge condition are all defined there. Do not implement your own polling logic without reading it first.
Run the CI wait + fix loop and the bot review polling per ../references/pr-bot-polling.md. The polling reference defines the canonical loop; this skill applies its --bot-action parameter to choose the post-bot behavior:
--bot-action sequential: after ywc-handle-pr-reviews runs, re-run the CI verification step before re-polling. Bot fixes may trigger a new CI run that must pass before merging.--bot-action parallel: after ywc-handle-pr-reviews runs, re-poll without re-gating on CI. The wave loop in ywc-parallel-executor handles CI at the wave level.CI failure handling (up to 2 fix attempts per failure cycle):
Identify failing checks:
gh pr checks <pr-number>
Get failure logs:
# Get the most recent failed run ID for the feature branch
gh run list --branch <feature-branch> \
--json databaseId,name,conclusion \
--jq '.[] | select(.conclusion == "failure") | .databaseId' | head -1
gh run view <run-id> --log-failed
Categorize and fix by failure type:
| Failure type | Fix action |
|---|---|
| Lint / format | Run the project's auto-fix command (eslint --fix, prettier --write, ruff --fix, biome check --apply), commit, push |
| Type errors | Read compiler output, fix type mismatches in affected files, commit, push |
| Test failures | Analyze failing test output, fix the implementation (never disable or weaken tests), commit, push |
| Build errors | Read compiler/bundler output, fix compilation or import errors, commit, push |
After each push, re-run the CI wait and bot review polling loop (return to the top of Step 4).
If CI still fails after 2 fix attempts, return BLOCKED with: the failing check name(s), a log excerpt (last 30 lines), the PR URL, and the failure category so the user has enough context to resolve it manually.
CI green is necessary but not sufficient — the base may have advanced into a conflict while CI ran. Before Step 5's gh pr merge, confirm the PR can actually merge:
Action required: Read
../references/pr-conflict-resolution.md— it defines themergeable/mergeStateStatustable and the merge-not-rebase update procedure.
gh pr view <pr-number> --json mergeable,mergeStateStatus --jq '{mergeable, mergeStateStatus}'
MERGEABLE / CLEAN → proceed to Step 5.BEHIND (branch protection requires up-to-date; no textual conflict) → merge the base into the feature branch (never rebase), push, and re-verify CI (counts as one CI cycle). Then re-check the gate.CONFLICTING / DIRTY that auto-resolves → merge the base into the feature branch (never rebase), push, and re-verify CI (counts as one CI cycle). Then re-check the gate.CONFLICTING / DIRTY with real textual conflicts → surface the conflicting files + PR URL and return BLOCKED. Do not auto-resolve or force-push.BLOCKED (a required check or review is missing — not a conflict) → do not run the base-merge procedure; surface the outstanding required check/review and return BLOCKED so the user can resolve the gate.UNKNOWN → poll briefly per the reference, then re-read.Skip this step entirely for --mode local-merge, --mode draft, --mode skip-ci-wait, --mode per-task-pr.
For --mode normal-pr:
gh pr merge <pr-number> --delete-branch
For --mode local-merge:
# Caller has already verified its Step 4 (Task Verification); this skill does not re-verify.
git checkout <base-branch>
git pull origin <base-branch>
git merge --no-ff feature/<task-name> -m "Merge branch 'feature/<task-name>'"
git push origin <base-branch> # omit when --defer-push is set
git branch -d feature/<task-name> # omit when --keep-branch is set (parallel wave loop case)
When --worktree-path <path> is set, prefix every git command above with -C <path> (e.g. git -C <path> checkout <base-branch>, git -C <path> merge --no-ff feature/<task-name> …, git -C <path> branch -d feature/<task-name>); here <base-branch> is the run's integration branch (the worktree's checkout). Per the Worktree-path mode HEAD invariant, the integration branch is checked out before the per-task feature/<task-name> is deleted, so the delete succeeds.
For all other modes: skip — merge happens later (manual for draft, caller for skip-ci-wait and per-task-pr).
Why git merge --no-ff specifically (not cherry-pick, not direct commit on main): preserves original commit SHAs, marks the task boundary in git log --graph, and lets git branch -d act as a built-in safety check that refuses to delete an unmerged branch. Full rationale lives in the upstream executor's references/branch-lifecycle.md.
Error handling: git merge conflict → stop and ask the user to resolve manually (do not auto-abort; the user may want to inspect). gh pr merge failure due to conflict/out-of-date → apply the Step 4 final Merge-Readiness Gate (references/pr-conflict-resolution.md): merge the base into the feature branch and re-verify, or return BLOCKED on a real textual conflict. Other gh pr merge failures → stop and report. Push rejected (remote moved) → stop; never force-push.
For --mode normal-pr and --mode local-merge:
git log -1 --format="%s"
The output must begin with Merge branch 'feature/. If not, the merge did not execute correctly — investigate and retry before proceeding to Step 7. Do not write a completion marker for a merge that did not happen; downstream tasks would resolve dependencies against a broken contract. When --worktree-path <path> is set, run this check as git -C <path> log -1 --format="%s".
For --mode normal-pr and --mode local-merge:
Use the shared marker script — it handles the .gitignore branch, the mandatory marker commit, and the post-move verification in one deterministic step (exits non-zero if anything failed):
bash claude-code/skills/scripts/mark-complete.sh <tasks-dir> <task-name> [--push | --defer-push]
Why a script, not inline git: git mv cannot stage a path inside a gitignored <tasks-dir> (the move produces no diff), so that case needs a plain mv plus an --allow-empty marker commit, while a tracked <tasks-dir> uses git mv. The script detects which applies. The chore: mark <task-name> as completed commit is mandatory in both cases — it is the git log audit boundary that humans, audit tooling, downstream skills, the Completion Report, and resume / replay all rely on to verify task completion at a specific commit. The script then verifies the move (destination exists, source gone, marker commit at HEAD) and exits non-zero if any check fails; do not declare DONE on a non-zero exit — investigate and retry.
When --worktree-path <path> is set, run mark-complete.sh so its git check-ignore / git mv / git commit and post-move git log operate on the worktree (invoke it with the worktree as the working directory, e.g. cd <path> && bash <repo>/claude-code/skills/scripts/mark-complete.sh …); the <tasks-dir> move acts on <path>/<tasks-dir>/… directly, and the verification checks resolve against the worktree's HEAD (see Worktree-path mode).
Push strategy: pass --push to mark-complete.sh for --mode local-merge and for --mode normal-pr when --defer-push is not set. Otherwise pass --defer-push (the script's default) so the caller can batch completion-marker commits and push them once at the end of the range. The caller is responsible for the eventual push when deferral is requested.
For --mode draft, --mode skip-ci-wait, --mode per-task-pr: skip Step 7 entirely. The task is not yet merged; marking it complete would be incorrect.
For --mode normal-pr:
git checkout <base-branch>
git pull origin <base-branch>
git branch -d feature/<task-name> # omit when --keep-branch is set
The local branch must be deleted explicitly because gh pr merge --delete-branch only removes the remote. Skip the deletion when --keep-branch is set — the caller (typically ywc-parallel-executor) will release the worktree and delete the branch in its own cleanup step. When --worktree-path <path> is set, run all three commands as git -C <path> … (here <base-branch> is the run's integration branch); the per-task feature/<task-name> deletion is safe because the worktree HEAD is on the integration branch per the Worktree-path mode invariant.
For --mode local-merge: already handled inside Step 5's command sequence (which honors --keep-branch likewise).
For all other modes: skip. The branch stays alive for the user or caller to handle.
Worktree prune (when caller used a worktree) — if the caller created a per-task worktree (ywc-parallel-executor does this), the post-cleanup prune is delegated to the ywc-worktrees skill:
ywc-worktrees --mode prune --task-name <task-name>
ywc-worktrees --mode prune runs the bundled cleanup-worktree.sh against the resolved worktree path — git worktree remove + local git branch -d + git worktree prune + post-removal verification, refusing to operate on dirty worktrees without --force. This skill does not inline the prune logic; the worktree priority chain (.worktrees/ > CLAUDE.md worktree_root > --root fallback) and the dirty-tree safety check live in one place. Skip this delegation when no worktree was created (the typical --mode local-merge / --mode normal-pr flow that ran inside the main checkout).
## Finish Branch Result: <task-name>
### Mode
<mode>
### Outcome
- PR: <pr-url-or-"none-local-merge">
- Merge: <commit-sha-or-"deferred">
- Mark Complete commit: <commit-sha-or-"skipped">
- Local branch: <"deleted"|"preserved">
### Verification
- Post-merge gate: <passed|skipped|failed>
- Post-move gate: <passed|skipped|failed>
### Completion Status
<DONE | DONE_WITH_CONCERNS | BLOCKED | NEEDS_CONTEXT>
Completion Status rules:
| Status | When to use |
|---|---|
DONE | All applicable steps for the chosen mode succeeded; verification gates passed |
DONE_WITH_CONCERNS | Delivery succeeded but with caveats (e.g., advisor budget exceeded for the bot-polling loop, or --defer-push was set and the caller now owns the push) |
BLOCKED | Cannot proceed — merge conflict, CI failure unfixable in 2 attempts, push rejected, or post-merge verification failed |
NEEDS_CONTEXT | One of --branch, --base-branch, --task-name, --tasks-dir, or --mode was missing or invalid; no work was performed |
This skill produces operational side effects (commits, pushes, merges), not source code. The "Banned Output Patterns" idea applied here means: never declare DONE while leaving any of the following:
chore: mark <task-name> as completed commit that has not been verified by the post-move testnormal-pr and local-merge, when --keep-branch is not set)local-merge always; normal-pr when --defer-push is not set)Each of these is a hidden defect that breaks the next task's dependency resolution. Surface them honestly via DONE_WITH_CONCERNS or BLOCKED rather than declaring DONE.
ywc-sequential-executor (replaces its Steps 5–8), ywc-parallel-executor (replaces the merge + mark-complete portion of Step 4e–4f; worktree cleanup at Step 4g stays in parallel).ywc-create-pr (Step 2), ywc-handle-pr-reviews (Step 4 inside the bot polling loop).This skill is a single-task delivery unit. It does not iterate over a range, manage a wave, or maintain checkpoint state — those concerns belong to the calling executor. Worktree creation and removal are intentionally out of scope, because the lifecycle differs between callers; placing it here would force one of them to special-case its cleanup. What this skill does support is operating inside a caller-supplied worktree via --worktree-path (see Worktree-path mode): ywc-sequential-executor --worktree runs its whole range inside one run worktree and passes that path so finish-branch's git commands target the worktree HEAD, while the worktree's own create/prune lifecycle stays with that caller. The historical "sequential (no worktree)" assumption now reads: sequential delivers in the main checkout unless --worktree is active, in which case --worktree-path redirects the git commands; parallel still owns its per-task worktrees.
npx claudepluginhub yongwoon/ywc-agent-toolkitGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.