diff --git a/tasks/backlog/59-finishing-a-phase-finishes-its-cards.md b/tasks/backlog/59-finishing-a-phase-finishes-its-cards.md index 139a0bd..d05ebe4 100644 --- a/tasks/backlog/59-finishing-a-phase-finishes-its-cards.md +++ b/tasks/backlog/59-finishing-a-phase-finishes-its-cards.md @@ -1,14 +1,17 @@ -# 59 — Finishing a phase finishes its cards +# 59 — Finishing a phase finishes its cards, and clears up after them **Status:** Backlog **Priority:** Medium — without it a phase ends by handing you a pile of -cards you have already judged +cards you have already judged and a heap of checkouts nobody will +remember to delete **Type:** Feature When a phase's PR merges into `main`, its members' work is in `main` too — but their cards are sitting in `review/`, where the board says they are -waiting on you. **Merge & clean up** on a phase card should move its -members to `done/` in the same operation that moves the phase. +waiting on you, and every one of them has left a worktree and three +branches behind. **Merge & clean up** on a phase card should finish its +members too: move them to `done/`, and clear the workspaces they are done +with. ## Context @@ -29,6 +32,16 @@ members to `done/` in the same operation that moves the phase. - Board-made moves commit themselves under `BOARD_COMMIT_MOVES` and, in team mode, push — so a sweep of five cards is five commits or one, and which it is deserves a decision rather than an accident. +- **The cleanup half is missing entirely.** `complete_task` works from + `stem = filename[:-3]`, so it removes the worktree and branch of the + card being completed and knows nothing about members. Measured on phase + 53 after it finished: three worktrees still on disk + (`.worktrees/47-…`, `.worktrees/52-…`, `.worktrees/53-…`, 3.8M each), + both member branches still local, and both still on origin. A grep of + `phases.py` finds worktrees created and never removed. +- An ordinary card already gets the full treatment on completion — + worktree removed, local branch deleted, remote branch deleted after the + push. Members should get exactly that, from the same place. **Affected areas:** `manager/core/github.py` (`complete_task`), and whatever narrates the result. @@ -38,9 +51,17 @@ whatever narrates the result. - **The sweep, inside the merge.** When a phase card is completed, every member the phase lists moves to `done/` as part of the same operation — after the merge into `main` has actually succeeded, never before. +- **Clear each merged member's workspace**, the way completing an + ordinary card clears its own: remove the worktree, delete the local + branch, delete the branch on the remote. The member's work is in `main` + by then, so there is nothing in any of them worth keeping. - **Only what the phase merged.** A member that never reached the phase - branch — halted, held, walked back — is not swept. It stays where it - is, and it is the reason a person will look at the phase afterwards. + branch — halted, held, walked back — is neither moved nor cleared. Its + card stays where it is, and so do its worktree and its branch: there is + work in them, and it is the reason a person will look at the phase + afterwards. The board already applies this rule to a failed run that + committed something, and removing a worktree with work in it is the one + unrecoverable thing in this card. - **Narrate it as one thing.** The ticker should say a phase finished and how many cards went with it, not five separate moves scrolling past. A person watching should see one event, because one thing happened. @@ -73,8 +94,13 @@ whatever narrates the result. number of cards. - [ ] Given the merge fails or conflicts, nothing moves — not the phase card, not one member. -- [ ] Given a member that never reached the phase branch, it is left - exactly where it is. +- [ ] Given a member that never reached the phase branch, its card, its + worktree and its branch are all left exactly as they are. +- [ ] Given the phase completes, no merged member leaves a worktree, + a local branch or a branch on the remote behind — `git worktree + list` and `git branch` name only what was there before the phase. +- [ ] Removing a worktree never discards uncommitted changes: a member + with a dirty worktree is reported and kept, not forced. - [ ] Given "just move the card" instead, no member moves. - [ ] Given the phase card is archived rather than merged, its members reappear on the board in their own stages and none of them is @@ -97,3 +123,10 @@ The reason it is separate from card 56 rather than folded in: 56 is about what the *Board view draws*, and this is about what the *board does*. One is a rendering rule, the other moves files and commits them. Keeping them apart also keeps 56 shippable on its own. + +**Why the cleanup is not cosmetic.** A stale worktree is not just disk — +`start_agent` refuses to launch a card whose worktree already exists on +the wrong branch, so a leftover from a finished phase is a trap laid for +whoever reopens that card. And the accumulation is per member per phase: +five-card phases leave five checkouts and fifteen branches each time, +until somebody notices and does it by hand.