feat: bounded cleanup-eligible apply for explicit lifecycle metadata#223
Merged
feat: bounded cleanup-eligible apply for explicit lifecycle metadata#223
Conversation
Adds an operator-safe bounded apply path so worktrees with explicit
lifecycle `cleanup_eligible` metadata can be reclaimed in bounded
batches without running the slow retention scan, full git worktree
discovery, or GitHub API lookups first.
Workspace::worktree_bounded_cleanup_eligible_apply():
- Uses the existing inventory_only path for candidate discovery
(no full worktree_list / fetch / GitHub work). Only worktrees
whose persisted lifecycle metadata satisfies
WorktreeContextInjector::has_cleanup_signal() are considered;
rows without explicit cleanup_eligible signal are skipped with a
stable reason code via the inventory gate.
- Caps each call with --limit (default 25, hard ceiling 200).
- Optional --older-than gate against lifecycle created_at metadata.
- Revalidates each candidate immediately before mutation through
cheap gates: missing-metadata, external/containment, missing path,
primary .git directory, dirty (porcelain), unpushed (hard stop —
never overridden by force), and missing primary checkout.
- --via-jobs schedules per-candidate worktree_cleanup_chunk jobs for
resumable async apply, reusing the existing chunk task and lock
flow.
- Evidence: summary.processed/removed/skipped/bytes_reclaimed plus a
continuation envelope (remaining_total/remaining_handles/
next_call_hint) so operators can drain the workspace in successive
bounded calls.
Surfaces:
- New ability: datamachine/workspace-worktree-bounded-cleanup-eligible-apply.
- New CLI subcommand:
wp datamachine-code workspace worktree bounded-cleanup-eligible-apply
[--dry-run] [--limit=N] [--older-than=DUR]
[--sort=size|age] [--force] [--via-jobs]
Routed through the new ability with table/JSON renderer.
Smoke test (tests/smoke-worktree-bounded-cleanup-eligible-apply.php)
covers the bounded limit, dry-run/apply paths, every revalidation
gate, and force semantics (dirty allowed, unpushed never).
Refs #216
4cfc619 to
209efb5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an operator-safe bounded cleanup-eligible apply path for worktrees that have explicit lifecycle
cleanup_eligiblemetadata. Closes the gap from #216: hundreds of worktrees pile up, full retention dry-run/apply paths time out or take too long to be useful, and there is no end-to-end way to safely reclaim the explicitcleanup_eligiblerows in bounded batches.This PR adds that path without touching the existing retention/artifact/emergency cleanup behaviors and without introducing any new internal pipelines or flows — purely WP-CLI wrapping a workspace ability, the same pattern as the rest of
workspace worktree *.What changed
Workspace::worktree_bounded_cleanup_eligible_apply()(new public method oninc/Workspace/Workspace.php):worktree_list/git fetch/ GitHub API work just to plan. Only worktrees whose persisted lifecycle metadata satisfiesWorktreeContextInjector::has_cleanup_signal()are considered; rows without an explicitcleanup_eligiblesignal stay skipped via the existing inventory gate (no_inventory_cleanup_signal,requires_full_scan).--limit(default 25, hard ceiling 200).--older-thangate against lifecyclecreated_atmetadata..gitdirectory (refuses to remove primaries)git status --porcelain, bounded byCLEANUP_GIT_PROBE_TIMEOUT)force)--via-jobsschedules per-candidateworktree_cleanup_chunkjobs (single-row chunks) for resumable async apply, reusing the existing chunk task + lock flow.summary.processed / removed / skipped / bytes_reclaimed, plus acontinuationenvelope (remaining_total,remaining_handles,next_call_hint) so operators can drain the workspace in successive bounded calls.Surfaces
datamachine/workspace-worktree-bounded-cleanup-eligible-apply(inc/Abilities/WorkspaceAbilities.php).inc/Cli/Commands/WorkspaceCommand.php).Acceptance criteria
worktree_list, no GitHub. Only rows with explicit lifecyclecleanup_eligiblemetadata become candidates.revalidate_bounded_cleanup_eligible_candidate()runs the cheap safety gates per candidate at apply time.reason_codeand the worktree survives on disk; verified by smoke.--via-jobsschedules per-candidateworktree_cleanup_chunkjobs viaTaskScheduler::scheduleBatch.summary+continuation.Tests
New
tests/smoke-worktree-bounded-cleanup-eligible-apply.php(30 assertions, all green) — builds a real workspace with a mix of cleanup-eligible / dirty / unpushed / active / missing-metadata worktrees and asserts:worktree_listcalls).--limitactually bounds the batch andcontinuation.remaining_totalreports the rest.--dry-runreturns success without mutating anything;--via-jobs+--dry-runis rejected with a stable error.reason_code.force=truepermits dirty cleanup but the unpushed-commit gate is never overridden.Out of scope
worktree_cleanup_merged/worktree_cleanup_artifacts/worktree_emergency_cleanupsemantics.AI assistance
Closes #216