fix(curator): purge ages archives by the newer of archived_at and dir mtime
Preferring the usage record's archived_at outright let a stale archived_at beat a fresh dir mtime: set_state only rewrites archived_at on a state change, so a skill moved out of .archive by hand (record still "archived") and archived again keeps the old timestamp and is purged at once, even though archive_skill just stamped a fresh mtime. Take the newer of the two signals. Legacy archives (stale mtime, fresh archived_at) still survive, the stale-record case now survives too, and any disagreement errs toward keeping the archive, which is the no-data-loss direction for an irreversible purge.
This commit is contained in:
@@ -440,11 +440,12 @@ def _cmd_purge(args) -> int:
|
||||
cutoff = time.time() - ttl_days * 86400
|
||||
|
||||
def _archived_ts(p: Path) -> float:
|
||||
# Prefer the usage record's archived_at: archives made before archive_skill stamped the
|
||||
# dir mtime still carry the skill's last-edit mtime and would be purged at once.
|
||||
# The NEWER of the record's archived_at and the dir mtime: archives made before
|
||||
# archive_skill stamped the mtime carry the skill's last-edit mtime, and a stale
|
||||
# archived_at survives a manual un-archive + re-archive. Never purge before either says so.
|
||||
rec = skill_usage.get_record(p.name)
|
||||
at = skill_usage._parse_iso_timestamp(rec.get("archived_at")) if rec.get("state") == skill_usage.STATE_ARCHIVED else None
|
||||
return at.timestamp() if at else p.stat().st_mtime
|
||||
return max(at.timestamp(), p.stat().st_mtime) if at else p.stat().st_mtime
|
||||
|
||||
candidates = sorted(
|
||||
p for p in archive_root.iterdir() if p.is_dir() and _archived_ts(p) < cutoff)
|
||||
|
||||
@@ -54,6 +54,10 @@ def test_purge_ages_an_archive_from_when_it_was_archived(tmp_path, monkeypatch):
|
||||
skill_usage.record_created("old-helper", agent_created=True)
|
||||
ok, msg = skill_usage.archive_skill("old-helper")
|
||||
assert ok, msg
|
||||
# A stale archived_at (left by a manual un-archive + re-archive) must not beat the fresh mtime.
|
||||
usage = skill_usage.load_usage()
|
||||
usage["old-helper"]["archived_at"] = "2000-01-01T00:00:00+00:00"
|
||||
assert skill_usage.save_usage(usage)
|
||||
|
||||
assert curator_cli._cmd_purge(_ns(days=30, dry_run=False, yes=True)) == 0
|
||||
assert (tmp_path / "skills" / ".archive" / "old-helper" / "SKILL.md").is_file()
|
||||
|
||||
Reference in New Issue
Block a user