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.
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user