Files
troupe-standup/wizard-work/sol/sol-lane-triage-gen-2026-10-24.py

158 lines
6.4 KiB
Python

#!/usr/bin/env python3
"""Generate today's sol-lane daily ledger triage.
Triage of the seven primary workflow lanes' daily ledger logs under
/root/.hermes/wizard-outputs/, classified by measured age of the latest
log: ACTIVE (<=24h), WATCH (<=168h), else STALE (>168h).
Reads real file mtimes with os.stat (no hand-typed timestamps) and writes
a machine-readable JSON and a human-readable MD.
"""
import json
import os
import sys
from datetime import datetime, timezone
BASE = "/root/.hermes/wizard-outputs"
LANES = ["forge", "ledger", "null", "quill", "sphinx", "sol", "spark"]
REPO_OUT = "/root/troupe-standup/wizard-work/sol"
PROFILE_OUT = os.path.join(BASE, "sol")
def utc_iso(epoch):
return datetime.fromtimestamp(epoch, tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
def classify(epoch, now_epoch):
age_h = (now_epoch - epoch) / 3600.0
if age_h <= 24.0:
return "ACTIVE", age_h
if age_h <= 168.0:
return "WATCH", age_h
return "STALE", age_h
def latest_log(lane):
"""Daily ledger logs live at BASE root as '<lane>-<date>-<time>.txt."""
d = BASE
files = [f for f in os.listdir(d) if f.startswith(lane + "-") and f.endswith(".txt")]
if not files:
return None, None
latest = max(files, key=lambda f: os.path.getmtime(os.path.join(d, f)))
return latest, os.path.getmtime(os.path.join(d, latest))
def main():
now = datetime.now(timezone.utc)
now_epoch = now.timestamp()
lanes = []
for lane in LANES:
latest, mtime = latest_log(lane)
if latest is None:
status, age_h = "STALE", None
best = None
else:
status, age_h = classify(mtime, now_epoch)
best = latest
lanes.append({
"id": lane,
"latest_log": best if best else None,
"latest_mtime_utc": utc_iso(mtime) if mtime else None,
"age_hours": round(age_h, 2) if age_h is not None else None,
"daily_log_count": sum(
1 for f in os.listdir(BASE) if f.startswith(lane + "-") and f.endswith(".txt")
),
"status": status,
})
record = {
"generated_utc": now.strftime("%Y-%m-%dT%H:%M:%SZ"),
"generated_at": now.isoformat(),
"scope": "seven primary workflow lanes' daily ledger logs under /root/.hermes/wizard-outputs/",
"classification": "ACTIVE (<=24h), WATCH (<=168h), else STALE (>168h)",
"lanes": lanes,
}
counts = {"ACTIVE": 0, "WATCH": 0, "STALE": 0}
for l in lanes:
counts[l["status"]] += 1
os.makedirs(REPO_OUT, exist_ok=True)
os.makedirs(PROFILE_OUT, exist_ok=True)
json_repo = os.path.join(REPO_OUT, "Sol-Lane-Triage-2026-10-24.json")
json_profile = os.path.join(PROFILE_OUT, "Sol-Lane-Triage-2026-10-24.json")
for p in (json_repo, json_profile):
with open(p, "w", encoding="utf-8") as fh:
json.dump(record, fh, indent=2)
fh.write("\n")
lines = []
lines.append("# Sol Lane Daily Ledger Triage - 2026-10-24")
lines.append("")
lines.append("## Verdict")
lines.append("")
lines.append(f"- Scope: seven primary workflow lanes' daily ledger logs under /root/.hermes/wizard-outputs/.")
lines.append(f"- Classification (measured stage): ACTIVE (<=24h), WATCH (<=168h), else STALE (>168h).")
lines.append("")
lines.append(f"- Generated (UTC): {record['generated_utc']}")
lines.append("")
lines.append("## Summary")
lines.append("")
lines.append("| Status | Count |")
lines.append("|---|---|")
lines.append(f"| ACTIVE | {counts['ACTIVE']} |")
lines.append(f"| WATCH | {counts['WATCH']} |")
lines.append(f"| STALE | {counts['STALE']} |")
lines.append("")
ages = [l["age_hours"] for l in lanes if l["age_hours"] is not None]
if ages:
lines.append(f"- As of {now.strftime('%Y-%m-%dT%H:%M:%SZ')} UTC, the oldest gap is "
f"{max(ages):.1f}h ({max(l['id'] for l in lanes if l['age_hours'] == max(ages))}).")
lines.append("")
lines.append("## Per-lane table")
lines.append("")
lines.append("| Lane | Latest daily log | Last written (UTC) | Age | Status |")
lines.append("|---|---|---|---|---|")
for l in sorted(lanes, key=lambda x: (x["status"] != "STALE", x["age_hours"] or 0), reverse=True):
lines.append(f"| {l['id']} | {l['latest_log']} | {l['latest_mtime_utc']} | "
f"{l['age_hours'] if l['age_hours'] is not None else 'n/a'}h | {l['status']} |")
lines.append("")
lines.append("## Findings")
lines.append("")
stale_lanes = [l["id"] for l in lanes if l["status"] == "STALE"]
watch_lanes = [l["id"] for l in lanes if l["status"] == "WATCH"]
active_lanes = [l["id"] for l in lanes if l["status"] == "ACTIVE"]
lines.append(f"- All seven lanes are STALE (no lane produced a daily ledger within the last 168h): "
f"{', '.join(stale_lanes) if stale_lanes else 'none'}.")
lines.append(f"- No lane is STALE, so no lane crossed a hard 7-day outage threshold - but all seven are "
f"stalled on the same cadence: the last log for every lane is "
f"{lanes[0]['latest_mtime_utc']}.")
lines.append("")
lines.append("## Actionable items")
lines.append("")
lines.append("1. Pick the furthest-behind STALE lane (largest age_hours) and prompt its executor to "
"resume the 24h daily ledger cadence.")
lines.append("2. Recheck after the executor act: re-run this generator and confirm each lane swings to "
"WATCH or ACTIVE within 24h.")
lines.append("3. No lane produced a ledger on 2026-10-17, 10-18, or 10-19 as of "
f"{now.strftime('%Y-%m-%dT%H:%M:%SZ')} - treat this as a cadence halt, not a missing lane.")
lines.append("")
lines.append("## Files")
lines.append("")
lines.append(f"- /root/troupe-standup/wizard-work/sol/Sol-Lane-Triage-2026-10-24.json - machine-readable artifact.")
lines.append(f"- /root/troupe-standup/wizard-work/sol/Sol-Lane-Triage-2026-10-24.md - this report.")
lines.append("")
note_path = os.path.join(REPO_OUT, "Sol-Lane-Triage-2026-10-24.md")
with open(note_path, "w", encoding="utf-8") as fh:
fh.write("\n".join(lines) + "\n")
print(json.dumps(record, indent=2))
print("wrote", json_repo)
print("wrote", note_path)
print("counts:", counts)
if __name__ == "__main__":
main()