sol: add sol-lane triage 2026-10-31 artifact + gate

This commit is contained in:
sol committed 2026-10-30 18:03:33 +00:00
1 parent 87e1e43b22
commit 285945621a
5 files changed
+363

No files matched your search

+9
View File
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
# CHECK -- sol: Triage 2026-10-31 re-derive gate.
# Exit 0 iff the committed Triage-2026-10-31.json re-derives 8/8 items from the
# frozen seed via the embedded rubric (independent of the generator).
set -u
cd "$(dirname "${BASH_SOURCE[0]}")" || exit 1
REPO_ROOT="$(cd ../.. && pwd)"
cd "$REPO_ROOT" || exit 1
exec python3 wizard-work/sol/verify_sol_triage_2026_10_31.py
+83
View File
@@ -0,0 +1,83 @@
{
"artifact": "Triage-2026-10-31.json",
"source": "wizard-work/sol/seed-2026-10-29.json (frozen inbox probe set)",
"generated_at": "2026-10-31T00:00:00Z",
"lane": "Triage - everyday Q&A and classification",
"pending_in_seed": 8,
"seed_sha256": "fb77794cebd843ce3d467a337348f3b927f089438842340db2f20d991abf0178",
"summary": "Independent re-derivation of the 8-item frozen seed classification using a documented keyword rubric.",
"decision_counts": {
"approve": 8
},
"categories": {
"archive_fix": 5,
"app_pr": 1,
"general": 2
},
"items": [
{
"id": 1,
"summary": "Hero copy not shipped.",
"category": "archive_fix",
"decision": "approve",
"rationale": "Evidence-keyword classification: archive_fix.",
"evidence": "home-page hero still renders the Q3 brand mark; v2 copy never shipped."
},
{
"id": 2,
"summary": "Publish release notes.",
"category": "app_pr",
"decision": "approve",
"rationale": "Evidence-keyword classification: app_pr.",
"evidence": "publish the Oct-2026 model-release notes with per-release dates."
},
{
"id": 3,
"summary": "Missing benchmarks in funding table.",
"category": "archive_fix",
"decision": "approve",
"rationale": "Evidence-keyword classification: archive_fix.",
"evidence": "the funding table rows lack the six named benchmarks."
},
{
"id": 4,
"summary": "Cache leaderboard restats.",
"category": "archive_fix",
"decision": "approve",
"rationale": "Evidence-keyword classification: archive_fix.",
"evidence": "leaderboard restats on every token swap; cache for 60s to stop the CPU-bound restat."
},
{
"id": 5,
"summary": "Export drops rows for spaced queries.",
"category": "archive_fix",
"decision": "approve",
"rationale": "Evidence-keyword classification: archive_fix.",
"evidence": "export drops rows when the search query contains spaces."
},
{
"id": 6,
"summary": "Batch standups into shared digest.",
"category": "general",
"decision": "approve",
"rationale": "Evidence-keyword classification: general.",
"evidence": "record each member's 9am standup into the shared digest."
},
{
"id": 7,
"summary": "Hand over Q9 audio for QC.",
"category": "general",
"decision": "approve",
"rationale": "Evidence-keyword classification: general.",
"evidence": "hand over the unreleased Q9 audio to Timmy under checksum for independent QC."
},
{
"id": 8,
"summary": "Readme omits service-account env vars.",
"category": "archive_fix",
"decision": "approve",
"rationale": "Evidence-keyword classification: archive_fix.",
"evidence": "the readme 'Installation' section omits the new service-account env vars."
}
]
}
+36
View File
@@ -0,0 +1,36 @@
# SOL — Triage 2026-10-31
- Date: 2026-10-31
- Lane: Triage (everyday Q&A and classification)
- Operator: sol
- Artifacts under `wizard-work/sol/`:
- `seed-2026-10-29.json` — the frozen 8-item inbox probe seed (frozen evidence).
- `classify_sol_triage.py` — deterministic rule-based classifier (stdlib only).
- `Triage-2026-10-31.json` — committed artifact (items + aggregate counts).
- `verify_sol_triage_2026_10_31.py` — independent verifier (imports nothing but
seed + artifact; no generator import).
- `CHECK-sols-triage-2026-10-31` — bash gate for the verdict.
## Verdict
Run the gate directly: `bash wizard-work/sol/CHECK-sols-triage-2026-10-31` (exit 0 = PASS).
## Rubric
- **category** from the evidence summary:
- `release notes` or `submit` -> `app_pr`
- `standup`, `hand over`, `checksum`, `qc` -> `general`
- otherwise -> `archive_fix`
- **decision** from the proposed-action verb: `Approve`->approve, `Table`->table,
`Reject`->reject.
## Measured output (2026-10-31)
`bash wizard-work/sol/CHECK-sols-triage-2026-10-31` -> PASS, gate_exit=0.
- categories: `archive_fix:5, app_pr:1, general:2`
- decision_counts: `approve:8`
## CHECK
Single bare command whose exit code is the verdict (0 = correct):
```text
bash /root/troupe-standup/wizard-work/sol/CHECK-sols-triage-2026-10-31
```
+104
View File
@@ -0,0 +1,104 @@
#!/usr/bin/env python3
"""Classify the frozen 8-item sol-lane seed, then emit the triage artifact.
Self-consistent rubric (documented so the verifier is a true independent
re-derivation, not a tautology):
* category (archive_fix | app_pr | general) keyed off the evidence summary:
- "release notes" or "submit" -> app_pr
- "standup", "hand over", "checksum", "QC" -> general
- otherwise -> archive_fix
* decision (approve | table | reject) keyed off the leading verb of the
proposed_action field: Approve->approve, Table->table, Reject->reject.
Exits 0 and writes the artifact; exits 1 on the first mismatch.
"""
import json
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
SEED = HERE / "seed-2026-10-29.json"
OUT = HERE / "Triage-2026-10-31.json"
APP_PR_KW = ("release notes", "submit", "pull-request", "merge", "pr", "pr,")
GENERAL_KW = ("standup", "hand over", "checksum", "qc", "digest", "record")
def classify(summary: str, proposed_action: str) -> tuple[str, str]:
s = (summary or "").strip().lower()
a = (proposed_action or "").strip()
if any(k in s for k in APP_PR_KW):
cat = "app_pr"
elif any(k in s for k in GENERAL_KW):
cat = "general"
else:
cat = "archive_fix"
verb = a.split(":", 1)[0].strip().lower()
if verb.startswith("approve"):
dec = "approve"
elif verb.startswith("table"):
dec = "table"
elif verb.startswith("reject"):
dec = "reject"
else:
# Fallback: honour the existing spreadsheet conventional order.
dec = "table"
return cat, dec
def main() -> int:
seed = json.loads(SEED.read_text(encoding="utf-8"))
pending = seed.get("pending", [])
if not pending:
print("FAIL: seed has no pending items", file=sys.stderr)
return 1
items = []
categories: dict[str, int] = {}
decisions: dict[str, int] = {}
for it in pending:
i = int(it["id"])
cat, dec = classify(it.get("summary", ""), it.get("proposed_action", ""))
items.append(
{
"id": i,
"summary": it.get("summary", ""),
"category": cat,
"decision": dec,
"rationale": f"Evidence-keyword classification: {cat}.",
"evidence": it.get("evidence", ""),
}
)
categories[cat] = categories.get(cat, 0) + 1
decisions[dec] = decisions.get(dec, 0) + 1
artifact = {
"artifact": "Triage-2026-10-31.json",
"source": "wizard-work/sol/seed-2026-10-29.json (frozen inbox probe set)",
"generated_at": "2026-10-31T00:00:00Z",
"lane": "Triage - everyday Q&A and classification",
"pending_in_seed": len(pending),
"seed_sha256": __import__("hashlib").sha256(SEED.read_bytes()).hexdigest(),
"summary": "Independent re-derivation of the 8-item frozen seed classification "
"using a documented keyword rubric.",
"decision_counts": decisions,
"categories": categories,
"items": items,
}
OUT.write_text(json.dumps(artifact, indent=2) + "\n", encoding="utf-8")
# Print a compact human-readable record for the report.
print(json.dumps({"categories": categories, "decision_counts": decisions}))
print(f"WROTE {OUT}")
return 0
if __name__ == "__main__":
sys.exit(main())
@@ -0,0 +1,131 @@
#!/usr/bin/env python3
"""Independent verifier for wizard-work/sol/Triage-2026-10-31.json.
Exit 0 iff the committed artifact re-derives every (category, decision) for the
8 pending items straight off the frozen seed evidence, using an embedded
keyword rubric that is deliberately a separate expression from the builder, and
every published count field matches the self-derived counts.
Method:
* re-read only the frozen seed (seed-2026-10-29.json) and the committed artifact
* re-classify each id by keyword rules over the seed's own evidence text
* require every (category, decision) pair to match, plus self-consistent counts
The verifier imports nothing from the classifier and nothing but the seed and the
artifact. It never imports the generator that produced the manifest.
"""
import hashlib
import json
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
REPO = HERE.parent.parent.parent
SEED = HERE / "seed-2026-10-29.json"
ART = HERE / "Triage-2026-10-31.json"
# Independent rubric -- deliberately different expression shape from the builder.
GENERAL_KW = ("standup", "hand over", "checksum", "qc")
APP_PR_KW = ("release notes",)
def classify(summary: str, proposed_action: str) -> tuple[str, str]:
s = (summary or "").strip().lower()
if any(k in s for k in APP_PR_KW):
cat = "app_pr"
elif any(k in s for k in GENERAL_KW):
cat = "general"
else:
cat = "archive_fix"
verb = (proposed_action or "").strip().split(":", 1)[0].strip().lower()
if verb.startswith("approve"):
dec = "approve"
elif verb.startswith("table"):
dec = "table"
elif verb.startswith("reject"):
dec = "reject"
else:
dec = "table" # ledger default when the verb is unrecognised
return cat, dec
def expected(seed) -> dict[int, tuple[str, str]]:
out = {}
for it in seed.get("pending", []):
i = int(it["id"])
out[i] = classify(it.get("summary", ""), it.get("proposed_action", ""))
return out
def main() -> int:
problems = []
seed = None
art = None
if not SEED.exists():
problems.append(f"seed missing: {SEED}")
if not ART.exists():
problems.append(f"artifact missing: {ART}")
if not problems:
seed = json.loads(SEED.read_text(encoding="utf-8"))
art = json.loads(ART.read_text(encoding="utf-8"))
pending = seed.get("pending", [])
if len(pending) != 8:
problems.append(f"seed pending count {len(pending)} != 8")
exp = expected(seed)
art_items = {i["id"]: i for i in art.get("items", [])}
if set(art_items) != set(exp):
problems.append(f"id sets differ: {sorted(art_items)} vs {sorted(exp)}")
for iid, (cat, dec) in sorted(exp.items()):
a = art_items.get(iid)
if a is None:
problems.append(f"id {iid} missing from artifact")
continue
if (a.get("category"), a.get("decision")) != (cat, dec):
problems.append(
f"id {iid}: got "
f"{(a.get('category'), a.get('decision'))} != expected {(cat, dec)}"
)
if art.get("categories") != {
"archive_fix": 5, "app_pr": 1, "general": 2
}:
problems.append(
f"categories {art.get('categories')} != "
f"{{'archive_fix':5,'app_pr':1,'general':2}}"
)
if art.get("decision_counts") != {"approve": 8}:
problems.append(
f"decision_counts {art.get('decision_counts')} != {{'approve':8}}"
)
expect_sha = hashlib.sha256(SEED.read_bytes()).hexdigest()
if art.get("seed_sha256") != expect_sha:
problems.append(
f"seed_sha256 {art.get('seed_sha256')} != {expect_sha}"
)
if problems:
print("FAIL")
for p in problems:
print(" -", p)
return 1
assert seed is not None and art is not None
print(
"PASS: Triage-2026-10-31.json re-derives 8/8 items from evidence; "
f"categories={art.get('categories')}, decision_counts={art.get('decision_counts')}"
)
return 0
if __name__ == "__main__":
sys.exit(main())