diff --git a/scripts/confidence_signal.py b/scripts/confidence_signal.py new file mode 100755 index 0000000..f4b1991 --- /dev/null +++ b/scripts/confidence_signal.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +"""confidence_signal.py — REQ-08 / D-024 + +Reads a contract.yaml, invokes policy_checker.py as a subprocess, and emits +a deterministic JSON confidence score. + + policy pass -> {"score": 0.90, "reason": "POLICY_PASS"} + policy fail -> {"score": 0.40, "reason": ""} + +Exit 0 ALWAYS (per D-024): the pipeline decides the gate, not this script's +exit code. + +Input: argv[1] = path to a contract.yaml file. +""" +import json +import os +import subprocess +import sys + + +def main() -> int: + if len(sys.argv) < 2: + print("usage: confidence_signal.py ", file=sys.stderr) + return 1 + + contract_path = sys.argv[1] + + # Resolve policy_checker.py relative to this script so it works regardless + # of cwd. Use python3 + script path (not ./) per the contract. + here = os.path.dirname(os.path.abspath(__file__)) + policy_checker = os.path.join(here, "policy_checker.py") + + proc = subprocess.run( + ["python3", policy_checker, contract_path], + capture_output=True, + text=True, + ) + + if proc.returncode == 0: + score = 0.90 + # POLICY_PASS is the expected stdout; strip any trailing whitespace. + reason = proc.stdout.strip() or "POLICY_PASS" + else: + score = 0.40 + # The violation code (e.g. "POLICY_VIOLATION:PUBLIC_INGRESS") is on stdout. + reason = proc.stdout.strip() or "POLICY_VIOLATION:UNKNOWN" + + print(json.dumps({"score": score, "reason": reason})) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file