#!/usr/bin/env python3 """CASAN Loop Budget Governor (Plan-17 Track 1, harness-owned). The loop-breaker. Given a run's *cumulative* usage (steps / tokens / wall-clock / cost / corrections), decide whether the agent loop may CONTINUE or must HALT. Deny-by-default & fail-closed: * no policy file -> strictest built-in ceiling (STRICT_CEILING), * present-but-corrupt policy -> HALT (exit 3), never "run on", * any unexpected error -> HALT (exit 3). Loosening the ceiling is a security-sensitive setting: it must be changed through the governed settings store (control-plane-settings.py: approval JWT + SoD + versioned + rollback), not by editing this script. This governor only *reads* the (governed) policy — see Plan-17 17.3. Usage: loop-governor.py check --run-id R --step N \ [--tokens T] [--elapsed S] [--cost C] [--corrections K] \ [--profile prod|dev] [--delegation-level L2] [--project okr] Exit codes: 0 CONTINUE (within ceiling) 3 HALT(budget) (ceiling exceeded with on_exceed=halt, OR fail-closed error) 4 ESCALATE (ceiling exceeded with on_exceed=escalate) 2 usage error """ import argparse import json import sys import loop_common as lc def _emit(payload, stream=sys.stdout): json.dump(payload, stream, ensure_ascii=False, indent=2, sort_keys=True) stream.write("\n") def cmd_check(args): usage = { "steps": args.step, "tokens": args.tokens, "elapsed_s": args.elapsed, "cost_usd": args.cost, "corrections": args.corrections, } prof = args.profile or lc.profile() try: policy = lc.load_policy() ceiling, source = lc.resolve_budget( policy, prof, args.delegation_level, args.project ) except lc.PolicyError as exc: # Fail-closed: a policy we cannot trust must stop the loop, not free it. payload = { "decision": "HALT", "reason": "fail_closed", "detail": str(exc), "run_id": args.run_id, "step": args.step, "profile": prof, "provenance": lc.provenance("loop-governor", lc.policy_path(), verified=False), } lc.append_audit({"kind": "governor_halt", **payload}) _emit(payload) return 3 exceeded = [] checks = ( ("steps", "max_steps", usage["steps"]), ("tokens", "max_tokens", usage["tokens"]), ("elapsed_s", "max_wall_clock_sec", usage["elapsed_s"]), ("cost_usd", "max_cost_usd", usage["cost_usd"]), ("corrections", "max_corrections_per_step", usage["corrections"]), ) for usage_key, limit_key, value in checks: limit = ceiling[limit_key] if value is not None and value > limit: exceeded.append({"metric": usage_key, "value": value, "limit": limit}) on_exceed = ceiling.get("on_exceed", "halt") if not exceeded: payload = { "decision": "CONTINUE", "run_id": args.run_id, "step": args.step, "profile": prof, "policy_source": source, "limits": ceiling, "usage": usage, "provenance": lc.provenance("loop-governor", lc.policy_path(), verified=policy is not None), } _emit(payload) return 0 decision = "ESCALATE" if on_exceed == "escalate" else "HALT" payload = { "decision": decision, "reason": "budget_exceeded", "run_id": args.run_id, "step": args.step, "profile": prof, "policy_source": source, "limits": ceiling, "usage": usage, "exceeded": exceeded, "on_exceed": on_exceed, "provenance": lc.provenance("loop-governor", lc.policy_path(), verified=policy is not None), } lc.append_audit({"kind": "governor_" + decision.lower(), **payload}) _emit(payload) return 4 if decision == "ESCALATE" else 3 def build_parser(): p = argparse.ArgumentParser(description="CASAN Loop Budget Governor (Plan-17 T1)") sub = p.add_subparsers(dest="cmd", required=True) c = sub.add_parser("check", help="Evaluate cumulative usage against the ceiling") c.add_argument("--run-id", required=True) c.add_argument("--step", type=int, required=True) c.add_argument("--tokens", type=int, default=None) c.add_argument("--elapsed", type=float, default=None) c.add_argument("--cost", type=float, default=None) c.add_argument("--corrections", type=int, default=None) c.add_argument("--profile", default=None) c.add_argument("--delegation-level", default=None) c.add_argument("--project", default=None) c.set_defaults(func=cmd_check) return p def main(argv=None): args = build_parser().parse_args(argv) try: return args.func(args) except lc.PolicyError as exc: _emit({"decision": "HALT", "reason": "fail_closed", "detail": str(exc)}) return 3 except Exception as exc: # never fail open _emit({"decision": "HALT", "reason": "internal_error", "detail": str(exc)}, sys.stderr) return 3 if __name__ == "__main__": raise SystemExit(main())