Finishing a development branch
Skill anortham/razorback/skills/finishing-a-development-branch
Use when implementation is complete, branch verification passes, and you need to decide how to integrate the workFrom its SKILL.md
npx -y skills add anortham/razorback --skill finishing-a-development-branchAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
19.4 KB, as published. Nobody here has run it
Finishing a Development Branch
Overview
Guide completion of development work by selecting a mode, then executing the appropriate flow (autonomous push+PR or interactive menu).
Core principle: Verify the project-defined branch gate -> choose mode (autonomous default) -> execute -> clean up.
Announce at start: "I'm using the finishing-a-development-branch skill to complete this work."
Mode Selection
If the agent was just finishing an autonomous execution run (i.e. this skill is being invoked as the final step of razorback:executing-plans or razorback:subagent-driven-development), use Autonomous Mode. If the user invoked the skill directly (e.g. "finish this branch"), use Interactive Mode. In ambiguous cases, default to Autonomous - run-to-completion is the bias.
Autonomous Mode
No menu, no prompts. Push the branch, open a PR with the morning-report summary, write the full report to .memories/, emit a one-line terminal pointer, exit. Merge is never auto-performed.
Step 1: Verify branch gate
Use the plan's Verification Strategy and verification ledger.
Run the project-defined branch-gate scope before push or PR. If the verification ledger already has a passing branch-gate entry for the current HEAD, reuse that evidence instead of rerunning the same command. Add any required expensive-specialist scopes when touched areas demand them. Running the branch gate includes running the plan's declared Security scope commands (security-secrets, security-deps — razorback:security-review); a plan with none declared skips them, and the morning report renders that.
If required verification fails, keep the branch local and diagnose, repair, and rerun the failed scope while a safe, plan-consistent recovery path remains. Follow razorback:security-review for scanner or security-finding failures so its hard gates remain intact. Record each recovery attempt and refresh the verification ledger for the resulting HEAD. Do not classify the first failed run as blocker taxonomy #5.
Only after recovery paths are exhausted, classify the failure with the canonical blocker taxonomy. Repeated test failures with no further viable strategy are blocker taxonomy #5; environmental failures may instead be taxonomy #1. Do not create a PR. Instead:
- Render a partial morning report with
Status: Blocked, the failure summary in theTestssection, and the blocker description inBlockers hit. - Write it to
.memories/autonomous-run-YYYY-MM-DD-<slug>.md. - Emit terminal one-liner:
Blocked. Report: .memories/autonomous-run-YYYY-MM-DD-<slug>.mdand exit.
If required verification passes, continue.
Step 2: Determine base branch and merge-base commit
git merge-base returns a commit SHA, not a branch name. Autonomous Mode needs both: the branch name for gh pr create --base, and the SHA for diff-range computation. Resolve them as two separate values:
# Prefer an explicit base from the plan/user, then the remote's default branch,
# then main/master. Merge-base-with-main alone is wrong for repos whose PRs
# target another branch (develop, release/*) — main almost always shares history.
if [ -n "$PLAN_BASE" ]; then
BASE_BRANCH="$PLAN_BASE"
elif DEFAULT_REF=$(git symbolic-ref --quiet refs/remotes/origin/HEAD 2>/dev/null); then
BASE_BRANCH="${DEFAULT_REF#refs/remotes/origin/}"
elif git show-ref --verify --quiet refs/heads/main; then
BASE_BRANCH=main
elif git show-ref --verify --quiet refs/heads/master; then
BASE_BRANCH=master
fi
if [ -z "$BASE_BRANCH" ] || ! BASE_SHA=$(git merge-base HEAD "$BASE_BRANCH" 2>/dev/null); then
echo "Cannot determine PR base branch/merge-base." >&2
# Blocker taxonomy #3 (plan-contradicting data): the branch doesn't descend
# from a known base. Emit a Blocked report per the failure protocol below and
# exit. Do NOT push.
fi
Use $BASE_SHA for any base..HEAD range computation (e.g. git diff --stat $BASE_SHA..HEAD in Step 3). Use $BASE_BRANCH for gh pr create --base "$BASE_BRANCH" in Step 6.
If both lookups fail (no main, no master ancestor), that's a blocker per taxonomy #3. Render a partial morning report with Status: Blocked, describe the missing base in Blockers hit, write it to .memories/autonomous-run-YYYY-MM-DD-<slug>.md, emit the terminal one-liner, and exit. Do not push.
Step 2a: Reconcile source-control state
Run Check B of ../using-razorback/references/source-control-hygiene.md. This is the run's last chance to notice work that is finished but not integrated — after this, the report says "Complete".
git status --short --branch
git worktree list
# per listed worktree:
git -C <path> status --short --branch
git log --oneline "$BASE_BRANCH".."<branch>"
Classify every worktree and branch the inventory names:
- Landed — its commits are ancestors of this branch, or already merged into the base. Nothing to do.
- Riding along — its commits are on the branch this run is about to push. They ship with the PR. Nothing to do.
- Stranded — commits absent from both the base and this branch, or uncommitted changes in a worktree this run created. These must be resolved or named.
- The user's — a worktree outside razorback-managed locations, or a branch this run did not create. Report the path; change nothing.
For stranded work, take the plan-consistent path: if it belongs in this PR and merges cleanly, land it on the branch and re-run the branch gate (the diff changed, so Step 1's evidence is stale). If it does not belong, leave it and name it.
Autonomous Mode does not remove worktrees. The PR is open and the branch is still live; disposition is the user's call after review.
Feed the outcome into the report's Source control section (Step 3). Stranded work that was deliberately left is a Next steps item, not a blocker — the run still reports Complete. Stranded work you could neither land nor explain is a judgment call to log, not a reason to withhold the report.
Step 3: Render morning report
Fill the placeholders in ./morning-report-template.md using the fields the caller accumulated during execution (plan name + path, branch name, phases complete/total, tasks complete/total, duration, judgment calls log, external review outcome, tests summary, blockers, files changed from git diff --stat $BASE_SHA..HEAD, source-control state from Step 2a, next steps).
Produce three renderings:
- Full report — every section filled in, for
.memories/and for review. - PR summary — status, What shipped, External review, Blockers, Next steps only. The Judgment calls section is not inlined in the PR description; the PR body points at the
.memories/file instead (committed in Step 4, so the link is live the moment the PR opens). - Report digest (opt-in) — the full report's
.htmlsibling (same basename), composed per therazorback:using-razorbackskill'sreferences/digest-kit.md. Write it only when the user asked for a digest in this session or in project instructions. Never generate one unprompted. When no digest was requested, skip every digest step below.
Step 4: Write full report + commit
Write the full rendered report to .memories/autonomous-run-YYYY-MM-DD-<slug>.md — plus its digest sibling .memories/autonomous-run-YYYY-MM-DD-<slug>.html when one was requested — where <slug> is a short kebab-case identifier for the plan (e.g. autonomous-execution). Committing them before the push means the PR includes the report from its first revision — no dead link in the PR body. The PR does not exist yet, so render {{pr_url}} as pending — filled in after PR creation; Step 7 writes the real URL back.
git add .memories/autonomous-run-YYYY-MM-DD-<slug>.md # add the .html sibling too when a digest was requested
git commit -m "docs: autonomous run report for <plan name>"
This commit (and the Step 7 URL write-back) are metadata-only: they touch nothing outside .memories/, so the Step 1 branch-gate evidence carries over to the new HEAD. If anything outside .memories/ changes after Step 1, the evidence is invalidated — re-run the branch gate before pushing.
When a digest exists, every report mutation after this step updates the digest sibling's matching field and stages both files in the same commit — a committed digest that contradicts its markdown is worse than none.
Step 5: Push branch
git push -u origin <branch>
If the push is rejected (branch already tracks a different remote, non-fast-forward, network failure), log the exact error in the report's Blockers hit section, set Status: Blocked, commit the updated report, emit the terminal pointer, and exit. Do not retry with --force.
Step 6: Create PR
Work down the forge ladder. Stop at the first rung that succeeds; a rung that is unavailable or fails (not installed, auth, network, repo not on origin) drops to the next.
gh(preferred — machine-readable URL for the Step 7 write-back):
gh pr create \
--base "$BASE_BRANCH" \
--title "<plan name or feature name>" \
--body "$(rendered_pr_summary)"
Capture the PR URL from gh's output for Step 7.
-
Another forge CLI, if present —
glab mr create(GitLab) ortea pr create(Gitea/Forgejo), with the equivalent base/title/body arguments. Capture the PR URL it prints for Step 7. -
The creation URL the forge printed on push — many forges answer the Step 5
git pushwith a ready-made PR-creation URL. Record it in the report'sPRfield asnot created — open <creation-url>, setStatus: Partial(branch pushed; PR needs one click), commit and push the update (both siblings, per Step 4), and emit the URL in the terminal pointer. -
No rung worked — update the report with the failure in
Blockers hitandStatus: Partial(the branch was pushed but the PR was not created), commit and push the update (both siblings, per Step 4), emit the terminal pointer, and exit.
Step 7: Write the PR URL back into the report
Applies to ladder rungs 1–2, which return the created PR's URL; rungs 3–4 already wrote their outcome in Step 6. Replace the pending — filled in after PR creation value in the committed report with the captured URL, update the digest's PR field to match when a digest exists (per Step 4), then commit and push the update. This is a metadata-only commit; the branch-gate evidence still holds (see Step 4).
git add .memories/autonomous-run-YYYY-MM-DD-<slug>.md # add the .html sibling too when a digest was requested
git commit -m "docs: record PR URL in run report"
git push
Step 8: Emit terminal pointer
One line, then exit:
Done. PR: <url>. Report: .memories/autonomous-run-YYYY-MM-DD-<slug>.md.
When a digest was requested, append: Digest: .memories/autonomous-run-YYYY-MM-DD-<slug>.html
Autonomous Mode rules
- Never merge. Stopping at PR creation is the point; merge is a separate human (or agent) action after PR review.
- Never show a menu, never ask "which option". Autonomous means no prompts.
- Never fall back to Interactive Mode mid-run. If a step fails (push rejected, every forge-ladder rung failed, remote mismatch), emit a partial report with
Status: BlockedorStatus: Partialas appropriate and let the user resolve from there. - Always write the report to
.memories/, even on blocked/partial outcomes — the report is the user's morning read regardless of outcome. - Never report
Completewith unaccounted source-control state. Step 2a either lands stranded work or names it in theSource controlsection. A run that leaves commits stranded in another worktree without saying so is not complete, whatever the tests say. - Never remove a worktree in Autonomous Mode. The PR is open; disposition is the user's call.
Interactive Mode
Used when the user invokes this skill directly ("finish this branch"). Presents the classic 4-option menu.
Step 1: Verify Branch Gate
Before presenting options, verify the project-defined branch gate passes or reuse a passing ledger entry for current HEAD:
# Run the command specified by the plan's branch-gate scope
<branch-gate command>
Running the branch gate includes running the plan's declared Security scope commands (security-secrets, security-deps — razorback:security-review); a plan with none declared skips them.
If verification fails:
Branch verification failing (<N> failures). Must fix before completing:
[Show failures]
Cannot proceed with merge/PR until branch verification passes.
Stop. Don't proceed to Step 2.
If verification passes: Continue to Step 2.
Step 2: Determine Base Branch
git merge-base returns a commit SHA, not a branch name. Downstream steps need the branch name (git checkout <base-branch>, gh pr create --base), so resolve both values the same way Autonomous Step 2 does:
# Capture now: Step 4 changes branch and directory before Step 5 needs these values.
FEATURE_BRANCH=$(git branch --show-current)
WORKTREE_PATH=$(git rev-parse --show-toplevel)
# Prefer an explicit base from the plan/user, then the remote's default branch,
# then main/master. Merge-base-with-main alone is wrong for repos whose PRs
# target another branch (develop, release/*) — main almost always shares history.
if [ -n "$PLAN_BASE" ]; then
BASE_BRANCH="$PLAN_BASE"
elif DEFAULT_REF=$(git symbolic-ref --quiet refs/remotes/origin/HEAD 2>/dev/null); then
BASE_BRANCH="${DEFAULT_REF#refs/remotes/origin/}"
elif git show-ref --verify --quiet refs/heads/main; then
BASE_BRANCH=main
elif git show-ref --verify --quiet refs/heads/master; then
BASE_BRANCH=master
fi
if [ -z "$BASE_BRANCH" ] || ! BASE_SHA=$(git merge-base HEAD "$BASE_BRANCH" 2>/dev/null); then
echo "Cannot determine base branch/merge-base." >&2
# Interactive Mode: nothing resolved, so ask the user instead of guessing.
# Do NOT proceed to Step 3 until the base branch is confirmed.
fi
If nothing resolves, ask: "This branch split from main - is that correct?"
Step 3: Present Options
Present exactly these 4 options:
Implementation complete. What would you like to do?
1. Merge back to <base-branch> locally
2. Push and create a Pull Request
3. Keep the branch as-is (I'll handle it later)
4. Discard this work
Which option?
Don't add explanation - keep options concise.
Step 4: Execute Choice
Option 1: Merge Locally
# Switch to base branch
git checkout <base-branch>
# Pull latest
git pull
# Merge feature branch
git merge <feature-branch>
# Verify branch gate on merged result
<branch-gate command>
# If verification passes
git branch -d <feature-branch>
Then: Cleanup worktree (Step 5)
Option 2: Push and Create PR
# Push branch
git push -u origin <feature-branch>
# Create PR
gh pr create --title "<title>" --body "$(cat <<'EOF'
## Summary
<2-3 bullets of what changed>
## Test Plan
- [ ] <verification steps>
EOF
)"
If gh is unavailable or fails, walk the same forge ladder as Autonomous Step 6 (forge CLI → push-printed creation URL).
Then: Cleanup worktree (Step 5)
Option 3: Keep As-Is
Report: "Keeping branch <name>. Worktree preserved at <path>."
Don't cleanup worktree.
Option 4: Discard
Confirm first:
This will permanently delete:
- Branch <name>
- All commits: <commit-list>
- Worktree at <path>
Type 'discard' to confirm.
Wait for exact confirmation.
If confirmed:
git checkout <base-branch>
git branch -D <feature-branch>
Then: Cleanup worktree (Step 5)
Step 5: Cleanup Worktree
For Options 1, 2, 4:
Use $WORKTREE_PATH and $FEATURE_BRANCH captured in Step 2 — Step 4 already checked out the base branch, so re-deriving them here names the wrong branch and the cleanup silently no-ops.
Check whether the captured path is a listed worktree:
git worktree list | grep "$WORKTREE_PATH"
Provenance rule (canonical definition: ../using-razorback/references/source-control-hygiene.md): a worktree is yours to remove when this run created it, or when its path lies under a razorback-managed location — .worktrees/, worktrees/, or ~/.config/razorback/worktrees/<project>/. These are the locations razorback:using-git-worktrees Step 1b actually creates. A worktree anywhere else belongs to the host or the user: leave it in place and report its path instead.
If the path is listed and provenance says it is yours:
git worktree remove "$WORKTREE_PATH"
Then report: "Removed worktree $WORKTREE_PATH for branch $FEATURE_BRANCH."
For Option 3: Keep worktree.
Step 6: Reconcile Remaining Source-Control State
Run Check B of ../using-razorback/references/source-control-hygiene.md before reporting the branch finished. Report one line per outstanding item — a worktree still holding uncommitted changes, or a branch with commits absent from the base and from any PR opened here. Land it or name it; do not report "done" with the state unaccounted for.
Quick Reference
| Option | Merge | Push | Keep Worktree | Cleanup Branch |
|---|---|---|---|---|
| 1. Merge locally | Yes | - | - | Yes |
| 2. Create PR | - | Yes | Yes | - |
| 3. Keep as-is | - | - | Yes | - |
| 4. Discard | - | - | - | Yes (force) |
Common Mistakes
Skipping test verification
- Problem: Merge broken code, create failing PR
- Fix: Always verify tests before offering options
Open-ended questions
- Problem: "What should I do next?" -> ambiguous
- Fix: Present exactly 4 structured options
Automatic worktree cleanup
- Problem: Remove worktree when might need it (Option 2, 3)
- Fix: Only cleanup for Options 1 and 4
No confirmation for discard
- Problem: Accidentally delete work
- Fix: Require typed "discard" confirmation
Red Flags
Never:
- Claim the work is finished without running Check B (
../using-razorback/references/source-control-hygiene.md) - Leave commits stranded in another worktree or branch without naming them in the report
- Remove a worktree outside a razorback-managed location
- Proceed with failing tests
- Merge without verifying tests on result
- Delete work without confirmation
- Force-push without explicit request
- Merge in autonomous mode — merge is always a separate human (or agent) action after PR review
- Fall back to Interactive Mode mid-autonomous-run — if autonomous mode can't complete (e.g. every forge-ladder rung fails), emit a partial report with status
Blockedand let the user resolve; don't prompt for an option
Always:
- Reconcile source-control state (Check B) before reporting the work finished
- Verify tests before offering options (Interactive) or before pushing (Autonomous)
- In Interactive Mode, present exactly 4 options
- Get typed confirmation for Option 4
- Clean up worktree for Options 1 & 4 only
- In Autonomous Mode, emit the morning report to all three destinations (PR summary,
.memories/file, terminal one-liner) regardless of outcome
Integration
Called by:
razorback:executing-plans(Step 5) — Autonomous mode when the execution skill finishes cleanlyrazorback:subagent-driven-development(Step 5 or 4a+finish) — Autonomous mode- Direct user invocation ("finish this branch") — Interactive mode
Pairs with:
- using-git-worktrees - Cleans up worktree created by that skill (Interactive Mode, Options 1 & 4)
- morning-report-template.md (this directory) — template rendered by Autonomous Mode Step 3