--- name: retcon description: Retcon a PR branch — use when a maintainer says "retcon", or asks to clean up, regroup, squash, or reorganize a PR's commits into sensibly grouped commits; one commit per affected package with implementation and tests together, conventional-commit messages, and yarn.lock isolated in its own commit. Resets to the base and restages the same net diff; the merge result is byte-identical. Not a rebase onto a new base. --- # Retcon Reset a PR's branch to its base and restage the same net diff as a sensibly grouped commit history. The PR's diff doesn't change. Only the commit grouping changes. The name is short for *retroactive continuity*: fixing the story the branch's history tells. ## When to use - A maintainer says "retcon #N" or "please retcon this branch". - A PR's history sprawled during review: one commit per round of feedback, work-in-progress commits, lockfile churn mixed in with code. - Reviewers upstream read commit by commit, and the history isn't ready for that. A retcon is **not** a rebase onto a new base. If the branch lags its base, rebase first, then retcon. A retcon assumes the base is current. ## What a retcon produces A linear history with commits grouped as: - **One commit per affected package.** Changes under `packages/foo/` go in one commit, and changes under `packages/bar/` go in another. Each message is scoped to its package: `feat(foo): ...`, `fix(bar): ...`. - **One `chore: Update yarn.lock` commit**, separate from the `package.json` change that caused it. This applies the [yarn.lock separate-commit rule](../yarn-lock-separate-commit/SKILL.md) to the whole branch at once. - **Implementation and tests in the same commit.** A package's behavior change and the tests covering it ship together, not as a "test" commit plus an "implement" commit. - **Conventional-commit messages.** The first line is `(): `. The body, if any, says *why*, not what the diff shows. The PR's net diff (its merge result) is byte-identical before and after. ## Procedure Run on the PR's head branch, in a worktree that is up to date with the base. ```sh # 0. Sanity: clean tree, and the tip matches the remote we'll push to. git status --porcelain # must be empty git log --oneline origin/..HEAD # must be empty # 1. Save the pre-retcon tip for the no-net-change check. git tag pre-retcon HEAD # 2. Find the base branch. BASE=$(gh pr view --json baseRefName --jq .baseRefName) # 3. Reset to the base, keeping the net diff as unstaged changes. # --mixed, so each `git add` that follows is deliberate. git reset --mixed origin/$BASE # 4. Restage by package: implementation + tests together. git add packages/foo/ git commit -m "feat(foo): " git add packages/bar/ git commit -m "fix(bar): " # ... one commit per affected package ... # 5. Lockfile last. git add yarn.lock git commit -m "chore: Update yarn.lock" # 6. Verify: no net change. git diff pre-retcon HEAD # must be empty # 7. Push without clobbering concurrent work, then clean up. git push --force-with-lease origin HEAD: git tag -d pre-retcon ``` Run `git status` after each `git add` to confirm that only the intended files are staged. ## Changes that don't fit one package Name these groupings when you report the retcon, so the reviewer reads the new history as a deliberate shape: - **Top-level config or build:** one `chore:` or `build:` commit. - **Cross-package refactors that must land atomically:** one `refactor():` commit, where the area names the concept, not a package. - **Docs-only files** (READMEs, design notes): one `docs:` commit, or `docs():` if it's scoped. ## Net diff is invariant This is the property everything else rests on. `git diff pre-retcon HEAD` must be empty, because the two heads have the same tree. If it isn't empty, **don't push.** Run `git reset --hard pre-retcon` and start over. ## Pitfalls - **No pre-retcon tag:** the no-net-change check has nothing to compare against. Tag before you reset. - **`git reset --hard` instead of `--mixed`:** it throws away the net diff. Use `--mixed`, or `--soft` to keep the index. - **Lockfile in a package commit:** `git add packages/foo/` can stage a lockfile that lives under that package. Check `git status`. - **Implementation without tests:** if a package commit adds behavior and changes no tests, either the tests live in a separate cross-package commit, or the code is untested. That's a review finding, not something the retcon should fix. - **The lease is rejected:** someone pushed since your last fetch. Don't fall back to `--force`. Fetch, then redo the retcon on the new tip. ## Origin Kris Kowal coined the verb: *"Please reset this branch and restage the changes in sensibly grouped commits. This should generally have a single commit for each affected package, a separate commit for chore: Update yarn.lock, conventional-commit messages, and a single implementation and test commit."* Adapted from [the retcon skill in kriscendobot/garden](https://github.com/kriscendobot/garden/blob/aa94ebd0877a931ac67e29018b0fd4b62c257ece/skills/retcon/SKILL.md) (May 2026).