Pulse/scripts/release_control/readiness_assertion_guard.py
2026-03-18 16:06:30 +00:00

293 lines
10 KiB
Python

#!/usr/bin/env python3
"""Run machine-declared readiness assertion proof commands."""
from __future__ import annotations
import argparse
import os
from pathlib import Path
import shlex
import subprocess
import sys
from typing import Any
from control_plane import DEFAULT_CONTROL_PLANE, active_target_blocking_levels
from repo_file_io import REPO_ROOT, load_repo_json, read_repo_text
STATUS_REL = DEFAULT_CONTROL_PLANE["status_rel"]
def load_status_payload(*, staged: bool = False) -> dict[str, Any]:
return load_repo_json(STATUS_REL, staged=staged)
def _clean_relative_dir(path: str) -> str:
candidate = Path(path)
if candidate.is_absolute():
raise ValueError(f"cwd must be relative, got {path!r}")
normalized = candidate.as_posix()
if normalized != path or path.startswith("../") or "/../" in path:
raise ValueError(f"cwd must be a clean relative path, got {path!r}")
resolved = REPO_ROOT / path
if not resolved.exists():
raise ValueError(f"cwd {path!r} does not exist")
if not resolved.is_dir():
raise ValueError(f"cwd {path!r} is not a directory")
return path
def parse_args(argv: list[str]) -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Run proof commands declared in the active release profile status.json."
)
parser.add_argument(
"--blocking-level",
action="append",
choices=["repo-ready", "rc-ready", "release-ready"],
help="Run only assertions matching the given blocking level. Repeat to allow multiple values.",
)
parser.add_argument(
"--active-target",
action="store_true",
help=(
"Resolve blocking levels from the active target completion rule in control_plane.json "
"and run only proofs relevant to the current target phase."
),
)
parser.add_argument(
"--proof-type",
action="append",
choices=["automated", "manual", "hybrid"],
help="Run only assertions matching the given proof type. Repeat to allow multiple values.",
)
parser.add_argument(
"--assertion",
action="append",
help="Run only the given readiness assertion id. Repeat to allow multiple values.",
)
parser.add_argument(
"--staged",
action="store_true",
help="Read the readiness assertion catalog from the git index instead of the working tree.",
)
return parser.parse_args(argv)
def selected_proof_commands(
payload: dict[str, Any],
*,
blocking_levels: set[str] | None = None,
proof_types: set[str] | None = None,
assertion_ids: set[str] | None = None,
) -> tuple[list[dict[str, Any]], list[str]]:
selected: list[dict[str, Any]] = []
errors: list[str] = []
assertions = payload.get("readiness_assertions")
if not isinstance(assertions, list):
return [], ["status.json missing readiness_assertions list"]
for raw in assertions:
if not isinstance(raw, dict):
continue
assertion_id = str(raw.get("id", "")).strip()
blocking_level = str(raw.get("blocking_level", "")).strip()
proof_type = str(raw.get("proof_type", "")).strip()
if assertion_ids and assertion_id not in assertion_ids:
continue
if blocking_levels and blocking_level not in blocking_levels:
continue
if proof_types and proof_type not in proof_types:
continue
raw_commands = raw.get("proof_commands")
if raw_commands is None:
if proof_type == "automated":
errors.append(f"{assertion_id} is automated but has no proof_commands")
continue
if not isinstance(raw_commands, list) or not raw_commands:
errors.append(f"{assertion_id} proof_commands must be a non-empty list")
continue
for raw_command in raw_commands:
if not isinstance(raw_command, dict):
errors.append(f"{assertion_id} proof_commands contains a non-object entry")
continue
command_id = str(raw_command.get("id", "")).strip()
run = raw_command.get("run")
cwd = raw_command.get("cwd", ".")
if not command_id:
errors.append(f"{assertion_id} proof command is missing an id")
continue
if not isinstance(run, list) or not run or any(
not isinstance(entry, str) or not entry.strip() for entry in run
):
errors.append(f"{assertion_id}:{command_id} run must be a non-empty list of strings")
continue
if not isinstance(cwd, str) or not cwd.strip():
errors.append(f"{assertion_id}:{command_id} cwd must be a non-empty string when declared")
continue
try:
clean_cwd = _clean_relative_dir(cwd)
except ValueError as exc:
errors.append(f"{assertion_id}:{command_id} {exc}")
continue
selected.append(
{
"assertion_id": assertion_id,
"command_id": command_id,
"run": [str(entry) for entry in run],
"cwd": clean_cwd,
}
)
return selected, errors
def deduplicated_proof_commands(commands: list[dict[str, Any]]) -> list[dict[str, Any]]:
grouped: dict[tuple[str, tuple[str, ...]], dict[str, Any]] = {}
ordered_keys: list[tuple[str, tuple[str, ...]]] = []
for command in commands:
cwd = str(command["cwd"])
run = tuple(str(entry) for entry in command["run"])
key = (cwd, run)
record = grouped.get(key)
if record is None:
record = {
"assertion_ids": [str(command["assertion_id"])],
"command_ids": [str(command["command_id"])],
"cwd": cwd,
"run": list(run),
}
grouped[key] = record
ordered_keys.append(key)
continue
assertion_id = str(command["assertion_id"])
command_id = str(command["command_id"])
if assertion_id not in record["assertion_ids"]:
record["assertion_ids"].append(assertion_id)
if command_id not in record["command_ids"]:
record["command_ids"].append(command_id)
return [grouped[key] for key in ordered_keys]
def _clean_repo_relative_file(path: str, *, cwd: str) -> str | None:
candidate = Path(cwd) / path
normalized = candidate.as_posix()
if candidate.is_absolute():
return None
if normalized.startswith("../") or "/../" in normalized:
return None
resolved = (REPO_ROOT / candidate).resolve()
try:
return resolved.relative_to(REPO_ROOT.resolve()).as_posix()
except ValueError:
return None
def _staged_python_script_run(
command: dict[str, Any],
) -> tuple[list[str], str, dict[str, str]] | None:
run = [str(entry) for entry in command["run"]]
if len(run) < 2:
return None
interpreter = run[0]
if Path(interpreter).name not in {"python", "python3"}:
return None
script_path = run[1]
if script_path.startswith("-"):
return None
rel = _clean_repo_relative_file(script_path, cwd=str(command["cwd"]))
if rel is None or not rel.endswith(".py"):
return None
script_dir = str((REPO_ROOT / rel).parent)
env = os.environ.copy()
current_pythonpath = env.get("PYTHONPATH")
env["PYTHONPATH"] = (
script_dir if not current_pythonpath else f"{script_dir}{os.pathsep}{current_pythonpath}"
)
return [interpreter, "-", *run[2:]], read_repo_text(rel, staged=True), env
def run_selected_proof_commands(commands: list[dict[str, Any]], *, staged: bool = False) -> int:
unique_commands = deduplicated_proof_commands(commands)
if not commands:
print("Readiness assertion guard: no matching proof commands.")
return 0
print(
"Running readiness assertion guard: "
f"commands={len(unique_commands)} selected={len(commands)}"
)
for command in unique_commands:
assertion_ids = [str(entry) for entry in command["assertion_ids"]]
command_ids = [str(entry) for entry in command["command_ids"]]
run = [str(entry) for entry in command["run"]]
cwd = REPO_ROOT / str(command["cwd"])
print(
f"[{','.join(assertion_ids)}] {','.join(command_ids)}: {shlex.join(run)}"
)
staged_python_run = _staged_python_script_run(command) if staged else None
if staged_python_run is None:
result = subprocess.run(run, cwd=cwd, check=False)
else:
staged_run, script_content, env = staged_python_run
result = subprocess.run(
staged_run,
cwd=cwd,
env=env,
input=script_content,
text=True,
check=False,
)
if result.returncode != 0:
print(
"BLOCKED: readiness assertion proof failed for "
f"{','.join(assertion_ids)}:{','.join(command_ids)} "
f"(exit {result.returncode})"
)
return result.returncode or 1
print("Readiness assertion guard passed.")
return 0
def main(argv: list[str] | None = None) -> int:
args = parse_args(list(argv or []))
blocking_levels = set(args.blocking_level or [])
if args.active_target:
try:
active_levels = set(active_target_blocking_levels(staged=args.staged))
except ValueError as exc:
print(f"BLOCKED: {exc}")
return 1
if not active_levels:
print(
"Readiness assertion guard: active target declares no machine proof scope; "
"skipping phase-derived proof selection."
)
return 0
if blocking_levels:
blocking_levels &= active_levels
if not blocking_levels:
print("BLOCKED: explicit --blocking-level filters do not overlap the active target phase")
return 1
else:
blocking_levels = active_levels
commands, errors = selected_proof_commands(
load_status_payload(staged=args.staged),
blocking_levels=blocking_levels,
proof_types=set(args.proof_type or []),
assertion_ids=set(args.assertion or []),
)
if errors:
for error in errors:
print(f"BLOCKED: {error}")
return 1
return run_selected_proof_commands(commands, staged=args.staged)
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))