diff --git a/docs/project/readable-codebase/RD-15-remove-abandoned-worktrees.md b/docs/project/readable-codebase/RD-15-remove-abandoned-worktrees.md new file mode 100644 index 0000000..cb93c9e --- /dev/null +++ b/docs/project/readable-codebase/RD-15-remove-abandoned-worktrees.md @@ -0,0 +1,142 @@ +# RD-15 — Remove the 22 abandoned agent worktrees + +Status: done +Source: PLAN.md 2.1 + +## Why + +`.claude/worktrees/` holds **22 abandoned agent checkouts totalling 4.7 GB**, left behind by +past agent runs. They are gitignored (`.gitignore:64`), so they never reach a commit — but +they are on disk, and every unqualified repository-wide `grep -r` or `find` walks all 22 +copies of the source tree. + +That is a real tax on every future search, by a person or an agent, and it is larger than it +looks. Measured: + +| | files | +| -------------------------------- | ---------- | +| under `.claude/worktrees/` | **48,005** | +| tracked in the actual repository | **856** | + +An unqualified `grep -r` or `find` therefore walks **56× more files than the repository +contains**. This ticket removes the cause; the `git grep` habit in the ticket-authoring rules +above handles the symptom. + +## Read first + +- `.gitignore:64` — confirms the directory is ignored +- `git worktree list` — 23 entries: the main working tree plus the 22 to remove +- The verification block below. **Run it before removing anything.** + +## Decisions (pre-made, don't relitigate) + +1. **Use `git worktree remove`, never `rm -rf`.** These are **live registered git + worktrees**, not orphaned directories — each has a real `worktree-agent-` branch. An + `rm -rf` leaves 22 broken registrations behind in `.git/worktrees/`, which is worse than + the disk usage. This correction was made while executing RD-01, where the original plan + assumed they were plain directories. + +2. **Delete each `worktree-agent-*` branch too**, after removing its worktree. A worktree + removal does not delete the branch it had checked out, and 22 stale branches in + `git branch` are their own kind of noise. + +3. **Finish with `git worktree prune`** to clear any leftover administrative entries. + +4. **Re-verify before removing, even though it was verified when this ticket was written.** + This is the only destructive ticket in the arc. Both gates passed at authoring time — all + 22 branch tips are ancestors of `main` (the RB-01..RB-33 arc was merged in `637d500`), and + all 22 working trees are clean. **If either gate fails for any worktree, stop and report + it; do not use `--force`.** + +5. **This ticket changes no tracked file.** Its commit contains only this ticket file and the + README row. That is correct and expected — the work is entirely in gitignored paths and + local branch refs. + +## Files + +- `docs/project/readable-codebase/RD-15-remove-abandoned-worktrees.md` (this file) +- `docs/project/readable-codebase/README.md` (the RD-15 row) + +No source files. No configuration. `.gitignore` is already correct and must not change. + +## Steps + +1. Run the verification block below. Do not proceed unless it reports `unmerged: 0` and + `dirty: 0`. +2. For each worktree: `git worktree remove .claude/worktrees/`. +3. For each branch: `git branch -d worktree-agent-` (lowercase `-d`, which refuses to + delete anything unmerged — that is a second safety net, so do **not** use `-D`). +4. `git worktree prune`. +5. Confirm `.claude/worktrees/` is gone or empty. +6. Update this ticket's `Status:` to `done` and the README's RD-15 row to `done`. +7. Commit. + +## The verification gate — run this first + +```bash +cd /home/eho/repos/atomic-design-poc +unmerged=0 +for b in $(git branch --list 'worktree-agent-*' --format='%(refname:short)'); do + git merge-base --is-ancestor "$(git rev-parse "$b")" main 2>/dev/null \ + || { echo "UNMERGED: $b"; unmerged=$((unmerged+1)); } +done +dirty=0 +for d in .claude/worktrees/agent-*; do + [ -d "$d" ] || continue + out=$(git -C "$d" status --porcelain 2>/dev/null | grep -v '^?? node_modules') + [ -z "$out" ] || { echo "DIRTY: $(basename "$d")"; dirty=$((dirty+1)); } +done +echo "unmerged: $unmerged dirty: $dirty" +``` + +Expected, and what was measured when this ticket was written: `unmerged: 0 dirty: 0`. + +## Acceptance criteria + +```bash +git worktree list | wc -l # MUST be 1 (the main tree only) +git branch --list 'worktree-agent-*' | wc -l # MUST be 0 +ls .claude/worktrees 2>/dev/null | wc -l # MUST be 0 +du -sh .claude 2>/dev/null # was 4.7G under worktrees/ +``` + +The repository is still intact — this is the check that matters after a destructive step: + +```bash +git status --short # only the two doc files +git log --oneline -1 # HEAD unchanged from before your removals +npm run ci # exits 0 +``` + +Show the payoff, since it is the reason for the ticket: + +```bash +find .claude/worktrees -type f 2>/dev/null | wc -l # was 48005 -> MUST be 0 +git ls-files | wc -l # unchanged: 856 tracked files +``` + +## Verification + +`npm run ci`. No source file changes, so `--full` is not required — but run plain `ci` anyway, +because removing worktrees touches `.git` administrative state and the point is to prove the +repository is unharmed. + +## Out of scope + +- `.gitignore` — already correct at line 64. +- Any worktree that fails a gate. Report it instead (decision 4). +- Preventing future accumulation. Worth doing, but it is a change to how agents are launched, + not a cleanup, and no ticket covers it yet. Note it as a follow-up. + +## Risks + +- **This is the arc's only destructive ticket.** The two gates in decision 4 are what make it + safe. Run them, and stop on any failure. +- **`git branch -d`, never `-D`.** Lowercase refuses unmerged branches, which duplicates the + first gate at the moment of deletion. If `-d` refuses a branch, that branch has commits not + in `main` — stop and report it. +- **`git worktree remove` refuses a dirty worktree** unless forced. Do not force. A refusal + means the second gate missed something. +- **Do not delete `node_modules` anywhere else** while cleaning up. The verification block + deliberately ignores untracked `node_modules` inside a worktree, because that is build + output, not work. diff --git a/docs/project/readable-codebase/README.md b/docs/project/readable-codebase/README.md index a2f9559..726fcf0 100644 --- a/docs/project/readable-codebase/README.md +++ b/docs/project/readable-codebase/README.md @@ -109,7 +109,7 @@ two. Note that RD-15 exists because 22 abandoned agent worktrees are still on di | RD-12 | `ActionState` becomes `action` on `BriefState.Loaded` | 11 | | done | | RD-13 | Same for org-template, folding `pendingPublish` in | 12 | | done | | RD-14 | Move `SaveState` to `debounced-save.ts`; delete `action-state.ts` | 13 | | done | -| RD-15 | Remove 22 abandoned agent worktrees (4.7 GB) | 01 | | todo | +| RD-15 | Remove 22 abandoned agent worktrees (4.7 GB) | 01 | | done | | RD-16 | `parseDashboardView` returns `BigProfile`; delete `DashboardView` | 01 | | todo | | RD-17 | `successOf`/`successOr` sweep — 10 sites, 8 files | 01 | | todo | | RD-18 | Ticket-reference sweep, frontend — 181 refs, 100 files | 01 | | todo |