← Files LegalQuants TransactionalARCHIVED FILE

skills/read-redline/scripts/calibration_gate.py

5.71 KB · Oct 3, 2026 · 06:34 UTC

↓ Download file

#!/usr/bin/env python3
"""calibration_gate.py — record and enforce the calibration gate.

The calibration gate is an artifact, not a memory: once the colour table (PDF)
or the tracked-change report (Word) is confirmed, this script writes
``calibration.confirmed.json`` beside ``extract.json`` carrying the
calibration block, a status, and the SHA-256 of the extract it confirms.
``make_issues_list.py`` and both annotators refuse to build without a valid
artifact, so no deliverable can drift away from the calibration the reviewer
signed off.

    # after a human confirmed the calibration table:
    python3 scripts/calibration_gate.py extract.json --status confirmed

    # scripted, non-interactive runs only, with an explicit reason:
    python3 scripts/calibration_gate.py extract.json --status declared-default \\
        --reason "batch run over a folder; no reviewer present"

Re-run the same command after any edit to extract.json (for example appending
manual-review items under ``quarantined``): the artifact hashes the extract,
so a changed extract invalidates the old gate on purpose.

Runs on the standard library only.
"""

from __future__ import annotations

import argparse
import hashlib
import json
import sys
from pathlib import Path

GATE_NAME = "calibration.confirmed.json"
STATUSES = ("confirmed", "declared-default")


def extract_sha256(extract_path: Path) -> str:
    return hashlib.sha256(Path(extract_path).read_bytes()).hexdigest()


def gate_path_for(extract_path: Path) -> Path:
    return Path(extract_path).resolve().parent / GATE_NAME


def gate_state_line(gate: dict) -> str:
    """One plain sentence for receipts, identical across every downstream
    artifact."""
    if gate.get("status") == "confirmed":
        return "Calibration gate: confirmed."
    return f"Calibration gate: declared default — {gate.get('reason', '')}"


def write_gate(extract_path: Path, status: str, reason: str | None = None) -> Path:
    extract_path = Path(extract_path)
    if status not in STATUSES:
        raise SystemExit(
            f"--status must be one of {', '.join(STATUSES)}; got {status!r}."
        )
    if status == "declared-default" and not (reason or "").strip():
        raise SystemExit(
            "declared-default requires --reason: say why no human confirmed "
            "the calibration."
        )
    try:
        extract = json.loads(extract_path.read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError) as exc:
        raise SystemExit(f"cannot read {extract_path}: {exc}") from exc
    calibration = extract.get("calibration")
    if not isinstance(calibration, dict):
        raise SystemExit(
            f"{extract_path} has no 'calibration' block; re-run the parser "
            "before recording the gate."
        )
    gate = {
        "artifact": "calibration.confirmed",
        "extract_sha256": extract_sha256(extract_path),
        "status": status,
        "calibration": calibration,
    }
    if (reason or "").strip():
        gate["reason"] = (reason or "").strip()
    out = gate_path_for(extract_path)
    out.write_text(json.dumps(gate, indent=2) + "\n", encoding="utf-8")
    print(f"wrote {out} ({gate_state_line(gate)})")
    return out


def require_gate(extract_path: Path) -> dict:
    """Return the gate for ``extract_path`` or exit nonzero with a plain
    message. Downstream scripts call this before building anything."""
    extract_path = Path(extract_path)
    path = gate_path_for(extract_path)
    if not path.exists():
        raise SystemExit(
            f"refusing to build: {GATE_NAME} not found beside {extract_path}. "
            "Record the calibration gate first "
            "(python3 scripts/calibration_gate.py extract.json --status confirmed, "
            "or --status declared-default --reason ... in a scripted run)."
        )
    try:
        gate = json.loads(path.read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError) as exc:
        raise SystemExit(f"refusing to build: cannot read {path}: {exc}") from exc
    if not isinstance(gate, dict):
        raise SystemExit(f"refusing to build: {path} must contain a JSON object.")
    status = gate.get("status")
    if status not in STATUSES:
        raise SystemExit(
            f"refusing to build: {path} carries status {status!r}; "
            f"expected one of {', '.join(STATUSES)}."
        )
    if status == "declared-default" and not (gate.get("reason") or "").strip():
        raise SystemExit(
            f"refusing to build: {path} is declared-default with no reason."
        )
    if not isinstance(gate.get("calibration"), dict):
        raise SystemExit(f"refusing to build: {path} has no 'calibration' block.")
    recorded = gate.get("extract_sha256")
    current = extract_sha256(extract_path)
    if recorded != current:
        raise SystemExit(
            f"refusing to build: {path} confirms extract hash "
            f"{str(recorded)[:12]}… but the current extract hashes "
            f"{current[:12]}…. extract.json changed after the gate was "
            "recorded; re-run scripts/calibration_gate.py with the same "
            "status to re-stamp it."
        )
    return gate


def main(argv: list[str]) -> int:
    ap = argparse.ArgumentParser(
        prog="calibration_gate.py",
        description=__doc__,
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )
    ap.add_argument("extract", help="extract.json from either redline parser")
    ap.add_argument("--status", required=True, choices=STATUSES)
    ap.add_argument(
        "--reason",
        help="why no human confirmed the calibration (required for declared-default)",
    )
    a = ap.parse_args(argv[1:])
    write_gate(Path(a.extract), a.status, a.reason)
    return 0


if __name__ == "__main__":
    sys.exit(main(sys.argv))

SHA-256: 169cdbedee3cba7c845771dcb96e36e25efecce32f3707912a60dd412e05f8ca