Files
troupe-standup/wizard-work/sol/Triage-2026-09-28-classify.py
T

121 lines
4.7 KiB
Python

#!/usr/bin/env python3
"""Triage-2026-09-28: classify the 12 PENDING items in inbox/seed-2026-09-26.json.
Lane: Triage - everyday Q&A and classification.
This is a deterministic, marker-based classifier over the seed's own fields
(summary prefix + proposed_action). No editorial inference beyond the marker.
The CHECK file re-runs this exact classifier and diffs the result against the
manifest; exit 0 iff the manifest matches.
"""
import json
import os
import datetime
ROOT = "/root/troupe-standup"
SEED = os.path.join(ROOT, "inbox", "seed-2026-09-26.json")
OUT = os.path.join(ROOT, "wizard-work", "sol")
PRIMARY = "/root/.hermes/wizard-outputs/sol"
os.makedirs(OUT, exist_ok=True)
os.makedirs(PRIMARY, exist_ok=True)
seed = json.load(open(SEED))
assert set(seed.keys()) == {"cutover", "decided", "exported_at", "pending", "source"}
# Deterministic marker-based classifier. Category vocabulary is fixed and
# small; no free-form labels, so the result is a classification, not an essay.
def classify(item):
s = (item.get("summary") or "").strip()
if s.startswith("Archive fix:"):
return "archive_fix"
if s.startswith("Turd app:"):
return "app_pr"
if s.startswith("Trial by Timmy:") or s.startswith("Q9:"):
return "general"
return "other"
CAT_ORDER = ["archive_fix", "app_pr", "general", "other"]
DECISION_ORDER = ["approve", "reject", "table"]
items = []
for it in sorted(seed["pending"], key=lambda x: int(x["id"])):
assert set(it.keys()) >= {"id", "summary", "proposed_action", "evidence"}
cat = classify(it)
items.append({
"id": int(it["id"]),
"category": cat,
"status": "table", # pending/unresolved: not decided by anyone
"summary": (it["summary"] or "").strip(),
"proposed_action": (it["proposed_action"] or "").strip(),
"evidence_quote": (it["summary"] or "").strip(),
"classification_rule": "summary prefix marker",
})
record = {
"artifact": "Triage-2026-09-28-classify.json",
"seed": "inbox/seed-2026-09-26.json",
"generated_at": datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
"lane": "Triage - everyday Q&A and classification",
"summary": (
"Deterministic marker-based classification of the 12 PENDING items in the "
"inbox seed. Family is fixed by summary prefix; no editorial inference. "
"All 12 are 'table' (unresolved); only the seed's id 13 (decided) is recorded."
),
"decision_counts": {"approve": 0, "reject": 0, "table": 0},
"categories": {c: 0 for c in CAT_ORDER},
"items": items,
}
for it in items:
record["decision_counts"][it["status"]] += 1
record["categories"][it["category"]] += 1
json_path = os.path.join(OUT, "Triage-2026-09-28-classify.json")
with open(json_path, "w") as f:
json.dump(record, f, indent=2)
f.write("\n")
# Human-readable triage note.
lines = []
lines.append("# Triage - 2026-09-28")
lines.append("")
lines.append("## Decision")
lines.append("")
lines.append("Deterministic classification of the 12 PENDING items in `inbox/seed-2026-09-26.json`." )
lines.append("")
lines.append("| # | Category | Status | Evidence (summary)" )
lines.append("|---|---|---|---|")
for it in items:
lines.append(f"| {it['id']} | {it['category']} | {it['status']} | {(it['summary'] or '')[:120]} |")
lines.append("")
lines.append("## Counts")
lines.append("")
lines.append(f"- table (unresolved): {record['decision_counts']['table']}")
lines.append(f"- archive_fix: {record['categories']['archive_fix']}")
lines.append(f"- app_pr: {record['categories']['app_pr']}")
lines.append(f"- general: {record['categories']['general']}")
lines.append("")
lines.append("## Notes")
lines.append("")
lines.append("- Members: archive_fix ids 1,3,6,7,10,11,12; app_pr ids 2,4,5; general ids 8,9.")
lines.append("- Status 'table': none of the 12 has a recorded decision in the seed; each")
lines.append(" needs Alexander's (or maintainer's) decision before any action is taken.")
lines.append("- The seed's single decided item (id 13, magazine-091 Strategy) is NOT included")
lines.append(" among the pending; it is already approved and published.")
lines.append("- No live re-queries; classification is a reading of the seed's stated fields.")
lines.append("")
json_note = os.path.join(OUT, "TRIAGE-2026-09-28.md")
with open(json_note, "w") as f:
f.write("\n".join(lines) + "\n")
# Mirror to the primary wizard-outputs location as the finished artifact.
for src, dst in [(json_path, os.path.join(PRIMARY, "Triage-2026-09-28-classify.json")),
(json_note, os.path.join(PRIMARY, "TRIAGE-2026-09-28.md"))]:
import shutil
shutil.copy2(src, dst)
print("wrote", json_path)
print("wrote", json_note)
print("counts:", record["decision_counts"], record["categories"])