Files
TheTree_mock/docs/plans/original-completion-2026-10-02/execution/verify_save_preservation.py
T
Takeshi b84bade045 Save The Tree implementation and acceptance work in progress
Preserve production code, regression fixtures, audio assets and provenance, test infrastructure, and the original-completion plan and progress ledger. Final game and Windows acceptance remain pending; excluded local artifacts and native experiments are not part of this snapshot.
2026-10-11 15:58:00 +09:00

117 lines
6.3 KiB
Python

"""Reproduce preservation defects, then verify an isolated SaveService fix."""
import hashlib
import importlib.util
import json
import os
from pathlib import Path
import re
import shutil
import subprocess
import time
import uuid
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "artifacts/original-completion-2026-10-02/p03-save-preservation-r02"
PACKET = ROOT / "artifacts/original-completion-2026-10-02/p03-recovery-readonly-candidate"
TARGET = "scripts/core/save_service.gd"
def sha(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def write(path, value):
path.write_text(json.dumps(value, ensure_ascii=False, indent=2) + "\n")
def main():
OUT.mkdir(parents=True, exist_ok=False)
spec = importlib.util.spec_from_file_location("source_regression", Path(__file__).with_name("run_current_regression.py"))
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
source = module.source_map()
assert sha(ROOT / TARGET) == sha(PACKET / "save_service.base.gd")
write(OUT / "source-start.json", source)
run_id = uuid.uuid4().hex[:12]
stages = Path("/private/tmp") / ("thetree-save-preservation-" + run_id)
driver_sha = sha(PACKET / "test_recovery_readonly_candidate.gd")
report = {"scope": "identified foreign format preservation and explicit read-only recovery", "stages": {}, "rows": [], "production_applied": False, "driver_sha256": driver_sha}
for variant in ("base", "candidate"):
stage = stages / variant
stage.mkdir(parents=True)
for name, expected in source.items():
dest = stage / name
dest.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(ROOT / name, dest)
assert sha(dest) == expected
profile = "TheTreeSavePreservation-" + run_id + "-" + variant
config = (stage / "project.godot").read_text()
(stage / "project.godot").write_text(config.replace("[application]", '[application]\nconfig/use_custom_user_dir=true\nconfig/custom_user_dir_name="' + profile + '"', 1))
qa = stage / "tests/diagnostic/save_preservation"
qa.mkdir(parents=True)
shutil.copyfile(PACKET / "test_recovery_readonly_candidate.gd", qa / "acceptance.gd")
assert sha(qa / "acceptance.gd") == driver_sha
if variant == "candidate":
shutil.copyfile(PACKET / "save_service.candidate.gd", stage / TARGET)
report["stages"][variant] = {"path": str(stage), "profile": profile, "target_sha256": sha(stage / TARGET)}
write(OUT / "execution.json", report)
def run(variant, label, script=None, reproduction=False):
stage = Path(report["stages"][variant]["path"])
command = ["godot", "--headless", "--path", str(stage)]
command += ["--script", "res://" + script] if script else ["--editor", "--import"]
log = OUT / (label + ".log")
started = time.monotonic()
with log.open("w") as stream:
try:
code = subprocess.run(command, cwd=stage, env=dict(os.environ), stdout=stream, stderr=subprocess.STDOUT, timeout=900).returncode
except subprocess.TimeoutExpired:
code = 124
content = log.read_text()
counts = re.findall(r"^Recovery read-only candidate: (\d+) checks, (\d+) failures$", content, re.MULTILINE)
actual_checks = int(counts[0][0]) if len(counts) == 1 else None
if script and not reproduction and actual_checks is None:
old_report = json.loads((ROOT / "artifacts/facility-need-pricing-r02-full155-independent-review-2026-10-01-r01/actual155-source-marker-review.json").read_text())
old_markers = {row["test"]: row["actual_marker"] for row in old_report["rows"]}
marker = old_markers.get(script)
if marker is None:
matches = re.findall(r"^.*?: \d+ checks, 0 failures\n", content, re.MULTILINE)
marker = matches[0] if len(matches) == 1 else None
assert marker is not None and content.count(marker) == 1, label
match = re.search(r"checks=(\d+)|(\d+)(?: [A-Za-z]+)* checks", marker)
assert match is not None
actual_checks = int(next(g for g in match.groups() if g is not None))
failures = int(counts[0][1]) if len(counts) == 1 else None
if reproduction:
passed = code != 0 and failures is not None and failures > 0 and "SCRIPT ERROR:" not in content
else:
passed = code == 0 and "SCRIPT ERROR:" not in content and "\nERROR:" not in content and "Unicode parsing error" not in content
row = {"variant": variant, "label": label, "exit_code": code, "passed": passed,
"expected_defect_reproduction": reproduction, "checks": actual_checks,
"failures": failures, "seconds": round(time.monotonic() - started, 3), "log": log.name, "log_sha256": sha(log)}
report["rows"].append(row)
write(OUT / "execution.json", report)
print(json.dumps(row), flush=True)
assert passed, label
run("base", "base-import")
run("candidate", "candidate-import")
run("base", "base-defects", "tests/diagnostic/save_preservation/acceptance.gd", True)
run("candidate", "candidate-acceptance", "tests/diagnostic/save_preservation/acceptance.gd")
for script in ("tests/s6/test_save_preflight.gd", "tests/s6/test_save_operations.gd", "tests/s6/test_runtime_save_lifecycle.gd", "tests/run_tests.gd", "tests/transport/test_transport_save.gd", "tests/ui/test_dimension_migration_ui.gd", "tests/ui/test_supply_management.gd"):
if not (Path(report["stages"]["candidate"]["path"]) / script).is_file():
# Resolve only exact repository test filenames, never silently skip.
raise AssertionError("missing required suite " + script)
run("candidate", script.replace("/", "__").replace(".gd", ""), script)
after = module.source_map()
changes = [p for p in sorted(set(source) | set(after)) if source.get(p) != after.get(p)]
write(OUT / "source-end.json", after)
report.update(focused_acceptance_passed=True, source_changes=changes, full_merged_regression_pending=True, original_full_acceptance=False)
write(OUT / "execution.json", report)
assert not changes
print("Save preservation focused acceptance passed; production merge and full regression remain pending.")
if __name__ == "__main__":
main()