69 lines
2.4 KiB
Python
69 lines
2.4 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import argparse
|
||
|
|
import json
|
||
|
|
from pathlib import Path
|
||
|
|
from typing import Dict, List
|
||
|
|
|
||
|
|
|
||
|
|
def gate(packet: Dict[str, object], min_coverage: float, require_caps_for_complex: bool, min_complexity_score: int) -> Dict[str, object]:
|
||
|
|
coverage_ratio = float((((packet.get("coverage") or {}).get("coverage_ratio")) or 0.0))
|
||
|
|
anns = packet.get("annotations") or []
|
||
|
|
|
||
|
|
cap_count = 0
|
||
|
|
complexity_score = 0
|
||
|
|
for ann in anns:
|
||
|
|
if not isinstance(ann, dict):
|
||
|
|
continue
|
||
|
|
t = ann.get("type")
|
||
|
|
if t == "capability_requirement":
|
||
|
|
cap_count += 1
|
||
|
|
if t == "complexity":
|
||
|
|
fields = ann.get("fields") or {}
|
||
|
|
complexity_score = int(fields.get("score") or 0)
|
||
|
|
|
||
|
|
reasons: List[str] = []
|
||
|
|
if coverage_ratio < min_coverage:
|
||
|
|
reasons.append(f"coverage_below_threshold:{coverage_ratio}<{min_coverage}")
|
||
|
|
|
||
|
|
if require_caps_for_complex and complexity_score >= min_complexity_score and cap_count == 0:
|
||
|
|
reasons.append("complex_spec_missing_capability_requirements")
|
||
|
|
|
||
|
|
passed = len(reasons) == 0
|
||
|
|
return {
|
||
|
|
"passed": passed,
|
||
|
|
"reasons": reasons,
|
||
|
|
"metrics": {
|
||
|
|
"coverage_ratio": coverage_ratio,
|
||
|
|
"capability_requirement_count": cap_count,
|
||
|
|
"complexity_score": complexity_score,
|
||
|
|
"min_coverage": min_coverage,
|
||
|
|
"require_caps_for_complex": require_caps_for_complex,
|
||
|
|
"min_complexity_score": min_complexity_score,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def main() -> None:
|
||
|
|
ap = argparse.ArgumentParser(description="Check semantic planning packet quality gate.")
|
||
|
|
ap.add_argument("--packet", required=True)
|
||
|
|
ap.add_argument("--out", required=True)
|
||
|
|
ap.add_argument("--min-coverage", type=float, default=0.9)
|
||
|
|
ap.add_argument("--require-caps-for-complex", action="store_true")
|
||
|
|
ap.add_argument("--min-complexity-score", type=int, default=2)
|
||
|
|
args = ap.parse_args()
|
||
|
|
|
||
|
|
packet = json.loads(Path(args.packet).read_text(encoding="utf-8"))
|
||
|
|
res = gate(packet, args.min_coverage, args.require_caps_for_complex, args.min_complexity_score)
|
||
|
|
out = Path(args.out)
|
||
|
|
out.parent.mkdir(parents=True, exist_ok=True)
|
||
|
|
out.write_text(json.dumps(res, indent=2) + "\n", encoding="utf-8")
|
||
|
|
print(json.dumps(res, indent=2))
|
||
|
|
if not res["passed"]:
|
||
|
|
raise SystemExit(3)
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|