feat: add worktree cleanup audit integrity (Closes #404)

Reconcile before/after branches/ snapshots so preserved worktrees cannot
disappear without removal logs or explained state transitions.

- worktree_cleanup_audit.py: snapshot capture, disposition reconciliation
- gitea_capture_branches_worktree_snapshot, gitea_assess_worktree_cleanup_integrity
- Final-report rule author.worktree_cleanup_audit_proof
- worktree-cleanup.md bulk audit section
- tests/test_worktree_cleanup_audit.py (11 cases)
This commit is contained in:
2026-07-07 11:50:29 -04:00
parent 1a1e679246
commit e4c0a51e5a
5 changed files with 834 additions and 0 deletions
+54
View File
@@ -504,6 +504,7 @@ import already_landed_reconcile # noqa: E402
import author_mutation_worktree # noqa: E402
import issue_claim_heartbeat # noqa: E402
import merged_cleanup_reconcile # noqa: E402
import worktree_cleanup_audit # noqa: E402
import reconciler_profile # noqa: E402
import reconciliation_workflow # noqa: E402
import review_merge_state_machine # noqa: E402
@@ -3509,6 +3510,59 @@ def _remote_branch_exists(h: str, o: str, r: str, auth: str, branch: str) -> boo
raise
@mcp.tool()
def gitea_capture_branches_worktree_snapshot(
open_pr_branches: list[str] | None = None,
active_lock_branch: str | None = None,
leased_paths: list[str] | None = None,
worktree_path: str | None = None,
) -> dict:
"""Read-only: capture ``branches/`` and worktree audit snapshot (#404)."""
read_block = _profile_operation_gate("gitea.read")
if read_block:
return {
"success": False,
"reasons": read_block,
"permission_report": _permission_block_report("gitea.read"),
}
root = PROJECT_ROOT
if worktree_path:
root = os.path.realpath(os.path.abspath(worktree_path))
git_root = _get_git_root(root)
if git_root:
root = git_root
snapshot = worktree_cleanup_audit.capture_branches_worktree_snapshot(
root,
open_pr_branches=open_pr_branches,
active_lock_branch=active_lock_branch,
leased_paths=leased_paths,
)
return {"success": True, "snapshot": snapshot}
@mcp.tool()
def gitea_assess_worktree_cleanup_integrity(
before_snapshot: dict,
after_snapshot: dict,
removals: list[dict] | None = None,
explained_missing: dict[str, str] | None = None,
) -> dict:
"""Read-only: reconcile cleanup before/after snapshots (#404)."""
read_block = _profile_operation_gate("gitea.read")
if read_block:
return {
"integrity_passed": False,
"reasons": read_block,
"permission_report": _permission_block_report("gitea.read"),
}
return worktree_cleanup_audit.assess_worktree_cleanup_integrity(
before=before_snapshot,
after=after_snapshot,
removals=removals,
explained_missing=explained_missing,
)
@mcp.tool()
def gitea_reconcile_merged_cleanups(
dry_run: bool = True,