#!/usr/bin/env python3 """CASAN unified governance evidence report (Plan-09 tie-in, harness-owned). Aggregates the harness governance controls into ONE certified-run artifact so a reviewer can verify the whole posture from a single file instead of scattered outputs. Deterministic and offline — it composes existing harness cores: - Traceability : traceability-matrix.py (REQ→code→test, incl. symbol/line) - Control Plane : control-plane-settings.py verify-audit (audit hash-chain) - RBAC : rbac-check.py list-roles (policy present) - RAI/Data Gov : rai-guard.py presence + optional model-cards - Self-improve : self-improve.py presence A run is CERTIFIED only when: traceability has 0 failing FRs AND the control-plane audit chain is intact. `--gate` exits non-zero when not certified (no false certification). """ import argparse import json import os import subprocess import sys def here() -> str: return os.path.dirname(os.path.abspath(__file__)) def run(cmd): return subprocess.run(cmd, capture_output=True, text=True) def traceability_summary(out_dir): script = os.path.join(here(), "traceability-matrix.py") out = os.path.join(out_dir, "traceability-matrix.json") res = run([sys.executable, script, "--out", out, "--gate"]) summary = {"available": os.path.isfile(out), "gate_pass": res.returncode == 0} if summary["available"]: try: data = json.load(open(out, encoding="utf-8")) summary.update(data.get("summary", {})) except ValueError: pass return summary def audit_integrity(): script = os.path.join(here(), "control-plane-settings.py") res = run([sys.executable, script, "verify-audit"]) return {"ok": res.returncode == 0, "detail": (res.stdout or res.stderr).strip()} def rbac_present(): script = os.path.join(here(), "rbac-check.py") res = run([sys.executable, script, "list-roles"]) return {"available": res.returncode == 0, "roles": len([l for l in res.stdout.splitlines() if l.strip()])} def control_present(name): return os.path.isfile(os.path.join(here(), name)) def build_report(out_dir): trace = traceability_summary(out_dir) audit = audit_integrity() rbac = rbac_present() controls = { "traceability": control_present("traceability-matrix.py"), "control_plane_settings": control_present("control-plane-settings.py"), "rbac": control_present("rbac-check.py"), "rai_data_governance": control_present("rai-guard.py"), "self_improve": control_present("self-improve.py"), "compression": control_present("context-compress.py"), } certified = ( trace.get("gate_pass", False) and int(trace.get("failed", 1)) == 0 and audit["ok"] and all(controls.values()) ) return { "certified": certified, "controls_present": controls, "traceability": trace, "control_plane_audit": audit, "rbac": rbac, } def main() -> int: ap = argparse.ArgumentParser() ap.add_argument("--out", default=os.path.join( os.path.abspath(os.path.join(here(), "..", "..", "..")), "docs/output/casan/governance-report.json", )) ap.add_argument("--gate", action="store_true") args = ap.parse_args() out_dir = os.path.dirname(args.out) os.makedirs(out_dir, exist_ok=True) report = build_report(out_dir) with open(args.out, "w", encoding="utf-8") as fh: json.dump(report, fh, indent=2, ensure_ascii=False) fh.write("\n") badge = "CERTIFIED" if report["certified"] else "NOT_CERTIFIED" print( f"GOVERNANCE_REPORT badge={badge} traceability_fail={report['traceability'].get('failed')} " f"audit_ok={report['control_plane_audit']['ok']} controls={sum(report['controls_present'].values())}/" f"{len(report['controls_present'])} out={args.out}" ) if args.gate and not report["certified"]: print("GOVERNANCE_NOT_CERTIFIED", file=sys.stderr) return 1 return 0 if __name__ == "__main__": raise SystemExit(main())