diff --git a/wizard-work/sol/CHECK-sol-lane-triage-2026-11-04 b/wizard-work/sol/CHECK-sol-lane-triage-2026-11-04 new file mode 100644 index 0000000..d396654 --- /dev/null +++ b/wizard-work/sol/CHECK-sol-lane-triage-2026-11-04 @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# CHECK -- sol: sol-lane triage 2026-11-04 independent verifier. +# Exit 0 iff the committed JSON + TSV re-derive from the raw inbox files. +set -u + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" || exit 2 + +exec python3 verify_sol_lane_triage_2026_11_04.py diff --git a/wizard-work/sol/sol-lane-triage-2026-11-04.json b/wizard-work/sol/sol-lane-triage-2026-11-04.json new file mode 100644 index 0000000..52ab2dc --- /dev/null +++ b/wizard-work/sol/sol-lane-triage-2026-11-04.json @@ -0,0 +1,39 @@ +{ + "tool": "sol-lane-triage-2026-11-04.py", + "inbox": "wizard-work/sol/inbox", + "total": 5, + "counts": { + "ops": 2, + "finance": 1, + "person": 1, + "other": 1 + }, + "lanes": [ + "ops", + "finance", + "person", + "other" + ], + "items": [ + { + "file": "01-cron-breach.txt", + "lane": "ops" + }, + { + "file": "02-invoice.txt", + "lane": "finance" + }, + { + "file": "03-deploy.txt", + "lane": "ops" + }, + { + "file": "04-meeting.txt", + "lane": "person" + }, + { + "file": "05-market.txt", + "lane": "other" + } + ] +} \ No newline at end of file diff --git a/wizard-work/sol/sol-lane-triage-2026-11-04.py b/wizard-work/sol/sol-lane-triage-2026-11-04.py new file mode 100644 index 0000000..33f54a6 --- /dev/null +++ b/wizard-work/sol/sol-lane-triage-2026-11-04.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +"""Fresh, self-contained sol-lane triage classifier for raw mailbox messages. + +Task : sol lane "Triage - everyday Q&A and classification". +Input: directory of raw message files (subject:/body: lines). +Output: JSON (item + counts) and TSV (filelane). + +Rule (deterministic, checked in priority order, case-insensitive against +subject + body): + + ops -> cron, deploy, disk, backup, server, fail, uptime + finance-> invoice, payment, expense, salary, bitcoin, fee + person -> alexander, sol, wizard, troupe, meeting, alex + other -> default (no keyword matched) + +Exit code IS the verdict: 0 = success, nonzero = malformed input. +""" + +import json +import sys +from pathlib import Path + +LANES = [ + ("ops", ["cron", "deploy", "disk", "backup", "server", "fail", "uptime"]), + ("finance", ["invoice", "payment", "expense", "salary", "bitcoin", "fee"]), + ("person", ["alexander", "sol", "wizard", "troupe", "meeting", "alex"]), +] +ORDER = ["ops", "finance", "person", "other"] + + +def classify(subject, body): + text = " ".join([subject, body]).lower() + for lane, keywords in LANES: + if any(kw in text for kw in keywords): + return lane + return "other" + + +def read_messages(inbox): + items = [] + for path in sorted(Path(inbox).glob("*.txt")): + subject, body = "", "" + for raw in path.read_text(encoding="utf-8").splitlines(): + if raw.startswith("subject:"): + subject = raw.split(":", 1)[1].strip() + elif raw.startswith("body:"): + body = raw.split(":", 1)[1].strip() + items.append({"file": path.name, "subject": subject, "body": body}) + if not items: + print("FAIL: no *.txt messages found in %s" % inbox, file=sys.stderr) + return None + return items + + +def main(argv): + if len(argv) != 4: + print("usage: classify-messages.py INBOX OUT_JSON OUT_TSV", file=sys.stderr) + return 2 + inbox, out_json, out_tsv = [Path(a) for a in argv[1:4]] + if not inbox.is_dir(): + print("FAIL: inbox is not a directory: %s" % inbox, file=sys.stderr) + return 1 + + items = read_messages(inbox) + if items is None: + return 1 + for it in items: + it["lane"] = classify(it["subject"], it["body"]) + + counts = {lane: 0 for lane in ORDER} + for it in items: + counts[it["lane"]] += 1 + + out_json.write_text( + json.dumps( + { + "tool": "sol-lane-triage-2026-11-04.py", + "inbox": str(inbox), + "total": len(items), + "counts": counts, + "lanes": ORDER, + "items": [{"file": i["file"], "lane": i["lane"]} for i in items], + }, + indent=2, + ), + encoding="utf-8", + ) + + with out_tsv.open("w", encoding="utf-8") as fh: + fh.write("file\tlane\n") + for it in items: + fh.write("%s\t%s\n" % (it["file"], it["lane"])) + + print("OK: %d messages classified -> %s | %s (%s)" % ( + len(items), out_json, out_tsv, json.dumps(counts))) + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/wizard-work/sol/sol-lane-triage-2026-11-04.tsv b/wizard-work/sol/sol-lane-triage-2026-11-04.tsv new file mode 100644 index 0000000..8435269 --- /dev/null +++ b/wizard-work/sol/sol-lane-triage-2026-11-04.tsv @@ -0,0 +1,6 @@ +file lane +01-cron-breach.txt ops +02-invoice.txt finance +03-deploy.txt ops +04-meeting.txt person +05-market.txt other diff --git a/wizard-work/sol/verify_sol_lane_triage_2026_11_04.py b/wizard-work/sol/verify_sol_lane_triage_2026_11_04.py new file mode 100644 index 0000000..4b35623 --- /dev/null +++ b/wizard-work/sol/verify_sol_lane_triage_2026_11_04.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +"""Independent re-derivation verifier for the sol-lane triage. + +Verifies sol-lane-triage-2026-11-04.json and .tsv by re-deriving the lanes +from the raw inbox files, with no import of the generator. No network and no +claims: every asserted value is computed from the committed source files. + +Exit code IS the verdict: 0 = PASS, nonzero = FAIL. +""" + +import json +import sys +import traceback +from pathlib import Path + +REPO = Path(__file__).resolve().parent +INBOX = REPO / "inbox" +COMMITTED_JSON = REPO / "sol-lane-triage-2026-11-04.json" +COMMITED_TSV = REPO / "sol-lane-triage-2026-11-04.tsv" + + +def classify(subject, body): + text = " ".join([subject, body]).lower() + rules = [ + ("ops", ["cron", "deploy", "disk", "backup", "server", "fail", "uptime"]), + ("finance", ["invoice", "payment", "expense", "salary", "bitcoin", "fee"]), + ("person", ["alexander", "sol", "wizard", "troupe", "meeting", "alex"]), + ] + for lane, keywords in rules: + if any(kw in text for kw in keywords): + return lane + return "other" + + +def parse_raw(path): + subject, body = "", "" + for raw in path.read_text(encoding="utf-8").splitlines(): + if raw.startswith("subject:"): + subject = raw.split(":", 1)[1].strip() + elif raw.startswith("body:"): + body = raw.split(":", 1)[1].strip() + return subject, body + + +def re_derive(): + items = [] + for path in sorted(INBOX.glob("*.txt")): + subject, body = parse_raw(path) + items.append({"file": path.name, "subject": subject, "body": body, + "lane": classify(subject, body)}) + return items + + +def main(): + errors = [] + try: + expected = re_derive() + committed = json.loads(COMMITTED_JSON.read_text(encoding="utf-8")) + + # 1. All 5 raw files exist. + if len(expected) != 5: + errors.append("expected 5 raw messages, got %d" % len(expected)) + + # 2. JSON items must match re-derivation exactly. + committed_items = {c["file"]: c["lane"] for c in committed.get("items", [])} + if set(committed_items) != {e["file"] for e in expected}: + errors.append("JSON item files mismatch: %s vs %s" + % (sorted(committed_items), sorted(e["file"] for e in expected))) + for e in expected: + if committed_items.get(e["file"]) != e["lane"]: + errors.append("lane mismatch for %s: committed=%r re_derive=%r" + % (e["file"], committed_items.get(e["file"]), e["lane"])) + + # 3. Counts must match. + expected_counts = {lane: 0 for lane in ["ops", "finance", "person", "other"]} + for e in expected: + expected_counts[e["lane"]] += 1 + if committed.get("counts") != expected_counts: + errors.append("counts mismatch: %s vs %s" + % (committed.get("counts"), expected_counts)) + + # 4. TSV must match. + tsv_lines = [ln for ln in COMMITED_TSV.read_text(encoding="utf-8").splitlines() + if ln.strip()] + if len(tsv_lines) != 6: + errors.append("TSV header+rows count != 6: %d" % len(tsv_lines)) + for e in expected: + want = "%s\t%s" % (e["file"], e["lane"]) + if want not in tsv_lines: + errors.append("TSV missing line: %r" % want) + + if errors: + print("FAIL: %d problem(s)" % len(errors)) + for err in errors: + print(" - " + err) + return 1 + + print("OK: all 5/5 re-derived lanes match the committed JSON + TSV") + print("counts=%s" % json.dumps(expected_counts)) + return 0 + except Exception: + traceback.print_exc() + return 1 + + +if __name__ == "__main__": + sys.exit(main())