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
This commit is contained in:
dckc committed 2026-10-07 02:40:25 -05:00
1 parent ae2949253c
commit 792e535a56
2 files changed
+139 -98

No files matched your search

+94 -71
View File
@@ -1,106 +1,129 @@
---
created: 2026-05-14
updated: 2026-05-14
author: gardener
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.
---
# Skill: retcon
# 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 is unchanged; only the commit grouping changes. The verb is the maintainer's: *"retcon"* = retroactively continuity-fix the branch's commit history.
The retcon's typical executor is the [fixer](../../roles/fixer/AGENT.md), dispatched against an open PR whose substance is right but whose commit grouping is not. The [weaver](../../roles/weaver/AGENT.md) handles rebase-onto-current-base; the retcon is the orthogonal operation of regrouping commits without moving the base.
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". The verb names this procedure.
- A PR's commit history grew sprawling during the jury-fixer loop (one commit per fixer round, intermediate work-in-progress commits, lockfile churn interleaved with code).
- Pre-ferry: the boatman's upstream is reviewing commit-by-commit and the bot-side history is not in shape for that. Retcon, then ferry.
- 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.
The retcon is **not** a rebase onto a new base. If the branch lags its base, run the weaver first (or chain "weave then retcon"). Retcon assumes the base is current.
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 the retcon produces
## What a retcon produces
A linear history on the PR branch with commits grouped as:
A linear history with commits grouped as:
- **One commit per affected package.** Changes under `packages/foo/` ship in one commit; changes under `packages/bar/` ship in another. Conventional-commit message scoped to the package (`feat(foo): ...`, `fix(bar): ...`, etc.).
- **One `chore: Update yarn.lock` commit** for the lockfile, separate from any `package.json` commit that caused it. Cite [yarn-lock-separate-commit](../yarn-lock-separate-commit/SKILL.md) for the discipline; the retcon is the place that discipline is *applied retroactively* across the whole branch, not commit-by-commit at write time.
- **Implementation and tests in the same commit.** A package's behavior change and the tests covering that change ship together. This is a deliberate departure from the "test, then implement" or "implement, then test" patterns that produce two commits per feature; the retcon wants one.
- **Conventional-commit messages** on every commit. The first line is `<type>(<scope>): <imperative summary>`; the body (when present) names *why*, not the diff.
- **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
`<type>(<scope>): <imperative summary>`. The body, if any, says *why*,
not what the diff shows.
The PR's net diff (the merge result) is byte-identical before and after the retcon. The judge's panel re-runs against the new commit shape: each per-package commit gets its own scope of attention rather than the panel having to mentally re-group sprawling history.
The PR's net diff (its merge result) is byte-identical before and
after.
## Procedure
Run inside the PR's project worktree on its head branch. The branch must be up to date with its base; if not, run a weaver first.
Run on the PR's head branch, in a worktree that is up to date with the
base.
```sh
# 0. Sanity: confirm the working tree is clean and the branch tip matches the
# remote we'll push to.
git -C project status --porcelain # must be empty
git -C project log --oneline origin/<head-branch>..HEAD # must be empty
# 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. Identify the base. For a PR, this is the base branch named in the PR's
# metadata (typically master or main, or llm for design-roadmap PRs).
BASE=$(gh pr view <N> -R <owner>/<repo> --json baseRefName --jq '.baseRefName')
# 1. Save the pre-retcon tip for the no-net-change check.
git tag pre-retcon HEAD
# 2. Reset to the base, keeping the net diff as a working-tree state.
# --mixed (the default) unstages everything; --soft keeps the index. Use
# --mixed so the next step's `git add` is deliberate.
git -C project reset --mixed origin/$BASE
# 2. Find the base branch.
BASE=$(gh pr view <N> --json baseRefName --jq .baseRefName)
# 3. Restage by package. For each affected package, add the package's files
# and commit with a conventional-commit message.
git -C project add packages/foo/
git -C project commit -m "feat(foo): <one-line summary>"
# 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
git -C project add packages/bar/
git -C project commit -m "fix(bar): <one-line summary>"
# 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 ...
# (... one commit per affected package, implementation+tests bundled ...)
# 5. Lockfile last.
git add yarn.lock
git commit -m "chore: Update yarn.lock"
# 4. Stage and commit the lockfile last, per yarn-lock-separate-commit.
git -C project add yarn.lock
git -C project commit -m "chore: Update yarn.lock"
# 6. Verify: no net change.
git diff pre-retcon HEAD # must be empty
# 5. Confirm the net diff is unchanged.
git -C project diff origin/$BASE..HEAD --stat # compare to the pre-retcon diff
git -C project diff <pre-retcon-sha>..HEAD # must be empty (no net change)
# 6. Force-push with --force-with-lease.
git -C project push --force-with-lease origin HEAD:<head-branch>
# 7. Push without clobbering concurrent work, then clean up.
git push --force-with-lease origin HEAD:<head-branch>
git tag -d pre-retcon
```
The `--force-with-lease` (not `--force`) refuses to overwrite work pushed since the agent's last fetch; if the lease is rejected, fetch again and abort the retcon rather than clobbering concurrent work.
Run `git status` after each `git add` to confirm that only the intended
files are staged.
### Save a pre-retcon reference
## Changes that don't fit one package
Before step 2, tag (or note) the pre-retcon tip so the no-net-change verification in step 5 has something to diff against. `git tag pre-retcon-<short-id> HEAD` works locally; delete the tag after the push lands.
Name these groupings when you report the retcon, so the reviewer reads
the new history as a deliberate shape:
### Capture grouping decisions
When the branch's changes do not map cleanly to a per-package commit (a cross-package refactor, a touch in a top-level config file), name the groupings in the retcon's report so the panel and the maintainer can read the new history as a deliberate shape rather than guessing. Typical buckets when a per-package split does not exhaust the diff:
- **Top-level config or build changes.** One commit, `chore: <summary>` or `build: <summary>`.
- **Cross-package refactors that have to land atomically.** One commit covering all packages it touches, `refactor(<area>): <summary>`. The "area" names the conceptual change, not a single package.
- **Documentation-only files (READMEs, design notes).** One commit, `docs: <summary>` or `docs(<scope>): <summary>` when scoped.
- **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
The single load-bearing property of a retcon: the PR's net diff (base ... new head) is byte-identical to the PR's pre-retcon diff (base ... pre-retcon head). The retcon changes commit grouping; it does not change what the PR proposes to merge. Step 5's two diffs verify this:
- `git diff origin/$BASE..HEAD --stat` against the pre-retcon stat: same files, same line counts.
- `git diff <pre-retcon-sha>..HEAD`: empty. The two heads have the same tree.
If either check fails, the retcon went wrong; do not push. Roll back with `git reset --hard <pre-retcon-sha>` and start over.
## Notes from the field
- _2026-05-14_: the verb landed when kriskowal said *"A new verb I would like to use is 'retcon' meaning '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.'"* The directive bundles three disciplines (per-package grouping, lockfile separation, implementation+tests together) that the garden had as separate skills or norms; the retcon names the procedure that applies all three at once to a branch's full history.
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
- **Forgetting the pre-retcon tag.** Without it, step 5's no-net-change check has no reference. Tag before resetting.
- **`git reset --hard` instead of `--mixed`.** `--hard` discards the working tree and loses the net diff. Use `--mixed` (or `--soft` if you want the index preserved) so the diff stays available as unstaged changes.
- **Lockfile interleaved with a package commit.** If `git add packages/foo/` accidentally stages a lockfile that lives under that package (rare; typically lockfiles are root-level), the lockfile leaks into the package commit. Run `git status` after each `git add` and verify only the intended package's files are staged.
- **Implementation without tests, or tests without implementation.** The discipline is one commit per package containing both. A package commit with no test changes when the implementation is new behavior is wrong; either the tests live elsewhere (a cross-package test commit, separately) or the implementation is genuinely untested (a panel finding for the next round, not a retcon target).
- **Conflicting with concurrent fixer pushes.** A retcon force-pushes; if a fixer pushed since the agent's last fetch, `--force-with-lease` rejects the push. Do not override with `--force`; abort and rebase the retcon onto the new tip.
- **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).
+45 -27
View File
@@ -1,51 +1,69 @@
---
created: 2026-05-13
updated: 2026-05-17
author: liaison, gardener
name: yarn-lock-separate-commit
description: yarn.lock in its own commit — use when changing Yarn dependencies (package.json deps, workspace renames, yarn install), when organizing or reviewing commits that touch yarn.lock, when a rebase conflicts on yarn.lock, or during a retcon. Every lockfile change ships alone as `chore: Update yarn.lock`, after the package.json commit that caused it.
---
# Skill: yarn-lock-separate-commit
# yarn.lock in a separate commit
Adopted from `references/endo-but-for-bots/skills/yarn-lock-separate-commit.md`.
Every change that touches `yarn.lock` ships in its own commit, separate from the `package.json` change that caused the update, with the message `chore: Update yarn.lock`.
Every change that touches `yarn.lock` ships in its own commit, with the
message `chore: Update yarn.lock`. It stays separate from the
`package.json` change that caused it.
## Why
A separate lockfile commit can be dropped and regenerated cleanly on rebase. A combined commit turns lockfile churn into merge conflicts that have to be resolved by re-running `yarn install` and hand-stitching, which is error-prone and pollutes history.
A separate lockfile commit can be dropped on a rebase and cleanly
regenerated. A combined commit turns lockfile churn into merge
conflicts, and those get resolved by re-running `yarn install` and
stitching by hand. That's error-prone and clutters the history.
## Procedure
After making the dep change in `package.json`:
After the dependency change in `package.json`:
```sh
git add packages/foo/package.json
git commit -m "feat(foo): add bar dependency (#NNNN)"
git commit -m "feat(foo): add bar dependency"
npx corepack yarn install
git add yarn.lock
git commit -m "chore: Update yarn.lock"
```
When force-pushing a rebased branch, the order of the two commits matters: put the lockfile commit *after* the package.json commit, so a reviewer dropping the lockfile commit lands at a still-coherent state.
Order matters. Put the lockfile commit **after** the `package.json`
commit, so dropping the lockfile commit still leaves a coherent state.
## Rebasing past a lockfile conflict
If another PR changed dependencies in the meantime, rebasing onto the
moved base usually conflicts on `yarn.lock`. Don't hand-merge it. Drop
the old lockfile commit and regenerate the lockfile:
```sh
git rebase origin/<base>
# conflict on yarn.lock
git rebase --skip # drop the old lockfile commit
# ... any other fixes on top ...
npx corepack yarn install # regenerate against the new base
git add yarn.lock
git commit -m "chore: Update yarn.lock"
```
## Pitfalls
- **`git commit --amend` on the package.json commit silently drags the lockfile in if staged.** Stage and commit the lockfile last.
- **Accidental combining.** If caught on review, `git reset HEAD~1`, re-stage selectively, and recommit as two.
- **Package renames invalidate the lockfile.** Renaming a workspace's directory or `package.json:name` changes the workspace key (`"@scope/<name>@workspace:packages/<dir>"`) inside `yarn.lock`. Still ships in its own `chore: Update yarn.lock` commit, regenerated by re-running `npx corepack yarn install` after all renames are in place. Don't hand-edit the workspace key; the install produces the canonical form.
- **`git commit --amend` on the `package.json` commit** quietly pulls
the lockfile in if it's staged. Stage and commit the lockfile last.
- **Combined by accident:** run `git reset HEAD~1`, restage selectively,
and recommit as two commits.
- **Package renames change the lockfile.** Renaming a workspace's
directory or its `package.json` `name` changes the workspace key
(`"@scope/<name>@workspace:packages/<dir>"`) in `yarn.lock`. Once all
the renames are in, regenerate with `npx corepack yarn install`.
Commit the result on its own as usual, and don't hand-edit the key.
## Notes from the field
## See also
- _2026-05-13_: adopted from the reference. Session example from PR 71 lives in the journal, not here.
- _2026-05-17_: a PR carrying `chore: Update yarn.lock` typically conflicts on the lockfile when rebased onto a moved base (any other PR that touched dependencies has landed in the interim). The workflow that resolved it cleanly: skip the old lockfile commit during rebase, apply any review-feedback fixes on top, then regenerate the lockfile against the new base as its own commit. Source: PR #238 fixer, `journal/entries/2026/05/17/200805Z-message-fixer-3e9fd0.md`.
```sh
git rebase origin/<base>
# conflict on yarn.lock
git rebase --skip # drop the old lockfile commit
# apply review-feedback fixes on top
npx corepack yarn install # regenerate lockfile against new base
git add yarn.lock
git commit -m "chore: Update yarn.lock"
```
- [Retcon](../retcon/SKILL.md) applies this rule to a whole branch's
history after the fact.
- Adapted from
[the yarn-lock-separate-commit skill in kriscendobot/garden](https://github.com/kriscendobot/garden/blob/f6fd8db6abe012366aa4c843874c07adae46e7f7/skills/yarn-lock-separate-commit/SKILL.md)
(May 2026).