Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T04:31:08.204Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

cleanup.py

94 lines · 3.3 KB · python
1"""Prune stale build artifacts and build cache from the shared volume.
2
3Eval results are sacrosanct: this only ever touches the build-related top-level
4directories listed in `PRUNABLE_DIRS`. `runs/` (the eval outputs) is never in
5that set and is never walked, so a bug here cannot delete results.
6
7The logic is pure-filesystem (no Modal dependency) so it can be unit-tested
8against a temporary directory; `modal_app` wraps it with volume reload/commit and
9a daily schedule.
10"""
11
12from __future__ import annotations
13
14import datetime
15import shutil
16import time
17from pathlib import Path
18from typing import Any
19
20# The ONLY top-level volume directories cleanup may modify. Eval results live in
21# runs/, which is deliberately absent here.
22PRUNABLE_DIRS = frozenset({"builds", "build-locks", "tmp"})
23
24DEFAULT_BUILD_RETENTION_DAYS = 14.0
25DEFAULT_LOCK_TTL_HOURS = 6.0
26DEFAULT_TMP_MAX_AGE_HOURS = 24.0
27
28
29def _age_days(path: Path) -> float:
30    try:
31        return (time.time() - path.stat().st_mtime) / 86400.0
32    except OSError:
33        return 0.0
34
35
36def prune_artifacts(
37    data_root: Path | str,
38    *,
39    dry_run: bool = False,
40    build_retention_days: float = DEFAULT_BUILD_RETENTION_DAYS,
41    lock_ttl_hours: float = DEFAULT_LOCK_TTL_HOURS,
42    tmp_max_age_hours: float = DEFAULT_TMP_MAX_AGE_HOURS,
43) -> dict[str, Any]:
44    data_root = Path(data_root)
45    removed: dict[str, list[Any]] = {
46        "builds": [],
47        "locks": [],
48        "tmp": [],
49    }
50
51    # 1. Stale content-addressed builds, aged by their READY marker (or the dir
52    #    itself for never-finished builds). Cheap to rebuild on demand.
53    builds_dir = data_root / "builds"
54    if builds_dir.is_dir():
55        for build_dir in sorted(builds_dir.iterdir()):
56            if not build_dir.is_dir():
57                continue
58            ready = build_dir / "READY"
59            age = _age_days(ready if ready.exists() else build_dir)
60            if age > build_retention_days:
61                removed["builds"].append(
62                    {"build_id": build_dir.name, "age_days": round(age, 1)}
63                )
64                if not dry_run:
65                    shutil.rmtree(build_dir, ignore_errors=True)
66
67    # 2. Orphaned single-flight build leases left by crashed builds.
68    locks_dir = data_root / "build-locks"
69    if locks_dir.is_dir():
70        for lock in sorted(locks_dir.glob("*.json")):
71            if _age_days(lock) * 24.0 > lock_ttl_hours:
72                removed["locks"].append(lock.name)
73                if not dry_run:
74                    lock.unlink(missing_ok=True)
75
76    # 3. Orphaned temp build dirs from failed/raced builds.
77    tmp_builds = data_root / "tmp" / "builds"
78    if tmp_builds.is_dir():
79        for entry in sorted(tmp_builds.iterdir()):
80            if _age_days(entry) * 24.0 > tmp_max_age_hours:
81                removed["tmp"].append(entry.name)
82                if not dry_run:
83                    if entry.is_dir():
84                        shutil.rmtree(entry, ignore_errors=True)
85                    else:
86                        entry.unlink(missing_ok=True)
87
88    return {
89        "dry_run": dry_run,
90        "at": datetime.datetime.now(datetime.timezone.utc).isoformat(),
91        "removed": removed,
92        "counts": {key: len(value) for key, value in removed.items()},
93    }
94
Served at tenant.openagents/omega Member data and write actions are omitted.