#!/usr/bin/env python3 import argparse import hashlib import json from datetime import date, timedelta from pathlib import Path from typing import Any ROOT = Path(__file__).resolve().parent.parent KNOWN_GATE_KEYS = { "intent-canvas", "trigger-lab", "output-lab", "context-budget", "runtime-matrix", "trust-report", "permission-gates", "permission-runtime", "skill-atlas", "operations-loop", "registry-audit", "release-notes", } VALID_DECISIONS = {"accepted-risk", "false-positive", "temporary-exception"} MIN_REASON_CHARS = 20 def display_path(path: Path) -> str: try: return str(path.resolve().relative_to(ROOT.resolve())) except ValueError: return str(path.resolve()) def load_json(path: Path) -> dict[str, Any]: if not path.exists(): return {} try: payload = json.loads(path.read_text(encoding="utf-8")) except json.JSONDecodeError: return {} return payload if isinstance(payload, dict) else {} def today_from(value: str | None) -> date: if not value: return date.today() return date.fromisoformat(value[:10]) def parse_date(value: str) -> date | None: try: return date.fromisoformat(value[:10]) except (TypeError, ValueError): return None def waiver_id(entry: dict[str, Any]) -> str: raw = "|".join( [ str(entry.get("gate_key", "")), str(entry.get("reviewer", "")), str(entry.get("created_at", "")), str(entry.get("reason", "")), ] ) return hashlib.sha1(raw.encode("utf-8")).hexdigest()[:12] def normalize_waiver(entry: dict[str, Any]) -> dict[str, Any]: normalized = { "id": str(entry.get("id") or ""), "gate_key": str(entry.get("gate_key") or ""), "decision": str(entry.get("decision") or "accepted-risk"), "reviewer": str(entry.get("reviewer") or ""), "reason": str(entry.get("reason") or ""), "created_at": str(entry.get("created_at") or ""), "expires_at": str(entry.get("expires_at") or ""), "evidence": str(entry.get("evidence") or ""), "scope": str(entry.get("scope") or "current-release"), } normalized["id"] = normalized["id"] or waiver_id(normalized) return normalized def add_waiver(existing: list[dict[str, Any]], args: argparse.Namespace, today: date) -> list[dict[str, Any]]: created_at = args.created_at or today.isoformat() entry = normalize_waiver( { "gate_key": args.gate_key, "decision": args.decision, "reviewer": args.reviewer, "reason": args.reason, "created_at": created_at, "expires_at": args.expires_at, "evidence": args.evidence or "", "scope": args.scope, } ) return [*existing, entry] def validate_waivers(waivers: list[dict[str, Any]], today: date) -> tuple[list[dict[str, Any]], list[str], list[str]]: normalized = [] failures = [] warnings = [] seen_ids = set() for index, raw in enumerate(waivers, start=1): entry = normalize_waiver(raw) entry_failures = [] entry_warnings = [] if entry["id"] in seen_ids: entry_failures.append("duplicate waiver id") seen_ids.add(entry["id"]) if entry["gate_key"] not in KNOWN_GATE_KEYS: entry_failures.append(f"unknown gate_key: {entry['gate_key'] or ''}") if entry["decision"] not in VALID_DECISIONS: entry_failures.append(f"invalid decision: {entry['decision'] or ''}") if not entry["reviewer"]: entry_failures.append("reviewer is required") if len(entry["reason"]) < MIN_REASON_CHARS: entry_failures.append(f"reason must be at least {MIN_REASON_CHARS} characters") created_at = parse_date(entry["created_at"]) if created_at is None: entry_failures.append("created_at must be ISO date") expires_at = parse_date(entry["expires_at"]) if expires_at is None: entry_failures.append("expires_at must be ISO date") elif expires_at < today: entry_warnings.append("waiver is expired") entry["status"] = "invalid" if entry_failures else ("expired" if entry_warnings else "active") entry["validation"] = {"failures": entry_failures, "warnings": entry_warnings} normalized.append(entry) failures.extend([f"waiver {index} ({entry['id']}): {item}" for item in entry_failures]) warnings.extend([f"waiver {index} ({entry['id']}): {item}" for item in entry_warnings]) return normalized, failures, warnings def output_lab_candidate(skill_dir: Path, covered_gate_keys: set[str], today: date) -> dict[str, Any] | None: output_quality = load_json(skill_dir / "reports" / "output_quality_scorecard.json").get("summary", {}) output_execution = load_json(skill_dir / "reports" / "output_execution_runs.json").get("summary", {}) output_review = load_json(skill_dir / "reports" / "output_review_adjudication.json").get("summary", {}) if not output_quality and not output_execution and not output_review: return None pending = int(output_review.get("pending_count", 0) or 0) model_executed = int(output_execution.get("model_executed_count", 0) or 0) failure_count = int(output_quality.get("failure_count", 0) or 0) if pending == 0 and model_executed > 0 and failure_count == 0: return None status = "covered" if "output-lab" in covered_gate_keys else "needs-reviewer-decision" return { "gate_key": "output-lab", "label": "Output Lab", "status": status, "waiver_allowed": True, "decision_options": sorted(VALID_DECISIONS), "risk_summary": ( f"review pending {pending}; model-executed {model_executed}; " f"output failures {failure_count}" ), "required_review": [ "Reviewer confirms this release does not claim provider-backed or human-adjudicated output superiority.", "Reviewer names the release scope and expiry date.", "Reviewer links output_review_adjudication or output_execution evidence.", ], "suggested_evidence": "reports/output_review_adjudication.md", "suggested_command": ( "python3 scripts/yao.py review-waivers . --add-waiver " "--gate-key output-lab --reviewer \"\" " "--reason \"Output Lab has pending human/provider evidence; accepted only for this bounded review scope.\" " f"--expires-at {(today + timedelta(days=365)).isoformat()} " "--evidence reports/output_review_adjudication.md" ), "world_class_boundary": "Does not count as provider, human, or public world-class completion evidence.", } def world_class_boundary(skill_dir: Path) -> dict[str, Any] | None: ledger_summary = load_json(skill_dir / "reports" / "world_class_evidence_ledger.json").get("summary", {}) if not ledger_summary: return None pending = int(ledger_summary.get("pending_count", 0) or 0) if pending == 0 and ledger_summary.get("ready_to_claim_world_class") is True: return None return { "gate_key": "world-class-evidence", "label": "World-Class Evidence", "status": "cannot-waive", "waiver_allowed": False, "risk_summary": ( f"{pending} pending evidence entries; " f"{ledger_summary.get('human_pending_count', 0)} human pending; " f"{ledger_summary.get('external_pending_count', 0)} external pending" ), "required_review": [ "Do not use a waiver to claim public world-class readiness.", "Either submit accepted ledger evidence or state that this release does not claim world-class completion.", "Keep claim guard active until ledger summary.ready_to_claim_world_class is true.", ], "suggested_evidence": "reports/world_class_evidence_ledger.md", "suggested_command": ( "python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions " "&& python3 scripts/yao.py world-class-claim-guard ." ), "world_class_boundary": "Non-waivable completion boundary.", } def build_waiver_candidates(skill_dir: Path, covered_gate_keys: list[str], today: date) -> list[dict[str, Any]]: covered = set(covered_gate_keys) candidates = [] output_candidate = output_lab_candidate(skill_dir, covered, today) if output_candidate: candidates.append(output_candidate) world_boundary = world_class_boundary(skill_dir) if world_boundary: candidates.append(world_boundary) return candidates def render_report( skill_dir: Path, waivers_json: Path | None = None, output_json: Path | None = None, output_md: Path | None = None, generated_at: str | None = None, add_args: argparse.Namespace | None = None, ) -> dict[str, Any]: skill_dir = skill_dir.resolve() reports = skill_dir / "reports" reports.mkdir(parents=True, exist_ok=True) output_json = output_json or reports / "review_waivers.json" output_md = output_md or reports / "review_waivers.md" source_json = waivers_json or output_json today = today_from(generated_at) payload = load_json(source_json) raw_waivers = payload.get("waivers", []) if isinstance(payload.get("waivers", []), list) else [] if add_args is not None: raw_waivers = add_waiver(raw_waivers, add_args, today) waivers, failures, warnings = validate_waivers(raw_waivers, today) active = [item for item in waivers if item["status"] == "active"] expired = [item for item in waivers if item["status"] == "expired"] invalid = [item for item in waivers if item["status"] == "invalid"] covered_gate_keys = sorted({item["gate_key"] for item in active}) waiver_candidates = build_waiver_candidates(skill_dir, covered_gate_keys, today) waiverable_open = [ item for item in waiver_candidates if item["waiver_allowed"] and item["status"] != "covered" ] non_waivable = [item for item in waiver_candidates if not item["waiver_allowed"]] report = { "schema_version": "1.0", "ok": not failures, "skill_dir": display_path(skill_dir), "generated_at": generated_at or today.isoformat(), "summary": { "waiver_count": len(waivers), "active_count": len(active), "expired_count": len(expired), "invalid_count": len(invalid), "covered_gate_count": len(covered_gate_keys), "covered_gate_keys": covered_gate_keys, "waiver_candidate_count": len(waiver_candidates), "waiverable_open_count": len(waiverable_open), "non_waivable_count": len(non_waivable), }, "policy": { "blocker_waivers_allowed": False, "minimum_reason_chars": MIN_REASON_CHARS, "expires_required": True, "known_gate_keys": sorted(KNOWN_GATE_KEYS), }, "waivers": waivers, "waiver_candidates": waiver_candidates, "failures": failures, "warnings": warnings, "artifacts": { "json": display_path(output_json), "markdown": display_path(output_md), }, } output_json.parent.mkdir(parents=True, exist_ok=True) output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") output_md.write_text(render_markdown(report), encoding="utf-8") return report def render_markdown(report: dict[str, Any]) -> str: summary = report["summary"] lines = [ "# Review Waivers", "", f"- OK: `{report['ok']}`", f"- Waivers: `{summary['waiver_count']}`", f"- Active: `{summary['active_count']}`", f"- Expired: `{summary['expired_count']}`", f"- Invalid: `{summary['invalid_count']}`", f"- Covered gates: `{', '.join(summary['covered_gate_keys']) or 'none'}`", f"- Waiver candidates: `{summary['waiver_candidate_count']}`", f"- Open waiverable candidates: `{summary['waiverable_open_count']}`", f"- Non-waivable boundaries: `{summary['non_waivable_count']}`", "", "## Policy", "", "- Blocker waivers allowed: `False`", f"- Minimum reason chars: `{report['policy']['minimum_reason_chars']}`", "- Expiry is required for every waiver.", "- World-class evidence completion cannot be waived; it can only be proven by accepted ledger evidence.", "", "## Waivers", "", ] if not report["waivers"]: lines.append("- None") else: lines.extend(["| ID | Gate | Decision | Reviewer | Status | Expires | Reason |", "| --- | --- | --- | --- | --- | --- | --- |"]) for item in report["waivers"]: reason = str(item["reason"]).replace("|", "\\|") lines.append( f"| `{item['id']}` | `{item['gate_key']}` | `{item['decision']}` | {item['reviewer']} | `{item['status']}` | `{item['expires_at']}` | {reason} |" ) lines.extend(["", "## Candidate Actions", ""]) candidates = report.get("waiver_candidates", []) if not candidates: lines.append("- None") else: lines.extend(["| Gate | Status | Waiver | Risk | Evidence |", "| --- | --- | --- | --- | --- |"]) for item in candidates: risk = str(item.get("risk_summary", "")).replace("|", "\\|") lines.append( f"| `{item['gate_key']}` | `{item['status']}` | `{str(item['waiver_allowed']).lower()}` | {risk} | `{item.get('suggested_evidence', '')}` |" ) for item in candidates: lines.extend(["", f"### {item['label']}", ""]) lines.append(f"- gate: `{item['gate_key']}`") lines.append(f"- status: `{item['status']}`") lines.append(f"- waiver allowed: `{str(item['waiver_allowed']).lower()}`") lines.append(f"- risk: {item['risk_summary']}") lines.append(f"- evidence: `{item.get('suggested_evidence', '')}`") lines.append(f"- verification: `{item.get('suggested_command', '')}`") lines.append(f"- world-class boundary: {item.get('world_class_boundary', '')}") lines.extend(["", "#### Required Review", ""]) lines.extend(f"- {review}" for review in item.get("required_review", [])) lines.extend(["", "## Failures", ""]) lines.extend([f"- {item}" for item in report["failures"]] or ["- None"]) lines.extend(["", "## Warnings", ""]) lines.extend([f"- {item}" for item in report["warnings"]] or ["- None"]) return "\n".join(lines).strip() + "\n" def main() -> None: parser = argparse.ArgumentParser(description="Render or update Review Studio waiver evidence.") parser.add_argument("skill_dir", nargs="?", default=".") parser.add_argument("--waivers-json") parser.add_argument("--output-json") parser.add_argument("--output-md") parser.add_argument("--generated-at") parser.add_argument("--add-waiver", action="store_true") parser.add_argument("--gate-key", choices=sorted(KNOWN_GATE_KEYS)) parser.add_argument("--decision", choices=sorted(VALID_DECISIONS), default="accepted-risk") parser.add_argument("--reviewer") parser.add_argument("--reason") parser.add_argument("--expires-at") parser.add_argument("--created-at") parser.add_argument("--evidence") parser.add_argument("--scope", default="current-release") args = parser.parse_args() add_args = None if args.add_waiver: required = { "--gate-key": args.gate_key, "--reviewer": args.reviewer, "--reason": args.reason, "--expires-at": args.expires_at, } missing = [name for name, value in required.items() if not value] if missing: print(json.dumps({"ok": False, "failures": [f"Missing required fields for --add-waiver: {', '.join(missing)}"]}, ensure_ascii=False, indent=2)) raise SystemExit(2) add_args = args payload = render_report( Path(args.skill_dir), waivers_json=Path(args.waivers_json).resolve() if args.waivers_json else None, output_json=Path(args.output_json).resolve() if args.output_json else None, output_md=Path(args.output_md).resolve() if args.output_md else None, generated_at=args.generated_at, add_args=add_args, ) print(json.dumps(payload, ensure_ascii=False, indent=2)) raise SystemExit(0 if payload["ok"] else 2) if __name__ == "__main__": main()