Files
mad-skills/retcon/SKILL.md
T
dckc 792e535a56 Polish retcon and yarn-lock-separate-commit for this workspace
Prune references to other kriscendobot/garden skills and roles
(fixer, weaver, judge's panel, boatman/ferry, journal entries,
references/endo-but-for-bots) and per-PR session lore. Replace the
garden's created/updated/author frontmatter with name + a
trigger-first description. Keep the two skills cross-linked, since
both now live here, and credit upstream with links pinned to the
source commits.

retcon: tag pre-retcon tip as a numbered step; verify with a single
empty `git diff pre-retcon HEAD`; drop `git -C project`.
yarn-lock-separate-commit: promote the rebase --skip recipe from
field notes to its own section.

Generated-by: Claude Opus 5.5
2026-10-07 02:40:25 -05:00

5.2 KiB

name, description
name description
retcon 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 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 <type>(<scope>): <imperative summary>. 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.

# 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-branch>..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 <N> --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): <summary>"
git add packages/bar/
git commit -m "fix(bar): <summary>"
# ... 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:<head-branch>
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(<area>): commit, where the area names the concept, not a package.
  • Docs-only files (READMEs, design notes): one docs: commit, or docs(<scope>): 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 (May 2026).