sol: retire probe-retire-trip 2026-10-31 (6 expired root probes archived)

This commit is contained in:
sol committed 2026-10-31 08:40:52 +00:00
1 parent da29ec175d
commit 40b98f179e
11 files changed
+747

No files matched your search

@@ -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
@@ -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()
@@ -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"
}
@@ -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
```
@@ -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()
@@ -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()
@@ -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"])
@@ -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("---")
@@ -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()
@@ -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()
@@ -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())