145 lines
5.2 KiB
Python
145 lines
5.2 KiB
Python
import os, json, time, shutil
|
|
from datetime import datetime, timezone
|
|
|
|
OUT = "/root/.hermes/wizard-outputs"
|
|
RANDIR = "/root/troupe-standup/wizard-work/sol"
|
|
LANES = ["forge", "ledger", "null", "quill", "sphinx", "sol", "spark"]
|
|
|
|
def status_for(age_h):
|
|
if age_h < 24:
|
|
return "ACTIVE"
|
|
if age_h <= 168:
|
|
return "WATCH"
|
|
return "STALE"
|
|
|
|
now = time.time()
|
|
lanes_out = []
|
|
counts = {}
|
|
for L in LANES:
|
|
best = None
|
|
best_m = 0.0
|
|
for fn in os.listdir(OUT):
|
|
if fn.startswith(L + "-") and fn.endswith(".txt"):
|
|
p = os.path.join(OUT, fn)
|
|
m = os.path.getmtime(p)
|
|
if best_m is None or m > best_m:
|
|
best, best_m = fn, m
|
|
age_h = (now - best_m) / 3600.0
|
|
st = status_for(age_h)
|
|
counts[st] = counts.get(st, 0) + 1
|
|
lanes_out.append({
|
|
"id": L,
|
|
"latest_log": best,
|
|
"latest_mtime_utc": datetime.fromtimestamp(best_m, tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
"age_h": round(age_h, 2),
|
|
"status": st,
|
|
})
|
|
|
|
rec = {
|
|
"artifact": "sol-lane-status-rederived-2026-10-27.json",
|
|
"source": "daily ledger logs at /root/.hermes/wizard-outputs/<lane>-*.txt (latest by mtime)",
|
|
"measured_at_utc": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
"status_rule": "ACTIVE = latest log within 24h; WATCH = 24h-168h; STALE = >168h",
|
|
"lanes": lanes_out,
|
|
"status_counts": counts,
|
|
}
|
|
|
|
with open(RANDIR + "/sol-lane-status-rederived-2026-10-27.json", "w") as f:
|
|
json.dump(rec, f, indent=2)
|
|
|
|
rows = "\n".join(
|
|
"| %s | %s | %s | %s | %s |" % (l["id"], l["latest_log"], l["latest_mtime_utc"], l["age_h"], l["status"])
|
|
for l in lanes_out)
|
|
counts_line = "\n".join("- %s: %s" % (k, v) for k, v in sorted(counts.items()))
|
|
md = """# Sol Daily-Ledger Lane Status — Redrive — 2026-10-27 (measured)
|
|
|
|
Classification measured live from the on-disk mtimes of each primary lane's daily
|
|
ledger logs under `/root/.hermes/wizard-outputs/`. Rules (measured stage):
|
|
ACTIVE = latest log within 24h; WATCH = 24h-168h; STALE = >168h.
|
|
|
|
## Verdict
|
|
|
|
- status_counts: **%s**.
|
|
- No lane is ACTIVE or WATCH.
|
|
|
|
## Per-lane table
|
|
|
|
| Lane | Latest log | Last written (UTC) | Age (h) | Status |
|
|
| --- | --- | --- | --- | --- |
|
|
%s
|
|
|
|
## Artifact
|
|
|
|
- `sol-lane-status-rederived-2026-10-27.json` — machine-readable record (mtime + age measured).
|
|
- `check_sol_lane_status_rederived.sh` — the verdict: a single bare shell command;
|
|
`bash check_sol_lane_status_rederived.sh` exits 0 iff the committed JSON matches an
|
|
independent recompute straight from the filesystem.
|
|
""" % (counts_line, rows)
|
|
|
|
with open(RANDIR + "/sol-lane-status-rederived-2026-10-27.md", "w") as f:
|
|
f.write(md)
|
|
|
|
check_src = """#!/usr/bin/env bash
|
|
# CHECK -- redrive of the 7 primary daily-ledger lanes.
|
|
# Recomputes live from the filesystem and diffs against the committed record.
|
|
# Exit 0 = committed JSON matches independent recompute; nonzero = wrong.
|
|
set -u
|
|
cd "$(dirname "${BASH_SOURCE[0]}")" || exit 1
|
|
JSON=sol-lane-status-rederived-2026-10-27.json
|
|
OUT=/root/.hermes/wizard-outputs
|
|
LANE_LIST="forge ledger null quill sphinx sol spark"
|
|
|
|
python3 - "$JSON" "$OUT" "$LANE_LIST" <<'PY'
|
|
import json, os, sys, glob, time
|
|
json_path, out, lane_list = sys.argv[1:4]
|
|
lanes = lane_list.split()
|
|
now = time.time()
|
|
recomputed = {}
|
|
for L in lanes:
|
|
files = sorted(glob.glob(os.path.join(out, L + "-*.txt")))
|
|
latest = max(files, key=os.path.getmtime)
|
|
age = (now - os.path.getmtime(latest)) / 3600.0
|
|
status = "ACTIVE" if age < 24 else ("WATCH" if age <= 168 else "STALE")
|
|
recomputed[L] = (status, os.path.basename(latest))
|
|
with open(json_path) as f:
|
|
rec = json.load(f)
|
|
ok = True
|
|
for L in lanes:
|
|
jl = next((x for x in rec["lanes"] if x["id"] == L), None)
|
|
if jl is None:
|
|
print("FAIL: lane %s missing" % L); ok = False; continue
|
|
exp_status, exp_log = recomputed[L]
|
|
if jl.get("status") != exp_status or jl.get("latest_log") != exp_log:
|
|
print("FAIL: lane %s json=%s/%s recomputed=%s/%s" % (L, jl.get("status"), jl.get("latest_log"), exp_status, exp_log))
|
|
ok = False
|
|
counts = {}
|
|
for _, v in recomputed.values():
|
|
counts[v] = counts.get(v, 0) + 1
|
|
if rec.get("status_counts") != counts:
|
|
print("FAIL: counts json=%s recomputed=%s" % (rec.get("status_counts"), counts)); ok = False
|
|
if not ok:
|
|
print("RECOMPUTE FAILED: redrive artifact does not match live filesystem")
|
|
sys.exit(1)
|
|
print("OK: lanes=%d statuses=%s counts=%s" % (len(lanes), ",".join("%s=%s" % (k, v) for k, v in sorted(recomputed.items())), rec.get("status_counts")))
|
|
sys.exit(0)
|
|
PY
|
|
rc=$?
|
|
if [ $rc -ne 0 ]; then
|
|
echo "FAIL: independent recompute disagrees with the committed redrive artifact" >&2
|
|
exit 1
|
|
fi
|
|
echo "PASS: redrive artifact verified against the live filesystem"
|
|
exit 0
|
|
"""
|
|
|
|
with open(RANDIR + "/check_sol_lane_status_rederived.sh", "w") as f:
|
|
f.write(check_src)
|
|
|
|
for fn in ["sol-lane-status-rederived-2026-10-27.json", "sol-lane-status-rederived-2026-10-27.md"]:
|
|
shutil.copy2(RANDIR + "/" + fn, "/root/.hermes/wizard-outputs/sol/" + fn)
|
|
|
|
print(json.dumps(rec, indent=2))
|
|
print("WROTE:", RANDIR + "/sol-lane-status-rederived-2026-10-27.json")
|
|
print("WROTE:", RANDIR + "/sol-lane-status-rederived-2026-10-27.md")
|
|
print("WROTE:", RANDIR + "/check_sol_lane_status_rederived.sh")
|