Spaces:
Paused
Paused
File size: 16,672 Bytes
6342806 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 | """Deterministic release gate: composes the real verdicts into one decision.
ReleaseGate.evaluate(safety_metrics, regression_results, distribution_shift,
policy) -> ReleaseDecision, where the inputs are the actual outputs of the
existing engines:
safety_metrics sensorflow.safety.gates.evaluate_gates(...) result
(decision RELEASE_READY/BLOCKED + layered gates)
regression_results sensorflow.seqeval.evaluate_regression(...) verdict
(decision PASS/REGRESSION/INCONCLUSIVE + regression map)
distribution_shift sensorflow.megaeval.analysis.distribution_shift(...)
report ({"shifts": [...]})
agentic_outcome sensorflow.agentic.policy.evaluate(...) result (optional)
closed_loop sensorflow.nextgen gauntlet result or causal-replay
verdict (optional)
hardware_matrix sensorflow.studio2.hardware.evaluate_matrix(...) report
(optional)
Hard rules (all covered by tests):
- GO never authorizes deployment. A ReleaseDecision with status GO still has
deployment_authorized=False; authorization is a separate recorded human
approval action (Registry 'approvals' + audit).
- A missing or failed required subsystem degrades the decision to REVIEW
with the gap named — never a silent GO.
- The policy is content-hash versioned, and every decision records the full
evidence tuple it was computed from.
"""
from __future__ import annotations
from typing import Dict, List, Optional
from sensorflow.studio2 import store
from sensorflow.studio2.registry import Registry, get_registry, new_id
GO, REVIEW, NO_GO = "GO", "REVIEW", "NO_GO"
DEFAULT_RELEASE_POLICY: Dict = {
"policy_name": "studio2-release-v1",
# subsystems that MUST contribute evidence; absence -> REVIEW, named
"required_inputs": ["safety_gates", "sequential_regression",
"distribution_shift"],
# consulted when supplied; never silently skipped once configured here
"optional_inputs": ["agentic_policy", "closed_loop", "hardware_matrix"],
# a cohort whose eval share shifted AND whose recall trails overall by
# more than this gap forces REVIEW (megaeval shift report semantics)
"shift_review_recall_gap": -0.05,
"inconclusive_outcome": REVIEW, # never GO on absence of evidence
"critical_hardware_failure_outcome": NO_GO,
"confidence_penalty_per_gap": 0.25,
"confidence_penalty_per_review_condition": 0.10,
}
class ReleaseGate:
def __init__(self, registry: Optional[Registry] = None,
policy: Optional[Dict] = None):
self.registry = registry or get_registry()
doc = {**DEFAULT_RELEASE_POLICY, **(policy or {})}
self.policy_entity = self.registry.register_policy(
name=doc.get("policy_name", "studio2-release"), doc=doc,
provenance={"source_package": "studio2"})
self.policy = doc
# ---------------------------------------------------------------- signals
def _signal_safety(self, payload: Optional[Dict]) -> Dict:
if not payload or "decision" not in payload:
return {"present": False, "gap": "safety gate results unavailable "
"(sensorflow.safety evaluate_gates output missing)"}
blocking = payload.get("blocking_gates") or []
if payload["decision"] == "RELEASE_READY" and not blocking:
return {"present": True, "verdict": "pass",
"evidence": {"safety_gate_run": payload.get("candidate_run_id"),
"evidence_package_id": payload.get("evidence_package_id")}}
return {"present": True, "verdict": "block",
"conditions": [f"safety gate blocked: {g}" for g in blocking]
or ["safety layer decision BLOCKED"],
"evidence": {"safety_gate_run": payload.get("candidate_run_id"),
"evidence_package_id": payload.get("evidence_package_id")}}
def _signal_seqeval(self, payload: Optional[Dict]) -> Dict:
if not payload or "decision" not in payload:
return {"present": False, "gap": "sequential regression verdict "
"unavailable (sensorflow.seqeval evaluate_regression missing)"}
decision = payload["decision"]
evidence = {"seqeval_run_id": payload.get("run_id"),
"samples_used": payload.get("samples_used")}
if decision == "PASS":
return {"present": True, "verdict": "pass", "evidence": evidence}
if decision == "REGRESSION":
strata = payload.get("affected_strata") or []
return {"present": True, "verdict": "block",
"conditions": [
"sequential regression confirmed"
+ (f" in strata: {', '.join(map(str, strata))}" if strata else "")],
"evidence": evidence}
# INCONCLUSIVE / INSUFFICIENT: absence of evidence is never a pass
reason = payload.get("stopping_reason") or "budget exhausted without a decision"
return {"present": True, "verdict": "review",
"questions": [f"sequential regression verdict {decision}: {reason}"],
"evidence": evidence}
def _signal_shift(self, payload: Optional[Dict]) -> Dict:
if not payload or "shifts" not in payload:
return {"present": False, "gap": "distribution shift report "
"unavailable (megaeval shift monitor missing)"}
gap_limit = self.policy["shift_review_recall_gap"]
flagged = [s for s in payload["shifts"]
if s.get("recall_gap") is not None and s["recall_gap"] <= gap_limit]
evidence = {"shift_run_id": payload.get("run_id"),
"shifts_reported": len(payload["shifts"])}
if flagged:
return {"present": True, "verdict": "review",
"questions": [
f"distribution shift with recall impact on {s['cohort']}: "
f"eval share {s.get('relative_change', 0)*100:+.0f}% vs train, "
f"cohort recall gap {s['recall_gap']:+.3f} — open an RCA "
"investigation (offline-vs-shadow 4-way root cause)"
for s in flagged[:5]],
"evidence": evidence}
return {"present": True, "verdict": "pass", "evidence": evidence}
def _signal_agentic(self, payload: Optional[Dict]) -> Dict:
if not payload or "outcome" not in payload:
return {"present": False, "gap": "agentic policy outcome unavailable "
"(sensorflow.agentic not importable or not evaluated)"}
outcome = payload["outcome"]
evidence = {"agentic_policy_version": payload.get("policy_version"),
"failure_id": payload.get("failure_id"),
"outcome": outcome}
if outcome == "AUTOMATIC_STOP_SHIP":
return {"present": True, "verdict": "block",
"conditions": ["agentic policy fired AUTOMATIC_STOP_SHIP "
f"(severity {payload.get('severity')})"],
"evidence": evidence}
if outcome == "INDETERMINATE":
return {"present": True, "verdict": "review",
"questions": ["agentic policy INDETERMINATE (fail-safe): "
+ "; ".join(payload.get("indeterminate_reasons") or
["integrity failure"])],
"evidence": evidence}
return {"present": True, "verdict": "pass", "evidence": evidence}
def _signal_closed_loop(self, payload: Optional[Dict]) -> Dict:
if not payload:
return {"present": False, "gap": "closed-loop verdict unavailable "
"(sensorflow.nextgen not importable or no replay/gauntlet run)"}
# accept either a gauntlet result or a causal replay result
evidence = {"closed_loop_run_id": payload.get("run_id")
or payload.get("scenario_id")}
if payload.get("halted") or payload.get("recommendation") == "NO_GO":
return {"present": True, "verdict": "block",
"conditions": ["closed-loop gauntlet halted: "
+ str(payload.get("halt_reason")
or payload.get("recommendation"))],
"evidence": evidence}
if payload.get("verdict") == "BEHAVIORALLY_CONSEQUENTIAL":
return {"present": True, "verdict": "review",
"questions": ["closed-loop replay shows behaviorally "
"consequential perception divergence on "
f"{payload.get('scenario_id')}"],
"evidence": evidence}
return {"present": True, "verdict": "pass", "evidence": evidence}
def _signal_hardware(self, payload: Optional[Dict]) -> Dict:
if not payload or "status" not in payload:
return {"present": False, "gap": "hardware gate matrix unavailable"}
evidence = {"hardware_matrix_id": payload.get("matrix_id"),
"combinations": payload.get("n_combinations")}
questions = [
f"insufficient evidence for {c['combination_label']}: {c['reason']}"
for c in (payload.get("insufficient") or [])[:5]]
if payload["status"] == "FAIL_CRITICAL":
outcome = self.policy["critical_hardware_failure_outcome"]
return {"present": True,
"verdict": "block" if outcome == NO_GO else "review",
"conditions": [
f"critical hardware combination failing: {c['combination_label']}"
f" ({', '.join(c['failed_checks'])})"
for c in (payload.get("critical_failures") or [])[:5]],
"questions": questions, "evidence": evidence}
if payload["status"] == "FAIL":
return {"present": True, "verdict": "review",
"questions": questions + [
f"non-critical hardware combination failing: {c['combination_label']}"
for c in (payload.get("failures") or [])[:5]],
"evidence": evidence}
return {"present": True,
"verdict": "review" if questions else "pass",
"questions": questions, "evidence": evidence}
# ---------------------------------------------------------------- evaluate
def evaluate(self, safety_metrics: Optional[Dict],
regression_results: Optional[Dict],
distribution_shift: Optional[Dict],
policy: Optional[Dict] = None,
agentic_outcome: Optional[Dict] = None,
closed_loop: Optional[Dict] = None,
hardware_matrix: Optional[Dict] = None,
context: Optional[Dict] = None,
actor: str = "studio2") -> Dict:
"""Pure function of inputs + versioned policy; persists and returns a
ReleaseDecision."""
if policy:
gate = ReleaseGate(self.registry, policy={**self.policy, **policy})
return gate.evaluate(safety_metrics, regression_results,
distribution_shift, agentic_outcome=agentic_outcome,
closed_loop=closed_loop,
hardware_matrix=hardware_matrix,
context=context, actor=actor)
signals = {
"safety_gates": self._signal_safety(safety_metrics),
"sequential_regression": self._signal_seqeval(regression_results),
"distribution_shift": self._signal_shift(distribution_shift),
"agentic_policy": self._signal_agentic(agentic_outcome),
"closed_loop": self._signal_closed_loop(closed_loop),
"hardware_matrix": self._signal_hardware(hardware_matrix),
}
required = self.policy["required_inputs"]
optional = self.policy["optional_inputs"]
considered = list(required) + list(optional)
blocking: List[str] = []
questions: List[str] = []
gaps: List[str] = []
evidence_tuple: Dict = {"release_policy_version":
self.policy_entity["policy_version"]}
for name in considered:
sig = signals[name]
if not sig["present"]:
if name in required:
gaps.append(sig["gap"])
else:
# optional-but-configured subsystems: absence is surfaced
# as an open question, never silently ignored
questions.append(f"[{name}] {sig['gap']}")
evidence_tuple[name] = None
continue
evidence_tuple[name] = sig.get("evidence")
blocking.extend(sig.get("conditions") or [])
questions.extend(sig.get("questions") or [])
if context:
evidence_tuple["context"] = context
if blocking:
status = NO_GO
elif gaps or questions:
status = REVIEW
else:
status = GO
n_present = sum(1 for n in considered if signals[n]["present"])
evidence_completeness = round(n_present / len(considered), 3)
confidence = 1.0
confidence -= self.policy["confidence_penalty_per_gap"] * len(gaps)
confidence -= (self.policy["confidence_penalty_per_review_condition"]
* len(questions))
confidence = round(max(0.05, min(confidence, evidence_completeness
if status != NO_GO else 1.0)), 3)
decision = {
"entity_id": new_id("rd"),
"status": status,
"confidence": confidence,
"evidence_completeness": evidence_completeness,
"blocking_conditions": blocking,
"unresolved_questions": questions,
"degraded_inputs": gaps,
# GO is a *recommendation*; deployment always needs a human
"human_approval_required": True,
"deployment_authorized": False,
"approval": None,
"policy_version": self.policy_entity["policy_version"],
"evidence_tuple": evidence_tuple,
"signals": {k: {kk: vv for kk, vv in v.items() if kk != "evidence"}
for k, v in signals.items()},
"evaluated_at": store.now_iso(),
}
self.registry.put("decisions", decision)
store.audit("release_decision", "decisions", decision["entity_id"],
actor=actor, detail=f"status={status} "
f"completeness={evidence_completeness}",
payload={"blocking": blocking[:5], "gaps": gaps[:5]})
return decision
# ---------------------------------------------------------------- approval
def approve(self, decision_id: str, approver: str, rationale: str) -> Dict:
"""The separate human action that authorizes deployment. Only a GO
decision can be approved; REVIEW/NO_GO must be re-evaluated first."""
if not approver.strip() or not rationale.strip():
raise ValueError("human approval requires a named approver and a rationale")
decision = self.registry.get("decisions", decision_id)
if decision is None:
raise KeyError(f"unknown release decision {decision_id}")
if decision["status"] != GO:
raise ValueError(
f"decision {decision_id} has status {decision['status']}; only GO "
"decisions can be approved for deployment — resolve the blocking "
"conditions / unresolved questions and re-evaluate")
approval = {
"entity_id": new_id("app"),
"decision_id": decision_id,
"approver": approver,
"rationale": rationale,
"approved_at": store.now_iso(),
}
self.registry.put("approvals", approval)
decision["deployment_authorized"] = True
decision["approval"] = approval
self.registry.put("decisions", decision)
store.audit("human_approval", "approvals", approval["entity_id"],
actor=approver, detail=f"authorized deployment for {decision_id}",
payload={"rationale": rationale})
return decision
|