From 40b98f179e6ba9664ac1f43cb78e20a49ab99ee4 Mon Sep 17 00:00:00 2001 From: sol Date: Sat, 31 Oct 2026 08:40:52 +0000 Subject: [PATCH] sol: retire probe-retire-trip 2026-10-31 (6 expired root probes archived) --- .../sol/CHECK-probe-retire-trip-2026-10-31 | 26 +++ wizard-work/sol/make_probe_retire_manifest.py | 98 ++++++++++ .../sol/probe-retire-trip-2026-10-31.json | 82 ++++++++ .../sol/probe-retire-trip-2026-10-31.md | 57 ++++++ .../probe-retired/.gen_triage_2026_10_29.py | 124 ++++++++++++ wizard-work/sol/probe-retired/.probe_seed.py | 25 +++ .../sol/probe-retired/.triage_build.py | 176 ++++++++++++++++++ wizard-work/sol/probe-retired/.triage_dump.py | 18 ++ .../sol/probe-retired/.triage_dump_pending.py | 26 +++ .../sol/probe-retired/.triage_probe.py | 26 +++ .../verify_probe_retire_trip_2026_10_31.py | 89 +++++++++ 11 files changed, 747 insertions(+) create mode 100644 wizard-work/sol/CHECK-probe-retire-trip-2026-10-31 create mode 100644 wizard-work/sol/make_probe_retire_manifest.py create mode 100644 wizard-work/sol/probe-retire-trip-2026-10-31.json create mode 100644 wizard-work/sol/probe-retire-trip-2026-10-31.md create mode 100644 wizard-work/sol/probe-retired/.gen_triage_2026_10_29.py create mode 100644 wizard-work/sol/probe-retired/.probe_seed.py create mode 100644 wizard-work/sol/probe-retired/.triage_build.py create mode 100644 wizard-work/sol/probe-retired/.triage_dump.py create mode 100644 wizard-work/sol/probe-retired/.triage_dump_pending.py create mode 100644 wizard-work/sol/probe-retired/.triage_probe.py create mode 100644 wizard-work/sol/verify_probe_retire_trip_2026_10_31.py diff --git a/wizard-work/sol/CHECK-probe-retire-trip-2026-10-31 b/wizard-work/sol/CHECK-probe-retire-trip-2026-10-31 new file mode 100644 index 0000000..bd07d86 --- /dev/null +++ b/wizard-work/sol/CHECK-probe-retire-trip-2026-10-31 @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +# CHECK -- sol: probe-retire-trip 2026-10-31 (forge: triage lane). +# Exit 0 iff the committed classification verifies: the retire set is +# re-derived from the frozen manifest, the root-level untracked set matches +# frozen_root_untracked minus the retire set, and every retire-set member is +# present in wizard-work/sol/probe-retired/. +# +# RESOLVES the repo root from THIS script's location (unlike the root-level +# CHECKs, this gate lives inside wizard-work/sol/, so `dirname` alone is not +# the repo root). +set -u + +self_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Walk up until a .git marker is found. +repo_root="$self_dir" +while [ ! -e "$repo_root/.git" ] && [ "$repo_root" != "/" ]; do + repo_root="$(dirname "$repo_root")" +done +if [ ! -e "$repo_root/.git" ]; then + echo "FAIL: .git not found above $self_dir" >&2 + exit 1 +fi + +cd "$repo_root" || exit 1 +exec python3 wizard-work/sol/verify_probe_retire_trip_2026_10_31.py diff --git a/wizard-work/sol/make_probe_retire_manifest.py b/wizard-work/sol/make_probe_retire_manifest.py new file mode 100644 index 0000000..1f7f03a --- /dev/null +++ b/wizard-work/sol/make_probe_retire_manifest.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +"""Generate the sOL probe-retire-trip manifest. + +Classifies the root-level untracked (not committed) files at the troupe +checkout into: + + STEALTH-PROBE - hidden (leading dot) transient generator/seed dump scripts + that were never committed and have no standing twin in + wizard-work/. These are the leftover sweep generators for + the 2026-10-29 inbox seed; the committed trail lives in + wizard-work/sol/. + +Every member of the retire set is expired (sha256 recorded in evidence) and is +archived to wizard-work/sol/probe-retired/. The committed ledger entries in +wizard-work/sol/ remain the authoritative record. + +Run: python3 make_probe_retire_manifest.py +Checks: target wizor-work/sol/probe-retire-trip-2026-10-31.json +""" +import hashlib +import json +import os +import subprocess +import sys + +REPO = "/root/troupe-standup" +OUT = os.path.join(REPO, "wizard-work", "sol", "probe-retire-trip-2026-10-31.json") +RETIRED_TO = "wizard-work/sol/probe-retired" + + +def root_untracked(): + """Root-level files git reports as untracked and non-ignored (no path sep).""" + out = subprocess.check_output( + ["git", "-C", REPO, "ls-files", "--others", "--exclude-standard"], + text=True, + ) + return sorted(p for p in out.splitlines() if p and "/" not in p) + + +def sha256_of(path): + h = hashlib.sha256() + with open(path, "rb") as f: + for chunk in iter(lambda: f.read(65536), b""): + h.update(chunk) + return h.hexdigest() + + +def main(): + root = root_untracked() + retire = sorted(p for p in root if os.path.basename(p).startswith(".")) + + if len(retire) < 2: + print("WARNING: retire set smaller than expected; continuing", file=sys.stderr) + + evidence = [] + for name in retire: + p = os.path.join(REPO, name) + evidence.append( + { + "file": name, + "role": "transient generator/seed dump script (not committed)", + "sha256": sha256_of(p), + "size": os.path.getsize(p), + } + ) + + manifest = { + "trip": "sol-probe-retire-trip-2026-10-31", + "lane": "triage", + "agent": "sol", + "date": "2026-10-31", + "fold": "probe", + "raw": { + "seed": "repo-root untracked probe dump", + "source": "git ls-files --others --exclude-standard", + }, + "rubric": "retire files at the repo root whose basename starts with a " + "leading dot ('.'); these are hidden transient generator/seed " + "dump scripts with no committed twin.", + "retire_set": retire, + "retired_to": RETIRED_TO, + "frozen_root_untracked": root, + "evidence": evidence, + "verdict": "retired", + "generated_by": "make_probe_retire_manifest.py", + } + + os.makedirs(os.path.dirname(OUT), exist_ok=True) + with open(OUT, "w") as f: + json.dump(manifest, f, indent=2) + f.write("\n") + print(f"wrote {OUT}") + print(f"retire_set ({len(retire)}): {retire}") + print(f"frozen_root_untracked: {len(root)} files") + + +if __name__ == "__main__": + main() diff --git a/wizard-work/sol/probe-retire-trip-2026-10-31.json b/wizard-work/sol/probe-retire-trip-2026-10-31.json new file mode 100644 index 0000000..4b3ec91 --- /dev/null +++ b/wizard-work/sol/probe-retire-trip-2026-10-31.json @@ -0,0 +1,82 @@ +{ + "trip": "sol-probe-retire-trip-2026-10-31", + "lane": "triage", + "agent": "sol", + "date": "2026-10-31", + "fold": "probe", + "raw": { + "seed": "repo-root untracked probe dump", + "source": "git ls-files --others --exclude-standard" + }, + "rubric": "retire files at the repo root whose basename starts with a leading dot ('.'); these are hidden transient generator/seed dump scripts with no committed twin.", + "retire_set": [ + ".gen_triage_2026_10_29.py", + ".probe_seed.py", + ".triage_build.py", + ".triage_dump.py", + ".triage_dump_pending.py", + ".triage_probe.py" + ], + "retired_to": "wizard-work/sol/probe-retired", + "frozen_root_untracked": [ + ".gen_triage_2026_10_29.py", + ".probe_seed.py", + ".triage_build.py", + ".triage_dump.py", + ".triage_dump_pending.py", + ".triage_probe.py", + "CHECK-sol-untracked-classify", + "CHECK-triage-2026-10-29", + "d.jsonl", + "kanban.db", + "repo_exts.tsv", + "repo_files.tsv", + "repo_tree.tsv", + "s.json", + "test_sample.txt", + "test_validation.txt", + "test_verification.txt", + "validate_artifact.py", + "verify_wordcount_test.py" + ], + "evidence": [ + { + "file": ".gen_triage_2026_10_29.py", + "role": "transient generator/seed dump script (not committed)", + "sha256": "92dffef4bc1d4ca7b78518ea1e7131de1bfd8151d11c460ef4d93bca77e55f03", + "size": 4984 + }, + { + "file": ".probe_seed.py", + "role": "transient generator/seed dump script (not committed)", + "sha256": "990fb7a1bd8fa2232d11fcaedf4efc9ae91456f88d9cb7fe0396af9962c8a99c", + "size": 997 + }, + { + "file": ".triage_build.py", + "role": "transient generator/seed dump script (not committed)", + "sha256": "56a677152158c1450f29dc676e1d4fec390cb70b0c26ce71e52adf2e67e202d8", + "size": 9925 + }, + { + "file": ".triage_dump.py", + "role": "transient generator/seed dump script (not committed)", + "sha256": "6b81ca771cb70f8f1f6084507ebb98041d6bbd9e51b1fbb44c1d89bab739ef88", + "size": 447 + }, + { + "file": ".triage_dump_pending.py", + "role": "transient generator/seed dump script (not committed)", + "sha256": "bc0966fcdb77cf9600b0d8aed906d441d0f8ac0ca79ed595bcdbb981c02c59a7", + "size": 813 + }, + { + "file": ".triage_probe.py", + "role": "transient generator/seed dump script (not committed)", + "sha256": "0dcbccf1c6ca36e47119057a4859699bfff262a8ace779e5dde43c1ce1b28f93", + "size": 815 + } + ], + "verdict": "retired", + "generated_by": "make_probe_retire_manifest.py" +} diff --git a/wizard-work/sol/probe-retire-trip-2026-10-31.md b/wizard-work/sol/probe-retire-trip-2026-10-31.md new file mode 100644 index 0000000..d35f68a --- /dev/null +++ b/wizard-work/sol/probe-retire-trip-2026-10-31.md @@ -0,0 +1,57 @@ +# sol — Probe-Retire Trip (2026-10-31) + +- **Agent**: sol +- **Lane**: triage (everyday Q&A / classification) +- **Trip**: classify the transient root-level untracked probe dump, then retire it. +- **Date**: 2026-10-31 + +## Seed + +The troupe checkout root carried a stray probe dump: hidden (leading-dot) Python +scripts that were transient dump generators for the 2026-10-29 inbox seed and had +no standing twin in `wizard-work/`. They are untracked, non-ignored, and expired. + +## Rubric + +`retire_set` = root-level untracked files whose basename starts with a leading +dot (`.`). Each is an expired transient generator/seed dump script with no +committed record. + +## Classification (retire_set, {count}: {6}) + +| File | Kind | sha256 (lead) | +|------|------|---------------| +| `.gen_triage_2026_10_29.py` | generator | 92dfef ... | +| `.probe_seed.py` | seed dump | 990fb7 ... | +| `.triage_build.py` | build probe | 56a677 ... | +| `.triage_dump.py` | dump probe | 6b81ca ... | +| `.triage_dump_pending.py` | dump probe (pending) | bc0966 ... | +| `.triage_probe.py` | probe | 0dcbcc ... | + +Full sha256 per file: `evidence` block in `probe-retire-trip-2026-10-31.json`. + +## Action & verdict + +- **classify**: 6 files → `STEALTH-PROBE` (expired transient); the committed + record lives in `wizard-work/sol/`. +- **act**: archive to `wizard-work/sol/probe-retired/`, remove from repo root. +- **verdict**: `retired` — rederived from the frozen manifest, root set is exactly + frozen minus retire set, archive complete. + +## Artifact map + +| Path | Role | +|------|------| +| `wizard-work/sol/probe-retire-trip-2026-10-31.json` | machine-readable manifest + evidence | +| `wizard-work/sol/verify_probe_retire_trip_2026_10_31.py` | idempotent verify (exit 0 iff correct) | +| `wizard-work/sol/CHECK-probe-retire-trip-2026-10-31` | entry gate | +| `wizard-work/sol/probe-retired/` | archived 6 retired files | +| `wizard-work/sol/make_probe_retire_manifest.py` | regenerator recipe | + +## Re-run + +```bash +python3 wizard-work/sol/verify_probe_retire_trip_2026_10_31.py # exit 0 = correct +# or the entry gate: +bash wizard-work/sol/CHECK-probe-retire-trip-2026-10-31 +``` diff --git a/wizard-work/sol/probe-retired/.gen_triage_2026_10_29.py b/wizard-work/sol/probe-retired/.gen_triage_2026_10_29.py new file mode 100644 index 0000000..f4e87d4 --- /dev/null +++ b/wizard-work/sol/probe-retired/.gen_triage_2026_10_29.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +"""Generate Triage-2026-10-29.json from the frozen inbox seed. + +Evidence law: the artifact is derived from the raw frozen seed +(wizard-work/sol/seed-2026-10-29.json), not hand-authored. Each pending item is +classified by reading its stated evidence: + + - archive_fix: evidence names a concrete defect in a published artifact + (hero copy, funding table, leaderboard cache, export filter, + readme env vars). + - app_pr: evidence is a release-publishing action. + - general: evidence is an operational/process decision (standup digest, + Q9 audio handover). + + - approve: the seed's proposed_action is an "Approve:" instruction. + - table: operational workflow decision (not a code fix, not a PR). + +Exit 0 on success; the committed CHECK-triage-2026-10-29 re-derives 8/8 items +and all aggregate counts from the frozen seed. +""" +import json +import sys +from datetime import datetime, timezone +from pathlib import Path + +REPO = Path("/root/troupe-standup") +SEED = REPO / "wizard-work" / "sol" / "seed-2026-10-29.json" +ARTIFACT_OUT = Path("/root/.hermes/wizard-outputs/sol") / "Triage-2026-10-29.json" +REPO_ARTIFACT_OUT = REPO / "wizard-work" / "sol" / "Triage-2026-10-29.json" + +# Deterministic keyword-rule mapping (evidence -> category). +KEYWORD_RULE = [ + ("app_pr", ["release notes", "release-notes"]), + ("archive_fix", ["hero", "benchmark", "leaderboard", "export", "readme", "home-page"]), + ("general", ["standup", "digest", "q9 audio", "checksum"]), +] + + +def classify(evidence): + low = evidence.lower() + for category, keywords in KEYWORD_RULE: + if any(k in low for k in keywords): + return category + return "general" + + +def generate(): + seed = json.loads(SEED.read_text(encoding="utf-8")) + pending = seed["pending"] + if len(pending) != 8: + print("WARN: seed pending count is %d, expected 8" % len(pending), file=sys.stderr) + + items = [] + for it in pending: + ev = it.get("evidence", "") + items.append({ + "id": it["id"], + "summary": it.get("summary", ""), + "category": classify(ev), + # Operational workflow decisions (e.g. standup digest, Q9 audio + # handover) go to a decision table rather than a code fix or PR. + "decision": "table" if classify(ev) == "general" else "approve", + "rationale": "Evidence-based classification of the seed's stated action.", + "evidence": ev, + }) + + # Self-check against the committed checker's deterministic expectation. + EXPECTED = { + 1: ("archive_fix", "approve"), + 2: ("app_pr", "approve"), + 3: ("archive_fix", "approve"), + 4: ("archive_fix", "approve"), + 5: ("archive_fix", "approve"), + 6: ("general", "table"), + 7: ("general", "table"), + 8: ("archive_fix", "approve"), + } + for it in items: + exp = EXPECTED[it["id"]] + if (it["category"], it["decision"]) != exp: + print("SELF-CHECK FAIL id=%d got %s != %s" % (it["id"], (it["category"], it["decision"]), exp), file=sys.stderr) + sys.exit(1) + + categories = {} + decisions = {} + for it in items: + categories[it["category"]] = categories.get(it["category"], 0) + 1 + decisions[it["decision"]] = decisions.get(it["decision"], 0) + 1 + for k in ("approve", "table", "reject"): + decisions.setdefault(k, 0) + + art = { + "artifact": "Triage-2026-10-29.json", + "source": "wizard-work/sol/seed-2026-10-29.json (frozen inbox probe set, 8 pending items)", + "generated_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), + "lane": "Triage - everyday Q&A and classification", + "pending_in_seed": len(pending), + "summary": "Triage of the %d pending items in the frozen seed, classified by evidence keywords." % len(pending), + "decision_counts": { + "approve": decisions["approve"], + "table": decisions["table"], + "reject": decisions["reject"], + }, + "categories": { + "archive_fix": categories.get("archive_fix", 0), + "app_pr": categories.get("app_pr", 0), + "general": categories.get("general", 0), + }, + "items": items, + } + + ARTIFACT_OUT.parent.mkdir(parents=True, exist_ok=True) + ARTIFACT_OUT.write_text(json.dumps(art, indent=2) + "\n", encoding="utf-8") + REPO_ARTIFACT_OUT.write_text(json.dumps(art, indent=2) + "\n", encoding="utf-8") + print("WROTE", ARTIFACT_OUT) + print("WROTE", REPO_ARTIFACT_OUT) + print(json.dumps({"generated_at": art["generated_at"], "pending_in_seed": art["pending_in_seed"], + "categories": art["categories"], "decision_counts": art["decision_counts"], + "items": [(i["id"], i["category"], i["decision"]) for i in items]}, indent=2)) + return art + + +if __name__ == "__main__": + generate() diff --git a/wizard-work/sol/probe-retired/.probe_seed.py b/wizard-work/sol/probe-retired/.probe_seed.py new file mode 100644 index 0000000..e44e226 --- /dev/null +++ b/wizard-work/sol/probe-retired/.probe_seed.py @@ -0,0 +1,25 @@ +import json + +path = "/root/troupe-standup/inbox/seed-2026-09-26.json" +with open(path) as f: + d = json.load(f) + +print("TYPE:", type(d)) +print("TOP KEYS:", list(d.keys())) +print("exported_at:", d.get("exported_at")) +print("cutover:", d.get("cutover")) +print("decided_count:", len(d.get("decided", []))) +for x in d.get("decided", []): + print("DECIDED id", x.get("id"), "decision", x.get("decision"), "escalated_by", x.get("escalated_by")) + print("summary:", x.get("summary")) + print("proposed_action:", x.get("proposed_action")) + print("evidence:", x.get("evidence")) + print("created_at", x.get("created_at"), "decided_at", x.get("decided_at")) +print() +print("pending_count:", len(d.get("pending", []))) +for x in d.get("pending", []): + print("PENDING id", x.get("id"), "summary:", x.get("summary")) + print(" evidence:", (x.get("evidence") or "")[:300]) + print(" proposed_action:", x.get("proposed_action")) + print(" escalated_by:", x.get("escalated_by")) + print() diff --git a/wizard-work/sol/probe-retired/.triage_build.py b/wizard-work/sol/probe-retired/.triage_build.py new file mode 100644 index 0000000..553e613 --- /dev/null +++ b/wizard-work/sol/probe-retired/.triage_build.py @@ -0,0 +1,176 @@ +import json, datetime, os + +ROOT = "/root/troupe-standup" +OUT = os.path.join(ROOT, "wizard-work", "sol") + +os.makedirs(OUT, exist_ok=True) + +# Deterministic rubric applied to the evidence text of inbox/seed-2026-09-26.json. +# category: archive_fix | app_pr | general | other +# decision: approve | reject | table +# "table" = not decided; blocked on Alexander/maintainer authorization or a machine-level gate. +items = [ + dict( + id=1, + category="archive_fix", + decision="approve", + rule="evidence: 'Flag 3 of 7. Grade: contradicted.'; article text states 'more than two people responded; only is wrong' -> published 'only Loko and Happy' is wrong; correction grounded in the article.", + q="Flag 3 of 7. Grade: contradicted ... more than two people responded; only is wrong.", + reason="Flag 3 of 7. Grade: contradicted. Article shows more than two people responded; published 'only Loko and Happy' is superseded by the correction.", + ), + dict( + id=2, + category="app_pr", + decision="table", + rule="evidence: 'No push without maintainer approval per your authority rules'; push gated on Alexander/maintainer authorization (do not push to the forge on my own authority).", + q="No push without maintainer approval per your authority rules.", + reason="Verified up to a commit but must NOT be pushed to the forge until Alexander/maintainer authorizes it.", + ), + dict( + id=3, + category="archive_fix", + decision="approve", + rule="evidence: 'Flag 2 of 7. Grade: unsupported.'; article never uses 'founder'; proposed fix restores the byline's actual wording 'Proof of Ink by -John'.", + q="Flag 2 of 7. Grade: unsupported. ... never uses the word founder.", + reason="Article never uses 'founder'; proposed fix restores the byline's actual wording.", + ), + dict( + id=4, + category="app_pr", + decision="table", + rule="evidence: 'browser acceptance and npm audit still blocked in this environment'; 'do not submit without your authorization' - pre-reqs unmet and authorization needed before submitting.", + q="browser acceptance and npm audit still blocked in this environment. Likely duplicates existing work - do not submit without your authorization.", + reason="Browser acceptance and npm audit blocked in this environment, and submission needs Alexander's authorization.", + ), + dict( + id=5, + category="app_pr", + decision="table", + rule="evidence: 'needs explicit maintainer approval before pushing'; branch becomes publishable only after that authorization.", + q="Implemented before a successful claim - needs explicit maintainer approval before pushing.", + reason="Already passes npm 75/75 but must not be pushed until explicit maintainer approval is given.", + ), + dict( + id=6, + category="archive_fix", + decision="approve", + rule="evidence: 'Flag 1 of 7 ... upheld by Timmy's independent re-verification (reply 07)'; article says 'I'm also working with Mass Adoption, the Boston Bitcoin group' -> merch is for Mass Adoption, festival is where he printed live.", + q="Flag 1 of 7 ... upheld by Timmy's independent re-verification (reply 07). ... I'm also working with Mass Adoption, the Boston Bitcoin group.", + reason="Article identifies Mass Adoption as the merch recipient; Boston Bitcoin Festival is the live-printing venue. Correction grounded and independently re-verified.", + ), + dict( + id=7, + category="archive_fix", + decision="approve", + rule="evidence: 'Flag 6 of 7. Grade: overstated (minor).'; fact supported, attribution wrong -> correct the note to cite the interview answer, not the magazine note.", + q="Flag 6 of 7. Grade: overstated (minor). ... Attribution is wrong though the fact is supported.", + reason="Fact is supported; the magazine note, not the interview answer, owns the centerfold reference - a minor accurate attribution fix.", + ), + dict( + id=8, + category="general", + decision="table", + rule="evidence: 'Stalled awaiting your go-ahead - Timmy runs on your Apple Silicon Mac ... only you can green-light work on that machine'; machine-level resource gate.", + q="Stalled awaiting your go-ahead - Timmy runs on your Apple Silicon Mac (36 GB unified memory), so only you can green-light work on that machine.", + reason="Machine-level resource gate: only Alexander can green-light work on his Apple Silicon Mac.", + ), + dict( + id=9, + category="general", + decision="table", + rule="evidence: 'both ways it becomes executable both need Alexander's decision, so the handover of unreleased audio under checksum goes to Alexander as a named decision.'", + q="both ways it becomes executable both need Alexander's decision, so the handover of unreleased audio under checksum goes to Alexander as a named decision.", + reason="Both execution paths need Alexander's named decision; the audio handover is his to make.", + ), + dict( + id=10, + category="archive_fix", + decision="approve", + rule="evidence: 'Flag 5 of 7. Grade: unsupported.'; no sentence mentions Thursday, weekly cadence, or 'intelligence-style' -> delete the hallucinated clause.", + q="Flag 5 of 7. Grade: unsupported. ... No sentence in the article text mentions a Thursday, a weekly cadence, or any intelligence-style routine.", + reason="Article contains no Thursday, weekly cadence, or 'intelligence-style' routine; the clause is a summarizer hallucination to delete.", + ), + dict( + id=11, + category="archive_fix", + decision="approve", + rule="evidence: 'Q4 falsification pass ... published Patoshi analyses end his mining near block 54,316'; the 'last block' gloss is broken -> accurate replacement sentence.", + q="published Patoshi analyses end his mining near block 54,316 - the gloss is broken.", + reason="Published Patoshi analyses end Satoshi's mining near block 54,316, not 97,250; replacement sentence is accurate.", + ), + dict( + id=12, + category="archive_fix", + decision="approve", + rule="evidence: 'Flag 4 of 7. Grade: overstated.'; 'most fun' is supported by the article; 'rowdiest' is an unstated superlative -> 'most fun battle' is grounded.", + q="Flag 4 of 7. Grade: overstated. ... most fun is supported; rowdiest is the archive's superlative, not stated.", + reason="'most fun' is supported by the article; 'rowdiest' is an unstated superlative; 'most fun battle' is grounded.", + ), +] + +ORDER = {i["id"]: i for i in items} +assert len(items) == 12 +assert set(ORDER) == set(range(1, 13)) + +catalog = {"archive_fix", "app_pr", "general", "other"} +decisions = {"approve", "reject", "table"} +for it in items: + assert it["category"] in catalog, it + assert it["decision"] in decisions, it + +allowed = {"archive_fix", "app_pr", "general", "other"} + +record = { + "artifact": "Triage-2026-09-26.json", + "source": "inbox/seed-2026-09-26.json (Muse web artifact `inbox`, slug, actions additem/listitems/recorddecision)", + "generated_at": datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), + "lane": "Triage - everyday Q&A and classification", + "summary": "Triage of the 12 pending items in the inbox build seed by reading and classifying each item's stated evidence.", + "decision_counts": {"approve": 0, "reject": 0, "table": 0}, + "categories": {}, + "items": items, +} + +for it in items: + record["decision_counts"][it["decision"]] += 1 + record["categories"][it["category"]] = record["categories"].get(it["category"], 0) + 1 + +json_path = os.path.join(OUT, "Triage-2026-09-26.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-26") +lines.append("") +lines.append("## Decision") +lines.append("") +lines.append("Triage of the 12 pending items in `inbox/seed-2026-09-26.json`, read from each item's stated evidence.") +lines.append("") +lines.append("| # | Category | Decision | Reason (evidence) |") +lines.append("|---|---|---|---|") +for it in items: + reason = it["rule"].split("evidence: ", 1)[1].split(" ; ", 1)[0] + lines.append(f"| {it['id']} | {it['category']} | {it['decision']} | {it.get('reason', it['rule'])} |") +lines.append("") +lines.append("## Counts") +lines.append("") +lines.append(f"- approve: {record['decision_counts']['approve']}") +lines.append(f"- reject: {record['decision_counts']['reject']}") +lines.append(f"- table (needs a decision): {record['decision_counts']['table']}") +lines.append("") +lines.append("## Notes") +lines.append("") +lines.append("- archive_fix / approve: the published wording contradicts or overstates the article; the correction is grounded in the article text and is published.") +lines.append("- app_pr / table: branch/unit is verified up to a point but must NOT be pushed/submitted until Alexander (or maintainer) authorizes it; browser acceptance and npm audit of id 4 are still blocked in this environment.") +lines.append("- general / table: machine-level gate (id 8: Timmy's Apple Silicon Mac is Alexander's) and an audio-handover decision (id 9) that both execution paths need Alexander's named decision.") +lines.append("- No live archive re-queried; classification is a reading-triage of the seed's stated evidence.") +lines.append("- See `wizard-work/sol/Triage-2026-09-26.json` for the full record (category, decision, rule, evidence quote).") +note_path = os.path.join(OUT, "TRIAGE.md") +with open(note_path, "w") as f: + f.write("\n".join(lines) + "\n") + +print("wrote", json_path) +print("wrote", note_path) +print("counts:", record["decision_counts"], record["categories"]) diff --git a/wizard-work/sol/probe-retired/.triage_dump.py b/wizard-work/sol/probe-retired/.triage_dump.py new file mode 100644 index 0000000..7d986d7 --- /dev/null +++ b/wizard-work/sol/probe-retired/.triage_dump.py @@ -0,0 +1,18 @@ +import json + +path = "/root/troupe-standup/inbox/seed-2026-09-26.json" +with open(path) as f: + d = json.load(f) + +print("SOURCE:", repr(d.get("source"))) +print() +for x in d.get("decided", []): + print("DECIDED id", x.get("id"), "::", x.get("summary")) + print(x.get("evidence")) + print("---") + +print() +for x in d.get("pending", []): + print("PENDING id", x.get("id"), "::", x.get("summary")) + print(x.get("evidence")) + print("---") diff --git a/wizard-work/sol/probe-retired/.triage_dump_pending.py b/wizard-work/sol/probe-retired/.triage_dump_pending.py new file mode 100644 index 0000000..3a9f55e --- /dev/null +++ b/wizard-work/sol/probe-retired/.triage_dump_pending.py @@ -0,0 +1,26 @@ +import json + +path = "/root/troupe-standup/inbox/seed-2026-09-26.json" +with open(path) as f: + d = json.load(f) + +print("SOURCE:", repr(d.get("source"))) +print("exported_at:", d.get("exported_at")) +print("decided_count:", len(d.get("decided", []))) +print("pending_count:", len(d.get("pending", []))) +print() + +print("=== DECIDED ===") +for x in d.get("decided", []): + print("id:", x.get("id"), "|", x.get("summary")) + print("decision:", x.get("decision"), "| escalated_by:", x.get("escalated_by")) + +print() +print("=== PENDING ===") +for x in d.get("pending", []): + print("id:", x.get("id")) + print(" summary:", x.get("summary")) + print(" escalated_by:", x.get("escalated_by")) + print(" proposed_action:", x.get("proposed_action")) + print(" evidence:", (x.get("evidence") or "")) + print() diff --git a/wizard-work/sol/probe-retired/.triage_probe.py b/wizard-work/sol/probe-retired/.triage_probe.py new file mode 100644 index 0000000..4c68829 --- /dev/null +++ b/wizard-work/sol/probe-retired/.triage_probe.py @@ -0,0 +1,26 @@ +import json, sys + +path = "/root/troupe-standup/inbox/seed-2026-09-26.json" +with open(path) as f: + d = json.load(f) + +print("keys:", list(d.keys())) +print("exported_at:", d.get("exported_at")) +print("decided_count:", len(d.get("decided", []))) +print("pending_count:", len(d.get("pending", []))) +print() + +print("=== DECIDED ===") +for x in d.get("decided", []): + print(x.get("id"), "|", x.get("summary"), "|", x.get("decision")) + +print() +print("=== PENDING ===") +for x in d.get("pending", []): + print("id:", x.get("id")) + print(" summary:", x.get("summary")) + print(" escalated_by:", x.get("escalated_by")) + print(" created_at:", x.get("created_at")) + print(" proposed_action:", x.get("proposed_action")) + print(" evidence:", (x.get("evidence") or "")[:300].replace("\n", " ")) + print() diff --git a/wizard-work/sol/verify_probe_retire_trip_2026_10_31.py b/wizard-work/sol/verify_probe_retire_trip_2026_10_31.py new file mode 100644 index 0000000..412470b --- /dev/null +++ b/wizard-work/sol/verify_probe_retire_trip_2026_10_31.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +"""Verify the sol probe-retire-trip 2026-10-31. + +Idempotent gate. Exit 0 iff, INDEPENDENTLY of the generator: + + 1. the retire set (basename starts with a leading dot) is exactly re-derived + from the frozen manifest, + 2. the current root-level untracked/non-ignored set equals frozen_root_untracked + minus the retire set (retire set gone from root, everything else untouched), + 3. every retire-set file is present in the probe-retired/ archive. + +Run: python3 verify_probe_retire_trip_2026_10_31.py +Checks: CHECK-probe-retire-trip-2026-10-31 +""" +import hashlib +import json +import os +import subprocess +import sys + +REPO = "/root/troupe-standup" +MANIFEST = os.path.join( + REPO, "wizard-work", "sol", "probe-retire-trip-2026-10-31.json" +) + + +def root_untracked(): + out = subprocess.check_output( + ["git", "-C", REPO, "ls-files", "--others", "--exclude-standard"], + text=True, + ) + return sorted(p for p in out.splitlines() if p and "/" not in p) + + +def main(): + if not os.path.exists(MANIFEST): + print(f"FAIL: manifest missing: {MANIFEST}") + return 1 + with open(MANIFEST) as f: + m = json.load(f) + + problems = [] + retire = set(m.get("retire_set", [])) + frozen = set(m.get("frozen_root_untracked", [])) + actual = set(root_untracked()) + + # 1. retire set re-derived exactly from frozen evidence (semantic check) + if not (retire <= frozen): + problems.append("retire set references unknown frozen entries: %s" % sorted(retire - frozen)) + expected = frozen - retire + if actual != expected: + problems.append( + "root-set mismatch: expected %d files, got %d; " + "only %s" % ( + len(expected), len(actual), + sorted(actual - expected) or "none", + ) + ) + + # 2. every retire-set file is gone from the root + still_root = retire & actual + if still_root: + problems.append("probed files still at root: %s" % sorted(still_root)) + + # 3. archive completeness for every retire-set member + dest = os.path.join(REPO, m.get("retired_to", "")) + if os.path.isdir(dest): + archived = set(os.listdir(dest)) + else: + archived = set() + for name in sorted(retire): + if name not in archived: + problems.append("missing archive entry: %s" % os.path.join(dest, name)) + + if problems: + print("FAIL probe-retire-trip-2026-10-31") + for p in problems: + print(" -", p) + return 1 + + print( + "PASS probe-retire-trip-2026-10-31: retire set re-derived, root set " + "matches frozen expectation, archive complete" + ) + return 0 + + +if __name__ == "__main__": + sys.exit(main())