Repository navigation
veche: make the decision record explain itself (verdict vs tally, duplicated dissent, near-duplicate kill criteria) #2140
Copy link
Copy link
Closed
Labels
area:decisionsDecision quality and roadmapDecision quality and roadmapenhancementNew feature or requestNew feature or request
Description
Problem
Three things in a decided record read as inconsistent to a human reader and to a downstream agent.
verdictcan differ fromtally. With seat votes HOLD, HOLD and PASS_TO_USER and--origin owner-request, the record has"verdict": "REVISE"next to"tally": {"HOLD": 2, "REVISE": 0, "PASS_TO_USER": 1}. The rewrite is documented in prose (references/council.md:51-52) and implemented at.claude/skills/idea-critic/scripts/veche.py:316-319(owner-requestplusHOLDbecomesREVISE; ado-nothingdecision turnsPASS_TO_USERintoREVISE), but nothing in the record says a rewrite happened. The tally also counts votes after the critique round (:304), not the round-1 votes.dissentis the full raw reply of every seat whose final vote differs fromverdict(:322); in the example that is all three seats, because the rewritten verdict matches no vote.minority_report.dissenting_seatsrepeats the same list (:345), so the full replies appear twice, and neither copy says which lens wrote which reply.kill_criteriais de-duplicated by exact string (:341,dict.fromkeys). Seats that state the same tripwire with different case, punctuation or wording all survive, so the list reads as three criteria.
Reproduce (stub seats, no model): save as seat.py:
import json, sys
vote, kill = sys.argv[1], sys.argv[2]
prompt = sys.stdin.read()
if "STAGE: CRITIQUE" in prompt:
print(json.dumps({"verdict": vote, "attacks": []}))
else:
print(json.dumps({"verdict": vote, "claims": [], "would_change_it": "x", "premortem": "x",
"kill_criterion": kill, "leading_metric": "x"}))python3 /path/to/perun/.claude/skills/idea-critic/scripts/veche.py --origin owner-request --claim c --steelman s \
--cmd 'python3 seat.py HOLD "error rate above 2% by 2030-01-01"' \
--cmd 'python3 seat.py HOLD "Error rate above 2% by 2030-01-01."' \
--cmd 'python3 seat.py PASS_TO_USER "error rate above 2 percent by 2030-01-01"' \
| python3 -c 'import json, sys; r = json.load(sys.stdin); print(r["verdict"], r["tally"], len(r["dissent"]), r["kill_criteria"])'Observed: REVISE {'HOLD': 2, 'REVISE': 0, 'PASS_TO_USER': 1} 3 followed by the three kill-criterion strings.
Proposal
Additive fields only (validate_verdict.py requires dissent, so it stays):
verdict_basis: a short list filled where the verdict is chosen or rewritten (:312-319), for example["votes HOLD 2, REVISE 0, PASS_TO_USER 1 -> HOLD", "origin owner-request: HOLD rewritten to REVISE"].minority_report.dissenting_lenses: lens names, so a reader can see who dissented without a second copy of the raw replies. Note inreferences/council.mdthatdissenting_seatsduplicatesdissent.kill_criteria_by_lens({lens: text}), and compare kill criteria onnorm()(:65) lower-cased with trailing punctuation stripped, so the unchangedkill_criterialist drops the trivial duplicates.
Why not a different pack
The record schema and validate_verdict.py are Perun's own, and the new fields are additive, so existing consumers keep working.
Confidentiality
No secrets, real names, or internal IDs.
Metadata
Metadata
Assignees
Labels
area:decisionsDecision quality and roadmapDecision quality and roadmapenhancementNew feature or requestNew feature or request
Activity
Wave 5: provisioned scanners with honest reporting, spend and pipelin…
Wave 6: decision-council surface fixes, standing every-test-wired che…
remigiusz-antczak commentedon Oct 30, 2026
Fixed across #2145 (release 1.622.0) and #2147 (release 1.623.0).
verdict_basis, which explains the verdict: the majority, the tie rule, or the owner-request and do-nothing rewrites.minority_report.dissenting_seatsnow lists lens names only, so each raw reply appears once, indissent.kill_criteria_by_lenskeeps each seat's own wording.Covered by
scripts/test_veche_record.pyandscripts/test_veche_surface.py.