"""Exercise plan-validator rejection guards in temporary planning copies only.""" from contextlib import redirect_stdout from datetime import datetime import importlib.util import io import json from pathlib import Path import shutil import tempfile from zoneinfo import ZoneInfo PLAN = Path(__file__).resolve().parents[1] ROOT = PLAN.parents[2] INPUTS = [ "work_packages.json", "requirement_coverage.json", "baseline.json", "decisions.json", "progress_contract.json", "atomic_requirements.json", "atomic_denominator_freeze.json", "current_evidence_links.json", "progress_snapshot.json", "IMPLEMENTATION_PLAN.md", ] def exercise(case, filename, mutate, expected): with tempfile.TemporaryDirectory(prefix="thetree-progress-guard-") as directory: stage = Path(directory) / "docs/plans/original-completion-2026-10-02" stage.mkdir(parents=True) for name in INPUTS: shutil.copyfile(PLAN / name, stage / name) decisions = json.loads((PLAN / "decisions.json").read_text()) for decision in decisions["confirmed"]: if "reference" in decision: destination = stage / decision["reference"] destination.parent.mkdir(parents=True, exist_ok=True) shutil.copyfile(PLAN / decision["reference"], destination) data = json.loads((stage / filename).read_text()) mutate(data) (stage / filename).write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n") spec = importlib.util.spec_from_file_location("isolated_plan_validation", PLAN / "validate_plan.py") validator = importlib.util.module_from_spec(spec) spec.loader.exec_module(validator) validator.PLAN = stage validator.ROOT = ROOT exit_code = 0 with redirect_stdout(io.StringIO()): try: validator.main() except SystemExit as exc: exit_code = exc.code result = json.loads((stage / "plan_validation.json").read_text()) expected_result = (exit_code == 1 and expected in result["failed_checks"]) if expected else (exit_code == 0 and not result["failed_checks"]) return {"case": case, "exit_code": exit_code, "expected_guard": expected, "expected_result": expected_result, "failed_checks": result["failed_checks"]} def main(): rows = [ exercise("unmodified_control", "progress_contract.json", lambda data: None, None), exercise("unresolved_final_percentage", "progress_contract.json", lambda data: data.update(total_game_acceptance_percent=1.0), "unresolved_final_denominator_not_reported_as_total"), exercise("stale_static_index_register", "current_evidence_links.json", lambda data: data.update(atomic_register_sha256="0" * 64), "evidence_index_register_pin_matches"), exercise("unreviewed_atom_promoted_to_pass", "atomic_requirements.json", lambda data: data["requirements"][0]["view_results"]["runtime"].update(state="pass"), "unreviewed_atomic_views_remain_unverified"), exercise("P03_whole_package_completed", "work_packages.json", lambda data: next(row for row in data if row["id"] == "P03").update(state="completed"), "P01_P03_not_whole_package_completed"), ] report = {"scope": "plan-validator negative guards; isolated document copies, no game execution", "checked_at_jst": datetime.now(ZoneInfo("Asia/Tokyo")).isoformat(), "passed": all(row["expected_result"] for row in rows), "rows": rows, "production_game_source_edits": 0, "new_engine_runs": 0} (PLAN / "progress_validator_guard_checks.json").write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n") print(json.dumps(report, ensure_ascii=False)) if not report["passed"]: raise SystemExit(1) if __name__ == "__main__": main()