diff --git a/plugins/repo-fleet-hygiene/.claude-plugin/plugin.json b/plugins/repo-fleet-hygiene/.claude-plugin/plugin.json index 10f732196d..248e4f392b 100644 --- a/plugins/repo-fleet-hygiene/.claude-plugin/plugin.json +++ b/plugins/repo-fleet-hygiene/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "repo-fleet-hygiene", - "version": "0.13.0", + "version": "0.13.1", "description": "Read-only Git/GitHub fleet audit for merged local branches, orphaned or mismatched worktree registrations, and repository transfers or renames. Findings are confidence-tiered and hand off exact targets to existing per-repository cleanup tools; this plugin never deletes branches or worktrees.", "author": { "name": "Melodic Software", diff --git a/plugins/repo-fleet-hygiene/CHANGELOG.md b/plugins/repo-fleet-hygiene/CHANGELOG.md index fec523d3ef..d70e70b366 100644 --- a/plugins/repo-fleet-hygiene/CHANGELOG.md +++ b/plugins/repo-fleet-hygiene/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to `repo-fleet-hygiene` are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.13.1] + +### Fixed + +- **`merged-pr-tip-drift` evidence no longer claims commits "may never have been pushed" (#2603).** + Absence from the last-fetched remote-tracking ref does not prove the tip was never on GitHub — + post-merge head deletion plus prune is the common case, and the tip object may still exist on the + remote. The non-matching push-state clause now states that the tip differs from the merged PR + `headRefOid` and that commits may still be on the remote. + ## [0.13.0] ### Added diff --git a/plugins/repo-fleet-hygiene/skills/audit/scripts/audit-fleet.sh b/plugins/repo-fleet-hygiene/skills/audit/scripts/audit-fleet.sh index 6b88540d8e..3a3fa0da8d 100755 --- a/plugins/repo-fleet-hygiene/skills/audit/scripts/audit-fleet.sh +++ b/plugins/repo-fleet-hygiene/skills/audit/scripts/audit-fleet.sh @@ -1382,15 +1382,17 @@ analyze_repo() { fi elif [[ -n "$pr_any" && "$branch" != "$default_branch" ]]; then IFS='|' read -r pr_num pr_oid pr_merged pr_url <<<"$pr_any" - # Whether the drift commits were ever pushed changes the risk profile of cleanup, so the - # evidence names the push state from the already-collected remote-tracking inventory -- - # purely local, no network. A remote-tracking ref only records what the remote advertised + # Whether the local tip matches the last-fetched remote-tracking ref changes the cleanup + # risk profile, so the evidence names that observation from the already-collected inventory + # -- purely local, no network. A remote-tracking ref only records what the remote advertised # at the LAST FETCH (the branch may have been deleted or force-pushed since), so the # evidence is framed as cached local observation, never as current remote reachability. + # Absence from that ref does not prove the tip was never pushed: post-merge head deletion + # plus prune is common, and the tip object may still exist on the remote under another ref. if [[ "$remote_inventory_failed" == "true" || -z "$canonical_remote" ]]; then push_state="remote-tracking inventory unavailable, push state unknown" else - push_state="local tip not on the last-fetched remote-tracking ref (drift commits may never have been pushed)" + push_state="local tip not on the last-fetched remote-tracking ref (tip differs from merged PR headRefOid; commits may still be on the remote)" for ((ri = 0; ri < ${#REMOTE_BRANCH_NAMES[@]}; ri++)); do if [[ "${REMOTE_BRANCH_NAMES[$ri]}" == "$branch" && "${REMOTE_BRANCH_TIPS[$ri]}" == "$tip" ]]; then push_state="local tip matches the last-fetched remote-tracking ref (pushed as of the last fetch; verify current remote state before relying on recoverability)" diff --git a/plugins/repo-fleet-hygiene/skills/audit/scripts/audit-fleet.test.sh b/plugins/repo-fleet-hygiene/skills/audit/scripts/audit-fleet.test.sh index 3ea7768a77..d9b28329bf 100755 --- a/plugins/repo-fleet-hygiene/skills/audit/scripts/audit-fleet.test.sh +++ b/plugins/repo-fleet-hygiene/skills/audit/scripts/audit-fleet.test.sh @@ -274,7 +274,8 @@ for-each-ref) case "$base" in canonical-a) # stale/gone: merged-PR batch row exists at a different OID (drift) but the branch has no - # remote-tracking ref -- the drift finding must state the local tip is absent (unpushed). + # remote-tracking ref -- the drift finding must state tip/headRefOid differ without claiming + # the commits were never pushed (they may still be on the remote). printf 'main\tmain-a\0\nfeature/shared\tsha-a\0\nstale/changed\tdrift-tip\0\nfeature/mismatch\tmismatch\0\nstale/gone\tgone-tip\0\n' ;; repo-b) @@ -583,11 +584,14 @@ assert_contains "privacy-gated handoff distinguishes never-pushed locals" \ "Never-pushed locals: push to publish the branch name, then rerun" # Drift push-state evidence: stale/changed has a same-named remote-tracking ref at the SAME OID -# (pushed); stale/gone has a drift-batch row but NO remote-tracking ref (may be unpushed). +# (pushed as of last fetch); stale/gone has a drift-batch row but NO remote-tracking ref — that +# absence must not be framed as "never pushed" (#2603). assert_contains "pushed drift named in evidence" \ "current local tip is drift-tip; local tip matches the last-fetched remote-tracking ref (pushed as of the last fetch; verify current remote state before relying on recoverability)" -assert_contains "unpushed drift named in evidence" \ - "current local tip is gone-tip; local tip not on the last-fetched remote-tracking ref (drift commits may never have been pushed)" +assert_contains "absent remote-tracking tip named without unpushed claim" \ + "current local tip is gone-tip; local tip not on the last-fetched remote-tracking ref (tip differs from merged PR headRefOid; commits may still be on the remote)" +assert_not_contains "tip-drift evidence never claims unpushed without proof" \ + "may never have been pushed" # Header names the authenticated gh account; a failed login probe degrades to the plain line. assert_contains "header names gh account" "GitHub evidence: available (account: test-login)"