Skip to content

fix(worktree): remove finished checkouts without --force - #632

Merged
kixelated merged 3 commits into
mainfrom
quest/m0/worktree-ignored-scratch
Nov 11, 2026
Merged

kixelated merged 3 commits into
mainfrom
quest/m0/worktree-ignored-scratch

Conversation

@kixelated

@kixelated kixelated commented Nov 11, 2026 •

Copy link
Copy Markdown
Collaborator

Finishes quest/m0/worktree-ignored-scratch.md.

Problem

A /quest-spawn run in moq-dev/moq.pro left 40 finished agent checkouts, and quest worktree remove refused 38, for two reasons:

  • Ignored build output. remove refused any ignored file outside the scratch derivedDefaults fixed at creation (the main checkout's top-level ignored entries). So node_modules, target, .svelte-kit, gate stamps and .tmp/ all blocked removal.
  • Merged heads refused. Root cause, reproduced against moq-dev/moq.pro#3771: the agent's local head 6bcb980f0 is not the PR's merged head. quest land merged the tested base into it (ba767b4ab, "quest land: merge tested base", first parent 6bcb980f0) before squashing. mergedHead accepted only headRefOid === commit, so it returned false even though GitHub's associatedPullRequests listed #3771 as merged.

Changes

  • remove now treats every ignored path, in the checkout and in its submodules, as scratch. It no longer prunes the declared scratch first or refuses leftover ignored files. Instead, before deleting anything, it refuses any ignored path that holds a nested repository or worktree (any .git entry, or a bare repository's HEAD, objects/ and refs/, below it, without following symlinks). It still refuses modified or untracked files, unreached commits, and the existing submodule cases. git worktree remove deletes the ignored files.
  • mergedHead is renamed mergedHolds. A merged PR counts when its head is the commit or descends from it. For each merged associated PR whose head differs, it calls GET /compare/{head}...{commit} and accepts ahead_by == 0. Comparing in that direction sends back no commits or files. A force-pushed PR whose head no longer reaches the commit, or a compare that 404s, still counts as no evidence.
  • release (prune) now refuses declared scratch that holds a nested repository, not just a linked worktree, so the same rule holds on both paths. This replaces linkedWorktree with the existing repositoryWithin.
  • README and quest worktree remove --help describe the new behavior. The quest file is deleted.

Choices made without asking

  • Ancestor instead of exact head. The quest asked why merged missed these heads and to fix it. Accepting any merged PR head that descends from the commit is the general fix: it also covers a reviewer or the land fixer pushing on top. It is safe because GitHub keeps refs/pull/N/head permanently, so the forge still holds the commit. I rejected special-casing a quest land merge commit's first parent, since that would tie the core rule to one landing tool.
  • Release refuses nested repositories too. Writing the remove test showed that release (including the automatic release when a supervised run ends) deleted a clone under declared scratch, because it checked only for linked worktrees. CONTEXT tells agents to put temporary clones in .tmp/, and .tmp is declared scratch wherever the main checkout ignores it, so this was the same work-loss risk the quest names. A release that hits one now fails and leaves the checkout reserved.
  • No registered-checkout check at remove. remove finds nested checkouts by their .git entry alone. A registered linked worktree whose .git file is gone is already prunable.
  • A .git directory inside dependency output, such as a package sourced from a repository, now blocks removal. That errs toward keeping data.

Public API

None. quest worktree remove refuses less and quest worktree release refuses more; there are no flag, schema or wire changes.

Validation

  • quest gate just check and quest gate just test pass (34 files, 1222 tests, 5 skipped). quest check --base origin/main passes.
  • New and updated tests cover: undeclared ignored files being removed; a repository nested deep under ignored scratch and a linked worktree under ignored .worktrees/ both being refused at remove; a bare repository under ignored scratch; a nested repository under a submodule's ignored path being refused while the submodule's ignored .env is removed; release refusing a temporary clone in scratch; and mergedHolds accepting a land-merged head through compare while rejecting a head that no longer reaches the commit (ahead_by 1) or a compare that 404s.

Caused by

  • moq-dev/quest#186 - accepted only a merged PR whose head was exactly the commit, and refused every ignored file outside the scratch declared at creation.

Follow-ups

None recommended.

Models

Size: M
Implementer: Claude Code, claude-opus-5-5, medium effort

Review

Rewritten by quest review after each round.

  • Reviewer: Codex, gpt-6.1-sol, medium effort
  • Session: 01a1ca37-9764-7030-a98f-047a39a5e1d9, round 2
  • Head: dfcdf5e
  • Base: origin/main at 5055d0c
  • Score: 10/10, The bare-repository guard fixes the reported data-loss case, and the incremental changes introduce no new findings.
  • Findings: none
  • The last round's findings, at ff73e1d:
    • Fixed: medium, src/cli/worktree.ts:791: Detect bare repositories before deleting ignored paths; repositoryWithin now detects directories containing HEAD, objects/, and refs/, protecting bare repositories through its existing callers, and the added remove test checks refusal for cache/mirror.git.

(Written by Claude Opus 5.5 with medium)

kixelated and others added 2 commits November 11, 2026 00:54
…out --force

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title Finished checkouts remove without --force fix(worktree): remove finished checkouts without --force Nov 11, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated marked this pull request as ready for review November 11, 2026 10:53
@coderabbitai

coderabbitai Bot commented Nov 11, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 20 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 2461922e-e7c2-4522-853c-520a1eda479b
Commits

Reviewing files that changed from the base of the PR and between f3301ef and dfcdf5e.

Files selected for processing: 6

  • README.md
  • quest/m0/worktree-ignored-scratch.md
  • src/cli/github.ts
  • src/cli/main.ts
  • src/cli/worktree.ts
  • test/worktree.test.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@kixelated
kixelated added this pull request to the merge queue Nov 11, 2026
Merged via the queue into main with commit a3875a6 Nov 11, 2026
15 checks passed
@kixelated
kixelated deleted the quest/m0/worktree-ignored-scratch branch November 11, 2026 18:32
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