sol: daily-ledger triage 2026-10-22 artifact
This commit is contained in:
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
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
Reference in new issue
Block a user