75 lines
2.8 KiB
Python
75 lines
2.8 KiB
Python
import json, os, glob, datetime
|
|
|
|
BASE = "/root/.hermes/wizard-outputs"
|
|
LANES = ["forge", "ledger", "null", "quill", "sphinx", "sol", "spark"]
|
|
OUT = "/root/.hermes/wizard-outputs/sol"
|
|
|
|
def classify(age_h):
|
|
if age_h <= 24.0:
|
|
return "ACTIVE"
|
|
if age_h <= 168.0:
|
|
return "WATCH"
|
|
return "STALE"
|
|
|
|
now = datetime.datetime.now(datetime.timezone.utc)
|
|
|
|
lanes = []
|
|
counts = {}
|
|
for pid in LANES:
|
|
pat = os.path.join(BASE, pid + "-*.txt")
|
|
files = glob.glob(pat)
|
|
if not files:
|
|
raise SystemExit("NO LOGS for lane: " + pid)
|
|
latest = max(files, key=os.path.getmtime)
|
|
mt = os.path.getmtime(latest)
|
|
mtime_utc = datetime.datetime.fromtimestamp(mt, tz=datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
age_h = (now - datetime.datetime.fromtimestamp(mt, tz=datetime.timezone.utc)).total_seconds() / 3600.0
|
|
status = classify(age_h)
|
|
lanes.append({
|
|
"id": pid,
|
|
"latest_log": os.path.basename(latest),
|
|
"latest_mtime_utc": mtime_utc,
|
|
"age_h": round(age_h, 2),
|
|
"status": status,
|
|
})
|
|
counts[status] = counts.get(status, 0) + 1
|
|
|
|
record = {
|
|
"artifact": "sol-lane-status-2026-10-27.json",
|
|
"source": "daily ledger logs under /root/.hermes/wizard-outputs/<lane>-*.txt (latest by mtime)",
|
|
"measured_at_utc": now.strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
"status_rule": "ACTIVE = latest log within 24h; WATCH = 24h-168h; STALE = >168h",
|
|
"lanes": lanes,
|
|
"status_counts": counts,
|
|
}
|
|
|
|
with open(os.path.join(OUT, "sol-lane-status-2026-10-27.json"), "w") as f:
|
|
json.dump(record, f, indent=2)
|
|
f.write("\n")
|
|
|
|
# Written result (plain-text triage verdict).
|
|
lines = ["# Sol Daily-Lane Status - 2026-10-27 (measured)", ""]
|
|
lines.append("Classification measured live from the on-disk mtimes of each primary lane's daily ledger logs under /root/.hermes/wizard-outputs/.")
|
|
lines.append("Rules: ACTIVE = latest log within 24h; WATCH = 24h-168h; STALE = >168h.")
|
|
lines.append("")
|
|
lines.append("## Verdict")
|
|
lines.append("")
|
|
lines.append("- status_counts: %s" % json.dumps(counts))
|
|
lines.append("")
|
|
lines.append("## Per-lane table")
|
|
lines.append("")
|
|
lines.append("| Lane | Latest daily log | Last written (UTC) | Age (h) | Status |")
|
|
lines.append("|-------|------------------|--------------------|---------|--------|")
|
|
for ln in lanes:
|
|
lines.append("| %s | %s | %s | %.2f | %s |" % (ln["id"], ln["latest_log"], ln["latest_mtime_utc"], ln["age_h"], ln["status"]))
|
|
lines.append("")
|
|
|
|
with open(os.path.join(OUT, "sol-lane-status-2026-10-27.md"), "w") as f:
|
|
f.write("\n".join(lines))
|
|
|
|
print("measured_at_utc:", record["measured_at_utc"])
|
|
print("status_counts:", json.dumps(counts))
|
|
print("lanes:")
|
|
for ln in lanes:
|
|
print(" ", ln["id"], ln["latest_log"], ln["latest_mtime_utc"], "age=%.2f" % ln["age_h"], "->", ln["status"])
|