Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins/repo-hygiene/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "repo-hygiene",
"version": "0.4.0",
"version": "0.4.1",
"description": "Repo hygiene action-router: /repo-hygiene:clean sweeps reclaimable caches, build artifacts, and stale git metadata, and can realign the working tree to a fresh-pull state — dry-run-first, with destructive tiers gated behind explicit confirmation and a session-scoped destructive-command guard. Ecosystem targets are detected at runtime; secrets, runtime dependencies, and skill data are preserved by default.",
"author": {
"name": "Melodic Software",
Expand Down
15 changes: 15 additions & 0 deletions plugins/repo-hygiene/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,21 @@
All notable changes to the `repo-hygiene` plugin are documented here. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning.

## [0.4.1]

### Fixed

- **`git-tree-reset` — exit-7 clean-failure path now emits the restore-guard
warning identically to the success path.** When `git clean -fdx` fails for a
non-locked-file cause (exit 7) after the restore guard recovered one or more
tracked files deleted via reparse-point traversal (`RestoredTracked: N`, N>0),
the path now prints the same `WARNING: restored N tracked file(s)` message the
success path already emits under that condition. Previously the warning was
emitted only on the success path, so an operator hitting the failure path saw
the machine-readable `RestoredTracked: N` line but not the human-visible signal
that data-loss recovery fired — parity between both paths for this specific
signal. (#605)

## [0.4.0]

### Added
Expand Down
8 changes: 8 additions & 0 deletions plugins/repo-hygiene/skills/clean/scripts/git-tree-reset.sh
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,14 @@ if [[ "$CLEAN_RC" -ne 0 && "${UNREMOVABLE:-0}" -eq 0 ]]; then
printf 'AppliedClean: failed\n'
printf 'RestoredTracked: %s\n' "${RESTORED:-0}"
printf 'Unremovable: %s\n' "0"
# Surface restore-guard activity identically to the success path: a clean that
# errored mid-run may still have deleted tracked files (reparse-point traversal)
# before failing, and the machine-readable RestoredTracked line alone can be
# missed. Emit the same human-visible warning the success path prints so an
# operator is not left unaware that data-loss recovery fired on the failure path.
if [[ "${RESTORED:-0}" -gt 0 ]]; then
printf 'WARNING: restored %s tracked file(s) deleted via reparse-point traversal (junction/symlink into tracked dir).\n' "$RESTORED" >&2
fi
exit 7
fi

Expand Down
41 changes: 41 additions & 0 deletions plugins/repo-hygiene/skills/clean/scripts/git-tree-reset.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,47 @@ assert_contains "clean failure emits RestoredTracked label" "$out" "RestoredTrac
assert_contains "clean failure emits Unremovable label (uniform apply-path contract)" "$out" "Unremovable: 0"
assert_file_exists "clean failure leaves untracked intact (clean did not silently succeed)" "$R4/scratch.txt"

# --- 12. clean failure with restore-guard activity emits the RESTORED>0 warning ---
# Parity fix (#605): on the exit-7 failure path, when the restore guard recovers a
# tracked file (a clean that deleted tracked files via reparse-point traversal
# before erroring), the human-visible `WARNING: restored N tracked file(s)` message
# the success path prints must also appear — not just the machine-readable
# RestoredTracked: N line. Simulate a partial clean via a shim that deletes a
# tracked file and THEN exits non-zero, so `git ls-files --deleted` reports it and
# the restore guard recovers it (RESTORED>0) on the failure path.
R5="$TEST_TMPDIR/repo-clean-fail-restore"
git init "$R5" >/dev/null 2>&1
git -C "$R5" config user.email "t@example.com"
git -C "$R5" config user.name "Test"
echo tracked >"$R5/tracked.txt"
git -C "$R5" add -A
git -C "$R5" commit -m "init" >/dev/null
git -C "$R5" branch -M main
git -C "$R5" checkout -b feat/clean-fail-restore >/dev/null 2>&1
git -C "$R5" branch -u main >/dev/null 2>&1

RESTORE_SHIM="$TEST_TMPDIR/git-clean-restore-shim"
mkdir -p "$RESTORE_SHIM"
cat >"$RESTORE_SHIM/git" <<SHIMEOF
#!/usr/bin/env bash
if [[ "\$1" == "clean" && "\$*" != *-n* ]]; then
# Simulate a clean that deleted a tracked file (reparse traversal) before failing.
rm -f "$R5/tracked.txt"
echo "fatal: simulated git clean failure after partial deletion" >&2
exit 1
fi
exec "$REAL_GIT" "\$@"
SHIMEOF
chmod +x "$RESTORE_SHIM/git"

rc=0
out="$(PATH="$RESTORE_SHIM:$PATH" bash -c "cd '$R5' && bash '$RESET' --apply" 2>&1)" || rc=$?
assert_exit "clean failure with restore exits 7" 7 "$rc"
assert_contains "clean failure emits AppliedClean: failed" "$out" "AppliedClean: failed"
assert_contains "clean failure reports a positive RestoredTracked count" "$out" "RestoredTracked: 1"
assert_contains "clean failure path emits the RESTORED>0 warning (parity with success path)" "$out" "WARNING: restored 1 tracked file(s) deleted via reparse-point traversal"
assert_file_exists "restore guard recovered the tracked file on the failure path" "$R5/tracked.txt"

if [[ $FAILED -ne 0 ]]; then
echo "FAILED: $FAILED test(s)"
exit 1
Expand Down
Loading