This commit is contained in:
Zhicheng Han 2026-04-24 17:29:39 -05:00 committed by GitHub
commit f771017358
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 48 additions and 1 deletions

View file

@ -10,7 +10,7 @@ Rules:
- test files delete immediately at task end (age >= 0)
- temp files delete after 7 days
- cron-output delete after 14 days
- empty dirs always delete (under HERMES_HOME)
- empty dirs delete when safe under HERMES_HOME (excluding checkpoint shadow-repo dirs and git internals)
- research keep 10 newest, prompt for older (deep only)
- chrome-profile prompt after 14 days (deep only)
- >500 MB files prompt always (deep only)
@ -145,6 +145,29 @@ ALLOWED_CATEGORIES = {
}
def _is_protected_empty_dir(dirpath: Path, hermes_home: Path) -> bool:
"""Return True when an empty-dir cleanup candidate must be preserved.
This protects git internals for both Hermes checkpoint shadow repos
(stored under ``checkpoints/<id>/...``) and ordinary repos living under
``HERMES_HOME`` (stored under ``.git/...``).
"""
try:
rel_parts = dirpath.relative_to(hermes_home).parts
except ValueError:
return False
if ".git" in rel_parts and rel_parts[-1] != ".git":
return True
if len(rel_parts) >= 2 and rel_parts[0] == "checkpoints":
repo_root = hermes_home / rel_parts[0] / rel_parts[1]
if any((repo_root / marker).exists() for marker in ("HEAD", "HERMES_WORKDIR", "config")):
return True
return False
def fmt_size(n: float) -> str:
for unit in ("B", "KB", "MB", "GB", "TB"):
if n < 1024:
@ -309,6 +332,8 @@ def quick() -> Dict[str, Any]:
rel_parts = dirpath.relative_to(hermes_home).parts
except ValueError:
continue
if _is_protected_empty_dir(dirpath, hermes_home):
continue
# Skip the well-known top-level state dirs themselves.
if len(rel_parts) == 1 and rel_parts[0] in _PROTECTED_TOP_LEVEL:
continue

View file

@ -195,6 +195,28 @@ class TestTrackForgetQuick:
for d in ("logs", "memories", "sessions", "cron", "cache"):
assert (_isolate_env / d).exists(), f"{d}/ should be preserved"
def test_quick_preserves_git_internal_dirs_and_removes_normal_empty_dirs(self, _isolate_env):
dg = _load_lib()
checkpoint_heads = _isolate_env / "checkpoints" / "demo" / "refs" / "heads"
checkpoint_info = _isolate_env / "checkpoints" / "demo" / "objects" / "info"
repo_heads = _isolate_env / "scratch" / "repo" / ".git" / "refs" / "heads"
repo_info = _isolate_env / "scratch" / "repo" / ".git" / "objects" / "info"
plain_empty = _isolate_env / "scratch" / "empty-dir"
for p in (checkpoint_heads, checkpoint_info, repo_heads, repo_info):
p.mkdir(parents=True, exist_ok=True)
(_isolate_env / "checkpoints" / "demo" / "HERMES_WORKDIR").write_text("/tmp/demo\n")
plain_empty.mkdir(parents=True)
summary = dg.quick()
assert checkpoint_heads.exists()
assert checkpoint_info.exists()
assert repo_heads.exists()
assert repo_info.exists()
assert not plain_empty.exists()
assert summary["empty_dirs"] == 1
class TestStatus:
def test_empty_status(self, _isolate_env):