scripts.freshness_guard
Reference › scripts › freshness_guard
The freshness guard: it detects, and refreshes, each class of stale data behind the 2026-08-04 incident. tests/scripts/test_freshness_guard.py imports it.
Invocation: main takes --check. With it, the guard only detects and exits 1 if anything is stale, refreshing nothing. Without it, the guard refreshes what it finds stale and exits 1 only if some check could not be brought to an ok state.
Six checks run in turn, each given a refresh flag and returning whether the data was stale and whether it is ok: check_ast_runtime_merge (the AST snapshot merged with the runtime call graph, see build_runtime_callgraph), check_prolog_kbs (the Prolog knowledge bases built from the snapshot), check_durations_sink (the test durations file), check_observer_registry (the test observer registry in a run directory), check_packer_cache (the context packer cache) and check_capture_age (the age of the runtime call-graph capture, detect only). _report prints one status line per check. A checks argument lets a caller supply its own checks.
At a glance
| Source | scripts/freshness_guard.py |
| Lines of code | 408 |
| Classes | 0 |
| Functions | 8 |
| Imports (internal) | none |
| Imported by | none |
| Tests that import it | tests/scripts/test_freshness_guard.py |
Class diagram
No classes; see Functions.
Dependency diagram
No internal imports and no importers.
API
Functions
| Function | Signature | Summary |
|---|---|---|
_report |
_report(name: str, status: str, detail: str) -> None |
|
check_ast_runtime_merge |
check_ast_runtime_merge(refresh: bool, *, ast_snapshot: Path | None = None, runtime_callgraph: Path | None = None) -> tuple[bool, bool] |
(1) ast-ultimate.json must carry runtime_calls when a capture exists. |
check_prolog_kbs |
check_prolog_kbs(refresh: bool, *, ast_snapshot: Path | None = None) -> tuple[bool, bool] |
(2) trace_intel + code_intel KBs no older than the AST snapshot. |
check_durations_sink |
check_durations_sink(refresh: bool, now: float | None = None, *, sink_path: Path | None = None, repo_root: Path | None = None) -> tuple[bool, bool] |
(3) compact the duration sink: recent records of existing files only. |
check_observer_registry |
check_observer_registry(refresh: bool, *, run_dir: Path | None = None) -> tuple[bool, bool] |
(4) mark dead-pid running sessions as killed. |
check_packer_cache |
check_packer_cache(refresh: bool, *, cache_path: Path | None = None, repo_root: Path | None = None) -> tuple[bool, bool] |
(5) drop packer-cache entries for modules that no longer exist. |
check_capture_age |
check_capture_age(_refresh: bool, now: float | None = None, *, runtime_callgraph: Path | None = None) -> tuple[bool, bool] |
(6) report-only: age of the runtime capture. |
main |
main(argv: list[str] | None = None, *, checks: Sequence[Callable[[bool], tuple[bool, bool]]] | None = None) -> int |
Source
"""Freshness guard — detect and refresh every stale-data class the 2026-08-04
run history hit (Mohamed ruling: "no staleness, every run is fresh").
Each check is a (detect, refresh) pair over one artifact. Run all via
``python scripts/freshness_guard.py`` (exit 0 = everything fresh or refreshed,
exit 1 = a refresh failed); ``--check`` reports without refreshing (non-zero
when anything is stale). ``scripts/run_tests.py`` invokes this before the test
phase so no gate run ever consumes stale inputs.
The classes, each traced to a measured incident that day:
1. **AST snapshot missing its runtime-call merge** (`plans/ast-ultimate.json`):
a bare ``ast-sync`` regeneration drops every function's ``runtime_calls``
field, silently un-counting DI/dispatch edges (derive_rtm fell 1055->575).
Detect: zero ``runtime_calls`` entries while ``plans/runtime-callgraph.json``
exists. Refresh: ``build_runtime_callgraph.py reaugment`` (in-place).
2. **Prolog KBs older than the snapshot** (``.cache/trace_intel/*.pl``,
``.claude/run/codebase_kb/facts.pl``): stale structural facts made
derive_rtm abort. Detect: facts file missing or mtime < snapshot mtime.
Refresh: delegate to ``scripts.trace_intel.ensure_kb`` (its own mtime logic,
single source of truth) for the trace_intel pair; reindex the code_intel KB
via ``scripts/code_intel.py reindex`` only when stale.
3. **Duration-sink bloat** (`reports/test-durations.ndjson`): grew to 267MB /
1.78M lines including July records of deleted tests (a 256s entry for a
decommissioned module distorted every pole analysis and cost ~4.4s per
runner invocation). Detect: any record older than the retention window OR
referencing a test file that no longer exists. Refresh: compact in place,
keeping only fresh records of existing files.
4. **Observer-registry ghosts** (``$XDG_RUNTIME_DIR/orchestrata/
pytest-sessions/*.json``): sessions whose process died without finalizing
stay ``status=running`` forever, corrupting live counts ("11 running" with
zero live pids). Detect: status==running and pid dead. Refresh: mark
``status=killed`` (facts preserved — never faked to "finished").
5. **Packer duration-cache dead entries** (``.cache/test_durations.json``):
per-module durations for modules that no longer exist skew duration-based
bin packing (worker idle). Detect: keys whose module path is gone.
Refresh: drop those keys.
6. **Runtime capture age** (`plans/runtime-callgraph.json`): report-only —
a fresh capture requires an instrumented suite run (per-batch env, NOT a
global PYTEST_ADDOPTS, which leaks into nested pytest and broke 6 modules
on 2026-08-04). The guard reports age so the operator schedules a capture
run; it never silently re-captures.
"""
from __future__ import annotations
import argparse
import json
import os
import subprocess
import sys
import time
from collections.abc import Callable, Sequence
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
DURATIONS_NDJSON = REPO_ROOT / "reports" / "test-durations.ndjson"
AST_SNAPSHOT = REPO_ROOT / "plans" / "ast-ultimate.json"
RUNTIME_CALLGRAPH = REPO_ROOT / "plans" / "runtime-callgraph.json"
PACKER_CACHE = REPO_ROOT / ".cache" / "test_durations.json"
CODE_INTEL_KB = REPO_ROOT / ".claude" / "run" / "codebase_kb" / "facts.pl"
RETENTION_DAYS = 7.0
CAPTURE_MAX_AGE_DAYS = 7.0
def _report(name: str, status: str, detail: str) -> None:
print(f"[freshness] {name}: {status} — {detail}")
def check_ast_runtime_merge(
refresh: bool,
*,
ast_snapshot: Path | None = None,
runtime_callgraph: Path | None = None,
) -> tuple[bool, bool]:
"""(1) ast-ultimate.json must carry runtime_calls when a capture exists."""
snapshot = ast_snapshot if ast_snapshot is not None else AST_SNAPSHOT
callgraph = (
runtime_callgraph if runtime_callgraph is not None else RUNTIME_CALLGRAPH
)
if not snapshot.exists() or not callgraph.exists():
_report("ast-runtime-merge", "SKIP", "snapshot or capture absent")
return False, True
data = json.loads(snapshot.read_text(encoding="utf-8"))
n = 0
for mod in data.get("modules", []):
for fn in mod.get("functions", []):
n += len(fn.get("runtime_calls", []))
for cls in mod.get("classes", []):
for meth in cls.get("methods", []):
n += len(meth.get("runtime_calls", []))
if n:
break
if n:
_report("ast-runtime-merge", "FRESH", "runtime_calls present")
return False, True
if not refresh:
_report("ast-runtime-merge", "STALE", "0 runtime_calls in snapshot")
return True, True
proc = subprocess.run(
[
sys.executable,
str(REPO_ROOT / "scripts" / "build_runtime_callgraph.py"),
"reaugment",
],
capture_output=True,
text=True,
timeout=600,
check=False,
cwd=REPO_ROOT,
)
ok = proc.returncode == 0
_report(
"ast-runtime-merge",
"REFRESHED" if ok else "REFRESH-FAILED",
(
(proc.stdout.strip().splitlines() or ["reaugment"])[-1]
if ok
else proc.stderr[-300:]
),
)
return True, ok
def check_prolog_kbs(
refresh: bool, *, ast_snapshot: Path | None = None
) -> tuple[bool, bool]:
"""(2) trace_intel + code_intel KBs no older than the AST snapshot."""
snapshot = ast_snapshot if ast_snapshot is not None else AST_SNAPSHOT
if not snapshot.exists():
_report("prolog-kbs", "SKIP", "no snapshot")
return False, True
snap_m = snapshot.stat().st_mtime
sys.path.insert(0, str(REPO_ROOT / "scripts"))
import trace_intel as t # noqa: PLC0415 (path-dependent script import)
stale_paths = [
p
for p in (t.STRUCTURAL_FACTS, t.TRACEABILITY_FACTS, CODE_INTEL_KB)
if not p.exists() or p.stat().st_mtime < snap_m
]
if not stale_paths:
_report("prolog-kbs", "FRESH", "all KBs newer than snapshot")
return False, True
if not refresh:
_report("prolog-kbs", "STALE", ", ".join(str(p) for p in stale_paths))
return True, True
t.ensure_kb(no_refresh=False)
if CODE_INTEL_KB in stale_paths:
proc = subprocess.run(
[sys.executable, str(REPO_ROOT / "scripts" / "code_intel.py"), "reindex"],
capture_output=True,
text=True,
timeout=600,
check=False,
cwd=REPO_ROOT,
)
if proc.returncode != 0:
_report("prolog-kbs", "REFRESH-FAILED", proc.stderr[-300:])
return True, False
_report("prolog-kbs", "REFRESHED", f"{len(stale_paths)} KB file(s) rebuilt")
return True, True
def check_durations_sink(
refresh: bool,
now: float | None = None,
*,
sink_path: Path | None = None,
repo_root: Path | None = None,
) -> tuple[bool, bool]:
"""(3) compact the duration sink: recent records of existing files only."""
sink = sink_path if sink_path is not None else DURATIONS_NDJSON
root = repo_root if repo_root is not None else REPO_ROOT
if not sink.exists():
_report("durations-sink", "SKIP", "absent")
return False, True
now = now if now is not None else time.time()
cutoff = now - RETENTION_DAYS * 86400
kept: list[str] = []
dropped = 0
seen_files: dict[str, bool] = {}
with sink.open(encoding="utf-8") as fh:
for line in fh:
line = line.strip()
if not line:
continue
try:
obj = json.loads(line)
except json.JSONDecodeError:
dropped += 1
continue
if not isinstance(obj, dict):
# Valid JSON but the wrong shape (e.g. a bare list/scalar) —
# treat the same as malformed: drop, never crash the compactor.
dropped += 1
continue
ts = obj.get("ts")
if not isinstance(ts, (int, float)) or ts < cutoff:
dropped += 1
continue
if obj.get("kind") == "run":
# A run-identity HEADER names no file to existence-check: it
# has no "test" key, so before this branch existed
# test = obj.get("test", "") -> "" -> fpath="" ->
# (root / "").exists() == root.exists() == True. Headers
# survived compaction ONLY by that pathlib accident. Made
# explicit here: headers are exempt from the file-existence
# check by design (they name no file); the ts-freshness
# check above is the only retention rule that applies to
# them.
kept.append(line)
continue
test = obj.get("test", "")
fpath = test.split("::", 1)[0]
exists = seen_files.get(fpath)
if exists is None:
exists = (root / fpath).exists()
seen_files[fpath] = exists
if not exists:
dropped += 1
continue
kept.append(line)
if not dropped:
_report("durations-sink", "FRESH", f"{len(kept)} records, all current")
return False, True
if not refresh:
_report(
"durations-sink",
"STALE",
f"{dropped} stale of {dropped + len(kept)} records",
)
return True, True
tmp = sink.with_suffix(".ndjson.tmp")
tmp.write_text("\n".join(kept) + ("\n" if kept else ""), encoding="utf-8")
os.replace(tmp, sink)
_report(
"durations-sink",
"REFRESHED",
f"compacted: kept {len(kept)}, dropped {dropped} "
f"({sink.stat().st_size // 1024}KB now)",
)
return True, True
def check_observer_registry(
refresh: bool, *, run_dir: Path | None = None
) -> tuple[bool, bool]:
"""(4) mark dead-pid running sessions as killed."""
if run_dir is None:
run_dir = (
Path(os.environ.get("XDG_RUNTIME_DIR", f"/run/user/{os.getuid()}"))
/ "orchestrata"
/ "pytest-sessions"
)
if not run_dir.exists():
_report("observer-registry", "SKIP", "no registry dir")
return False, True
ghosts = 0
for meta_path in run_dir.glob("*.json"):
try:
meta = json.loads(meta_path.read_text(encoding="utf-8"))
except OSError, json.JSONDecodeError:
continue
if meta.get("status") != "running":
continue
pid = meta.get("pid")
alive = False
if isinstance(pid, int):
try:
os.kill(pid, 0)
alive = True
except ProcessLookupError:
alive = False
except PermissionError:
alive = True
if alive:
continue
ghosts += 1
if refresh:
meta["status"] = "killed"
meta_path.write_text(json.dumps(meta), encoding="utf-8")
if not ghosts:
_report("observer-registry", "FRESH", "no dead-pid running sessions")
return False, True
_report(
"observer-registry",
"REFRESHED" if refresh else "STALE",
f"{ghosts} ghost session(s) {'marked killed' if refresh else 'found'}",
)
return True, True
def check_packer_cache(
refresh: bool,
*,
cache_path: Path | None = None,
repo_root: Path | None = None,
) -> tuple[bool, bool]:
"""(5) drop packer-cache entries for modules that no longer exist."""
cache_file = cache_path if cache_path is not None else PACKER_CACHE
root = repo_root if repo_root is not None else REPO_ROOT
if not cache_file.exists():
_report("packer-cache", "SKIP", "absent")
return False, True
try:
cache = json.loads(cache_file.read_text(encoding="utf-8"))
except OSError, json.JSONDecodeError:
_report(
"packer-cache",
"STALE",
"unreadable — removing" if refresh else "unreadable",
)
if refresh:
cache_file.unlink(missing_ok=True)
return True, True
if not isinstance(cache, dict):
_report("packer-cache", "STALE", "wrong shape")
if refresh:
cache_file.unlink(missing_ok=True)
return True, True
dead = [k for k in cache if not (root / k.split("::", 1)[0]).exists()]
if not dead:
_report("packer-cache", "FRESH", f"{len(cache)} entries, all current")
return False, True
if refresh:
for k in dead:
del cache[k]
cache_file.write_text(
json.dumps(cache, indent=0, sort_keys=True), encoding="utf-8"
)
_report(
"packer-cache",
"REFRESHED" if refresh else "STALE",
f"{len(dead)} dead-module entr{'ies' if len(dead) != 1 else 'y'}"
f" {'pruned' if refresh else 'found'}",
)
return True, True
def check_capture_age(
_refresh: bool,
now: float | None = None,
*,
runtime_callgraph: Path | None = None,
) -> tuple[bool, bool]:
"""(6) report-only: age of the runtime capture."""
callgraph = (
runtime_callgraph if runtime_callgraph is not None else RUNTIME_CALLGRAPH
)
if not callgraph.exists():
_report("runtime-capture", "SKIP", "no capture file")
return False, True
now = now if now is not None else time.time()
age_days = (now - callgraph.stat().st_mtime) / 86400
if age_days <= CAPTURE_MAX_AGE_DAYS:
_report("runtime-capture", "FRESH", f"{age_days:.1f} days old")
return False, True
_report(
"runtime-capture",
"STALE",
f"{age_days:.1f} days old (> {CAPTURE_MAX_AGE_DAYS:.0f}d) — schedule an "
"instrumented suite run (per-batch ORCH_RCG_DIR env; never global "
"PYTEST_ADDOPTS, it leaks into nested pytest)",
)
return True, True # report-only: stale but never a refresh failure
CHECKS = [
check_ast_runtime_merge,
check_prolog_kbs,
check_durations_sink,
check_observer_registry,
check_packer_cache,
check_capture_age,
]
def main(
argv: list[str] | None = None,
*,
checks: Sequence[Callable[[bool], tuple[bool, bool]]] | None = None,
) -> int:
parser = argparse.ArgumentParser(description=(__doc__ or "").splitlines()[0])
parser.add_argument(
"--check",
action="store_true",
help="Detect only; exit 1 if anything is stale, refresh nothing.",
)
args = parser.parse_args(argv)
any_stale = False
all_ok = True
for check in checks if checks is not None else CHECKS:
stale, ok = check(not args.check)
any_stale = any_stale or stale
all_ok = all_ok and ok
if args.check:
return 1 if any_stale else 0
return 0 if all_ok else 1
if __name__ == "__main__":
sys.exit(main())