Files
yao-meta-skill/scripts/review_studio_gate_contract.py
T
YAO 31ce04c655 Split meta skill CLI and review gates
Merge the beta-ready Yao Meta Skill architecture, report, evidence gate, and release-boundary updates.\n\nRelease boundary: beta/public testing is allowed; formal world-class, fully reviewed, or superiority claims remain blocked until the pending evidence gates are accepted.
2026-06-17 18:43:02 +08:00

83 lines
2.8 KiB
Python

#!/usr/bin/env python3
"""Review Studio gate keys, scoring, and contract checks."""
from collections import Counter
from typing import Any
SCRIPT_INTERFACE = "internal-module"
SCRIPT_INTERFACE_REASON = "Imported by review_studio_gates.py for stable gate scoring and contract checks."
GATE_WEIGHTS = {
"trigger-lab": 15,
"output-lab": 20,
"context-budget": 10,
"runtime-matrix": 10,
"trust-report": 10,
"python-compat": 10,
"architecture-maintainability": 10,
"permission-gates": 10,
"permission-runtime": 10,
"skill-atlas": 10,
"operations-loop": 10,
"review-waivers": 10,
"world-class-evidence": 10,
"registry-audit": 10,
"release-notes": 10,
"intent-canvas": 10,
}
REVIEW_STUDIO_GATE_KEYS = frozenset(GATE_WEIGHTS)
def status_label(status: str) -> str:
return {"pass": "通过", "warn": "关注", "block": "阻断"}.get(status, status)
def add_blockers_from_gate(gates: list[dict[str, str]]) -> tuple[list[dict[str, str]], list[dict[str, str]]]:
blockers = [item for item in gates if item["status"] == "block"]
warnings = [item for item in gates if item["status"] == "warn"]
return blockers, warnings
def gate_contract(gates: list[dict[str, str]]) -> dict[str, Any]:
rendered_gate_keys = [str(item.get("key", "")) for item in gates]
rendered_gate_set = set(rendered_gate_keys)
expected_gate_set = set(REVIEW_STUDIO_GATE_KEYS)
duplicate_gate_keys = sorted(key for key, count in Counter(rendered_gate_keys).items() if count > 1)
missing_gate_keys = sorted(expected_gate_set - rendered_gate_set)
unknown_gate_keys = sorted(rendered_gate_set - expected_gate_set)
unweighted_gate_keys = sorted(rendered_gate_set - set(GATE_WEIGHTS))
return {
"ok": not (missing_gate_keys or unknown_gate_keys or duplicate_gate_keys or unweighted_gate_keys),
"expected_gate_count": len(expected_gate_set),
"actual_gate_count": len(rendered_gate_keys),
"expected_gate_keys": sorted(expected_gate_set),
"rendered_gate_keys": rendered_gate_keys,
"missing_gate_keys": missing_gate_keys,
"unknown_gate_keys": unknown_gate_keys,
"duplicate_gate_keys": duplicate_gate_keys,
"unweighted_gate_keys": unweighted_gate_keys,
}
def min_output_cases(maturity: str) -> int:
if maturity in {"library", "governed"}:
return 5
if maturity == "production":
return 3
return 1
def weighted_score(gates: list[dict[str, str]]) -> int:
earned = 0.0
total = 0.0
for item in gates:
weight = GATE_WEIGHTS.get(item["key"], 5)
total += weight
if item["status"] == "pass":
earned += weight
elif item["status"] == "warn":
earned += weight * 0.6
return int(round(earned / total * 100)) if total else 0