Use this workflow when local contribution branches, linked worktrees, dependency trees, and build output have accumulated. Cleanup is a terminal-PR maintenance step, not a general disk cleaner.
RepoStew cleans only explicitly registered linked worktrees whose tracked pull
request is currently MERGED or CLOSED. Ordinary PR worktrees must match the
tracked PR branch and pushed tip. Batch workers require the separate terminal
proof described below. It never deletes:
- a canonical clone, workspace root, fork, or remote branch;
- an active PR's worktree or branch;
- a detached, moved, unregistered, or repository-mismatched worktree;
- tracked changes, untracked files, unpushed commits, credentials, keys, or ignored data that is not recognizable disposable build/dependency output;
- RepoStew state or retained cleanup history.
This means an unregistered old worktree is reported but not inferred to be task-owned. Register it only after matching it to the exact tracked PR and reviewing its provenance. Do not convert a particular repository's path naming convention into a global ownership rule.
After opening and tracking a PR from a linked worktree, record that exact local resource while its remote provenance is easy to verify:
python scripts/workspace_cleanup.py register \
--workspace "$REPOSTEW_REPOS_HOME" \
--worktree "$REPOSTEW_REPOS_HOME/repo-issue" \
--pr-url https://github.com/owner/repo/pull/123When RepoStew runs on a different operating system from the one that registered worktrees, the stored paths become unreachable (e.g., Windows D:\repo\... paths on macOS). The cleanup script detects these automatically and the purge-cross-platform command removes them:
# Dry run first
python scripts/workspace_cleanup.py purge-cross-platform \
--workspace "$REPOSTEW_REPOS_HOME"
# Apply the purge
python scripts/workspace_cleanup.py purge-cross-platform \
--workspace "$REPOSTEW_REPOS_HOME" --applyPurged entries are moved to history with status cross_platform_purged before removal, preserving the audit trail.
Similarly, entries that are already in removed state can be cleared from the registry with purge-terminal:
python scripts/workspace_cleanup.py purge-terminal \
--workspace "$REPOSTEW_REPOS_HOME" --applyOn Windows PowerShell, pass normal absolute paths:
```powershell
python scripts\workspace_cleanup.py register `
--workspace "D:\maintenance" `
--worktree "D:\maintenance\repo-issue" `
--pr-url "https://github.com/owner/repo/pull/123"
Registration rejects canonical clones and requires all of the following:
- the target is the root of a linked Git worktree below the exact workspace;
- its branch matches the tracked PR
head_ref; - a configured GitHub remote matches the PR base or head repository; and
- its tip matches the tracked PR head or an exact remote-tracking ref.
Refresh the PR first if an imported terminal entry predates the tracker fields:
python scripts/pr_tracker.py check --include-terminal --repo owner/repoRegistration creates workspace_resources.json in REPOSTEW_HOME. Back up
that file with the other RepoStew state files; it is the durable ownership and
cleanup history ledger.
If review maintenance rebases, amends, or force-pushes the same PR branch, first
refresh that PR in pr_tracker.json, push the new tip, and then explicitly
refresh the existing ownership record:
python scripts/workspace_cleanup.py rebind \
--workspace "$REPOSTEW_REPOS_HOME" \
--worktree "$REPOSTEW_REPOS_HOME/repo-issue" \
--pr-url https://github.com/owner/repo/pull/123rebind repeats the full workspace, linked-worktree, branch, repository-remote,
tracked-PR, and pushed-tip checks used by initial registration. It can update
only registered_head and its timestamp for the same active worktree and PR;
it cannot transfer ownership to another path, branch, repository, or PR. The
previous and replacement commits are retained as a rebound history event.
An unpushed rewrite is rejected.
Do not register a worker with the ordinary register command. After the
integration PR is terminal and refreshed, use the exact worker path and the
full batch-start commit recorded when the batch began:
python scripts/workspace_cleanup.py register-worker \
--workspace "$REPOSTEW_REPOS_HOME" \
--worktree "$REPOSTEW_REPOS_HOME/repo-batch-worker" \
--pr-url https://github.com/owner/repo/pull/123 \
--base-oid 0123456789abcdef0123456789abcdef01234567The command requires a linked, clean worker in the same repository, a terminal tracked integration PR, an exact 40-character base that is an ancestor of both heads, and at least one worker commit. It accepts either direct ancestry into the integration head or a merge-free range whose every patch has an equivalent in that head. Patch-equivalent worker tips must also match an exact remote-tracking ref so the original commits are not discarded while unpushed. The recorded worker head, base, integration head, inclusion method, and verified commits are immutable cleanup provenance. There is no worker rebind: any later head or integration-head change blocks cleanup and requires a new explicit assessment.
If the clean worker already contains repository-documented, reproducible
Git-ignored output, approve each exact path atomically with registration by
repeating --path. Registration applies the same relative-path, exact-ignore
and credential-like-path checks as approve-output; any remaining unknown
ignored data still rejects the worker. This avoids deleting output merely to
establish ownership and does not broaden the global disposable-path list:
python scripts/workspace_cleanup.py register-worker \
--workspace "$REPOSTEW_REPOS_HOME" \
--worktree "$REPOSTEW_REPOS_HOME/repo-batch-worker" \
--pr-url https://github.com/owner/repo/pull/123 \
--base-oid 0123456789abcdef0123456789abcdef01234567 \
--path public/generated-data \
--path test-resultsGeneric build directories such as dist/ and node_modules/ are recognized
automatically. If a target repository documents another ignored path as wholly
generated and reproducible, attach that exact path to an already registered
worktree instead of broadening RepoStew's global disposable-name list:
python scripts/workspace_cleanup.py approve-output \
--workspace "$REPOSTEW_REPOS_HOME" \
--worktree "$REPOSTEW_REPOS_HOME/repo-issue" \
--pr-url https://github.com/owner/repo/pull/123 \
--path public/generated-data \
--path test-resultsEach path must exactly match a path currently reported by Git as ignored. It
must be relative, stay outside .git, and not look credential-like. The
approval is bound to the existing worktree, PR, and registered head and is
retained in cleanup history. This is for repository-documented reproducible
output only; source assets, downloads, user data, and uncertain caches remain
blocked.
The cleanup command is a dry run unless --apply is explicit:
python scripts/workspace_cleanup.py cleanup \
--workspace "$REPOSTEW_REPOS_HOME"
python scripts/workspace_cleanup.py cleanup \
--workspace "$REPOSTEW_REPOS_HOME" \
--jsonTo limit a run to exact registered worktrees, repeat --worktree. This only
narrows the registered-resource inventory; each selected path is still subject
to every terminal-PR, boundary, ownership, cleanliness, ignored-data, pushed-tip,
and branch-ownership check. A selected path with no active ownership record
fails safely, including a missing path that was never registered:
python scripts/workspace_cleanup.py cleanup \
--workspace "$REPOSTEW_REPOS_HOME" \
--worktree "$REPOSTEW_REPOS_HOME/repo-batch-worker-a" \
--worktree "$REPOSTEW_REPOS_HOME/repo-batch-worker-b" \
--jsonThe inventory distinguishes registered linked worktrees from protected canonical clones and unregistered worktrees. For every registered resource it rechecks:
- the exact resolved path remains below the workspace and differs from the canonical clone;
- the tracker entry is terminal and still names the same branch for an ordinary PR worktree, or still has the exact recorded integration head for a batch worker;
- the worktree belongs to the recorded common Git directory;
- tracked and ordinary untracked state is clean;
- ignored paths are recognizable dependency/build/cache output, with credential-like paths and unknown ignored data blocking cleanup;
- the local tip exactly matches its registered head and has the required pushed PR provenance or revalidated worker-inclusion proof; and
- no other worktree owns the local branch.
Logical file sizes include ignored dependency and build output. Symlinks are not followed. Windows extended-length paths and read-only generated files are handled only after the same exact-path safety checks.
After reviewing the dry run, repeat the same command with --apply:
python scripts/workspace_cleanup.py cleanup \
--workspace "$REPOSTEW_REPOS_HOME" \
--worktree "$REPOSTEW_REPOS_HOME/repo-batch-worker-a" \
--worktree "$REPOSTEW_REPOS_HOME/repo-batch-worker-b" \
--apply --jsonEach candidate is re-evaluated immediately before mutation. For an eligible live worktree, the helper removes only the Git-enumerated ignored paths already classified as disposable build/dependency output, then asks Git to remove the worktree without force. Git therefore performs another independent check and refuses a tracked or untracked change that races with evaluation. The exact local branch ref is deleted only if it still has the verified expected commit. A missing worktree can have its stale Git metadata pruned after the same registration, terminal-state, branch, and pushed-provenance checks. The script does not push branch deletion.
If a previous applied cleanup failed after it recorded that it freed every estimated logical byte for that exact registered path, PR, branch, and head, and both the worktree directory and its Git worktree metadata have since gone, a retry may recover by pruning the stale state and deleting the verified local branch. This is only a retry of an evidenced helper failure: an arbitrary missing worktree without Git metadata remains blocked. The terminal PR, registered-head, remote-provenance, and branch-owner checks still apply.
The result reports estimated and actual freed logical bytes. Successful and
failed attempts remain in workspace_resources.json, including PR URL, branch,
commit, timestamps, and byte counts. Keep this history even after the local
code has been retired.
If any item is blocked or uncertain, report it and leave it untouched. Do not
use git clean -X, recursive filesystem deletion, or broad branch deletion as
a substitute for the guarded workflow.
