global setup and configuration.
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Report staleness of the three inputs the adversarial-refresh promise depends
|
||||
on: the Wikipedia wiki-sync state, the model-parity bench, and the newest
|
||||
adversarial-eval row. Pure stdlib, network-free, always exits 0 — this is a
|
||||
reporter an agent reads before deciding what to run next
|
||||
(see references/refresh.md), not a gate that blocks anything itself.
|
||||
|
||||
Usage:
|
||||
python3 scripts/refresh_status.py
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import date, datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
WIKI_STATE_FILE = ROOT / "scripts" / ".wiki_sync_state.json"
|
||||
PIPELINE_DOC = ROOT / "references" / "pipeline.md"
|
||||
ADVERSARIAL_EVALS = ROOT / "evals" / "adversarial-evals.json"
|
||||
|
||||
# Guidance thresholds, not hard limits -- the numbers below are the point at
|
||||
# which an agent should proactively check, not a required cadence. Every
|
||||
# field is a plain reporter value; nothing here fails the build.
|
||||
#
|
||||
# Wikipedia's "Signs of AI writing" page is a slow-moving reference page;
|
||||
# quarterly is enough to catch drift without chasing noise.
|
||||
WIKI_STALE_DAYS = 90
|
||||
# The GPT/Anthropic model spectrums shift on the order of months, and the
|
||||
# rule in references/pipeline.md already forces a re-run on any co-writer,
|
||||
# mimic, or detector-pack change regardless of this threshold -- this is a
|
||||
# backstop for the case where no such change happened but time still passed.
|
||||
BENCH_STALE_DAYS = 180
|
||||
# The adversarial-eval catalog is the product's growth signal (docs/PRODUCT.md,
|
||||
# Growth); a two-month gap with no new row is worth a look even if nothing
|
||||
# else prompted one.
|
||||
ROW_STALE_DAYS = 60
|
||||
|
||||
# The convention this script relies on in references/pipeline.md: the
|
||||
# recorded parity date is written as "Live matrix recorded **YYYY-MM-DD**."
|
||||
# If that sentence's wording changes, this regex needs to change with it.
|
||||
PIPELINE_DATE_RE = re.compile(
|
||||
r"Live matrix recorded \*\*(\d{4}-\d{2}-\d{2})\*\*"
|
||||
)
|
||||
|
||||
|
||||
def _today() -> date:
|
||||
return datetime.now(timezone.utc).date()
|
||||
|
||||
|
||||
def _days_since(d: date | None) -> int | None:
|
||||
if d is None:
|
||||
return None
|
||||
return (_today() - d).days
|
||||
|
||||
|
||||
def _status(last: date | None, stale_days: int) -> dict:
|
||||
days = _days_since(last)
|
||||
stale = days is None or days > stale_days
|
||||
return {
|
||||
"last": last.isoformat() if last else None,
|
||||
"days": days,
|
||||
"stale": stale,
|
||||
}
|
||||
|
||||
|
||||
def _parse_iso_date(value: str) -> date | None:
|
||||
"""Best-effort parse of an ISO-8601 timestamp (MediaWiki style, 'Z'
|
||||
suffix included) down to a plain date."""
|
||||
try:
|
||||
return datetime.fromisoformat(value.replace("Z", "+00:00")).date()
|
||||
except (ValueError, AttributeError):
|
||||
return None
|
||||
|
||||
|
||||
def wiki_sync_status() -> dict:
|
||||
if not WIKI_STATE_FILE.exists():
|
||||
# Never synced. Treat as stale so it surfaces for attention.
|
||||
return _status(None, WIKI_STALE_DAYS)
|
||||
|
||||
last: date | None = None
|
||||
try:
|
||||
state = json.loads(WIKI_STATE_FILE.read_text())
|
||||
last = _parse_iso_date(state.get("last_timestamp", ""))
|
||||
except (json.JSONDecodeError, OSError):
|
||||
last = None
|
||||
|
||||
if last is None:
|
||||
# Content didn't parse; fall back to the state file's mtime.
|
||||
try:
|
||||
mtime = WIKI_STATE_FILE.stat().st_mtime
|
||||
last = datetime.fromtimestamp(mtime, tz=timezone.utc).date()
|
||||
except OSError:
|
||||
last = None
|
||||
|
||||
return _status(last, WIKI_STALE_DAYS)
|
||||
|
||||
|
||||
def parity_bench_status() -> dict:
|
||||
last: date | None = None
|
||||
try:
|
||||
text = PIPELINE_DOC.read_text()
|
||||
match = PIPELINE_DATE_RE.search(text)
|
||||
if match:
|
||||
last = date.fromisoformat(match.group(1))
|
||||
except OSError:
|
||||
last = None
|
||||
|
||||
return _status(last, BENCH_STALE_DAYS)
|
||||
|
||||
|
||||
def newest_pattern_row_status() -> dict:
|
||||
last: date | None = None
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"log",
|
||||
"-1",
|
||||
"--format=%cs",
|
||||
"--",
|
||||
str(ADVERSARIAL_EVALS),
|
||||
],
|
||||
cwd=str(ROOT),
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10,
|
||||
)
|
||||
out = result.stdout.strip()
|
||||
if result.returncode == 0 and out:
|
||||
last = date.fromisoformat(out)
|
||||
except (OSError, subprocess.SubprocessError, ValueError):
|
||||
last = None
|
||||
|
||||
return _status(last, ROW_STALE_DAYS)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
output = {
|
||||
"wiki_sync": wiki_sync_status(),
|
||||
"parity_bench": parity_bench_status(),
|
||||
"newest_pattern_row": newest_pattern_row_status(),
|
||||
}
|
||||
print(json.dumps(output, indent=2))
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user