From a439f772321f80348156708dcf22bec3f1a314f2 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Thu, 20 Aug 2026 07:19:56 -0700 Subject: [PATCH] Require post-merge worktree cleanup --- .agents/skills/git-commit-conventions/SKILL.md | 4 +--- .../skills/operational-vs-release-workflow/SKILL.md | 1 + .agents/skills/pr-review-conduct/SKILL.md | 2 ++ .agents/skills/repo-worktree/SKILL.md | 11 ++++++----- .claude-plugin/fleet-skills/.source-digest | 2 +- .../skills/git-commit-conventions/SKILL.md | 4 +--- .../skills/operational-vs-release-workflow/SKILL.md | 1 + .../fleet-skills/skills/pr-review-conduct/SKILL.md | 2 ++ .../fleet-skills/skills/repo-worktree/SKILL.md | 11 ++++++----- .github/skills/git-commit-conventions/SKILL.md | 4 +--- .../skills/operational-vs-release-workflow/SKILL.md | 1 + .github/skills/pr-review-conduct/SKILL.md | 2 ++ .github/skills/repo-worktree/SKILL.md | 11 ++++++----- 13 files changed, 31 insertions(+), 25 deletions(-) diff --git a/.agents/skills/git-commit-conventions/SKILL.md b/.agents/skills/git-commit-conventions/SKILL.md index 85e46eb3..5c1454f1 100644 --- a/.agents/skills/git-commit-conventions/SKILL.md +++ b/.agents/skills/git-commit-conventions/SKILL.md @@ -164,6 +164,4 @@ any rewrite. See `references/history-rewrite.md` for the full two-gate rule. ## Never run destructive git commands without being asked -`git reset --hard`, `git checkout .`, `git restore .`, `git clean -f`, and anything else that -discards uncommitted work runs only on explicit developer instruction, never as a convenience step -inside a larger task. +`git reset --hard`, `git checkout .`, `git restore .`, `git clean -f`, and other commands that discard work require explicit developer instruction. Never use them as a convenience inside a larger task. One narrow cleanup exception applies to `git branch -D ` after a squash merge. It requires live proof that the pull request for that exact branch merged and a clean worktree at the verified head SHA, per `repo-worktree`. The exception never applies to `develop`, an unmerged branch, an unresolved pull request, or a branch with uncommitted work. diff --git a/.agents/skills/operational-vs-release-workflow/SKILL.md b/.agents/skills/operational-vs-release-workflow/SKILL.md index 0834b424..9a9e9f25 100644 --- a/.agents/skills/operational-vs-release-workflow/SKILL.md +++ b/.agents/skills/operational-vs-release-workflow/SKILL.md @@ -58,6 +58,7 @@ rather than guessing from the repo's contents. promotion PR's head *is* `develop`, so `--delete-branch` deletes it. An EOL-only conflict on a workflow YAML file resolves on a throwaway branch off `main`, not on `develop`. Full recovery and conflict-resolution commands: `references/branch-protection-and-promotion.md`. +- **A merge or release ends with worktree cleanup and the base clone on current `develop`.** Run the `repo-worktree` post-merge procedure after a feature squash merge. Run it again after a promotion or release completes, unless the user explicitly asks to retain a checkout or branch. Remove finished task, conflict-resolution, installer, and release helpers. Never delete `develop`, and never leave the base clone on `main` merely because `main` was promoted or released. - **Issue-closing keywords (`Closes #N`, `Fixes #N`) go in the `develop -> main` promotion PR, not the feature -> `develop` PR.** GitHub auto-closes an issue only when the closing keyword merges into the **default branch** (`main`), so a feature -> `develop` PR merge never fires it. diff --git a/.agents/skills/pr-review-conduct/SKILL.md b/.agents/skills/pr-review-conduct/SKILL.md index f2424686..0f2fefa1 100644 --- a/.agents/skills/pr-review-conduct/SKILL.md +++ b/.agents/skills/pr-review-conduct/SKILL.md @@ -83,6 +83,8 @@ then apply the Merge Gate above. **Never exit the loop early.** A round count is condition, and neither is patience running out. Reporting only that the PR was opened is an early exit unless the maintainer explicitly instructed the agent not to monitor or drive its review. +After an authorized merge, run the `repo-worktree` post-merge cleanup procedure unless the user explicitly asks to retain the checkout or branch. The pull request loop is incomplete while its finished worktree or local task branch remains. It is also incomplete until the base clone returns to fetched and fast-forwarded `develop`. + ## Every finding ends in one of five outcomes 1. **Real, so fix it.** Reply with the fixing commit SHA. For a finding on platform-specific code diff --git a/.agents/skills/repo-worktree/SKILL.md b/.agents/skills/repo-worktree/SKILL.md index b2d3ec6e..27e2e69a 100644 --- a/.agents/skills/repo-worktree/SKILL.md +++ b/.agents/skills/repo-worktree/SKILL.md @@ -191,11 +191,12 @@ fails, report that boundary and fix the setup. Never bypass the hook to make the - `git worktree list`, run in any checkout of a repo, names that repo's base clone and every worktree with its branch. On the convention layout, one `ls ~/repos/worktrees/` reads what is in flight across the whole fleet. -- After the task's pull request merges, remove the worktree and its branch from the base clone: - `git worktree remove ~/repos/worktrees/-`, then `git branch -d `. -- After the task's pull request merges, remove a temporary standalone clone at its exact - `/-` path. The remote branch follows the repository's normal - pull request cleanup policy. +- **Cleanup after merge is the default terminal step.** Run it after a squash merge into `develop`. Run it again after a merge-commit promotion into `main`, unless the user explicitly says to retain a checkout or branch. A merge or release handoff is incomplete while finished task, conflict-resolution, installer, or release worktrees remain registered. +- **Verify before removing.** Read the pull request's merged state and head SHA from live GitHub state. Confirm the worktree is clean and resolves to that head. A dirty worktree stops cleanup because force-removing it would discard work. A detached helper worktree needs no pull request, but its commit must be contained in the branch whose completed operation created it. +- **Remove the exact finished worktree, then its local task branch.** Use `git worktree remove `. Try `git branch -d ` after a merge commit. A squash merge does not make the feature tip an ancestor of `develop`, so `-d` cannot recognize it as merged. After the live merged-PR and clean-worktree checks prove that exact branch finished, use `git branch -D ` under the narrow post-squash exception in `git-commit-conventions`. Never apply that exception to an unverified branch or to `develop`. +- **Remove temporary standalone clones and detached helper worktrees too.** Remove the exact `/-` path after confirming it is clean. The remote feature branch follows the repository's normal pull request cleanup policy. Never delete `develop` after a promotion because it is the permanent integration branch. +- **Return the base clone to current `develop`.** Fetch and prune `origin`, confirm the base clone is clean, switch it to `develop` when needed, and fast-forward it with `git merge --ff-only origin/develop`. A completed promotion or release does not leave the base clone on `main`. Stop and report a dirty base clone or a non-fast-forward instead of switching or reconciling it. +- **Prove the cleanup.** Finish with `git status --short --branch` in the base clone and `git worktree list`. The expected result is a clean base clone at `origin/develop` and no worktree belonging only to the completed task. - A worktree that refuses removal is dirty, and force is not the fix: look at what is uncommitted in it first, since discarding uncommitted work runs only on explicit instruction, per the `git-commit-conventions` skill. diff --git a/.claude-plugin/fleet-skills/.source-digest b/.claude-plugin/fleet-skills/.source-digest index bec201fb..7adf27da 100644 --- a/.claude-plugin/fleet-skills/.source-digest +++ b/.claude-plugin/fleet-skills/.source-digest @@ -1 +1 @@ -598e48f2c76dc42b +5c6b9955c54c1e80 diff --git a/.claude-plugin/fleet-skills/skills/git-commit-conventions/SKILL.md b/.claude-plugin/fleet-skills/skills/git-commit-conventions/SKILL.md index 85e46eb3..5c1454f1 100644 --- a/.claude-plugin/fleet-skills/skills/git-commit-conventions/SKILL.md +++ b/.claude-plugin/fleet-skills/skills/git-commit-conventions/SKILL.md @@ -164,6 +164,4 @@ any rewrite. See `references/history-rewrite.md` for the full two-gate rule. ## Never run destructive git commands without being asked -`git reset --hard`, `git checkout .`, `git restore .`, `git clean -f`, and anything else that -discards uncommitted work runs only on explicit developer instruction, never as a convenience step -inside a larger task. +`git reset --hard`, `git checkout .`, `git restore .`, `git clean -f`, and other commands that discard work require explicit developer instruction. Never use them as a convenience inside a larger task. One narrow cleanup exception applies to `git branch -D ` after a squash merge. It requires live proof that the pull request for that exact branch merged and a clean worktree at the verified head SHA, per `repo-worktree`. The exception never applies to `develop`, an unmerged branch, an unresolved pull request, or a branch with uncommitted work. diff --git a/.claude-plugin/fleet-skills/skills/operational-vs-release-workflow/SKILL.md b/.claude-plugin/fleet-skills/skills/operational-vs-release-workflow/SKILL.md index 0834b424..9a9e9f25 100644 --- a/.claude-plugin/fleet-skills/skills/operational-vs-release-workflow/SKILL.md +++ b/.claude-plugin/fleet-skills/skills/operational-vs-release-workflow/SKILL.md @@ -58,6 +58,7 @@ rather than guessing from the repo's contents. promotion PR's head *is* `develop`, so `--delete-branch` deletes it. An EOL-only conflict on a workflow YAML file resolves on a throwaway branch off `main`, not on `develop`. Full recovery and conflict-resolution commands: `references/branch-protection-and-promotion.md`. +- **A merge or release ends with worktree cleanup and the base clone on current `develop`.** Run the `repo-worktree` post-merge procedure after a feature squash merge. Run it again after a promotion or release completes, unless the user explicitly asks to retain a checkout or branch. Remove finished task, conflict-resolution, installer, and release helpers. Never delete `develop`, and never leave the base clone on `main` merely because `main` was promoted or released. - **Issue-closing keywords (`Closes #N`, `Fixes #N`) go in the `develop -> main` promotion PR, not the feature -> `develop` PR.** GitHub auto-closes an issue only when the closing keyword merges into the **default branch** (`main`), so a feature -> `develop` PR merge never fires it. diff --git a/.claude-plugin/fleet-skills/skills/pr-review-conduct/SKILL.md b/.claude-plugin/fleet-skills/skills/pr-review-conduct/SKILL.md index f2424686..0f2fefa1 100644 --- a/.claude-plugin/fleet-skills/skills/pr-review-conduct/SKILL.md +++ b/.claude-plugin/fleet-skills/skills/pr-review-conduct/SKILL.md @@ -83,6 +83,8 @@ then apply the Merge Gate above. **Never exit the loop early.** A round count is condition, and neither is patience running out. Reporting only that the PR was opened is an early exit unless the maintainer explicitly instructed the agent not to monitor or drive its review. +After an authorized merge, run the `repo-worktree` post-merge cleanup procedure unless the user explicitly asks to retain the checkout or branch. The pull request loop is incomplete while its finished worktree or local task branch remains. It is also incomplete until the base clone returns to fetched and fast-forwarded `develop`. + ## Every finding ends in one of five outcomes 1. **Real, so fix it.** Reply with the fixing commit SHA. For a finding on platform-specific code diff --git a/.claude-plugin/fleet-skills/skills/repo-worktree/SKILL.md b/.claude-plugin/fleet-skills/skills/repo-worktree/SKILL.md index b2d3ec6e..27e2e69a 100644 --- a/.claude-plugin/fleet-skills/skills/repo-worktree/SKILL.md +++ b/.claude-plugin/fleet-skills/skills/repo-worktree/SKILL.md @@ -191,11 +191,12 @@ fails, report that boundary and fix the setup. Never bypass the hook to make the - `git worktree list`, run in any checkout of a repo, names that repo's base clone and every worktree with its branch. On the convention layout, one `ls ~/repos/worktrees/` reads what is in flight across the whole fleet. -- After the task's pull request merges, remove the worktree and its branch from the base clone: - `git worktree remove ~/repos/worktrees/-`, then `git branch -d `. -- After the task's pull request merges, remove a temporary standalone clone at its exact - `/-` path. The remote branch follows the repository's normal - pull request cleanup policy. +- **Cleanup after merge is the default terminal step.** Run it after a squash merge into `develop`. Run it again after a merge-commit promotion into `main`, unless the user explicitly says to retain a checkout or branch. A merge or release handoff is incomplete while finished task, conflict-resolution, installer, or release worktrees remain registered. +- **Verify before removing.** Read the pull request's merged state and head SHA from live GitHub state. Confirm the worktree is clean and resolves to that head. A dirty worktree stops cleanup because force-removing it would discard work. A detached helper worktree needs no pull request, but its commit must be contained in the branch whose completed operation created it. +- **Remove the exact finished worktree, then its local task branch.** Use `git worktree remove `. Try `git branch -d ` after a merge commit. A squash merge does not make the feature tip an ancestor of `develop`, so `-d` cannot recognize it as merged. After the live merged-PR and clean-worktree checks prove that exact branch finished, use `git branch -D ` under the narrow post-squash exception in `git-commit-conventions`. Never apply that exception to an unverified branch or to `develop`. +- **Remove temporary standalone clones and detached helper worktrees too.** Remove the exact `/-` path after confirming it is clean. The remote feature branch follows the repository's normal pull request cleanup policy. Never delete `develop` after a promotion because it is the permanent integration branch. +- **Return the base clone to current `develop`.** Fetch and prune `origin`, confirm the base clone is clean, switch it to `develop` when needed, and fast-forward it with `git merge --ff-only origin/develop`. A completed promotion or release does not leave the base clone on `main`. Stop and report a dirty base clone or a non-fast-forward instead of switching or reconciling it. +- **Prove the cleanup.** Finish with `git status --short --branch` in the base clone and `git worktree list`. The expected result is a clean base clone at `origin/develop` and no worktree belonging only to the completed task. - A worktree that refuses removal is dirty, and force is not the fix: look at what is uncommitted in it first, since discarding uncommitted work runs only on explicit instruction, per the `git-commit-conventions` skill. diff --git a/.github/skills/git-commit-conventions/SKILL.md b/.github/skills/git-commit-conventions/SKILL.md index 85e46eb3..5c1454f1 100644 --- a/.github/skills/git-commit-conventions/SKILL.md +++ b/.github/skills/git-commit-conventions/SKILL.md @@ -164,6 +164,4 @@ any rewrite. See `references/history-rewrite.md` for the full two-gate rule. ## Never run destructive git commands without being asked -`git reset --hard`, `git checkout .`, `git restore .`, `git clean -f`, and anything else that -discards uncommitted work runs only on explicit developer instruction, never as a convenience step -inside a larger task. +`git reset --hard`, `git checkout .`, `git restore .`, `git clean -f`, and other commands that discard work require explicit developer instruction. Never use them as a convenience inside a larger task. One narrow cleanup exception applies to `git branch -D ` after a squash merge. It requires live proof that the pull request for that exact branch merged and a clean worktree at the verified head SHA, per `repo-worktree`. The exception never applies to `develop`, an unmerged branch, an unresolved pull request, or a branch with uncommitted work. diff --git a/.github/skills/operational-vs-release-workflow/SKILL.md b/.github/skills/operational-vs-release-workflow/SKILL.md index 0834b424..9a9e9f25 100644 --- a/.github/skills/operational-vs-release-workflow/SKILL.md +++ b/.github/skills/operational-vs-release-workflow/SKILL.md @@ -58,6 +58,7 @@ rather than guessing from the repo's contents. promotion PR's head *is* `develop`, so `--delete-branch` deletes it. An EOL-only conflict on a workflow YAML file resolves on a throwaway branch off `main`, not on `develop`. Full recovery and conflict-resolution commands: `references/branch-protection-and-promotion.md`. +- **A merge or release ends with worktree cleanup and the base clone on current `develop`.** Run the `repo-worktree` post-merge procedure after a feature squash merge. Run it again after a promotion or release completes, unless the user explicitly asks to retain a checkout or branch. Remove finished task, conflict-resolution, installer, and release helpers. Never delete `develop`, and never leave the base clone on `main` merely because `main` was promoted or released. - **Issue-closing keywords (`Closes #N`, `Fixes #N`) go in the `develop -> main` promotion PR, not the feature -> `develop` PR.** GitHub auto-closes an issue only when the closing keyword merges into the **default branch** (`main`), so a feature -> `develop` PR merge never fires it. diff --git a/.github/skills/pr-review-conduct/SKILL.md b/.github/skills/pr-review-conduct/SKILL.md index f2424686..0f2fefa1 100644 --- a/.github/skills/pr-review-conduct/SKILL.md +++ b/.github/skills/pr-review-conduct/SKILL.md @@ -83,6 +83,8 @@ then apply the Merge Gate above. **Never exit the loop early.** A round count is condition, and neither is patience running out. Reporting only that the PR was opened is an early exit unless the maintainer explicitly instructed the agent not to monitor or drive its review. +After an authorized merge, run the `repo-worktree` post-merge cleanup procedure unless the user explicitly asks to retain the checkout or branch. The pull request loop is incomplete while its finished worktree or local task branch remains. It is also incomplete until the base clone returns to fetched and fast-forwarded `develop`. + ## Every finding ends in one of five outcomes 1. **Real, so fix it.** Reply with the fixing commit SHA. For a finding on platform-specific code diff --git a/.github/skills/repo-worktree/SKILL.md b/.github/skills/repo-worktree/SKILL.md index b2d3ec6e..27e2e69a 100644 --- a/.github/skills/repo-worktree/SKILL.md +++ b/.github/skills/repo-worktree/SKILL.md @@ -191,11 +191,12 @@ fails, report that boundary and fix the setup. Never bypass the hook to make the - `git worktree list`, run in any checkout of a repo, names that repo's base clone and every worktree with its branch. On the convention layout, one `ls ~/repos/worktrees/` reads what is in flight across the whole fleet. -- After the task's pull request merges, remove the worktree and its branch from the base clone: - `git worktree remove ~/repos/worktrees/-`, then `git branch -d `. -- After the task's pull request merges, remove a temporary standalone clone at its exact - `/-` path. The remote branch follows the repository's normal - pull request cleanup policy. +- **Cleanup after merge is the default terminal step.** Run it after a squash merge into `develop`. Run it again after a merge-commit promotion into `main`, unless the user explicitly says to retain a checkout or branch. A merge or release handoff is incomplete while finished task, conflict-resolution, installer, or release worktrees remain registered. +- **Verify before removing.** Read the pull request's merged state and head SHA from live GitHub state. Confirm the worktree is clean and resolves to that head. A dirty worktree stops cleanup because force-removing it would discard work. A detached helper worktree needs no pull request, but its commit must be contained in the branch whose completed operation created it. +- **Remove the exact finished worktree, then its local task branch.** Use `git worktree remove `. Try `git branch -d ` after a merge commit. A squash merge does not make the feature tip an ancestor of `develop`, so `-d` cannot recognize it as merged. After the live merged-PR and clean-worktree checks prove that exact branch finished, use `git branch -D ` under the narrow post-squash exception in `git-commit-conventions`. Never apply that exception to an unverified branch or to `develop`. +- **Remove temporary standalone clones and detached helper worktrees too.** Remove the exact `/-` path after confirming it is clean. The remote feature branch follows the repository's normal pull request cleanup policy. Never delete `develop` after a promotion because it is the permanent integration branch. +- **Return the base clone to current `develop`.** Fetch and prune `origin`, confirm the base clone is clean, switch it to `develop` when needed, and fast-forward it with `git merge --ff-only origin/develop`. A completed promotion or release does not leave the base clone on `main`. Stop and report a dirty base clone or a non-fast-forward instead of switching or reconciling it. +- **Prove the cleanup.** Finish with `git status --short --branch` in the base clone and `git worktree list`. The expected result is a clean base clone at `origin/develop` and no worktree belonging only to the completed task. - A worktree that refuses removal is dirty, and force is not the fix: look at what is uncommitted in it first, since discarding uncommitted work runs only on explicit instruction, per the `git-commit-conventions` skill.