#!/usr/bin/env python3 from __future__ import annotations import argparse import json from pathlib import Path from typing import Any import yaml REQUIRED_TASK_IDS = { "WBS-10-A1", "WBS-10-A2", "WBS-10-A3", "WBS-10-B1", "WBS-10-B2", "WBS-10-B3", "WBS-10-C1", "WBS-10-C2", "WBS-10-C3", } def load_yaml(path: Path) -> dict[str, Any]: if not path.exists(): raise FileNotFoundError(path) return yaml.safe_load(path.read_text(encoding="utf-8")) def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser(description="Validate WBS-10 dotnet migration roadmap") parser.add_argument("--roadmap", default="docs/WBS_10_DOTNET_MIGRATION_ROADMAP.yaml") args = parser.parse_args(argv) roadmap_path = Path(args.roadmap).resolve() payload: dict[str, Any] = { "formula_id": "WBS_10_DOTNET_MIGRATION_ROADMAP_V1", "gate": "FAIL", "missing": [], "evidence": {"roadmap": str(roadmap_path)}, } try: data = load_yaml(roadmap_path) except FileNotFoundError: payload["missing"].append("roadmap missing") print(json.dumps(payload, ensure_ascii=False, indent=2)) return 1 if data.get("formula_id") != "WBS_10_DOTNET_MIGRATION_ROADMAP_V1": payload["missing"].append("formula_id") roadmap = data.get("roadmap") or {} tracks = roadmap.get("tracks") or [] if roadmap.get("phase_name") != "WBS-10 .NET 엔진 고도화": payload["missing"].append("roadmap.phase_name") if roadmap.get("execution_order") is None: payload["missing"].append("roadmap.execution_order") task_ids: set[str] = set() for track in tracks: for task in track.get("tasks") or []: task_ids.add(task.get("task_id", "")) if not task.get("success_data_guide"): payload["missing"].append(f"{task.get('task_id','')}.success_data_guide") if not task.get("depends_on") and task.get("task_id") not in {"WBS-10-A1", "WBS-10-B1", "WBS-10-C1"}: payload["missing"].append(f"{task.get('task_id','')}.depends_on") guide = task.get("success_data_guide") or {} if "expected_artifact_schema" not in guide: payload["missing"].append(f"{task.get('task_id','')}.expected_artifact_schema") if "failure_conditions" not in guide: payload["missing"].append(f"{task.get('task_id','')}.failure_conditions") missing_tasks = sorted(REQUIRED_TASK_IDS - task_ids) for task_id in missing_tasks: payload["missing"].append(f"{task_id} missing") alignment = data.get("alignment") or {} spec_task_map = alignment.get("spec_task_map") or {} if not spec_task_map: payload["missing"].append("alignment.spec_task_map") else: for spec_id in ("WBS-10.1", "WBS-10.2", "WBS-10.3", "WBS-10.4", "WBS-10.5", "WBS-10.6", "WBS-10.7", "WBS-10.8", "WBS-10.9", "WBS-10.10", "WBS-10.11", "WBS-10.12"): if spec_id not in spec_task_map: payload["missing"].append(f"alignment.{spec_id}") payload["gate"] = "PASS" if not payload["missing"] else "FAIL" payload["message"] = ( "WBS-10 dotnet migration roadmap validation passed." if payload["gate"] == "PASS" else "WBS-10 dotnet migration roadmap validation failed." ) out_path = roadmap_path.parent.parent / "Temp" / "wbs_10_dotnet_migration_roadmap_v1.json" out_path.parent.mkdir(parents=True, exist_ok=True) out_path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8") print(json.dumps(payload, ensure_ascii=False, indent=2)) return 0 if payload["gate"] == "PASS" else 1 if __name__ == "__main__": raise SystemExit(main())