feat: prune orphaned missing_path inventory rows#870
Merged
Conversation
Add `workspace inventory prune-missing` and the `datamachine-code/workspace-worktree-inventory-prune-missing` ability to reap ghost rows whose on-disk directory was deleted. Safety guards: - Re-probes each candidate's path; only deletes when STILL absent. - Refuses rows with unpushed_count > 0 or non-empty pr_url unless --force. - --dry-run previews candidates and skips without mutating. Closes #869.
Contributor
Homeboy Results —
|
This reverts commit 9258593.
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.
What this does
Adds a non-SQL path to reap orphaned
missing_path=1rows from the DB-backed workspace inventory (c8c_datamachine_code_worktrees). When a primary checkout directory is deleted from disk, its row becomes an unreachable ghost —inventory refreshflags it andhygienecounts it, but nothing ever deleted it, andworkspace remove <handle>structurally cannot (it resolves handles from disk). The only fix was manual SQL.New CLI:
Backed by a new ability
datamachine-code/workspace-worktree-inventory-prune-missing.Flags
--dry-run— Preview candidates and skips without deleting anything.--yes— Skip the confirmation prompt (a dry-run preview is shown before any real deletion unless--yesis passed).--force— Allow pruning rows withunpushed_count > 0or a non-emptypr_url(otherwise they are skipped and reported).--format—table(default) orjson.Safety guards
pathis re-probed on disk. Only rows whose path is still absent are deleted. A row flaggedmissing_path=1whose directory came back is skipped (reason: path_present_on_disk).unpushed_count > 0or a non-emptypr_urlare skipped unless--forceis passed (skip reasons:unpushed_count,pr_url).--yesis set. If nothing would be deleted, it short-circuits with a success message.Success output mirrors the terse refresh style:
Inventory pruned: N deleted, M skipped.(or(dry-run)variant).Files changed and why
inc/Storage/WorktreeInventoryRepository.php— NewpruneMissing(array $opts): arraystorage primitive that fetchesmissing_path=1rows (via privatemissing_path_rows()helper using a targetedWHERE missing_path = 1query that hits the existing index), re-probes disk, applies unpushed/PR guards, deletes survivors, and returns a summary withdeleted/skippedlists. Reuses the existingdelete()/table_name()/decode_row()patterns in the class.inc/Workspace/WorkspaceWorktreeLifecycle.php— Thin public wrapperworktree_inventory_prune_missing(array $opts)mirroringworktree_inventory_refresh(), delegating to the repository via the existingworktree_inventory()accessor.inc/Abilities/WorkspaceAbilities.php— Registersdatamachine-code/workspace-worktree-inventory-prune-missing(same shape/permission/schema conventions as the sibling-refreshability) and adds theworktreeInventoryPruneMissing()execute callback.inc/Cli/Commands/WorkspaceCommand.php— Splits theinventoryop handler intorefresh/prune-missingdispatchers, adds theprune-missinghandler with--dry-run/--yes/--force/--formathandling and a confirmation flow, and updates the docblock/usage string soprune-missingis documented.tests/worktree-inventory-prune-missing.php— New standalone smoke test (5 cases).Test results
This repo uses standalone PHP smoke tests under
tests/(no PHPUnit). New test added and passing:Cases covered:
--dry-runreturns candidates without deleting (rows remain in store).missing_path=1flag not trusted).unpushed_count > 0and non-emptypr_urlare skipped without--force.--forceoverrides the unpushed/PR guards.missing_path=0are never considered as candidates.Existing related smoke tests still green (
json-codec,workspace-handle). PHP lint clean on all 4 changed source files.Judgment-call forks
pruneMissing()primitive (rather than the Workspace/CLI layer). Rationale: the issue explicitly asked for apruneMissing()storage primitive that "returns a summary", the repository already encodes disk-reconciliation semantics (mark_missing()/missing_path/build_worktree_inventory_row_from_handle), and keeping it in one place makes it unit-testable without a WP stack. The Workspace method is a one-line delegate, exactly likeworktree_inventory_refresh().--yes).--dry-runalone never prompts. If zero rows would be deleted, it short-circuits with success before prompting. This follows the existingworkspace remove --yesconfirmation convention while giving operators visibility into what a destructive run will touch.WHERE missing_path = 1(uses the existingmissing_pathkey) rather than pulling all rows and filtering in PHP. The smoke-test$wpdbstub honors the predicate so the production SQL path is exercised faithfully.Closes #869.