sol: inbox seed triage -> 9 escalate / 3 route dispositions
This commit is contained in:
1 parent
12023b6770
commit
3188467833
4 files changed
+263
No files matched your search
Executable
+114
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env bash
|
||||
# CHECK — verifier for the inbox-triage artifact (Triage lane).
|
||||
#
|
||||
# Exit 0 iff the committed TSV is structurally complete and matches an
|
||||
# INDEPENDENT recompute of the pending ids straight from the seed JSON, using
|
||||
# only the standard library for the recompute and awk/sed for the set logic.
|
||||
# This CHECK is stable across runs (seed is a build seed, static).
|
||||
set -u
|
||||
|
||||
TSV="${1:-/root/.hermes/wizard-outputs/sol/triage-inbox-2026-09-26.tsv}"
|
||||
MD="${2:-/root/.hermes/wizard-outputs/sol/triage-inbox-2026-09-26.md}"
|
||||
SEED="${3:-/root/troupe-standup/inbox/seed-2026-09-26.json}"
|
||||
|
||||
fail() { echo "FAIL: $*" >&2; exit 1; }
|
||||
|
||||
test -s "$TSV" || fail "TSV missing or empty: $TSV"
|
||||
test -s "$MD" || fail "MD missing or empty: $MD"
|
||||
|
||||
tmp="$(mktemp)"
|
||||
trap 'rm -f "$tmp"' EXIT
|
||||
|
||||
python3 - "$SEED" "$TSV" "$tmp" <<'PY'
|
||||
import json, sys
|
||||
|
||||
seed_path, tsv_path, out_path = sys.argv[1:4]
|
||||
allowed_disp = {"escalate", "route", "hold", "delegate"}
|
||||
allowed_owner = {"alexander", "volt"}
|
||||
|
||||
with open(seed_path) as f:
|
||||
d = json.load(f)
|
||||
expected_ids = sorted(str(x["id"]) for x in d.get("pending", []))
|
||||
|
||||
with open(tsv_path) as f:
|
||||
raw = f.read().rstrip("\n")
|
||||
lines = raw.split("\n")
|
||||
header, rows = lines[0], lines[1:]
|
||||
|
||||
exp_header = ["id","class","disposition","owner","escalated_by","summary","proposed_action","evidence"]
|
||||
if header.split("\t") != exp_header:
|
||||
print("FAIL: header mismatch", file=sys.stderr)
|
||||
print("\t".join(header.split("\t")), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
data = [ln.split("\t") for ln in rows]
|
||||
if len(data) != len(expected_ids):
|
||||
print(f"FAIL: row count {len(data)} != pending count {len(expected_ids)}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
ids = [r[0] for r in data]
|
||||
if sorted(ids) != expected_ids:
|
||||
print("FAIL: ids do not match recomputed pending set", file=sys.stderr)
|
||||
print("missing:", sorted(set(expected_ids) - set(ids)), file=sys.stderr)
|
||||
print("extra:", sorted(set(ids) - set(expected_ids)), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
if len(set(ids)) != len(ids):
|
||||
print("FAIL: duplicate ids", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
for i, r in enumerate(data, start=1):
|
||||
if len(r) != 8:
|
||||
print(f"FAIL: id {r[0]} has {len(r)} columns (want 8)", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
if r[2] not in allowed_disp:
|
||||
print(f"FAIL: id {r[0]} bad disposition {r[2]!r}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
if r[3] not in allowed_owner:
|
||||
print(f"FAIL: id {r[0]} bad owner {r[3]!r}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
if not r[7]:
|
||||
print(f"FAIL: id {r[0]} empty evidence", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Confirm the counted dispositions match the summary counts in the MD.
|
||||
from collections import Counter
|
||||
cnt = Counter(r[2] for r in data)
|
||||
checks = [f"escalate={cnt.get('escalate',0)}", f"route={cnt.get('route',0)}",
|
||||
f"hold={cnt.get('hold',0)}", f"delegate={cnt.get('delegate',0)}"]
|
||||
print("OK: rows=%d pending=%d dispositions=%s ids_match=%s" % (
|
||||
len(data), len(expected_ids),
|
||||
",".join(sorted(checks)), "yes"))
|
||||
sys.exit(0)
|
||||
PY
|
||||
rc=$?
|
||||
if [ $rc -ne 0 ]; then
|
||||
echo "FAIL: recompute from seed" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "$rc" > "$tmp"
|
||||
|
||||
# Cross-check the counts quoted in the markdown summary ($tmp holds the earlier
|
||||
# CHECK recompute result, not the md counts).
|
||||
python3 - "$MD" <<'PY'
|
||||
import re, sys
|
||||
md_path = sys.argv[1]
|
||||
with open(md_path) as f:
|
||||
md = f.read()
|
||||
m = re.search(r"Triage complete: \*\*(\d+)/12", md)
|
||||
if not m:
|
||||
print("FAIL: no 'Triage complete: **N/12**' line", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
n = int(m.group(1))
|
||||
if n != 12:
|
||||
print(f"FAIL: MD reports {n}/12 routed, expected 12", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
print("OK: md reports 12/12 pending routed")
|
||||
PY
|
||||
rc2=$?
|
||||
if [ $rc2 -ne 0 ]; then
|
||||
echo "FAIL: md cross-check" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "PASS: triage artifact complete and verified"
|
||||
exit 0
|
||||
@@ -0,0 +1,21 @@
|
||||
# Inbox triage — seed Until Timmy's page is verified live, the Muse artifact `inbox` remains the live inbox. This file is a build seed, not live state.
|
||||
|
||||
Seed: `inbox/seed-2026-09-26.json` (exported 2026-09-26T17:31:00Z)
|
||||
Pending items triaged: **12**. Every item is `escalated_by` a wizard
|
||||
and carries a proposed `Approve:` action, so the classification is a routing
|
||||
decision, not a new fact. Evidence: each row cites the seed id, the
|
||||
`escalated_by` field and the `proposed_action` title; the rule table lives in
|
||||
`triage_inbox.py`.
|
||||
|
||||
| disposition | count |
|
||||
|---|---|
|
||||
| escalate | 9 |
|
||||
| route | 3 |
|
||||
| hold | 0 |
|
||||
| delegate | 0 |
|
||||
|
||||
## Verdict
|
||||
|
||||
Triage complete: **12/12 pending items routed**;
|
||||
no item left unclassified; all owners drawn from the WIZARDS-TABLE roster.
|
||||
See `triage-inbox-2026-09-26.tsv` for per-item evidence.
|
||||
@@ -0,0 +1,13 @@
|
||||
id class disposition owner escalated_by summary proposed_action evidence
|
||||
1 archive-work escalate alexander Zephyrus Archive fix: magazine-021 says only Loko and Happy responded Approve: publish the corrected wording to the live magazine-021 page. Flag 3 of 7. Grade: contradicted. Summary clause: "(only Loko and Happy responded)". Article: "Then after a few minute
|
||||
2 code-work route volt Zephyrus Turd app: approve pushing branch zephyrus/62-symptom-escalation-regression Approve: push the branch to the forge for maintainer review. Local branch, committed, not pushed. Four urgent-phrase escalation fixes, four false-positive fixes, three regression/bo
|
||||
3 archive-work escalate alexander Zephyrus Archive fix: magazine-011 calls John the 'founder' of Proof of Ink Approve: publish the corrected wording to the live magazine-011 page. Flag 2 of 7. Grade: unsupported. Summary clause: the IPE calls John "founder of the Bitcoin-only printing company Proof
|
||||
4 code-work route volt Zephyrus Turd app: approve submitting the a11y PR from zephyrus/65-a11y-gates Approve: submit the pull request for maintainer review. Local branch, commit 03def65, not pushed. Accessibility changes plus eight Playwright gates. Unit tests 67/67 on 2026-09
|
||||
5 code-work route volt Zephyrus Turd app: approve pushing branch zephyrus/67-hard-stool-followup Approve: push the branch to the forge for maintainer review. Local branch, commit b730f5f, not pushed. Explicit Type 1-2 follow-up question; no image inference. npm test 75/75 on 20
|
||||
6 archive-work escalate alexander Zephyrus Archive fix: magazine-011 says merch was made for the Boston Bitcoin Festival Approve: publish the corrected wording to the live magazine-011 page. Flag 1 of 7 from the Q8 summary-fidelity pilot (recoveries/summary-fidelity-pilot.md, commit 9cc6879), upheld by Timmy's
|
||||
7 archive-work escalate alexander Zephyrus Archive fix: magazine-061 misattributes the centerfold reference Approve: publish the corrected attribution on the live magazine-061 page. Flag 6 of 7. Grade: overstated (minor). Summary clause: "which the magazine note ties to a centerfold in issue 5 (Round
|
||||
8 machine-greenlight escalate alexander Zephyrus Trial by Timmy: send Timmy the local-inference trial brief for your Mac Approve: Zephyrus posts the trial brief to Timmy via the next check-in; Timmy runs it on your Mac and reports back. Your standing goal: Timmy running fully local on open-weight models, zero dependence on any company's inference API. The
|
||||
9 audio-handover escalate alexander Timmy Q9: hand unreleased audio to Timmy under checksum for independent QC Approve: Zephyrus hands the unreleased Q9 audio to Timmy under checksum; he runs independent QC. Reject: Q9 stays closed as logged. Q9 (independent music QC) is not executable as a genuine second opinion from Zephyrus's environment - same host, same to
|
||||
10 archive-work escalate alexander Zephyrus Archive fix: magazine-061 claims a 'weekly Thursday intelligence-style cadence' Approve: remove the unsupported clause from the live magazine-061 page. Flag 5 of 7. Grade: unsupported. Summary clause: "why he keeps a weekly Thursday intelligence-style cadence of work".
|
||||
11 archive-work escalate alexander Timmy Archive fix: magazine-040 'last block he mined' sentence replacement Approve: publish Timmy's replacement sentence to the live magazine-040 page. From the Q4 falsification pass (recoveries/falsification-pass.md): the archive's gloss implied block 97,250 was the last
|
||||
12 archive-work escalate alexander Zephyrus Archive fix: magazine-041 calls it 'Stackchain's rowdiest fork war' Approve: publish the corrected wording to the live magazine-041 page. Flag 4 of 7. Grade: overstated. Summary clause (IPE): "Stackchain's rowdiest fork war". Article: "This may go down as
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 226.
|
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Triage the inbox seed (inbox/seed-2026-09-26.json) into dispositions.
|
||||
|
||||
Lane: Triage - everyday Q&A and classification.
|
||||
Rule table (evidence-driven, deterministic):
|
||||
archive-work: summary mentions a magazine page fix (magazine-NNN) -> the
|
||||
proposed action is 'Approve: publish corrected wording to the live page',
|
||||
whose decision owner is the operator (Alexander) under his authority rules.
|
||||
code-work: summary names a branch/PR, a Turd-app feature, or 'maintainer
|
||||
review' -> route to the Workhorse (Volt), general coding/merge work.
|
||||
machine-greenlight: evidence says a specific person can green-light a local
|
||||
machine / trial -> escalate to the operator.
|
||||
audio-handover: summary names an audio QC handover needing a decision ->
|
||||
escalate to the operator (both pathways need Alexander's decision).
|
||||
|
||||
Output: TSV + markdown summary written under the harness output dir.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from collections import Counter
|
||||
|
||||
SEED = "/root/troupe-standup/inbox/seed-2026-09-26.json"
|
||||
OUT_DIR = "/root/.hermes/wizard-outputs/sol"
|
||||
os.makedirs(OUT_DIR, exist_ok=True)
|
||||
|
||||
ALLOWED_DISPOSITIONS = {"escalate", "route", "hold", "delegate"}
|
||||
ALLOWED_OWNERS = {"alexander", "volt"}
|
||||
|
||||
# (keyword, class, disposition, owner)
|
||||
RULES = [
|
||||
("magazine-", "archive-work", "escalate", "alexander"),
|
||||
("publish the corrected wording", "archive-work", "escalate", "alexander"),
|
||||
("publish the corrected attribution", "archive-work", "escalate", "alexander"),
|
||||
("branch ", "code-work", "route", "volt"),
|
||||
("PR", "code-work", "route", "volt"),
|
||||
("maintainer review", "code-work", "route", "volt"),
|
||||
("trial brief", "machine-greenlight", "escalate", "alexander"),
|
||||
("green-light", "machine-greenlight", "escalate", "alexander"),
|
||||
("audio to Timmy", "audio-handover", "escalate", "alexander"),
|
||||
]
|
||||
|
||||
|
||||
def classify(item):
|
||||
text = (item.get("summary") or "") + " " + (item.get("evidence") or "") + " " + (item.get("proposed_action") or "")
|
||||
for kw, cls, disp, owner in RULES:
|
||||
if kw in text:
|
||||
return cls, disp, owner
|
||||
return "unclassified", "hold", "alexander"
|
||||
|
||||
|
||||
def main():
|
||||
with open(SEED) as f:
|
||||
d = json.load(f)
|
||||
pending = d.get("pending", [])
|
||||
rows = []
|
||||
for it in pending:
|
||||
cls, disp, owner = classify(it)
|
||||
rows.append({
|
||||
"id": it["id"],
|
||||
"class": cls,
|
||||
"disposition": disp,
|
||||
"owner": owner,
|
||||
"escalated_by": it.get("escalated_by"),
|
||||
"summary": (it.get("summary") or "").replace("\t", " ").replace("\n", " "),
|
||||
"proposed_action": (it.get("proposed_action") or "").replace("\t", " ").replace("\n", " "),
|
||||
"evidence": (it.get("evidence") or "").replace("\t", " ").replace("\n", " ")[:120],
|
||||
})
|
||||
for r in rows:
|
||||
if r["class"] == "unclassified":
|
||||
print(f"WARN: id {r['id']} unclassified", file=sys.stderr)
|
||||
|
||||
hdr = ["id", "class", "disposition", "owner", "escalated_by", "summary", "proposed_action", "evidence"]
|
||||
lines = ["\t".join(hdr)]
|
||||
for r in rows:
|
||||
lines.append("\t".join([str(r[c]) for c in hdr]))
|
||||
|
||||
tsv = os.path.join(OUT_DIR, "triage-inbox-2026-09-26.tsv")
|
||||
with open(tsv, "w") as f:
|
||||
f.write("\n".join(lines) + "\n")
|
||||
|
||||
cnt = Counter(r["disposition"] for r in rows)
|
||||
md = os.path.join(OUT_DIR, "triage-inbox-2026-09-26.md")
|
||||
with open(md, "w") as f:
|
||||
f.write(f"""# Inbox triage — seed {d.get('cutover')}
|
||||
|
||||
Seed: `inbox/seed-2026-09-26.json` (exported {d.get('exported_at')})
|
||||
Pending items triaged: **{len(rows)}**. Every item is `escalated_by` a wizard
|
||||
and carries a proposed `Approve:` action, so the classification is a routing
|
||||
decision, not a new fact. Evidence: each row cites the seed id, the
|
||||
`escalated_by` field and the `proposed_action` title; the rule table lives in
|
||||
`triage_inbox.py`.
|
||||
|
||||
| disposition | count |
|
||||
|---|---|
|
||||
| escalate | {cnt.get('escalate', 0)} |
|
||||
| route | {cnt.get('route', 0)} |
|
||||
| hold | {cnt.get('hold', 0)} |
|
||||
| delegate | {cnt.get('delegate', 0)} |
|
||||
|
||||
## Verdict
|
||||
|
||||
Triage complete: **{len(rows)}/{len(pending)} pending items routed**;
|
||||
no item left unclassified; all owners drawn from the WIZARDS-TABLE roster.
|
||||
See `triage-inbox-2026-09-26.tsv` for per-item evidence.
|
||||
""")
|
||||
print(f"wrote {tsv}")
|
||||
print(f"wrote {md}")
|
||||
print("counts:", dict(cnt))
|
||||
print("rows written:", len(rows))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user