ouroboros/scripts/validate_scope_receipt.py
Anton Razzhigaev f627fa835d Contributor review: agentic checklist path, honest script-lane budget shape (#395)
- CONTRIBUTING makes the agentic checklist review the main path: the
  separate review agent covers all eight Intent/Scope checklist items from
  docs/CHECKLISTS.md under that checklist's own output contract, and the
  JSON receipt lands in the PR's review-evidence block (validated locally by
  scripts/validate_scope_receipt.py — a thin CLI over the runtime
  normalize_scope_items contract, no CI workflow changes).
- The project-native review command is honestly labeled maintainer /
  large-window tooling: the scope pack is required regardless of diff size
  and can exceed a default install's scope window (SCOPE_REVIEW_BLOCKED);
  the documented larger-window routes are an agent_session scope row or an
  API slot whose confirmed window fits the pack.
- Reading requirements proportioned: docs/CHECKLISTS.md stays
  read-in-full; the other four documents are mapped by headings with every
  relevant section read in full.
- PR template: scope-checklist coverage table + reviewer JSON block inside
  the single review-evidence section; governance wording matches.
- tests/test_contributor_flow.py pins the new copy and exercises the
  validator against the real contract (coverage, duplicates, broken JSON).
2026-08-31 15:51:01 +00:00

73 lines
2.6 KiB
Python

#!/usr/bin/env python3
"""Validate a contributor scope-checklist receipt against the runtime contract.
CONTRIBUTING.md's main review path asks the separate review agent to output a
JSON array covering all eight Intent/Scope checklist items. This validator is
a thin CLI over the SAME contract the runtime scope review enforces
(`ouroboros.tools.scope_review_contract.normalize_scope_items`) — coverage,
duplicate/contradictory rows, severities, and justified PASS reasons — so a
receipt that passes here matches what the project's own reviewers must
produce. It validates SHAPE, not truth: the maintainer still reads the
evidence.
The array is accepted bare, inside a ```json fence, or embedded in the
reviewer's surrounding prose — extraction reuses the runtime's own
`extract_json_array`, so what the project's scope parser would read is what
gets validated.
Usage:
python scripts/validate_scope_receipt.py path/to/receipt.json
... | python scripts/validate_scope_receipt.py -
Exit code 0 when the receipt is valid; 1 with the contract errors otherwise.
"""
from __future__ import annotations
import json
import pathlib
import sys
def main(argv: list[str]) -> int:
if len(argv) == 2 and argv[1] in {"-h", "--help"}:
print(__doc__.strip())
return 0
if len(argv) != 2:
print(__doc__.strip(), file=sys.stderr)
return 1
try:
raw = (sys.stdin.read() if argv[1] == "-"
else pathlib.Path(argv[1]).read_text(encoding="utf-8"))
except OSError as exc:
print(f"invalid: cannot read receipt ({exc})", file=sys.stderr)
return 1
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1]))
from ouroboros.tools.scope_review_contract import normalize_scope_items
from ouroboros.triad_review import extract_json_array
try:
items = json.loads(raw)
except json.JSONDecodeError:
items = extract_json_array(raw)
if items is None:
print("invalid: no JSON array found in the receipt "
"(bare, fenced, or embedded)", file=sys.stderr)
return 1
normalized, error = normalize_scope_items(items)
if error:
print(f"invalid: {error}", file=sys.stderr)
return 1
fails = [item for item in normalized if item["verdict"] == "FAIL"]
print(
f"valid: {len(normalized)} rows cover all checklist items; "
f"{len(fails)} FAIL row(s)"
+ (" — " + ", ".join(sorted({f['item'] for f in fails})) if fails else "")
)
return 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv))