Skip to content

Harden cdd-worktree-done's removal and branch-deletion paths - #122

Merged
drabaioli merged 2 commits into
mainfrom
worktree_done_hardening
Oct 4, 2026
Merged

drabaioli merged 2 commits into
mainfrom
worktree_done_hardening

Conversation

@drabaioli

Copy link
Copy Markdown
Owner

Summary

Fixes the defects in cdd-worktree-done reported in #104:

  • Removal target: the worktree's top level, not $PWD, so a run from a subdirectory removes the whole worktree.
  • Dirty-tree check: untracked files now block the command too, and the blocking paths are listed before anything is removed.
  • sudo fallback: sudo rm -rf is offered only when git reports a permission error. Any other failure (a locked worktree, "not a working tree") stops with nothing deleted.
  • Head check: pr-merged now also returns head_sha in the GitHub and GitLab adapters (an added field, so the contract stays at 1). done force-deletes only when the merged PR includes the local branch's last commit, whether as its own last commit or an earlier one (the case where the branch is behind its PR). If the PR doesn't include it, or the adapter reports no head commit, done asks instead. gc also keeps a task whose local branch isn't in the merged PR.

Deviation from the handoff

Requirement 4 said a head commit that differs from the local tip must never be force-deleted. Pre-PR review relaxed that, with approval: a PR whose head comes after the local tip still counts as merged. Exact matching misread a branch that was behind its PR (commits pushed from another machine, GitHub's "Update branch") as a reused name. On a regular merge, the issues then stayed open and the state record was deleted, so gc couldn't close them later.

Tests

  • New worktree-done gate (scripts/worktree-done-assert.sh) covers: running from a subdirectory, untracked files, the sudo path (through a stub sudo), a locked worktree, "not a working tree", and each branch outcome.
  • scripts/adapter-answers-assert.sh (under adapter-conformance) checks both adapters' pr-merged answers against saved backend responses, offline.
  • gc-assert.sh adds the head-check cases.
  • ./scripts/ci.sh: 25/25 pass.

Template

No template change: the helper and adapters live in tools/, and the template ships no copy of them.

Closes #104

🤖 Generated with Claude Code

https://claude.ai/code/session_01S8CZycScdeRVU2doJPP4sN

drabaioli and others added 2 commits October 3, 2026 23:29
Fixes the defects in issue #104:
- remove the worktree's top level, not $PWD, so a run from a subdirectory
  removes the whole worktree
- refuse on untracked files too (git status --porcelain), naming them
- offer sudo rm -rf only on a permission error; any other removal failure
  (locked, not a working tree) aborts with nothing deleted
- pr-merged reports the PR's head commit (head_sha: GitHub headRefOid,
  GitLab sha), and done force-deletes only when it equals the local tip;
  a mismatch or a missing head falls to the keep/delete/abort prompt. gc
  keeps a task whose local branch is other commits than the merged head.

New worktree-done gate (25 gates); adapter-conformance also runs
adapter-answers-assert.sh over the shipped code-host adapters' pr-merged.
No template change: the helper and adapters live in tools/, and the
template ships no copy of either.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01S8CZycScdeRVU2doJPP4sN
The head check required head_sha to equal the local tip, so a branch
behind its PR (commits pushed from another machine, GitHub's "Update
branch", a web-UI suggestion) read as a reused name: done stopped
force-deleting it, and on the ancestry path dropped the state record
without closing the task's issues. Now the head need only contain the
tip (equal, or a descendant, fetched best-effort when absent), in done
and gc alike. Tests cover the behind-its-PR case in both.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01S8CZycScdeRVU2doJPP4sN
@drabaioli
drabaioli merged commit 6d7c16c into main Oct 4, 2026
1 check passed
@drabaioli
drabaioli deleted the worktree_done_hardening branch October 4, 2026 10:40
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.

cdd-worktree-done can sudo rm -rf the wrong directory, and has no test coverage

1 participant