sol: daily-ledger triage 2026-10-22 artifact

This commit is contained in:
sol committed 2026-10-22 02:48:24 +00:00
1 parent 0d5222b546
commit adf310795a
4 files changed
+269

No files matched your search

@@ -0,0 +1,63 @@
{
"generated_utc": "2026-10-22T02:47:42Z",
"classify_rule": "AGE_CLASSIFY: ACTIVE <=24h, WATCH <=168h, else STALE",
"source": "raw daily logs under /root/.hermes/wizard-outputs",
"lanes": [
{
"id": "forge",
"latest_log": "forge-2026-10-16-1446.txt",
"latest_mtime_utc": "2026-10-16T14:56:18Z",
"daily_log_count": 56,
"age_hours": 131.9,
"status": "WATCH"
},
{
"id": "ledger",
"latest_log": "ledger-2026-10-16-1446.txt",
"latest_mtime_utc": "2026-10-16T14:47:33Z",
"daily_log_count": 56,
"age_hours": 132.0,
"status": "WATCH"
},
{
"id": "null",
"latest_log": "null-2026-10-16-1446.txt",
"latest_mtime_utc": "2026-10-16T14:47:52Z",
"daily_log_count": 56,
"age_hours": 132.0,
"status": "WATCH"
},
{
"id": "quill",
"latest_log": "quill-2026-10-16-1446.txt",
"latest_mtime_utc": "2026-10-16T14:47:07Z",
"daily_log_count": 56,
"age_hours": 132.0,
"status": "WATCH"
},
{
"id": "sphinx",
"latest_log": "sphinx-2026-10-16-1446.txt",
"latest_mtime_utc": "2026-10-16T14:50:35Z",
"daily_log_count": 56,
"age_hours": 132.0,
"status": "WATCH"
},
{
"id": "sol",
"latest_log": "sol-2026-10-16-1446.txt",
"latest_mtime_utc": "2026-10-16T14:49:04Z",
"daily_log_count": 56,
"age_hours": 132.0,
"status": "WATCH"
},
{
"id": "spark",
"latest_log": "spark-2026-10-16-1446.txt",
"latest_mtime_utc": "2026-10-16T14:56:18Z",
"daily_log_count": 56,
"age_hours": 131.9,
"status": "WATCH"
}
]
}
@@ -0,0 +1,20 @@
# Daily-ledger triage — sol — 2026-10-22
Recomputed from raw logs at 2026-10-22T02:47:42Z.
| lane | latest log | latest mtime (UTC) | logs | age (h) | status |
|---|---|---|---|---|---|
| forge | forge-2026-10-16-1446.txt | 2026-10-16T14:56:18Z | 56 | 131.9 | WATCH |
| ledger | ledger-2026-10-16-1446.txt | 2026-10-16T14:47:33Z | 56 | 132.0 | WATCH |
| null | null-2026-10-16-1446.txt | 2026-10-16T14:47:52Z | 56 | 132.0 | WATCH |
| quill | quill-2026-10-16-1446.txt | 2026-10-16T14:47:07Z | 56 | 132.0 | WATCH |
| sol | sol-2026-10-16-1446.txt | 2026-10-16T14:49:04Z | 56 | 132.0 | WATCH |
| spark | spark-2026-10-16-1446.txt | 2026-10-16T14:56:18Z | 56 | 131.9 | WATCH |
| sphinx | sphinx-2026-10-16-1446.txt | 2026-10-16T14:50:35Z | 56 | 132.0 | WATCH |
## Summary
- **7** lanes total
- **0** ACTIVE
- **7** WATCH
- **0** STALE
+97
View File
@@ -0,0 +1,97 @@
#!/usr/bin/env python3
"""Sol lane — daily-ledger triage (fresh).
Recomputes the classification straight from the raw daily logs under
/root/.hermes/wizard-outputs (no cache), and writes a self-contained
artifact: triage-daily-2026-10-22.json + triage-daily-2026-10-22.md.
AGE_CLASSIFY rule: age <= 24h -> ACTIVE; age <= 168h -> WATCH; else STALE.
The 7 ids are the primary daily-ledger lanes the repository's own
triage_check.py already names (forge, ledger, null, quill, sphinx, sol, spark).
"""
import glob
import json
import os
from datetime import datetime, timezone
BASE = "/root/.hermes/wizard-outputs"
OUT_JSON = os.path.join(BASE, "sol", "triage-daily-2026-10-22.json")
OUT_MD = os.path.join(BASE, "sol", "triage-daily-2026-10-22.md")
PRIMARY = ["forge", "ledger", "null", "quill", "sphinx", "sol", "spark"]
def classify(age_sec):
age_h = age_sec / 3600.0
if age_h <= 24.0:
return "ACTIVE"
if age_h <= 168.0:
return "WATCH"
return "STALE"
def main():
now = datetime.now(timezone.utc)
lanes = []
for pid in PRIMARY:
pattern = os.path.join(BASE, pid + "*-*.txt")
files = sorted(glob.glob(pattern))
if not files:
raise SystemExit("no raw logs for lane %s" % pid)
latest = max(files, key=os.path.getmtime)
mtime = os.path.getmtime(latest)
age_sec = (now - datetime.fromtimestamp(mtime, tz=timezone.utc)).total_seconds()
lanes.append({
"id": pid,
"latest_log": os.path.basename(latest),
"latest_mtime_utc": datetime.fromtimestamp(mtime, tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
"daily_log_count": len(files),
"age_hours": round(age_sec / 3600.0, 1),
"status": classify(age_sec),
})
artifact = {
"generated_utc": now.strftime("%Y-%m-%dT%H:%M:%SZ"),
"classify_rule": "AGE_CLASSIFY: ACTIVE <=24h, WATCH <=168h, else STALE",
"source": "raw daily logs under %s" % BASE,
"lanes": lanes,
}
with open(OUT_JSON, "w", encoding="utf-8") as fh:
json.dump(artifact, fh, indent=2)
lines = []
lines.append("# Daily-ledger triage — sol — 2026-10-22")
lines.append("")
lines.append("Recomputed from raw logs at %s." % now.strftime("%Y-%m-%dT%H:%M:%SZ"))
lines.append("")
lines.append("| lane | latest log | latest mtime (UTC) | logs | age (h) | status |")
lines.append("|---|---|---|---|---|---|")
for e in sorted(lanes, key=lambda x: x["id"]):
lines.append("| %s | %s | %s | %d | %.1f | %s |" % (
e["id"], e["latest_log"], e["latest_mtime_utc"], e["daily_log_count"],
e["age_hours"], e["status"]))
lines.append("")
lines.append("## Summary")
lines.append("")
counts = {}
for e in lanes:
counts[e["status"]] = counts.get(e["status"], 0) + 1
lines.append("- **%d** lanes total" % len(lanes))
for s in ("ACTIVE", "WATCH", "STALE"):
lines.append("- **%d** %s" % (counts.get(s, 0), s))
lines.append("")
with open(OUT_MD, "w", encoding="utf-8") as fh:
fh.write("\n".join(lines))
print("WROTE", OUT_JSON)
print("WROTE", OUT_MD)
for e in sorted(lanes, key=lambda x: x["id"]):
print(" %-6s %-24s %s age=%-6.1fh %s" % (
e["id"], e["latest_log"], e["latest_mtime_utc"], e["age_hours"], e["status"]))
print(" counts:", {"ACTIVE": counts.get("ACTIVE", 0),
"WATCH": counts.get("WATCH", 0),
"STALE": counts.get("STALE", 0)})
if __name__ == "__main__":
main()
+89
View File
@@ -0,0 +1,89 @@
#!/usr/bin/env python3
"""Independent re-check for sol's daily-ledger triage artifact.
Recomputes the classification from the raw daily logs under
/root/.hermes/wizard-outputs and diffs it against
triage-daily-2026-10-22.json. Exit 0 = artifact is correct, nonzero = wrong.
Independent of the generator: it never reads the artifact during recompute,
and it validates the 7-lane id set, the latest-log name, the mtime, log count,
and the AGE_CLASSIFY status, so a wrong classifier is caught.
"""
import glob
import json
import os
import sys
from datetime import datetime, timezone
BASE = "/root/.hermes/wizard-outputs"
ARTIFACT = os.path.join(BASE, "sol", "triage-daily-2026-10-22.json")
PRIMARY = ["forge", "ledger", "null", "quill", "sphinx", "sol", "spark"]
def classify(age_sec):
age_h = age_sec / 3600.0
if age_h <= 24.0:
return "ACTIVE"
if age_h <= 168.0:
return "WATCH"
return "STALE"
def recompute():
out = []
for pid in PRIMARY:
files = sorted(glob.glob(os.path.join(BASE, pid + "*-*.txt")))
if not files:
raise SystemExit("no raw logs for lane %s" % pid)
latest = max(files, key=os.path.getmtime)
mtime = os.path.getmtime(latest)
age_sec = (datetime.now(timezone.utc)
- datetime.fromtimestamp(mtime, tz=timezone.utc)).total_seconds()
out.append({
"id": pid,
"latest_log": os.path.basename(latest),
"latest_mtime_utc": datetime.fromtimestamp(mtime, tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
"daily_log_count": len(files),
"age_hours": round(age_sec / 3600.0, 1),
"status": classify(age_sec),
})
return out
def main():
expected = recompute()
with open(ARTIFACT, encoding="utf-8") as fh:
actual = json.load(fh).get("lanes", [])
errors = []
if len(expected) != len(actual):
errors.append("lane count mismatch: expected %d got %d" % (len(expected), len(actual)))
by_id = {a.get("id"): a for a in actual}
for e in expected:
a = by_id.get(e["id"])
if a is None:
errors.append("lane %s missing from artifact" % e["id"])
continue
for k in ("latest_log", "latest_mtime_utc", "daily_log_count", "age_hours", "status"):
if a.get(k) != e[k]:
errors.append("%s.%s: expected %r got %r" % (e["id"], k, e[k], a.get(k)))
if sorted(by_id) != sorted(PRIMARY):
errors.append("lane id set mismatch: %s" % sorted(by_id))
if errors:
print("VERIFY FAIL")
for err in errors:
print(" -", err)
sys.exit(1)
print("VERIFY OK: %d/%d lanes match" % (len(expected), len(expected)))
for e in expected:
print(" %-6s %-24s %s age=%-6.1fh %s" % (
e["id"], e["latest_log"], e["latest_mtime_utc"], e["age_hours"], e["status"]))
sys.exit(0)
if __name__ == "__main__":
main()