JSON output schema
Both the rubric and synthesis prompts instruct the model to return JSON
matching the shapes below. The parser is tolerant of fenced JSON
(```json ... ```) and surrounding whitespace; prose-wrapped JSON
is not repaired and falls through as unstructured.
Review object (per reviewer)
{ "summary": "string, 1-3 sentences", "findings": [ { "id": "string, stable slug within this review", "severity": "blocker | major | minor | nit", "category": "correctness | design | security | performance | clarity | scope | other", "location": "string, optional", "title": "string, one line", "body": "string, markdown allowed", "suggestion": "string, optional" } ], "open_questions": ["string"], "verdict": "ship | iterate | reject"}Rules:
summary,findings,verdictare required.open_questionsmay be an empty array but must be present.findingsmay be empty. Reviewers should not invent findings to fill space.severityis one of the four enums; no custom values.idmust be unique within a single review.
Synthesis object
{ "summary": "string, 2-4 sentences", "findings": [ { "title": "string", "body": "string, markdown allowed", "severity": "blocker | major | minor | nit", "tag": "consensus | contested | singleton", "sources": ["reviewer_id", "..."], "disagreement": "string, required when tag = contested" } ], "verdict": "ship | iterate | reject", "verdict_rationale": "string"}Tagging rules are enforced by the synthesis prompt, not by code:
consensus— raised by 2+ reviewers with substantively the same point.contested— raised by one and disputed by another;disagreementmust summarize the split.singleton— raised by exactly one reviewer, not contradicted; kept.
Parser tolerance
Order of attempts when reading reviewer stdout:
- Strip leading/trailing whitespace and
```json/```fences. json_decodestrict.- On failure, the output is stored as unstructured (raw text) and still shown to the human at checkpoint 2.
The parser does NOT attempt to repair JSON, ask a model to fix it, or heuristically extract fields. Fix the prompt instead.