Skip to content

veche: make the decision record explain itself (verdict vs tally, duplicated dissent, near-duplicate kill criteria) #2140

Closed

Description

@remigiusz-antczak

Problem

Three things in a decided record read as inconsistent to a human reader and to a downstream agent.

  1. verdict can differ from tally. 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-request plus HOLD becomes REVISE; a do-nothing decision turns PASS_TO_USER into REVISE), but nothing in the record says a rewrite happened. The tally also counts votes after the critique round (:304), not the round-1 votes.
  2. dissent is the full raw reply of every seat whose final vote differs from verdict (:322); in the example that is all three seats, because the rewritten verdict matches no vote. minority_report.dissenting_seats repeats the same list (:345), so the full replies appear twice, and neither copy says which lens wrote which reply.
  3. kill_criteria is 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 in references/council.md that dissenting_seats duplicates dissent.
  • kill_criteria_by_lens ({lens: text}), and compare kill criteria on norm() (:65) lower-cased with trailing punctuation stripped, so the unchanged kill_criteria list 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.

Activity

  1. remigiusz-antczak commented on Oct 30, 2026

    @remigiusz-antczak
    CollaboratorAuthor

    Fixed across #2145 (release 1.622.0) and #2147 (release 1.623.0).

    1. The record carries verdict_basis, which explains the verdict: the majority, the tie rule, or the owner-request and do-nothing rewrites.
    2. minority_report.dissenting_seats now lists lens names only, so each raw reply appears once, in dissent.
    3. Kill criteria are deduplicated on a normalized key that keeps comparison operators, and kill_criteria_by_lens keeps each seat's own wording.

    Covered by scripts/test_veche_record.py and scripts/test_veche_surface.py.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:decisionsDecision quality and roadmapenhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions