Skip to content

fix: treat patch-equivalence to default as a merged cleanup signal#883

Merged
chubes4 merged 1 commit into
mainfrom
fix-squash-merge-cleanup
Jul 8, 2026
Merged

fix: treat patch-equivalence to default as a merged cleanup signal#883
chubes4 merged 1 commit into
mainfrom
fix-squash-merge-cleanup

Conversation

@chubes4

@chubes4 chubes4 commented Jul 8, 2026

Copy link
Copy Markdown
Member

Root cause

workspace worktree cleanup / active-no-signal-merged-apply decided a branch was "merged to default" primarily via git ancestry (rev-list --count origin/main..branch). Squash merges create a brand-new commit on origin/main, so the original branch tip is never an ancestor of origin/main. Every squash-merged worktree was therefore classified not_merged_to_default and never auto-cleaned.

The tooling already computed patch-equivalence (git cherry) and emitted the label patch_equivalent_default, but then treated it as a skip reason instead of a removal signal.

What changed

WorkspaceWorktreeCleanupEngine::detect_local_merged_signal()

After the ancestry check fails, the method now falls back to git cherry <default_ref> <branch_ref>. If every local commit has a patch-equivalent match on the remote default branch, it returns a new signal patch-equivalent-merged and marks preserve_local_branch: true (the commits are equivalent but not contained, so we keep the local branch rather than force-delete it).

WorktreeCleanupCandidateClassifier::classify_merge_signal_path()

Passes preserve_local_branch from the signal into the candidate row so the removal loop skips local-branch deletion for squash-merged worktrees.

WorkspaceActiveNoSignalCleanup

  • suggest_active_no_signal_action() now evaluates dirty/unpushed and open-PR guards before patch-equivalence, so an open PR or dirty worktree cannot be classified patch_equivalent_default.
  • worktree_active_no_signal_merged_apply() accepts patch_equivalent_default rows and dispatches to a new metadata builder that revalidates current state via git cherry.
  • New build_current_patch_equivalent_to_default_cleanup_evidence() validates clean/unpushed and re-proves patch-equivalence before writing STATE_MERGED cleanup metadata.

Safety guards kept intact

  • Dirty worktrees → still unsafe_dirty_or_unpushed / dirty_worktree.
  • Unpushed commits → still unpushed_commits.
  • Open PRs → still active_open_pr.
  • The new patch-equivalent-merged signal only fires after the existing dirty/unpushed probes have passed.

GitHub PR state

Not wired into the default cleanup path. The fix is intentionally pure-git (git cherry) and does not require a GitHub API call, matching the issue's request to prefer the self-contained fix as primary.

Verification

  • homeboy review data-machine-code lint --changed-since main → passed (0 PHPCS/PHPStan findings).
  • homeboy review data-machine-code test --changed-since main → test runner could not start because the local WP Codebox CLI is not configured (wp-codebox --version failed). This is an environment bootstrap issue, not a code issue.
  • Native repo tests: php tests/*.php all pass, including the extended tests/worktree-cleanup-patch-equivalence.php which now asserts:
    • squash-equivalent branch produces patch-equivalent-merged signal,
    • clean/no-PR patch-equivalent rows suggest patch_equivalent_default,
    • open PR blocks to active_open_pr,
    • dirty worktree blocks to unsafe_dirty_or_unpushed,
    • active_no_signal_merged_apply plans patch_equivalent_default rows.

Closes #882

Squash merges create a new commit on the default branch, so the original branch tip is never an ancestor of origin/main. The cleanup classifier already computed patch-equivalence via git cherry but treated it as a skip reason.

- detect_local_merged_signal() falls back to git cherry patch-equivalence and emits patch-equivalent-merged, preserving the local branch.

- WorktreeCleanupCandidateClassifier passes preserve_local_branch through so removal skips local branch deletion.

- active-no-signal merged_apply accepts patch_equivalent_default rows, with open-PR/dirty/unpushed guards evaluated first.

- Tests verify squash-equivalent branches are removable and guards still block dirty/open-PR rows.

Closes #882
@homeboy-ci

homeboy-ci Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Homeboy Results — data-machine-code

Lint

review lint — passed

ℹ️ Full options: homeboy self docs commands/lint
Deep dive: homeboy review lint data-machine-code --changed-since b6a9525

Artifacts and drill-down
  • CI results artifact: homeboy-ci-results-data-machine-code-review-lint-quality-Linux-node24 contains immediate command JSON for this action invocation.
  • Observation artifact: homeboy-observations-data-machine-code-review-lint-quality-Linux-node24 contains exported Homeboy run history for deeper queries.
  • Drill-down: download the observation artifact, then run homeboy runs import <dir>, homeboy runs list, and homeboy runs findings <run-id>.
  • Artifacts are attached to the workflow run: https://github.com/Extra-Chill/data-machine-code/actions/runs/28955873646

Test

review test — passed

ℹ️ Auto-fix lint issues: homeboy refactor data-machine-code --from lint --write
ℹ️ Collect coverage: homeboy test data-machine-code --coverage
ℹ️ Save test baseline: homeboy test data-machine-code --baseline
ℹ️ Pass args to test runner: homeboy test -- [args]
ℹ️ Full options: homeboy self docs commands/test
Deep dive: homeboy review test data-machine-code --changed-since b6a9525

Artifacts and drill-down
  • CI results artifact: homeboy-ci-results-data-machine-code-review-test-quality-Linux-node24 contains immediate command JSON for this action invocation.
  • Observation artifact: homeboy-observations-data-machine-code-review-test-quality-Linux-node24 contains exported Homeboy run history for deeper queries.
  • Drill-down: download the observation artifact, then run homeboy runs import <dir>, homeboy runs list, and homeboy runs findings <run-id>.
  • Artifacts are attached to the workflow run: https://github.com/Extra-Chill/data-machine-code/actions/runs/28955873646

Audit

review audit — passed

  • audit — 9 finding(s)
  • Total: 9 finding(s)

Deep dive: homeboy review audit data-machine-code --changed-since b6a9525

Artifacts and drill-down
  • CI results artifact: homeboy-ci-results-data-machine-code-review-audit-quality-Linux-node24 contains immediate command JSON for this action invocation.
  • Observation artifact: homeboy-observations-data-machine-code-review-audit-quality-Linux-node24 contains exported Homeboy run history for deeper queries.
  • Drill-down: download the observation artifact, then run homeboy runs import <dir>, homeboy runs list, and homeboy runs findings <run-id>.
  • Artifacts are attached to the workflow run: https://github.com/Extra-Chill/data-machine-code/actions/runs/28955873646
Tooling versions
  • Homeboy CLI: homeboy 0.281.20+123145ee082b+9702c22
  • Extension: wordpress from https://github.com/Extra-Chill/homeboy-extensions
  • Extension revision: f89b4297
  • Action: unknown@unknown

@chubes4 chubes4 merged commit 6211d16 into main Jul 8, 2026
5 checks passed
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.

Worktree cleanup never removes squash-merged branches — ancestry-based 'merged' detection defeated by squash merges, causing ghost-worktree pile-up

1 participant