You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
veche: a seat reply with a brace in the surrounding prose, or with scores as a second JSON object, is dropped as unparseable #2137
parse() slices a seat reply from the first { to the last } and calls json.loads once (.claude/skills/idea-critic/scripts/veche.py:100; the evil twin uses the same slice at :331). A reply is therefore rejected when the prose around the answer contains any brace, or when it holds two top-level JSON objects. Real claude -p replies do both: a preamble that quotes {...}, a trailing note, or the answer object followed by a second object that carries scores. A rejected seat loses its vote (:260-262), a rejected critique reply is skipped without a trace (:291-293), and a rejected twin leaves only a dropped note (:336-338). The reply contract (CONTRACT, :34-44) does not mention scores, which --option requests separately in the body (:195), so a seat that follows the contract literally emits the answer first and the scores after it. In a scan of about 690 raw replies from unattended runs, 10 (about 1.5 percent) failed with Extra data; 7 of those 10 were an answer plus a second scores object.
What you expected
Any reply that contains one valid answer object is accepted, wherever the surrounding prose puts braces. A later scores object is merged (or the contract is changed so that one object holds everything), and references/council.md states the reply contract a custom --cmd must meet.
How to reproduce
importimportlib.utilspec=importlib.util.spec_from_file_location(
"veche", "/path/to/perun/.claude/skills/idea-critic/scripts/veche.py")
veche=importlib.util.module_from_spec(spec)
spec.loader.exec_module(veche)
obj='{"verdict": "REVISE", "claims": []}'cases= {
"brace in prose before the object": "Here is the {final} answer:\n"+obj,
"brace in prose after the object": obj+"\nNote: use {curly} braces.",
"scores as a second object": obj+'\n{"scores": {"do-nothing": {"impact": 1}}}',
"plain prose around the object": "Sure.\n"+obj+"\nDone.",
}
forname, textincases.items():
try:
print(name, "->", veche.parse(text, "claims")["verdict"])
exceptExceptionase:
print(name, "->", type(e).__name__, e)
Observed: the first three raise JSONDecodeError (Expecting property name enclosed in double quotes for the first, Extra data for the other two). The last parses.
Proposed fix (smallest)
One helper shared by parse() and the twin: scan with json.JSONDecoder().raw_decode from each {, keep the first object that is a dict with a valid verdict (for the twin: a string dissent), and, when --option is in force and that object has no scores, take scores from a later object. Add scores to CONTRACT when --option is used, and document the seat contract (one JSON object, no tool-call markup) in references/council.md next to the paragraph that says any script reading the prompt on stdin can be a seat (:81-82).
Environment
deep-code-review version (from .claude/skills/deep-code-review/VERSION): 1.620.0
Fixed across #2145 (release 1.622.0) and #2147 (release 1.623.0).
Seat, critique and evil-twin replies are now scanned with json.JSONDecoder().raw_decode from each {, and the first object with a valid verdict (for the twin, a non-empty dissent) is used. Braces in the surrounding prose no longer drop a seat, and with --option the scores map may sit in that object or in any other object of the reply. references/council.md states the reply contract a custom --cmd must meet, as the parser accepts it. Covered by scripts/test_veche_surface.py.
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).
Seat, critique and evil-twin replies are now scanned with
json.JSONDecoder().raw_decodefrom each{, and the first object with a validverdict(for the twin, a non-emptydissent) is used. Braces in the surrounding prose no longer drop a seat, and with--optionthescoresmap may sit in that object or in any other object of the reply.references/council.mdstates the reply contract a custom--cmdmust meet, as the parser accepts it. Covered byscripts/test_veche_surface.py.