Skip to content
Development
Skill

/post-merge-cleanup

Clean up an already-merged local branch after proving containment. Confirm each discard and preserve unique work.

From plugin
codearbiter
15256 skills20 agents38 commands

Claude Code

$ npx -y skills add arbiterForge/codeArbiter --skill post-merge-cleanup --agent claude-code

SKILL.md

post-merge-cleanup.SKILL.md
name: post-merge-cleanup
description: "Clean up an already-merged local branch after proving containment. Confirm each discard and preserve unique work."
argument-hint: "(none)"

post-merge-cleanup

The walk back from a merged PR, selected by cleanup intent or an explicit entry.

The work has landed on the default branch. What is left is local: the branch you are standing on, and whatever the run left in the tree. This skill returns you to a clean, fast-forwarded default checkout without losing anything you have not explicitly agreed to lose.

**It is ordinary lifecycle work and never needs `{{CMD:override}}`.** When it cannot proceed, it names the gate that stopped it. Issue #308 recorded the alternative: with no owner for this transition, the routing loop reached for `{{CMD:chore}}`, then for `{{CMD:override}}` — a bypass manufactured to cover a coverage hole.

<!-- catalog-compatibility-notice:start --> > Compatibility route. Prefer `{{CMD:pr}} --cleanup` for new explicit usage. The retained entry > and natural-language intent use this owner under > {{PLUGIN_ROOT}}/includes/command-compatibility.md. <!-- catalog-compatibility-notice:end -->

Entry boundaries

Select this owner for an already-merged branch cleanup request, `{{CMD:pr}} --cleanup`, or the explicit `{{CMD:cleanup}}` compatibility entry. Do not require repeated slash syntax. An explanation-only or dirty-state question is read-only, not a deletion request. An unmerged branch belongs to the finishing owner, not this cleanup. MUST NOT require `{{CMD:override}}` for ordinary cleanup; name a real blocker instead. This owner neither stages nor commits. Keeping work may hand it to the existing commit owner; no discard is implied by that handoff or by a cleanup request.

Pre-flight

Read this, or STOP and surface the gap — never guess the default-branch name:

  • `{{PROJECT_DIR}}/.codearbiter/CONTEXT.md` — the default-branch name.

If HEAD is already the default branch, there is no transition to make. Report the dirty state read-only and exit; branch pruning across *other* branches belongs to `{{CMD:standup}}`.

Phase 1 — Prove the merge · gate: BLOCK

A branch that looks merged is not merged. Establish it against the network, not against a stale local ref. This repo squash-merges by default, so SHA-ancestry alone is the wrong instrument for most landings: it holds for a fast-forward or a merge-commit landing, but a squash merge writes a new commit with no SHA lineage back to the branch, and `--is-ancestor` will report non-zero for a branch that landed cleanly. The gate is **content-containment**, proven by whichever instrument fits the landing, always reported as a fact:

1. `git fetch` the remote holding the default branch. A fetch that fails STOPs — an unfetched comparison proves nothing. 2. Prove containment with two primary instruments plus a fallback, tried in order: 1. **Ancestry** — `git merge-base --is-ancestor HEAD origin/<default>`. Holds for a fast-forward or merge-commit landing. If it holds, that is the proof: report it and move on. 2. **Squash-merge proof via the PR record** — if ancestry fails, run `gh pr list --head "$(git branch --show-current)" --state merged --json number,state,headRefOid,mergeCommit`. A `MERGED` PR whose `headRefOid` equals local `git rev-parse HEAD` proves every commit on this branch rode that PR's squash into the default branch. Report the PR number, that `headRefOid == HEAD`, the merge commit (`mergeCommit.oid` in that JSON), **and** the ancestry check's negative result — all as facts, not as a failure. Corroborate with `git diff --quiet origin/<default> HEAD` when it happens to hold; a **non-empty** diff alongside a valid PR proof is normal (the default branch advanced since the merge landed) and is reported as a fact, never treated as a failure. The PR-record identity — `MERGED` plus `headRefOid == HEAD` — is the load-bearing proof; the diff is corroboration only when it happens to be fresh, never a requirement. 3. **Fallback when `gh` is unavailable or no PR record exists** — `git diff --quiet origin/<default> HEAD` (the tree is byte-identical to the fetched default) is an acceptable fallback proof. Report it as such. 3. Report whichever instrument held as a fact — the default branch, the fetched SHA, and which of the three proofs established containment.

If none of the three hold, STOP exactly as today. Name the un-landed commits and route to `{{CMD:pr}}`; nothing is deleted here.

> Deliberate deviation from issue #586's suggested contract: that suggestion > required the squash-merge proof's diff to be empty. That requirement > re-breaks this gate the moment any later PR merges to the default branch — > the common state, not an edge case. The PR-record identity is what proves > containment; the diff is corroboration, demoted from requirement to fact.

Gate: the remote is fetched and HEAD is proven contained in the fetched default branch — by ancestry, by the PR-record squash proof, or by the byte-identical fallback — or the skill has stopped.

Phase 2 — Classify the residue · gate: BLOCK

List every dirty tracked change, every untracked file, and every stash reachable from this branch. Classify each into exactly one of three, and say *why* for each:

  • **Redundant** — byte-identical to content already on the default branch, or a

regenerated build artifact whose generator is committed and rerunnable. Safe to remove because removing it loses no information.

  • **Superseded** — an earlier form of something the merged PR already landed in a

better shape. Name what supersedes it.

  • **Unique** — anything else. This is the default: an artifact that cannot be

proven redundant or superseded IS unique. Uncertainty classifies as unique, not as redundant.

Present the classification before touching anything. A file whose class you cannot establish is repo

Read more
@arbiterforge
Ships withcodearbiter

Open-source governance and hard gates for AI coding agents across Claude Code, Codex CLI, and Pi.

15256 skills20 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
152
Star history of arbiterForge/codeArbiter on GitHub, a chart by star-history.com

Other skills on codearbiter.

brainstorming

brainstorming

Refine feature and sprint intent into a useful specification. Capture scope, observable criteria and material choices without…

commit-gate

commit-gate

Create a verified local Git commit when committing changes is requested. Not for explaining commit history, drafting a message…

debug

debug

Investigate an unexplained defect or unexpected behavior without changing application code. Use for root-cause diagnosis and an…

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills11d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc13d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react2d252k
@facebook@facebookInstall Skill

Questions about post-merge-cleanup.

Which coding agents can use post-merge-cleanup?
post-merge-cleanup installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.