109 lines
3.9 KiB
Python
109 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
|
"""check_sol_triage.py -- real test for the sol-profile triage report.
|
|
|
|
triage.py classifies every content file under /root/.hermes/profiles/sol/ into
|
|
ACTIVE / STALE / ARTIFACT / DEAD, writing sol_triage_report.md plus a JSON
|
|
summary to stdout. This test:
|
|
|
|
1. re-enumerates the filesystem with independent os.walk logic and compares the
|
|
total file count against the Findings rows in the report;
|
|
2. parses the report's Summary table and Findings table independently, and
|
|
asserts the per-tier counts agree with each other;
|
|
3. asserts the action-guidance text names the ACTIVE and STALE counts; and
|
|
4. asserts the report header carries a generated date.
|
|
|
|
Exit 0 = the report is correct; nonzero = the report is wrong.
|
|
This is a real test, not a tautology: it recomputes the file list and re-parses
|
|
the report with its own logic instead of trusting the generator's own numbers.
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
PROFILE = "/root/.hermes/profiles/sol"
|
|
REPORT = "/root/.hermes/wizard-outputs/sol/sol_triage_report.md"
|
|
EXCLUDE = {".git", "node_modules", "__pycache__"}
|
|
DIAG_FILES = ["diag.py", "diag2.py", "diag3.py", "diag4.py", "diag5.py"]
|
|
TIERS = ("ACTIVE", "STALE", "ARTIFACT", "DEAD")
|
|
|
|
|
|
def independent_file_count():
|
|
"""Re-enumerate files exactly as triage.py does (top-9 subdirs + diag.py)."""
|
|
total = 0
|
|
for sub in ("skills", "memories", "cron", "logs", "backups", "plans",
|
|
"pairing", "runtime", "diagnostics", "hooks", "image_cache"):
|
|
root = os.path.join(PROFILE, sub)
|
|
if not os.path.isdir(root):
|
|
continue
|
|
for _cur, dirs, names in os.walk(root):
|
|
dirs[:] = [d for d in dirs if d not in EXCLUDE]
|
|
total += len(names)
|
|
for f in DIAG_FILES:
|
|
if os.path.exists(os.path.join(PROFILE, f)):
|
|
total += 1
|
|
return total
|
|
|
|
|
|
def parse_report():
|
|
text = open(REPORT, encoding="utf-8").read()
|
|
summary = {}
|
|
for line in text.splitlines():
|
|
m = re.match(r"^\|\s*(ACTIVE|STALE|ARTIFACT|DEAD)\s*\|\s*(\d+)\s*\|", line)
|
|
if m:
|
|
summary[m.group(1)] = int(m.group(2))
|
|
rows = []
|
|
for line in text.splitlines():
|
|
if re.match(r"^\|\s*\d+\s*\|", line):
|
|
parts = [p.strip() for p in line.split("|")]
|
|
# | # | path | tier | evidence |
|
|
if len(parts) >= 5 and parts[3] in TIERS and parts[2].startswith("`"):
|
|
rows.append(parts[3])
|
|
return summary, rows, text
|
|
|
|
|
|
def main():
|
|
expected_files = independent_file_count()
|
|
summary, rows, text = parse_report()
|
|
errors = []
|
|
|
|
if expected_files != len(rows):
|
|
errors.append("file list mismatch: recount=%d findings_rows=%d"
|
|
% (expected_files, len(rows)))
|
|
|
|
found_counts = {t: 0 for t in TIERS}
|
|
for t in rows:
|
|
found_counts[t] += 1
|
|
for t in TIERS:
|
|
if summary.get(t) != found_counts[t]:
|
|
errors.append("summary[%s]=%s findings[%s]=%d"
|
|
% (t, summary.get(t), t, found_counts[t]))
|
|
|
|
if sum(summary.values()) != len(rows):
|
|
errors.append("summary total %d != findings rows %d"
|
|
% (sum(summary.values()), len(rows)))
|
|
|
|
if not re.search(r"Attend to the %d fresh content items" % summary.get("ACTIVE", -1), text):
|
|
errors.append("action guidance does not name ACTIVE=%d"
|
|
% summary.get("ACTIVE"))
|
|
|
|
if not re.search(r"generated:\s*\d{4}-\d{2}-\d{2}", text):
|
|
errors.append("report header has no generated date")
|
|
|
|
if errors:
|
|
print("CHECK FAIL")
|
|
for e in errors:
|
|
print(" -", e)
|
|
return 1
|
|
|
|
print("CHECK OK: triage report is self-consistent and matches the filesystem")
|
|
print(" files enumerated=%d findings=%d" % (expected_files, len(rows)))
|
|
print(" ACTIVE=%d STALE=%d ARTIFACT=%d DEAD=%d"
|
|
% (summary["ACTIVE"], summary["STALE"],
|
|
summary["ARTIFACT"], summary["DEAD"]))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|