Skip to content

feat: add wspace sweep - remove every merged feature worktree workspace-wide - #154

Merged
EthanThatOneKid merged 1 commit into
mainfrom
feat/wspace-sweep
Oct 1, 2026
Merged

EthanThatOneKid merged 1 commit into
mainfrom
feat/wspace-sweep

Conversation

@EthanThatOneKid

Copy link
Copy Markdown
Contributor

Implements #153.

What it does

wspace sweep removes every linked worktree in the workspace whose branch is
already merged, workspace-wide, in one command — replacing a seven-step ritual
repeated per repository.

Design notes worth reviewing

Discovery comes from git, not paths. Candidates come from
git worktree list --porcelain; every entry after the first (the main
worktree) is a candidate. No directory is globbed, so no location convention is
imposed, and branch names are read from git rather than inferred from the
worktree leaf — which routinely differs (world-id-hard-cutover,
feat/frontmatter-fields).

A .git guard comes first. Without it, a non-repo directory in the
repositories tree (this workspace has repos/worktrees/) makes
git -C <dir> worktree list walk up and report an ancestor repo's worktrees,
leaking root state into a child's sweep. There's a test for exactly this.

The proof ladder is offline. Tree equality → empty diff → zero commits ahead.
These tiers authorise rebase/squash merges, whose commits have different SHAs
than anything on the default branch and which git branch -d refuses to delete.
The baseline is origin/<default> when that ref exists: immediately after a
merge the canonical checkout is usually still behind it, so proving against
the local ref would refuse cleanup in exactly the case this exists for.

The workspace root is swept too. It is not a manifest entry, and the
motivating worktree (worktrees/workspace/post-merge-cleanup) lives there, so
check/update's (workspace root) inclusion applies.

Two gates, each --force-overridable: merge proof, and clean
git status --porcelain (untracked included). The default branch and the main
worktree are never targets; detached worktrees are skipped.

Refusals are reported, not fatal — exit non-zero only on FAILED, matching
update's conservative tone rather than check's stricter contract. --strict
promotes refusals. --delete-remote is opt-in.

Purge escalation handles the Windows Filename too long failure that
git worktree remove cannot recover from: recursive delete → verbatim \?\
path → empty-source robocopy /MIR + verbatim rmdir (rm -rf on POSIX), then
worktree prune. The strategy that worked is reported.

Decisions taken on the issue's open questions

  • ADR-0005 reconciliation — argued in ADR-0006 from the "safety, not
    management" carve-out ADR-0005 point 6 deliberately kept in update.
  • gh pr view tier — not implemented. The offline tiers cover the
    rebase/squash cases, and keeping the common path network-free keeps a
    workspace-wide sweep fast. Open to revisiting.
  • Unworked merged branches — out of scope. The command acts on linked
    worktrees, which is where the worktree-isolation pattern leaves its residue.
  • Canonical fast-forward — left to wspace update, so the commands compose
    (wspace sweep && wspace update) instead of overlapping.

Verification

  • deno task ci green: 185 passed, 0 failed. 13 new tests against real local
    git repositories, following integration_test.ts conventions.
  • Read-only dry run against this workspace: it finds the root's
    post-merge-cleanup worktree, refuses all three unmerged
    docs/agents-cross-repo-guidance branches with SKIP_NOT_MERGED, and
    refuses dirty worktrees with SKIP_DIRTY. Nothing claimed REMOVED,
    correctly, since nothing has merged yet.

Review focus

The two safety gates and the origin/<default> baseline choice. Everything
else is mechanical.

Reclaiming a merged branch was a seven-step ritual repeated per repository,
and step 2 (git worktree remove) usually fails on Windows with "Filename too
long", leaving the tree registered and the branch locked. Across ~40 repos that
is how stale worktrees accumulate.

sweep discovers linked worktrees from `git worktree list --porcelain` rather
than from directory layout, so no on-disk convention is imposed, and deletes
only after proving the merge offline (tree equality, empty diff, or ancestry).
Those tiers are what authorise rebase/squash merges, whose commits have
different SHAs than anything on the default branch and which `git branch -d`
refuses. The baseline is origin/<default> when present: right after a merge the
canonical checkout is usually still behind it, so proving against the local ref
would refuse cleanup in exactly the case this exists for.

Refusals are reported rather than fatal, matching update's conservative tone,
so the exit code is non-zero only when a mutation actually broke; --strict
promotes refusals. Remote branches are kept unless --delete-remote is passed.

ADR-0006 records the reconciliation with ADR-0005, which removed worktree
management: sweep creates and places nothing, and acts within the same
read-only-worktree "safety, not management" carve-out ADR-0005 kept in update.

Closes #153

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@wazoocomputer

wazoocomputer Bot commented Oct 1, 2026

Copy link
Copy Markdown

This pull request introduces a new command, wspace sweep, which removes merged git worktrees across the entire workspace. This command automates the cleanup process for merged feature branches, addressing issues like Filename too long errors on Windows and providing a safer, more efficient way to manage worktrees. The implementation includes offline merge proof verification, clean tree checks, and escalation strategies for failed worktree removals.

Here's a summary of the changes:

File Change Type Summary
README.md modified Updates the command list to include wspace sweep with a description of its functionality.
docs/adr/0006-sweep-merged-worktrees.md added Adds an Architecture Decision Record (ADR) detailing the rationale and design principles behind wspace sweep.
skills/wspace/references/COMMANDS.md modified Updates the command reference table to include wspace sweep.
src/cli-options.ts modified Adds new command-line options for wspace sweep (e.g., --dry-run, --force).
src/cli.ts modified Integrates the new sweep command into the CLI.
src/commands/sweep.ts added Implements the core logic for the wspace sweep command.
src/git.ts modified Adds new Git utility functions used by wspace sweep, such as worktree listing and merge proof checks.
src/purge.ts added Provides functions for robust directory purging when git worktree remove fails.
src/sweep.ts added Contains the main sweep orchestration logic.
src/types.ts modified Updates type definitions to support the new sweep command and its options.
tests/sweep_test.ts added Adds comprehensive integration tests for the wspace sweep command, covering various scenarios including dry runs, forced removals, and different merge types.

Reviewers should focus on the docs/adr/0006-sweep-merged-worktrees.md for the design decisions and src/commands/sweep.ts, src/sweep.ts, and src/git.ts for the implementation of the core logic and safety mechanisms.

@EthanThatOneKid
EthanThatOneKid merged commit b3aec7f into main Oct 1, 2026
1 check passed
@EthanThatOneKid
EthanThatOneKid deleted the feat/wspace-sweep branch October 1, 2026 00:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant