ops-mason/src/ops_mason/readiness.py
tegwick 36445ae679 Enforce readiness tiers and reconcile blocked workplans
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0e75a-fc5c-7913-9dba-9846210c766d
2026-09-28 11:40:57 +02:00

145 lines
7 KiB
Python

"""Readiness quality gate; no credential or Kubernetes access."""
from __future__ import annotations
import hashlib
import re
import subprocess
from datetime import date
from pathlib import Path
from typing import Any
import yaml
TRANSITION_END = date(2026, 12, 21)
NON_PRODUCTION = {"declared", "installed", "verified"}
def resolve_tier(
state: str | None, target: dict[str, Any], activation: str, today: date,
) -> dict[str, Any]:
"""Resolve already verified source facts. Unknown facts stay production."""
tier = "non-production" if state in NON_PRODUCTION or state == "explicit-whitehat" else "production"
transition = (
tier == "production" and state == "production-approved"
and target.get("kind") == "rapp"
and target.get("rapp_id") == "rapp-policy-nexus"
and today < TRANSITION_END and activation == "APPROVED"
)
return {
"tier": tier,
"direct_apply_allowed": activation in {"APPROVED", "BREAK_GLASS"}
and (tier == "non-production" or activation == "BREAK_GLASS" or transition),
"transition": transition,
}
def inspect_readiness(
bundle, runner, repo: Path | None, activation: str = "APPROVED",
today: date | None = None,
) -> dict[str, Any]:
"""Verify pinned source, local freshness and history before trusting a tier.
A reviewed bundle supplies the namespace-to-rApp mapping. It must identify
the namespace explicitly; source mappings, when present, must agree.
"""
today = today or date.today()
block = bundle.readiness or {}
target = block.get("target", {})
source = block.get("source", {})
evidence: dict[str, Any] = {
"target": target, "source": source, "activation": activation,
"state": None, "reason": "missing or unverifiable readiness",
}
def finish(state=None, reason="unverifiable readiness"):
evidence.update(state=state, reason=reason)
evidence.update(resolve_tier(state, target, activation, today))
return evidence
if not block or target.get("namespace") != bundle.expected_namespace:
return finish(reason="missing readiness or namespace mapping")
# A namespace placement never covers other cluster-scoped objects.
if any(not ref.namespace and (ref.kind != "Namespace" or ref.name != bundle.expected_namespace)
for ref in bundle.allowed_objects):
return finish(reason="target includes objects outside the namespace mapping")
if target.get("kind") not in {"rapp", "namespace"}:
return finish(reason="unmapped platform target")
if source.get("repo") != "reef-railiance" or source.get("path") != "bindings/rapps.yaml":
return finish(reason="unsupported readiness source")
revision = source.get("revision", "")
digest = source.get("sha256", "")
if not isinstance(revision, str) or not re.fullmatch(r"[0-9a-f]{40}", revision):
return finish(reason="source needs a full commit id")
if not isinstance(digest, str) or not re.fullmatch(r"[0-9a-f]{64}", digest):
return finish(reason="source needs a SHA-256 digest")
root = repo or bundle.repo_root.parent / "reef-railiance"
prefix = ["git", "-C", str(root)]
path = source["path"]
def git(*args):
result = runner([*prefix, *args])
if result.returncode:
raise ValueError("readiness git verification failed")
return result.stdout
def rows(content):
data = yaml.safe_load(content)
if not isinstance(data, dict) or not isinstance(data.get("bound_rapps"), list):
raise ValueError("invalid readiness document")
result = data["bound_rapps"]
if any(not isinstance(row, dict) or not isinstance(row.get("rapp_id"), str) for row in result):
raise ValueError("invalid readiness binding")
if len({row["rapp_id"] for row in result}) != len(result):
raise ValueError("duplicate readiness binding")
return result
try:
if git("rev-parse", "--is-shallow-repository").strip() != "false":
return finish(reason="complete readiness history is required")
content = git("show", f"{revision}:{path}")
if hashlib.sha256(content.encode()).hexdigest() != digest:
return finish(reason="readiness digest mismatch")
git("merge-base", "--is-ancestor", revision, "HEAD")
git("diff", "--exit-code", revision, "HEAD", "--", path)
if git("status", "--porcelain", "--", path).strip():
return finish(reason="readiness source has uncommitted changes")
bindings = rows(content)
if target["kind"] == "namespace":
# Only the founder's one named placement is compiled into this gate.
if bundle.id != "whitehat-foundational-plane" or bundle.expected_namespace != "whitehat":
return finish(reason="no explicit tier placement for target")
if any(row.get("namespace") == "whitehat" or "whitehat" in row.get("namespaces", [])
or row["rapp_id"] == "rapp-whitehat" for row in bindings):
return finish(reason="whitehat has a binding; repin using the binding target")
return finish("explicit-whitehat", "founder placement 2026-09-21")
rapp_id = target.get("rapp_id")
matches = [row for row in bindings if row["rapp_id"] == rapp_id]
if len(matches) != 1:
return finish(reason="rApp target is not listed")
row = matches[0]
if (row.get("namespace") and row["namespace"] != bundle.expected_namespace) or (
"namespaces" in row and bundle.expected_namespace not in row["namespaces"]
):
return finish(reason="source namespace mapping disagrees with bundle")
state = row.get("readiness_state")
# Scan all reachable history, including intervening promotions later
# reverted. An evidence lapse must never silently lower the tier.
history = git("log", "--format=%H", "HEAD", "--", path).splitlines()
if not history:
return finish(reason="readiness history is missing")
previous = []
for commit in history:
if not re.fullmatch(r"[0-9a-f]{40}", commit):
return finish(reason="invalid readiness history")
for old in rows(git("show", f"{commit}:{path}")):
if old["rapp_id"] == rapp_id:
previous.append(old.get("readiness_state"))
if "production-approved" in previous:
return finish("production-approved", "production tier retained from binding history")
if state == "deprecated":
state = next((s for s in previous if s != "deprecated"), None)
if state not in NON_PRODUCTION | {"production-approved"}:
return finish(reason="unknown readiness state or prior tier")
return finish(state, "verified pinned binding and history")
except (OSError, ValueError, TypeError, subprocess.TimeoutExpired, yaml.YAMLError):
return finish(reason="readiness source or history unavailable or invalid")