P28-01-RESTART: 現HEADの性能基準:途中保存・別PID再開の再現

This commit is contained in:
Agent Control Runner
2026-10-21 01:51:44 +09:00
parent 2132bbe3d3
commit f2a1249440
4 changed files with 1697 additions and 0 deletions
@@ -0,0 +1,473 @@
#!/usr/bin/env python3
"""RESTART evidence validation; typed state comparisons execute in Godot."""
import ast
import copy
import hashlib
import importlib.util
import json
from pathlib import Path
import re
import sys
import sysconfig
HERE = Path(__file__).resolve().parent
ROOT = HERE.parents[6]
BASE = "docs/plans/original-completion-2026-10-02/execution/p28-01/"
DEPENDENCIES = {
"tools/run_all_tests.py": "320ca668732b4ff72fc4449998af2327d4e5c527b06896740806aaf0487282b4",
"tools/regression/owned_outer.py": "c2e3483b76db1a3ff940f23f88ea0083fc78736e874ebcdb385d766dde8866e7",
"tools/regression/owned_diagnostic_process.py": "e31e93c6e2c5ea46a7d954ef8bc0b4caec1c08b86b8aef19e93b52ef24993087",
"tools/regression/p09_owned_engine_process.py": "c007e712c281573254faa1233c2ac92e100ab87393256a6dc096b35283e43037",
BASE + "collector/collect_p28_01.py": "a43e1f963ef8505ddd5acf8bf747d9bfac22a028cbb805c0b964ee11b9b40a14",
BASE + "collector/check_p28_01_report.py": "6c0851d498ca80bdc120fad047b072255f48713f63114521f3d4dfc902b2512b",
}
LOCAL = {BASE + "restart/probe/" + n for n in
("restart_pair.py", "check_restart.py", "test_restart.py", "restart_probe.gd")}
PHASES = ["import", "parse-base", "parse-wrapper", "R-writer", "R-reader", "O-writer", "O-reader"]
CROSS = [
{"condition": "原本条件とUI操作が一致", "applicable": False, "reference": "collector C1/C2",
"reason": "既存collectorのUI経路受入を参照。このtaskは再開の比較を担当する。"},
{"condition": "Runtimeの実利用・会計・行動へ接続", "applicable": False, "reference": "collector C2/C3",
"reason": "既存collectorの実Runtime観測を参照。製品の接続は変更しない。"},
{"condition": "途中保存・再起動で再現", "applicable": True, "reference": "RS1/RS2/RS3/RS4/RS5",
"reason": "別PIDのR/O writerとreaderで適用する。"},
{"condition": "異常/境界/中断を検証", "applicable": False, "reference": "collector C5/C6/C7",
"reason": "横断受入は既存collectorを参照。再開固有の異常と境界はRS5で検証する。"},
{"condition": "関連回帰PASS", "applicable": False, "reference": "collector C8",
"reason": "既存collectorの正式回帰証拠を参照。workerは全回帰を実行しない。"},
{"condition": "要件別ログとソースハッシュを保存", "applicable": False, "reference": "collector C6/C7/C9",
"reason": "既存collectorの証拠契約を参照。再開のraw結合はRS7で保存する。"},
]
def require(condition, message):
if not condition:
raise ValueError(message)
def sha(path):
return hashlib.sha256(Path(path).read_bytes()).hexdigest()
def pin(path):
path = Path(path)
require(path.is_file() and not path.is_symlink(), "missing/symlink evidence")
return {"sha256": sha(path), "bytes": path.stat().st_size}
def check_dependencies(root=ROOT):
for name, expected in DEPENDENCIES.items():
require(sha(root / name) == expected, "dependency SHA: " + name)
def check_imports(text, external=None):
tree = ast.parse(text)
if external is not None:
require(external in DEPENDENCIES, "external AST closure")
# The explicitly forbidden run_all_tests.main imports are not executed.
tree.body = [node for node in tree.body if not (
isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and node.name == "main")]
allowed = {Path(p).stem for p in set(DEPENDENCIES) | LOCAL}
for node in ast.walk(tree):
if isinstance(node, (ast.Import, ast.ImportFrom)):
names = [node.module] if isinstance(node, ast.ImportFrom) else [a.name for a in node.names]
for name in names:
require(name and (standard_library(name.split(".")[0]) or name in allowed),
"import outside closure")
if isinstance(node, ast.Call):
name = node.func.id if isinstance(node.func, ast.Name) else getattr(node.func, "attr", "")
require(name not in ("__import__", "eval", "import_module"), "dynamic code forbidden")
require(name != "exec" or external == "tools/regression/owned_diagnostic_process.py",
"unapproved exec")
if name == "spec_from_file_location":
for sub in ast.walk(node):
if isinstance(sub, ast.Constant) and isinstance(sub.value, str):
for path in re.findall(r"[\w./-]+\.py", sub.value):
require(path in DEPENDENCIES or path in LOCAL or
path in {Path(p).name for p in DEPENDENCIES}, "loader outside closure")
if external and isinstance(node, ast.BinOp):
for sub in ast.walk(node):
if isinstance(sub, ast.Constant) and isinstance(sub.value, str) and sub.value.endswith(".py"):
require(sub.value in DEPENDENCIES or sub.value in {Path(p).name for p in DEPENDENCIES},
"external loader path outside closure")
def standard_library(name):
# Python 3.9 on the native host has no sys.stdlib_module_names.
spec = importlib.util.find_spec(name)
if spec is None:
return False
if spec.origin in ("built-in", "frozen"):
return True
if not spec.origin:
return False
path = Path(spec.origin).resolve()
return path.is_relative_to(Path(sysconfig.get_path("stdlib")).resolve()) and not any(
part in ("site-packages", "dist-packages") for part in path.parts)
def check_modules(modules=None, root=ROOT):
for module in (sys.modules if modules is None else modules).values():
name = getattr(module, "__file__", None)
if name:
path = Path(name).resolve()
if path.is_relative_to(root):
require(path.relative_to(root).as_posix() in set(DEPENDENCIES) | LOCAL,
"loaded project module outside closure")
def check_probe(text):
function = None
blocks = []
pauses = 0
for raw in text.splitlines():
line = raw.split("#", 1)[0]
match = re.match(r"^func (\w+)\(", line)
if match:
function = match[1]
if function == "deterministic_step":
blocks.append([])
if function == "deterministic_step":
blocks[-1].append(line)
require(not re.search(r"set_paused\s*\(\s*false|\bclear\s*\(|spawn_guest|SimulationRuntime\.new|\binitialize\s*\(|enable_production_|_toggle_pause", line),
"forbidden probe API")
if re.search(r"\badvance_", line):
require(function == "deterministic_step" and line.strip() == "GameClock.advance_minutes(1)",
"advance outside deterministic_step")
if "set_paused(" in line:
require(function == "on_tick" and line.strip() == "GameClock.set_paused(true)", "pause outside tick")
pauses += 1
require(not re.search(r"Time\.get_ticks_usec|\b\w+_us\b|Performance\.|get_performance_report", line),
"performance instrumentation")
require(len(blocks) == 1 and pauses == 1, "deterministic/tick functions required")
block = "\n".join(blocks[0])
require(block.count("GameClock.advance_minutes(1)") == 1 and
"io_ready" in block and "GameClock.paused" in block, "step admission")
def event_id(counter):
require(type(counter) is int and counter >= 0, "counter type")
return "domain_event.%012d" % (counter + 1)
def transform(witness, event):
"""Consistency oracle for recorded IO paths, not a Godot state decoder."""
result = copy.deepcopy(witness)
eid = event_id(witness["counter"])
ids, recent = witness["published_ids"], witness["recent_events"]
require(event["event_id"] == eid, "IO event ID")
require(ids == sorted(set(ids)) and eid not in ids and len(recent) <= 256, "event history")
require(len({x["event_id"] for x in recent}) == len(recent) and
all(x["event_id"] in ids for x in recent), "recent IDs")
result["counter"] += 1
result["published_ids"] = sorted(ids + [eid])
result["recent_events"] = (recent[1:] if len(recent) == 256 else recent) + [event]
return result
def exact(row):
require(row["method"] == "var_to_bytes" and row["equal"] is True and row["diff_paths"] == [],
"typed bytes differ")
require(row["excluded_paths"] == [], "state path exclusion")
require(re.fullmatch("[0-9a-f]{64}", row["expected_sha256"]) and
row["actual_sha256"] == row["expected_sha256"], "typed comparison contradiction")
def check_io(row, kind):
require(row["kind"] == kind and row["signal_count"] == 1, "one IO signal required")
signal, latest, ev = row["signal_payload"], row["latest"], row["event"]
require(signal == latest == ev["payload"], "signal/latest/event mismatch")
require(row["signal_projection"] == "_save_event_payload" and row["payload_bytes_equal"] is True,
"typed payload comparison")
success = kind != "tamper"
require(type(signal["ok"]) is bool and signal["ok"] is success and
((signal["code"] == "OK") is success), "IO result")
require(signal["operation"] == ("save" if kind == "save" else "load"), "operation")
require(signal["save_path"].startswith("user://p28_restart/") and ".." not in signal["save_path"],
"private IO path")
require(row["return_kind"] == ("bool" if kind == "save" else "void"), "return kind")
require(row["return_value"] is signal["ok"] if kind == "save" else row["return_value"] is None,
"return/signal mismatch")
expected_type = {"save": "SAVE_CHECKPOINT_SUCCEEDED", "load": "SAVE_LOADED",
"tamper": "SAVE_LOAD_FAILED"}[kind]
require(ev["event_type"] == expected_type and ev["source_service"] == "save_service" and
ev["entity_ids"] == [] and ev["causation_command_id"] == 0, "event shape")
require(ev["absolute_minute"] == row["minute_after"] == row["minute_before"], "IO minute")
require(row["after"] == transform(row["before"], ev), "not X(P,event)")
require(row["dropped"] == (row["before"]["recent_events"][0]
if len(row["before"]["recent_events"]) == 256 else None), "wrong dropped head")
exact(row["comparison"])
if success:
require(signal["saved_absolute_minute"] == row["minute_after"] and bool(signal["save_id"]),
"save identity")
require(row["paused_after"] is True, "IO clock not paused")
else:
failure = row["failure"]
require(failure["preload_pause_restored"] is True and
failure["loaded_clock_paused"] is row["paused_before"] and
row["paused_after"] is row["paused_before"], "tamper pause")
if failure["recovery_report"] is not None:
require(failure["recovery_report"]["restore_committed"] is False, "tamper restore")
require(type(failure["migration_attempted"]) is bool, "migration observation")
def check_report(report):
check_no_metrics(report)
require(report["schema"] == "p28-restart-v1" and report["stop_reason"] == "complete", "incomplete probe")
require(report["role"] in PHASES[3:] and type(report["pid"]) is int and report["pid"] > 0, "role/PID")
require(report["main_added"] is True and report["k_target"] == 30 and report["actual_k"] == 30, "main/K")
require(report["comparison_scope"] == "complete_typed_var_to_bytes" and report["excluded_paths"] == [],
"partial digest/path exclusion")
require(report["transfer_before_guard_writes"] == 0 and report["probe_deadline_seconds"] == 1740,
"profile/deadline")
writer = report["role"].endswith("writer")
kinds = ["save"] if writer else ["tamper", "load"]
require(len(report["io"]) == len(kinds), "IO sequence")
for row, kind in zip(report["io"], kinds):
check_io(row, kind)
if writer:
exact(report["package_matches_before"])
require(report["package_matches_before"]["actual_sha256"] == report["states"]["P"]["sha256"],
"P not package snapshot")
else:
require(report["failure_event_absent"] is True and report["transfer_verified"] is True,
"failed event survived/transfer not verified")
io = report["io"][-1]
require(report["event_id"] == io["event"]["event_id"] and report["saved_minute"] == io["minute_after"],
"continuation IO identity")
require(report["paused_idle"]["before"] == report["paused_idle"]["after"], "paused clock consumed backlog")
require(report["paused_idle"]["before"]["paused"] is True, "paused observation")
exact(report["paused_idle"]["comparison"])
rows = report["minutes"]
require(len(rows) == 30, "actual K not 30")
previous, lost = report["saved_minute"], False
for row in rows:
require(row["before"] == previous and row["after"] == previous + 1, "K counts actual minute advances")
require(row["paused"] is True and row["backlog_before"] == row["backlog_after"], "step consumed backlog")
require(row["scope"] == "complete_typed_var_to_bytes" and row["excluded_paths"] == [], "partial state")
require(re.fullmatch("[0-9a-f]{64}", row["sha256"]), "minute SHA")
if row["replacement"] is not None:
require(not lost and row["replacement"] == io["event"] and row["replacement_count"] == 1,
"replacement must be unchanged in-window E")
else:
lost = True
require(row["replacement_count"] == 0, "replacement after E left window")
require(0 <= row["recent_count"] <= 256, "recent limit")
require(len(row["recent_ids"]) == len(set(row["recent_ids"])) == row["recent_count"] and
row["recent_ids"].count(report["event_id"]) == row["replacement_count"],
"replacement does not match actual window")
previous = row["after"]
require(report["final_minute"] == previous, "final minute")
for name, entry in report["states"].items():
require(re.fullmatch("[0-9a-f]{64}", entry["sha256"]) and entry["bytes"] > 0 and
entry["path"] == "p28_restart/evidence/" + name + ".bin", "state binding")
required = {"P", "after_save", "final"} if writer else {"P", "B", "after_tamper", "after_load", "final"}
require(set(report["states"]) == required, "missing complete state files")
require(report["states"]["after_save" if writer else "after_load"]["sha256"] ==
io["comparison"]["actual_sha256"], "IO state file differs")
if not writer:
require(report["states"]["after_tamper"]["sha256"] == report["io"][0]["comparison"]["actual_sha256"],
"tamper state file differs")
if report["role"].startswith("O-"):
boundary = report["boundary"]
require(boundary is not None and boundary["guest_auto_count"] > 0, "arrival not processed")
require(len(boundary["guest_auto_ids"]) == len(set(boundary["guest_auto_ids"])) ==
boundary["guest_auto_count"], "arrival IDs")
require(all(x.startswith("guest-auto-") for x in boundary["guest_auto_ids"]), "arrival identity")
require(boundary["clock"] == report["saved_minute"] and boundary["processed_arrival_minutes"],
"arrival boundary not processed")
if writer:
require(0 < report["saved_minute"] - report["start_minute"] <= 120, "arrival cap")
ticks = report["ui_ticks"]
require(ticks and ticks[-1] == boundary and all(t["guest_auto_count"] == 0 for t in ticks[:-1]),
"not first arrival tick")
next_boundary = report["next_boundary"]
added = [m for m in next_boundary["after"] if m not in next_boundary["before"]]
require(next_boundary["before"] == boundary["processed_arrival_minutes"] and
added == [next_boundary["minute"]] and next_boundary["processed_once"] is True and
next_boundary["minute"] > boundary["clock"] and
next_boundary["minute"] <= report["final_minute"], "next arrival missing/duplicate")
minutes = next_boundary["after"]
require(minutes == sorted(set(minutes)), "duplicate processed arrivals")
require([r["minute"] for r in next_boundary["arrival_reports"]] == minutes,
"arrival report duplicates/omissions")
else:
require(report["boundary"] is None and report["next_boundary"] is None, "R Operations boundary")
return True
def check_no_metrics(value):
if isinstance(value, dict):
for key, child in value.items():
# Mandatory verbatim domain/IO payloads contain product diagnostics.
# They are evidence, never performance measurements or verdicts.
if key in ("payload", "signal_payload", "latest"):
continue
require(not key.endswith(("_us", "_ms")) and key not in
("performance", "statistics", "fps", "budget_verdict"), "performance field forbidden")
check_no_metrics(child)
elif isinstance(value, list):
for child in value:
check_no_metrics(child)
def check_pair(writer, reader):
check_report(writer)
check_report(reader)
require(writer["role"].replace("writer", "reader") == reader["role"] and writer["pid"] != reader["pid"],
"pair roles/PID")
require(writer["event_id"] == reader["event_id"] and writer["saved_minute"] == reader["saved_minute"],
"pair IO identity")
require(writer["states"]["P"] == reader["states"]["P"], "package P differs")
require(writer["io"][0]["before"] == reader["io"][1]["before"], "package history differs")
for left, right in zip(writer["minutes"], reader["minutes"]):
require(all(left[k] == right[k] for k in
("before", "after", "sha256", "scope", "replacement_count", "recent_count")),
"cross-PID complete typed state mismatch")
require(writer["boundary"] == reader["boundary"] and writer["next_boundary"] == reader["next_boundary"],
"Operations boundary differs")
def bound_raw(folder, row):
folder = Path(folder)
require(set(row["raw"]) == {"stdout.log", "stderr.log", "engine-native.log"}, "all raw required")
for name, expected in {**row["raw"], **row["owned_files"]}.items():
require(Path(name).name == name and pin(folder / name) == expected, "raw/owned SHA mismatch")
require(set(row["owned_files"]) == {"process.json", "started.json", "ended.json",
"adapter-owned-cleanup.json", "outer-owned-finally.json"}, "owned receipts missing")
require(json.loads((folder / "process.json").read_text()) == row["process"], "process receipt mismatch")
process, capture = row["process"], row["capture"]
require(row["passed"] is True and process["exit_code"] == 0 and process["helper_exit_code"] == 0 and
process["timed_out"] is False and process["error_lines"] == [] and process["lifecycle_error"] is None and
process["external_process_or_group_signals"] == 0 and process["owned_popen_ended"] is True and
process["old_helper_engine_ended"] is True and process["start_receipt_written"] is True and
process["end_receipt_written"] is True, "owned phase failed")
require(capture["passed"] is True and capture["all_captured_ended"] is True and
capture["outer_cleanup_errors"] == [] and capture["adapter_exception"] is None and
capture["foreign_process_or_group_signals"] == 0 and
capture["all_cleanup_before_outer_receipt"] is True and capture["outer_receipt_written"] is True and
capture["outer_receipt_error"] is None and len(capture["actual_captured_pids"]) == 1, "outer capture")
actual = json.loads((folder / "outer-owned-finally.json").read_text())
require(actual == {k: v for k, v in capture.items() if k not in
("passed", "outer_receipt_written", "outer_receipt_error")}, "outer raw differs")
pid = capture["actual_captured_pids"][0]
require(process["pid"] == pid and process["argv"] == row["argv"], "process PID/argv")
for filename, key in (("started.json", "actual_start_binding"), ("ended.json", "actual_end_binding")):
binding = json.loads((folder / filename).read_text())
require(binding == process[key] and binding["pid"] == binding["actual_pid"] == pid and
binding["argv"] == binding["command"] == row["argv"] and binding["owner_pid"] > 0,
"START/END binding")
require(process["actual_start_binding"]["owner_pid"] == process["actual_end_binding"]["owner_pid"],
"START/END owners differ")
require(json.loads((folder / "adapter-owned-cleanup.json").read_text()) == process["adapter_owned_cleanup"]
and process["adapter_owned_cleanup"]["engine_ended"] is True and
process["adapter_owned_cleanup"]["errors"] == [], "adapter cleanup binding")
require(process["stdout_sha256"] == row["raw"]["stdout.log"]["sha256"] and
process["stderr_sha256"] == row["raw"]["stderr.log"]["sha256"], "process raw binding")
require(row["timeout_seconds"] == 1800 and row["diagnostics"] == [], "phase timeout/diagnostics")
raw = (folder / "stdout.log").read_text()
for name in ("stdout.log", "stderr.log", "engine-native.log"):
require(not re.search(r"SCRIPT ERROR|Parse Error|ERROR:", (folder / name).read_text()),
"raw engine error")
if row["phase"] not in PHASES[3:]:
require("P28_RESTART_REPORT=" not in raw, "parse/import report")
if row["phase"].startswith("parse"):
require("--check-only" in row["argv"], "parse not check-only")
return None
reports = [s.partition("=")[2] for s in raw.splitlines() if s.startswith("P28_RESTART_REPORT=")]
closes = [s.partition("=")[2] for s in raw.splitlines() if s.startswith("P28_RESTART_AUDIO_CLOSE=")]
require(len(reports) == len(closes) == 1, "report/audio close count")
report, close = json.loads(reports[0]), json.loads(closes[0])
require(report == row["report"] and report["role"] == row["phase"] and
capture["actual_captured_pids"] == [report["pid"]], "raw report/PID binding")
require(close.get("coordinator") is True and close.get("passed") is True and
close.get("owned_engine_pid") == report["pid"] and close.get("all_owned_targets_released") is True and
close.get("deadline_reached") is False and close.get("clock_process_held_stopped") is True and
close.get("window_input_held_disabled") is True and close.get("restored_before_quit") is False and
close.get("shared_bus_deleted") is False and close.get("settings_mutated") is False, "audio close")
check_report(report)
return report
def check_receipt(receipt, out):
from restart_pair import COLLECTOR_PINS, check_transfer
out = Path(out)
require(receipt["passed"] is True and receipt["error"] is None and receipt["cleanup_errors"] == [],
"execution failed")
require(receipt["source_before"] == receipt["source_after"] and len(receipt["source_before"]) == 1407,
"source map mismatch")
require(receipt["default_player_before"] == receipt["default_player_after"], "ordinary player changed")
require(receipt["dependencies_before"] == receipt["dependencies_after"] == DEPENDENCIES, "dependency pins")
require(receipt["collector_before"] == receipt["collector_after"] == COLLECTOR_PINS,
"collector changed")
require(receipt["worker_before"] == receipt["worker_after"] and set(receipt["worker_before"]) ==
{"restart_pair.py", "restart_probe.gd", "check_restart.py", "test_restart.py"}, "worker changed")
require(receipt["cross_cutting"] == CROSS, "cross-cutting record")
require(receipt["original_credit"] == 0 and receipt["windows_verified"] is False and
receipt["operations_200_guests_verified"] is False, "scope expanded")
rows = receipt["phases"]
require([r["phase"] for r in rows] == PHASES, "phase order/incomplete execution")
pids = []
for row in rows:
report = bound_raw(out / row["phase"], row)
pids.extend(row["capture"]["actual_captured_pids"])
require(row["stage_before"] == row["stage_after"] if row["phase"] != "import"
else row["stage_preserved"] is True, "stage changed")
require(row["source_preserved"] is True and row["player_preserved"] is True, "phase preservation")
startup = row["startup_project"]
require(startup["fresh_before"] is True and sha(startup["path"]) == startup["sha256"], "startup profile")
if report:
guard = row["guard"]
expected = {"verified": True, "conditions": [True] * 5, "conditions_checked": 5,
"actual_user_data_dir": row["runtime_profile"],
"expected_user_data_dir": row["runtime_profile"],
"globalized_probe": str(Path(row["runtime_profile"]) / "guard-probe"),
"custom_user_dir_name": row["runtime_name"], "fresh_before_parent_initialize": True,
"profile_writes_before_guard": 0, "project_settings_persisted": False}
require(all(type(guard.get(k)) is type(v) and guard[k] == v for k, v in expected.items()),
"guard mismatch")
guard_path = out / (row["phase"] + "-guard.json")
require(sha(guard_path) == row["guard_sha256"] and json.loads(guard_path.read_text()) == guard,
"guard raw binding")
for name, evidence in report["states"].items():
require(pin(out / (row["phase"] + "-states") / (name + ".bin")) ==
{k: evidence[k] for k in ("sha256", "bytes")}, "state bytes SHA")
require(len(set(pids)) == len(pids), "reused PID")
require({r["pid"] for r in receipt["cleanup"]} == set(pids) and all(
r["engine_ended"] is True and r["outer_cleanup_errors"] == [] and
r["foreign_process_or_group_signals"] == 0 for r in receipt["cleanup"]), "cleanup mismatch")
for start in (3, 5):
check_pair(rows[start]["report"], rows[start + 1]["report"])
transfer = out / (rows[start]["phase"][0] + "-transfer")
manifest_path = transfer / "manifest.json"
manifest = json.loads(manifest_path.read_text())
check_transfer(transfer, manifest)
require(rows[start + 1]["report"]["transfer_manifest_sha256"] == sha(manifest_path),
"reader manifest SHA")
for phase_index, kinds in ((start, ("good",)), (start + 1, ("good", "tampered"))):
profile = rows[phase_index]["runtime_profile"]
for kind in kinds:
for name, expected in manifest["files"][kind].items():
relative = "p28_restart/" + kind + "/" + name
require(receipt["profiles"][profile].get(relative) == expected,
"private package differs from transfer")
for path, expected in receipt["script_pins"].items():
require(sha(path) == expected, "executed script changed")
check_probe((out / "restart_probe.gd").read_text())
require(sha(out / "restart_probe.gd") == receipt["worker_before"]["restart_probe.gd"],
"executed probe differs from submitted source")
require(receipt["owned_writes"] == [], "probe/driver wrote owned folder")
return True
def main():
import argparse
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("execution", type=Path)
args = parser.parse_args()
check_receipt(json.loads(args.execution.read_text()), args.execution.parent)
print("P28_RESTART_CHECK=PASS")
if __name__ == "__main__":
main()
@@ -0,0 +1,310 @@
#!/usr/bin/env python3
"""Root-only four-PID RESTART driver. --check performs no writes or launches."""
import argparse
import importlib.util
import json
import os
from pathlib import Path
import shutil
import subprocess
import sys
import uuid
sys.dont_write_bytecode = True
import check_restart as check
HERE, ROOT = check.HERE, check.ROOT
COLLECTOR = check.BASE + "collector/collect_p28_01.py"
COLLECTOR_PINS = {
"collect_p28_01.py": check.DEPENDENCIES[COLLECTOR],
"check_p28_01_report.py": check.DEPENDENCIES[check.BASE + "collector/check_p28_01_report.py"],
"measure_p28_01.gd": "4acef88bce4ac77f3f5d2659d766afcc5fd139cee381118839012022772946bb",
"test_collect_p28_01.py": "99d86ea723c976c95dbd283acbb85981eabf427b30994e652002ec0e76dc4ab0",
}
def load_dependencies():
check.check_dependencies()
for name in check.DEPENDENCIES:
check.check_imports((ROOT / name).read_text(), external=name)
for name in ("restart_pair.py", "check_restart.py", "test_restart.py"):
check.check_imports((HERE / name).read_text())
check.check_modules()
sys.path.insert(0, str((ROOT / COLLECTOR).parent))
spec = importlib.util.spec_from_file_location("restart_collector", ROOT / COLLECTOR)
collector = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = collector
spec.loader.exec_module(collector)
standard = collector.load_tool(collector.EXTERNAL_IMPORTS[0])
outer = collector.load_tool(collector.EXTERNAL_IMPORTS[1])
check.check_modules()
check.check_modules({"collector": collector, "standard": standard, "outer": outer, "native": outer.native})
check.require(Path(outer.native.helper["__file__"]).resolve() ==
ROOT / "tools/regression/p09_owned_engine_process.py", "exec helper closure")
return collector, standard, outer
def collector_pins():
actual = {name: check.sha((ROOT / COLLECTOR).parent / name) for name in COLLECTOR_PINS}
check.require(actual == COLLECTOR_PINS, "collector differs from Q")
return actual
def plan(collector, engine, out):
stage, wrapper = out / "stage", out / "guarded.gd"
return [collector.argv_for(engine, stage, wrapper, True),
collector.parse_argv(engine, stage, out / "restart_probe.gd"),
collector.parse_argv(engine, stage, wrapper)] + [
collector.argv_for(engine, stage, wrapper) for _ in range(4)]
def schedule(rows):
for index, label in enumerate(check.PHASES):
check.require(all(row["passed"] is True for row in rows), "previous phase failed")
if label.endswith("reader"):
check.require(rows and rows[-1]["phase"] == label.replace("reader", "writer") and
rows[-1].get("report", {}).get("stop_reason") == "complete",
"reader requires successful writer")
yield index, label
check.require(len(rows) == index + 1 and rows[-1]["phase"] == label and rows[-1]["passed"] is True,
"phase did not complete")
def safe_inventory(directory):
directory = Path(directory)
check.require(directory.is_dir() and not directory.is_symlink(), "package missing/symlink")
result = {}
for path in sorted(directory.rglob("*")):
check.require(not path.is_symlink(), "package symlink")
if path.is_file():
result[path.relative_to(directory).as_posix()] = check.pin(path)
check.require(result, "empty package")
return result
def create_transfer(source, destination, write):
"""Copy only after writer exit; never pre-populate the reader profile."""
before = safe_inventory(source)
check.require("world.bin" in before and not destination.exists(), "transfer admission")
destination.mkdir()
shutil.copytree(source, destination / "good")
check.require(safe_inventory(destination / "good") == before, "good copy differs")
shutil.copytree(destination / "good", destination / "tampered")
world = destination / "tampered/world.bin"
data = bytearray(world.read_bytes())
check.require(data, "empty world")
offset = len(data) // 2
data[offset] ^= 1
world.write_bytes(data)
manifest = {"files": {"good": before, "tampered": safe_inventory(destination / "tampered")},
"tamper": {"file": "world.bin", "offset": offset, "xor": 1,
"before_sha256": before["world.bin"]["sha256"], "after_sha256": check.sha(world)}}
write(destination / "manifest.json", manifest)
check_transfer(destination, manifest)
check.require(safe_inventory(source) == before, "writer package changed")
return manifest
def check_transfer(directory, manifest):
check.require(set(manifest["files"]) == {"good", "tampered"}, "transfer kinds")
for kind in ("good", "tampered"):
check.require(safe_inventory(directory / kind) == manifest["files"][kind], "transfer SHA mismatch")
good = (directory / "good/world.bin").read_bytes()
bad = (directory / "tampered/world.bin").read_bytes()
tamper = manifest["tamper"]
check.require(len(good) == len(bad) > 0 and tamper["offset"] == len(good) // 2 and tamper["xor"] == 1,
"tamper location")
expected = bytearray(good)
expected[len(good) // 2] ^= 1
check.require(bytes(expected) == bad and tamper["before_sha256"] == check.sha(directory / "good/world.bin")
and tamper["after_sha256"] == check.sha(directory / "tampered/world.bin"), "tamper bytes")
for name, value in manifest["files"]["good"].items():
if name != "world.bin":
check.require(manifest["files"]["tampered"].get(name) == value, "extra tamper difference")
check.require(set(manifest["files"]["good"]) == set(manifest["files"]["tampered"]), "tamper inventory")
def fresh_profile(path):
check.require(not path.exists(), "profile must be fresh; pre-guard writes forbidden")
def committed_source(source, commit):
"""Read-only Git operations: the 1407 files must be the actual HEAD bytes."""
tracked = subprocess.check_output(["git", "-C", str(ROOT), "ls-tree", "-r", commit], text=True)
selected = {}
for line in tracked.splitlines():
metadata, name = line.split("\t", 1)
if name.split("/", 1)[0] in ("autoload", "scripts", "scenes", "data", "tests", "assets", "tools") or \
name in ("project.godot", "export_presets.cfg", "Makefile", "AGENTS.md"):
if Path(name).name != ".DS_Store" and "__pycache__" not in Path(name).parts:
selected[name] = metadata.split()[2]
check.require(set(selected) == set(source), "source map differs from HEAD")
for name, expected in selected.items():
actual = subprocess.check_output(["git", "-C", str(ROOT), "hash-object", str(ROOT / name)], text=True).strip()
check.require(actual == expected, "dirty product source: " + name)
def bind_report(out, label, row):
text = (out / label / "stdout.log").read_text()
reports = [s.partition("=")[2] for s in text.splitlines() if s.startswith("P28_RESTART_REPORT=")]
check.require(len(reports) == 1, "one raw report required")
row["report"] = json.loads(reports[0])
check.bound_raw(out / label, row)
return row["report"]
def copy_states(out, row):
folder = out / (row["phase"] + "-states")
folder.mkdir()
profile = Path(row["runtime_profile"])
for name, expected in row["report"]["states"].items():
source = profile / expected["path"]
check.require(source.resolve().is_relative_to(profile.resolve()) and
check.pin(source) == {k: expected[k] for k in ("bytes", "sha256")}, "state binding")
shutil.copyfile(source, folder / (name + ".bin"))
check.require(check.pin(source) == check.pin(folder / (name + ".bin")), "state copy differs")
def main():
parser = argparse.ArgumentParser(description=__doc__)
mode = parser.add_mutually_exclusive_group(required=True)
mode.add_argument("--check", action="store_true")
mode.add_argument("--run", action="store_true")
for name in ("readonly", "fixture", "engine", "output"):
parser.add_argument("--" + name, type=Path, required=True)
args = parser.parse_args()
collector, standard, outer = load_dependencies()
pinned = collector_pins()
readonly, fixture, engine, out = (p.resolve() for p in (args.readonly, args.fixture, args.engine, args.output))
inputs = collector.inputs(readonly)
check.require(check.sha(fixture) == collector.FIXTURE_SHA and check.sha(engine) == collector.ENGINE_SHA,
"fixture/engine SHA")
check.check_probe((HERE / "restart_probe.gd").read_text())
source = collector.source_map(standard, ROOT)
check.require(len(source) == 1407, "source count")
commit = subprocess.check_output(["git", "-C", str(ROOT), "rev-parse", "HEAD"], text=True).strip()
committed_source(source, commit)
worker_pins = {name: check.sha(HERE / name) for name in
("restart_pair.py", "restart_probe.gd", "check_restart.py", "test_restart.py")}
commands = plan(collector, engine, out)
admission = {"commit": commit, "source_map": source, "readonly": inputs, "planned_argv": commands,
"engine_sha256": check.sha(engine), "fixture_sha256": check.sha(fixture),
"dependencies": check.DEPENDENCIES, "collector_Q": pinned,
"worker_pins": worker_pins,
"phases": check.PHASES, "engines_started": 0}
check.require(not out.exists(), "new output required")
if args.check:
print(json.dumps(admission, ensure_ascii=False, indent=2))
return 0
check.require(sys.platform == "darwin", "reviewed native platform is macOS")
collector.no_live_engine()
check.require(shutil.disk_usage(out.parent).free >= 1200000000, "disk space")
player = standard.default_player(ROOT)
out.mkdir(parents=True)
collector.write(out / "admission.json", admission)
rows, profiles = [], []
error = None
scripts = {}
receipt = {}
try:
stage = out / "stage"
stage.mkdir()
for name in source:
target = stage / name
target.parent.mkdir(parents=True, exist_ok=True)
standard.copy_source(ROOT / name, target)
expected = standard.inventory(stage, stage=True)
check.require(expected == source, "stage source differs")
probe, guard, wrapper = (out / n for n in ("restart_probe.gd", "profile_guard.gd", "guarded.gd"))
probe.write_bytes((HERE / probe.name).read_bytes())
guard.write_bytes((readonly / "run-r02/profile_guard.gd").read_bytes())
wrapper.write_text("extends " + json.dumps(str(probe)) + "\nconst Guard = preload(" +
json.dumps(str(guard)) + ")\nfunc _initialize() -> void:\n"
"\tif not Guard.activate():\n\t\tquit(97)\n\t\treturn\n\tsuper._initialize()\n")
scripts = {str(p): check.sha(p) for p in (probe, guard, wrapper)}
public = (stage / "project.godot").read_bytes()
for index, label in schedule(rows):
check.check_dependencies()
collector_pins()
check.check_modules()
check.require(collector.source_map(standard, ROOT) == source and standard.default_player(ROOT) == player,
"source/player changed before phase")
check.require(standard.inventory(stage, stage=True) == expected and
all(check.sha(p) == value for p, value in scripts.items()) and
all(check.sha(HERE / p) == value for p, value in worker_pins.items()),
"stage/scripts changed")
startup_name = "TheTree-PCK-tests-" + uuid.uuid4().hex + "-restart-startup"
runtime_name = "TheTree-PCK-tests-" + uuid.uuid4().hex + "-restart-runtime"
startup, runtime = (standard.profile_path(n) for n in (startup_name, runtime_name))
fresh_profile(startup)
fresh_profile(runtime)
profiles.extend([startup, runtime])
transfer = out / (label[0] + "-transfer")
env = {"THE_TREE_PACK_PROFILE_NAME": runtime_name, "THE_TREE_PACK_PROFILE_EXPECTED": str(runtime),
"THE_TREE_PACK_PROFILE_REPORT": str(out / (label + "-guard.json")),
"THE_TREE_RESTART_ROLE": label, "THE_TREE_RESTART_K": "30",
"THE_TREE_RESTART_TRANSFER": str(transfer),
"THE_TREE_RESTART_TRANSFER_MANIFEST": str(transfer / "manifest.json"),
"THE_TREE_P28_FIXTURE": str(fixture), "THE_TREE_P28_FIXTURE_SHA": collector.FIXTURE_SHA}
if label.endswith("reader"):
check_transfer(transfer, json.loads((transfer / "manifest.json").read_text()))
before_stage = collector.digest(expected)
row = collector.run_private_engine(standard, outer, commands[index], out / label, env,
stage, public, startup_name)
row["phase"] = label
rows.append(row)
check.require(row["passed"], "owned engine failure: " + label)
after_stage = standard.inventory(stage, stage=True)
collector.stage_gate(expected, after_stage, importing=label == "import")
row.update(stage_before=before_stage, stage_after=collector.digest(after_stage), stage_preserved=True,
source_preserved=collector.source_map(standard, ROOT) == source,
player_preserved=standard.default_player(ROOT) == player)
expected = after_stage
if label in check.PHASES[3:]:
guard_path = out / (label + "-guard.json")
observation = json.loads(guard_path.read_text())
collector.guard_gate(observation, runtime, runtime_name)
row.update(runtime_profile=str(runtime), runtime_name=runtime_name,
guard=observation, guard_sha256=check.sha(guard_path))
report = bind_report(out, label, row)
copy_states(out, row)
collector.write(out / (label + "-report.json"), report)
if label.endswith("writer"):
create_transfer(runtime / "p28_restart/good", transfer, collector.write)
else:
check.require(report["transfer_manifest_sha256"] == check.sha(transfer / "manifest.json"),
"reader transfer manifest binding")
check.check_pair(rows[-2]["report"], report)
else:
check.bound_raw(out / label, row)
check.require(row["source_preserved"] and row["player_preserved"] and
check.sha(fixture) == collector.FIXTURE_SHA and
all(check.sha(p) == value for p, value in scripts.items()), "post-phase preservation")
except BaseException as caught:
error = repr(caught)
finally:
cleanup, cleanup_errors = outer.finish_captured(outer.OWNED)
receipt = {"admission": admission, "phases": rows, "error": error, "passed": error is None,
"source_before": source, "source_after": collector.source_map(standard, ROOT),
"default_player_before": player, "default_player_after": standard.default_player(ROOT),
"dependencies_before": check.DEPENDENCIES,
"dependencies_after": {p: check.sha(ROOT / p) for p in check.DEPENDENCIES},
"collector_before": pinned,
"collector_after": {p: check.sha((ROOT / COLLECTOR).parent / p) for p in pinned},
"script_pins": scripts, "cleanup": cleanup, "cleanup_errors": cleanup_errors,
"worker_before": worker_pins,
"worker_after": {p: check.sha(HERE / p) for p in worker_pins},
"profiles": {str(p): collector.profile_files(p) for p in profiles}, "owned_writes": [],
"cross_cutting": check.CROSS, "original_credit": 0,
"windows_verified": False, "operations_200_guests_verified": False}
if receipt["passed"]:
try:
check.check_receipt(receipt, out)
except (ValueError, KeyError, TypeError, OSError) as caught:
receipt.update(passed=False, error="checker: " + repr(caught))
collector.write(out / "execution.json", receipt)
return 0 if receipt["passed"] else 1
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,448 @@
extends SceneTree
## Root-owned execution only. All files are written after Guard.activate().
var GameClock: Variant
var runtime: Variant
var main: Variant
var report: Dictionary = {}
var signals: Array = []
var key_forms: Dictionary = {}
var role := ""
var start_minute := 0
var selected_tick := false
var overloaded := false
var io_ready := false
var finished := false
var E := ""
var package: Dictionary = {}
var failure_event: Dictionary = {}
var boundary: Variant = null
func field(d: Variant, name: String) -> Variant:
if d is Dictionary:
if d.has(StringName(name)):
key_forms[name] = "StringName"
return d[StringName(name)]
if d.has(String(name)):
key_forms[name] = "String"
return d[String(name)]
key_forms[name] = "missing_product_field"
return null
func _initialize() -> void:
call_deferred("begin")
func bytes_hash(data: PackedByteArray) -> String:
var context := HashingContext.new()
context.start(HashingContext.HASH_SHA256)
context.update(data)
return context.finish().hex_encode()
func runtime_part(snapshot: Dictionary) -> Dictionary:
return field(field(snapshot, "systems"), "runtime")
func witness(snapshot: Dictionary) -> Dictionary:
var part := runtime_part(snapshot)
var events: Dictionary = field(part, "domain_events")
return {"counter": field(field(field(part, "id_generator"), "counters"), "domain_event"),
"published_ids": field(events, "published_ids").duplicate(true),
"recent_events": field(events, "recent_events").duplicate(true)}
func different_paths(a: Variant, b: Variant, path := "$", depth := 0) -> Array:
var result: Array = []
if var_to_bytes(a) == var_to_bytes(b):
return result
if depth >= 4 or typeof(a) != typeof(b):
return [path]
if a is Dictionary:
for key: Variant in a:
if not b.has(key):
result.append(path + "." + str(key))
else:
result.append_array(different_paths(a[key], b[key], path + "." + str(key), depth + 1))
for key: Variant in b:
if not a.has(key):
result.append(path + "." + str(key))
elif a is Array and a.size() == b.size():
for index in range(a.size()):
result.append_array(different_paths(a[index], b[index], path + "[%d]" % index, depth + 1))
else:
result.append(path)
# Typed container/key/order differences can exist without value differences.
if result.is_empty():
result.append(path)
return result
func compare(expected: Dictionary, actual: Dictionary) -> Dictionary:
var a := var_to_bytes(expected)
var b := var_to_bytes(actual)
return {"method": "var_to_bytes", "equal": a == b, "expected_sha256": bytes_hash(a),
"actual_sha256": bytes_hash(b), "excluded_paths": [],
"diff_paths": different_paths(expected, actual)}
func record_state(name: String, value: Dictionary) -> bool:
var path := "user://p28_restart/evidence/" + name + ".bin"
if DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(path).get_base_dir()) != OK:
return false
var data := var_to_bytes(value)
var file := FileAccess.open(path, FileAccess.WRITE)
if file == null:
return false
file.store_buffer(data)
file.close()
report.states[name] = {"path": path.trim_prefix("user://"), "bytes": data.size(),
"sha256": bytes_hash(data)}
return FileAccess.get_sha256(path) == report.states[name].sha256
func on_save(result: Dictionary) -> void:
signals.append(result.duplicate(true))
func auto_ids() -> Array:
var ids: Array = []
for guest: Variant in runtime.guest_service.get_guests():
if str(guest.guest_id).begins_with("guest-auto-"):
ids.append(str(guest.guest_id))
ids.sort()
return ids
func operations_observation() -> Dictionary:
var ids := auto_ids()
var processed: Array = []
var arrivals: Array = []
if runtime.operations_coordinator != null:
var state: Dictionary = runtime.operations_coordinator.create_snapshot()
processed = field(state, "processed_arrival_minutes").duplicate()
arrivals = runtime.operations_coordinator.get_arrival_reports().duplicate(true)
return {"clock": GameClock.absolute_minute, "guest_auto_ids": ids, "guest_auto_count": ids.size(),
"operations": runtime.get_operations_report(), "processed_arrival_minutes": processed,
"arrival_reports": arrivals}
func on_tick(minute: int) -> void:
if io_ready or finished or selected_tick:
return
var observed := operations_observation()
if role == "O-writer":
report.ui_ticks.append(observed)
if minute == 390:
report["at_390"] = observed.duplicate(true)
report.at_390["domain_events"] = field(runtime_part(runtime.create_snapshot()), "domain_events")
var stop: bool = (role == "R-writer" and minute - start_minute >= 10) or \
(role == "O-writer" and (observed.guest_auto_count > 0 or minute - start_minute >= 120))
if stop:
GameClock.set_paused(true)
selected_tick = true
func on_overload(backlog: int) -> void:
overloaded = true
report.overloads.append({"minute": GameClock.absolute_minute, "backlog": backlog})
func clock_observation() -> Dictionary:
return {"minute": GameClock.absolute_minute, "paused": GameClock.paused,
"backlog": GameClock.get_backlog_minutes(),
"accumulator_hex": PackedFloat64Array([GameClock.get("_accumulator")]).to_byte_array().hex_encode()}
func begin() -> void:
GameClock = root.get_node("GameClock")
runtime = root.get_node("SimulationRuntime")
role = OS.get_environment("THE_TREE_RESTART_ROLE")
report = {"schema": "p28-restart-v1", "role": role, "pid": OS.get_process_id(),
"main_added": false, "k_target": 30, "actual_k": 0, "io": [], "states": {},
"minutes": [], "ui_ticks": [], "overloads": [], "boundary": null, "next_boundary": null,
"comparison_scope": "complete_typed_var_to_bytes", "excluded_paths": [],
"transfer_before_guard_writes": 0, "transfer_verified": false,
"probe_deadline_seconds": 1740}
create_timer(1740.0).timeout.connect(func() -> void: finish("probe_deadline"))
if OS.get_environment("THE_TREE_RESTART_K") != "30" or role not in ["R-writer", "R-reader", "O-writer", "O-reader"]:
finish("invalid_environment")
return
# No await before main: the default autoload clock cannot process here.
if role == "R-writer":
var fixture := OS.get_environment("THE_TREE_P28_FIXTURE")
if FileAccess.get_sha256(fixture) != OS.get_environment("THE_TREE_P28_FIXTURE_SHA"):
finish("fixture_sha")
return
var file := FileAccess.open(fixture, FileAccess.READ)
if file == null:
finish("fixture_missing")
return
var value: Variant = file.get_var(false)
file.close()
if not value is Dictionary or not runtime.restore_snapshot(value):
finish("fixture_restore")
return
main = load("res://scenes/main/main.tscn").instantiate()
root.add_child(main)
report.main_added = true
report["startup"] = {"start_report": main.get("_operations_start_report").duplicate(true),
"operations": runtime.get_operations_report(), "clock": clock_observation()}
if role != "R-writer" and not GameClock.paused:
finish("main_did_not_pause")
return
var start_result: Dictionary = report.startup.start_report
var operations_enabled: Variant = field(report.startup.operations, "enabled")
if role == "R-writer":
if operations_enabled != false or field(start_result, "code") != "INVALID_START_TIME":
finish("unexpected_R_start")
return
elif operations_enabled != true or field(start_result, "ok") != true or GameClock.absolute_minute != 360:
finish("unexpected_fresh_start")
return
runtime.save_operation_completed.connect(on_save)
GameClock.tick_completed.connect(on_tick)
GameClock.simulation_overloaded.connect(on_overload)
start_minute = GameClock.absolute_minute
report["start_minute"] = start_minute
if role.ends_with("writer"):
main.call("_set_speed", 1)
while not GameClock.paused and not finished:
await process_frame
if finished:
return
if not selected_tick and not overloaded:
finish("unexpected_ui_stop")
return
if role == "O-writer" and auto_ids().is_empty():
report["boundary"] = null
report["boundary_reason"] = "no_arrival_within_cap" if GameClock.absolute_minute - start_minute >= 120 else "stopped_before_arrival"
finish(report.boundary_reason)
return
if not writer_save():
finish("save_comparison_failed")
return
else:
if not import_transfer():
finish("transfer_failed")
return
if not reader_load():
finish("load_comparison_failed")
return
report["event_id"] = E
report["saved_minute"] = GameClock.absolute_minute
boundary = operations_observation() if role.begins_with("O-") else null
report["boundary"] = boundary
var idle_before: Dictionary = runtime.create_snapshot()
var clock_before := clock_observation()
await process_frame
await process_frame
if finished:
return
var idle_after: Dictionary = runtime.create_snapshot()
report["paused_idle"] = {"before": clock_before, "after": clock_observation(),
"comparison": compare(idle_before, idle_after)}
if not report.paused_idle.comparison.equal or report.paused_idle.before != report.paused_idle.after:
finish("paused_backlog_consumed")
return
io_ready = true
for _index in range(30):
if not deterministic_step():
finish("no_tick_advance_or_invalid_state")
return
# Keep the event loop responsive to deadline/audio while remaining paused.
await process_frame
if finished:
return
if not record_state("final", runtime.create_snapshot()):
finish("state_write_failed")
return
report["final_minute"] = GameClock.absolute_minute
if role.begins_with("O-"):
var last := operations_observation()
var added: Array = []
for minute: Variant in last.processed_arrival_minutes:
if minute not in boundary.processed_arrival_minutes:
added.append(minute)
report["next_boundary"] = {"minute": added[0] if added.size() == 1 else null,
"processed_once": added.size() == 1, "before": boundary.processed_arrival_minutes,
"after": last.processed_arrival_minutes, "arrival_reports": last.arrival_reports,
"guest_auto_ids": last.guest_auto_ids}
if added.size() != 1:
finish("next_arrival_boundary_missing_or_duplicate")
return
finish("complete")
func io_proof(base: Dictionary, after: Dictionary, kind: String, returned: Variant) -> bool:
var row := {"kind": kind, "signal_count": signals.size(),
"return_kind": "bool" if kind == "save" else "void", "return_value": returned}
report.io.append(row)
if signals.size() != 1:
return false
var signal_result: Dictionary = signals[0]
var payload: Dictionary = runtime.call("_save_event_payload", signal_result)
var latest: Dictionary = runtime.latest_save_operation_result.duplicate(true)
row["signal_projection"] = "_save_event_payload"
row["signal_payload"] = payload
row["latest"] = latest
row["payload_bytes_equal"] = var_to_bytes(payload) == var_to_bytes(latest)
var before := witness(base)
var after_witness := witness(after)
var eid := "domain_event.%012d" % (int(before.counter) + 1)
var found: Array = []
for event: Dictionary in after_witness.recent_events:
if field(event, "event_id") == eid:
found.append(event)
if found.size() != 1 or before.published_ids.has(eid):
return false
var event: Dictionary = found[0]
row["event"] = event
row["before"] = before
row["after"] = after_witness
row["minute_before"] = field(field(base, "clock"), "absolute_minute")
row["minute_after"] = field(field(after, "clock"), "absolute_minute")
row["paused_before"] = field(field(base, "clock"), "paused")
row["paused_after"] = field(field(after, "clock"), "paused")
row["dropped"] = before.recent_events[0] if before.recent_events.size() == 256 else null
var expected := base.duplicate(true)
var part := runtime_part(expected)
var counters: Dictionary = field(field(part, "id_generator"), "counters")
counters["domain_event"] = int(before.counter) + 1
var events: Dictionary = field(part, "domain_events")
var ids: Array = field(events, "published_ids")
ids.append(eid)
ids.sort()
var recent: Array = field(events, "recent_events")
if recent.size() > 256:
return false
if recent.size() == 256:
recent.pop_front()
recent.append(event.duplicate(true))
row["comparison"] = compare(expected, after)
var expected_type := {"save": "SAVE_CHECKPOINT_SUCCEEDED", "load": "SAVE_LOADED", "tamper": "SAVE_LOAD_FAILED"}
var ok: bool = row.payload_bytes_equal and row.comparison.equal and \
var_to_bytes(field(event, "payload")) == var_to_bytes(payload) and \
field(event, "event_type") == expected_type[kind] and field(event, "source_service") == "save_service" and \
field(event, "entity_ids").is_empty() and field(event, "causation_command_id") == 0 and \
row.minute_before == row.minute_after and field(event, "absolute_minute") == row.minute_after
if kind == "tamper":
var recovery: Variant = field(signal_result, "recovery_report")
var migration: Variant = field(signal_result, "migration_attempted")
row["failure"] = {"preload_pause_restored": field(signal_result, "preload_pause_restored"),
"loaded_clock_paused": field(signal_result, "loaded_clock_paused"), "recovery_report": recovery,
"migration_attempted": migration == true}
failure_event = event.duplicate(true)
return ok and field(payload, "ok") == false and field(payload, "code") != "OK" and \
row.failure.preload_pause_restored == true and row.failure.loaded_clock_paused == row.paused_before and \
row.paused_before == row.paused_after and (recovery == null or field(recovery, "restore_committed") == false)
E = eid
return ok and field(payload, "ok") == true and field(payload, "code") == "OK" and \
(returned == true if kind == "save" else returned == null) and row.paused_after == true and \
field(payload, "saved_absolute_minute") == row.minute_after
func writer_save() -> bool:
var before: Dictionary = runtime.create_snapshot()
signals = []
var returned: bool = main.call("_save_manual_at_path", "user://p28_restart/good", "P28 RESTART")
var after: Dictionary = runtime.create_snapshot()
var loaded := SaveService.load_snapshot("user://p28_restart/good")
if field(loaded, "ok") != true:
return false
package = field(loaded, "snapshot")
report["package_matches_before"] = compare(before, package)
if not record_state("P", package) or not record_state("after_save", after):
return false
return report.package_matches_before.equal and io_proof(package, after, "save", returned)
func import_transfer() -> bool:
var transfer := OS.get_environment("THE_TREE_RESTART_TRANSFER")
var manifest_path := OS.get_environment("THE_TREE_RESTART_TRANSFER_MANIFEST")
if not transfer.is_absolute_path() or not manifest_path.is_absolute_path():
return false
var manifest: Variant = JSON.parse_string(FileAccess.get_file_as_string(manifest_path))
if not manifest is Dictionary:
return false
for kind: String in ["good", "tampered"]:
var target := "user://p28_restart/" + kind
if DirAccess.dir_exists_absolute(target):
return false
for relative: String in manifest.files[kind]:
if relative.is_absolute_path() or ".." in relative.split("/") or "\\" in relative:
return false
var source := transfer.path_join(kind).path_join(relative)
var expected: Dictionary = manifest.files[kind][relative]
if FileAccess.get_sha256(source) != expected.sha256:
return false
var data := FileAccess.get_file_as_bytes(source)
if data.size() != int(expected.bytes):
return false
var destination := target.path_join(relative)
if DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(destination).get_base_dir()) != OK:
return false
var file := FileAccess.open(destination, FileAccess.WRITE)
if file == null:
return false
file.store_buffer(data)
file.close()
if FileAccess.get_sha256(destination) != expected.sha256:
return false
report["transfer_manifest_sha256"] = FileAccess.get_sha256(manifest_path)
report["transfer_verified"] = true
return true
func reader_load() -> bool:
var loaded := SaveService.load_snapshot("user://p28_restart/good")
if field(loaded, "ok") != true:
return false
package = field(loaded, "snapshot")
var before: Dictionary = runtime.create_snapshot()
if not record_state("P", package) or not record_state("B", before):
return false
signals = []
main.call("_load_manual_at_path", "user://p28_restart/tampered", true)
var failed: Dictionary = runtime.create_snapshot()
if not record_state("after_tamper", failed) or not io_proof(before, failed, "tamper", null):
return false
signals = []
main.call("_load_manual_at_path", "user://p28_restart/good", true)
var after: Dictionary = runtime.create_snapshot()
if not record_state("after_load", after) or not io_proof(package, after, "load", null):
return false
report["failure_event_absent"] = true
for event: Dictionary in witness(after).recent_events:
if var_to_bytes(event) == var_to_bytes(failure_event):
report.failure_event_absent = false
return report.failure_event_absent
func deterministic_step() -> bool:
if not io_ready or not GameClock.paused:
return false
var before: int = GameClock.absolute_minute
var backlog: int = GameClock.get_backlog_minutes()
GameClock.advance_minutes(1)
var after: int = GameClock.absolute_minute
if after != before + 1:
return false
var state: Dictionary = runtime.create_snapshot()
var normalized := state.duplicate(true)
var recent: Array = field(field(runtime_part(normalized), "domain_events"), "recent_events")
var replacement: Variant = null
var count := 0
var recent_ids: Array = []
for index in range(recent.size()):
recent_ids.append(field(recent[index], "event_id"))
if field(recent[index], "event_id") == E:
replacement = recent[index].duplicate(true)
recent[index] = {"event_id": E, "replaced_for_cross_pid_compare": true}
count += 1
if count > 1:
return false
report.minutes.append({"before": before, "after": after, "paused": GameClock.paused,
"backlog_before": backlog, "backlog_after": GameClock.get_backlog_minutes(),
"sha256": bytes_hash(var_to_bytes(normalized)), "scope": "complete_typed_var_to_bytes",
"excluded_paths": [], "replacement": replacement, "replacement_count": count,
"recent_count": recent.size(), "recent_ids": recent_ids})
report.actual_k += after - before
return GameClock.paused and backlog == GameClock.get_backlog_minutes()
func finish(reason: String) -> void:
if finished:
return
finished = true
report["stop_reason"] = reason
report["key_forms"] = key_forms
print("P28_RESTART_REPORT=" + JSON.stringify(report))
if is_instance_valid(main) and main.is_inside_tree():
main.ui_audio_window_close_drained.connect(emit_audio_close, CONNECT_ONE_SHOT)
get_root().close_requested.emit()
else:
quit(1)
func emit_audio_close(result: Dictionary) -> void:
print("P28_RESTART_AUDIO_CLOSE=" + JSON.stringify(result))
@@ -0,0 +1,466 @@
#!/usr/bin/env python3
"""Engine-free positive and adversarial controls. No native acceptance claimed."""
import copy
import hashlib
import json
import os
from pathlib import Path
import sys
import tempfile
import time
from types import SimpleNamespace
import unittest
from unittest.mock import patch
sys.dont_write_bytecode = True
import check_restart as check
import restart_pair as driver
DATA = b"synthetic typed bytes fixture (not engine evidence)"
HASH = hashlib.sha256(DATA).hexdigest()
def comparison():
return {"method": "var_to_bytes", "equal": True, "diff_paths": [], "excluded_paths": [],
"expected_sha256": HASH, "actual_sha256": HASH}
def history(count=256):
ids = ["domain_event.%012d" % n for n in range(1, count + 1)]
return {"counter": count, "published_ids": ids,
"recent_events": [{"event_id": eid, "event_type": "OLD", "payload": {"n": n}}
for n, eid in enumerate(ids)]}
def io(kind, minute=612, count=256):
before = history(count)
payload = {"ok": kind != "tamper", "code": "OK" if kind != "tamper" else "CORRUPT_WORLD",
"operation": "save" if kind == "save" else "load",
"save_path": "user://p28_restart/" + ("tampered" if kind == "tamper" else "good"),
"save_id": "synthetic", "saved_absolute_minute": minute}
event = {"event_id": check.event_id(count),
"event_type": {"save": "SAVE_CHECKPOINT_SUCCEEDED", "load": "SAVE_LOADED",
"tamper": "SAVE_LOAD_FAILED"}[kind],
"source_service": "save_service", "entity_ids": [], "causation_command_id": 0,
"absolute_minute": minute, "payload": copy.deepcopy(payload)}
return {"kind": kind, "signal_count": 1, "signal_payload": payload, "latest": copy.deepcopy(payload),
"event": event, "signal_projection": "_save_event_payload", "payload_bytes_equal": True,
"return_kind": "bool" if kind == "save" else "void",
"return_value": True if kind == "save" else None,
"before": before, "after": check.transform(before, event),
"dropped": before["recent_events"][0] if count == 256 else None,
"comparison": comparison(), "minute_before": minute, "minute_after": minute,
"paused_before": True, "paused_after": True,
"failure": {"preload_pause_restored": True, "loaded_clock_paused": True,
"migration_attempted": False, "recovery_report": {"restore_committed": False}}}
def report(role="R-writer", pid=100):
writer, ops = role.endswith("writer"), role.startswith("O-")
minute = 391 if ops else 612
rows = [io("save", minute)] if writer else [io("tamper", 360, 3), io("load", minute)]
names = ["P", "after_save", "final"] if writer else ["P", "B", "after_tamper", "after_load", "final"]
event = rows[-1]["event"]
boundary = {"clock": minute, "guest_auto_ids": ["guest-auto-00000001"], "guest_auto_count": 1,
"operations": {"enabled": True}, "processed_arrival_minutes": [390],
"arrival_reports": [{"minute": 390}]} if ops else None
next_boundary = {"minute": 420, "processed_once": True, "before": [390], "after": [390, 420],
"arrival_reports": [{"minute": 390}, {"minute": 420}],
"guest_auto_ids": ["guest-auto-00000001", "guest-auto-00000002"]} if ops else None
clock = {"minute": minute, "paused": True, "backlog": 121, "accumulator_hex": "00"}
return {"schema": "p28-restart-v1", "role": role, "pid": pid, "stop_reason": "complete",
"main_added": True, "k_target": 30, "actual_k": 30, "comparison_scope": "complete_typed_var_to_bytes",
"excluded_paths": [], "transfer_before_guard_writes": 0, "probe_deadline_seconds": 1740,
"io": rows, "package_matches_before": comparison(), "failure_event_absent": True,
"transfer_verified": True, "event_id": event["event_id"], "saved_minute": minute,
"start_minute": 360 if ops else 602, "final_minute": minute + 30,
"states": {n: {"path": "p28_restart/evidence/" + n + ".bin", "bytes": len(DATA), "sha256": HASH}
for n in names},
"paused_idle": {"before": clock, "after": copy.deepcopy(clock), "comparison": comparison()},
"boundary": boundary, "next_boundary": next_boundary, "ui_ticks": [copy.deepcopy(boundary)] if ops else [],
"minutes": [{"before": minute + n, "after": minute + n + 1, "paused": True,
"backlog_before": 121, "backlog_after": 121, "sha256": HASH,
"scope": "complete_typed_var_to_bytes", "excluded_paths": [],
"replacement": copy.deepcopy(event) if n < 5 else None,
"replacement_count": 1 if n < 5 else 0, "recent_count": 256,
"recent_ids": history(255)["published_ids"] +
([event["event_id"]] if n < 5 else ["domain_event.000000000256"])}
for n in range(30)]}
def write(path, value):
with Path(path).open("x") as stream:
json.dump(value, stream)
def audio(pid):
return {"coordinator": True, "passed": True, "owned_engine_pid": pid,
"all_owned_targets_released": True, "deadline_reached": False,
"clock_process_held_stopped": True, "window_input_held_disabled": True,
"restored_before_quit": False, "shared_bus_deleted": False, "settings_mutated": False}
def synthetic_receipt(out):
rows = []
profiles = {}
source_package = out / "synthetic-package"
source_package.mkdir()
(source_package / "world.bin").write_bytes(DATA)
(source_package / "manifest.json").write_text("{}")
transfers = {kind: driver.create_transfer(source_package, out / (kind + "-transfer"), write)
for kind in ("R", "O")}
for index, label in enumerate(check.PHASES):
folder = out / label
folder.mkdir()
pid = 100 + index
process = {"exit_code": 0, "helper_exit_code": 0, "timed_out": False, "error_lines": [],
"lifecycle_error": None, "external_process_or_group_signals": 0,
"owned_popen_ended": True, "old_helper_engine_ended": True,
"start_receipt_written": True, "end_receipt_written": True}
capture = {"passed": True, "all_captured_ended": True, "actual_captured_pids": [pid],
"outer_cleanup_errors": [], "adapter_exception": None, "foreign_process_or_group_signals": 0,
"all_cleanup_before_outer_receipt": True, "outer_receipt_written": True,
"outer_receipt_error": None, "cleanup": []}
write(folder / "outer-owned-finally.json", {k: v for k, v in capture.items() if k not in
("passed", "outer_receipt_written", "outer_receipt_error")})
for filename, key in (("started.json", "actual_start_binding"), ("ended.json", "actual_end_binding")):
binding = {"pid": pid, "actual_pid": pid, "owner_pid": 99,
"argv": ["fake", "--check-only"], "command": ["fake", "--check-only"]}
write(folder / filename, binding)
process[key] = binding
process["adapter_owned_cleanup"] = {"engine_ended": True, "errors": []}
write(folder / "adapter-owned-cleanup.json", process["adapter_owned_cleanup"])
process.update(pid=pid, argv=["fake", "--check-only"])
raw = ""
row = {"phase": label, "passed": True, "process": process, "capture": capture,
"timeout_seconds": 1800, "diagnostics": [], "argv": ["fake", "--check-only"],
"stage_before": HASH, "stage_after": HASH, "stage_preserved": True,
"source_preserved": True, "player_preserved": True}
startup = out / (label + "-startup")
startup.write_bytes(DATA)
row["startup_project"] = {"fresh_before": True, "path": str(startup), "sha256": HASH}
if index >= 3:
observed = report(label, pid)
if label.endswith("reader"):
observed["transfer_manifest_sha256"] = check.sha(out / (label[0] + "-transfer/manifest.json"))
row["report"] = observed
raw = "P28_RESTART_REPORT=" + json.dumps(observed) + "\nP28_RESTART_AUDIO_CLOSE=" + json.dumps(audio(pid)) + "\n"
profile = str(out / (label + "-private"))
row.update(runtime_profile=profile, runtime_name=label)
profiles[profile] = {}
for kind in (("good",) if label.endswith("writer") else ("good", "tampered")):
for name, value in transfers[label[0]]["files"][kind].items():
profiles[profile]["p28_restart/" + kind + "/" + name] = value
guard = {"verified": True, "conditions": [True] * 5, "conditions_checked": 5,
"actual_user_data_dir": profile, "expected_user_data_dir": profile,
"globalized_probe": str(Path(profile) / "guard-probe"), "custom_user_dir_name": label,
"fresh_before_parent_initialize": True, "profile_writes_before_guard": 0,
"project_settings_persisted": False}
guard_path = out / (label + "-guard.json")
write(guard_path, guard)
row.update(guard=guard, guard_sha256=check.sha(guard_path))
states = out / (label + "-states")
states.mkdir()
for name in observed["states"]:
(states / (name + ".bin")).write_bytes(DATA)
for name, content in (("stdout.log", raw), ("stderr.log", ""), ("engine-native.log", "native\n")):
(folder / name).write_text(content)
row["raw"] = {n: check.pin(folder / n) for n in ("stdout.log", "stderr.log", "engine-native.log")}
process.update(stdout_sha256=row["raw"]["stdout.log"]["sha256"],
stderr_sha256=row["raw"]["stderr.log"]["sha256"])
write(folder / "process.json", process)
row["owned_files"] = {n: check.pin(folder / n) for n in
("process.json", "started.json", "ended.json", "adapter-owned-cleanup.json", "outer-owned-finally.json")}
rows.append(row)
probe = out / "restart_probe.gd"
probe.write_bytes((check.HERE / probe.name).read_bytes())
source = {str(i): {"bytes": 1, "sha256": HASH} for i in range(1407)}
worker = {n: check.sha(check.HERE / n) for n in
("restart_pair.py", "restart_probe.gd", "check_restart.py", "test_restart.py")}
return {"passed": True, "error": None, "cleanup_errors": [], "phases": rows,
"source_before": source, "source_after": copy.deepcopy(source),
"default_player_before": {}, "default_player_after": {},
"dependencies_before": check.DEPENDENCIES.copy(), "dependencies_after": check.DEPENDENCIES.copy(),
"collector_before": driver.COLLECTOR_PINS.copy(), "collector_after": driver.COLLECTOR_PINS.copy(),
"worker_before": worker, "worker_after": worker.copy(), "profiles": profiles,
"cross_cutting": copy.deepcopy(check.CROSS), "original_credit": 0,
"windows_verified": False, "operations_200_guests_verified": False,
"script_pins": {str(probe): check.sha(probe)}, "owned_writes": [],
"cleanup": [{"pid": 100 + n, "engine_ended": True, "outer_cleanup_errors": [],
"foreign_process_or_group_signals": 0} for n in range(7)]}
class RestartTests(unittest.TestCase):
def rejects(self, original, mutations, validator):
for name, mutation in mutations.items():
with self.subTest(rejection=name):
value = copy.deepcopy(original)
mutation(value)
with self.assertRaises((ValueError, KeyError, TypeError)):
validator(value)
def test_rs1_signal_bool_void(self):
for kind in ("save", "load", "tamper"):
check.check_io(io(kind), kind)
self.rejects(io("save"), {
"true only": lambda x: x.pop("signal_count"),
"no signal": lambda x: x.update(signal_count=0),
"duplicate signal": lambda x: x.update(signal_count=2),
"bool differs": lambda x: x.update(return_value=False),
"latest differs": lambda x: x["latest"].update(code="FAILED"),
"payload typed mismatch": lambda x: x.update(payload_bytes_equal=False),
"state mismatch": lambda x: x["comparison"].update(equal=False),
}, lambda x: check.check_io(x, "save"))
self.rejects(io("load"), {
"void success": lambda x: x.update(return_value=True),
"void coerced bool": lambda x: x.update(return_kind="bool"),
"missing signal": lambda x: x.update(signal_count=0),
}, lambda x: check.check_io(x, "load"))
def test_rs2_exact_event_transform(self):
for kind in ("save", "load", "tamper"):
for count in (0, 255, 256):
check.check_io(io(kind, count=count), kind)
self.rejects(io(kind), {
"two events": lambda x: x["after"]["recent_events"].append(copy.deepcopy(x["event"])),
"different E": lambda x: x["event"].update(event_id="domain_event.000009999999"),
"counter": lambda x: x["after"].update(counter=x["before"]["counter"] + 2),
"missing ID": lambda x: x["after"]["published_ids"].pop(0),
"duplicate ID": lambda x: x["after"]["published_ids"].append(x["event"]["event_id"]),
"unsorted IDs": lambda x: x["after"]["published_ids"].reverse(),
"drop tail": lambda x: x["after"].update(recent_events=x["before"]["recent_events"][:-1] + [x["event"]]),
"wrong dropped record": lambda x: x.update(dropped=x["before"]["recent_events"][-1]),
"excluded path": lambda x: x["comparison"].update(excluded_paths=["systems.runtime.economy"]),
"diff hidden": lambda x: x["comparison"].update(diff_paths=["$.clock"]),
"digest only": lambda x: x["comparison"].update(method="SimulationDigest"),
"bytes SHA disagreement": lambda x: x["comparison"].update(actual_sha256="f" * 64),
}, lambda x: check.check_io(x, kind))
def test_clarification01_tamper(self):
self.rejects(io("tamper"), {
"failure event ignored": lambda x: x.update(after=x["before"]),
"failure ok": lambda x: x["signal_payload"].update(ok=True),
"failure OK code": lambda x: x["signal_payload"].update(code="OK"),
"not restored": lambda x: x["failure"].update(preload_pause_restored=False),
"paused differs": lambda x: x.update(paused_after=False),
"loaded pause differs": lambda x: x["failure"].update(loaded_clock_paused=False),
"restore committed": lambda x: x["failure"]["recovery_report"].update(restore_committed=True),
"migration unrecorded": lambda x: x["failure"].pop("migration_attempted"),
}, lambda x: check.check_io(x, "tamper"))
self.rejects(report("R-reader"), {
"failed event retained": lambda x: x.update(failure_event_absent=False),
"backlog consumed": lambda x: x["paused_idle"]["after"].update(backlog=120),
"reader not paused": lambda x: x["io"][1].update(paused_after=False),
}, check.check_report)
def test_rs3_actual_k_full_typed_cross_pid(self):
writer, reader = report(), report("R-reader", 101)
check.check_pair(writer, reader)
self.rejects(reader, {
"K call count": lambda x: x["minutes"][0].update(after=x["saved_minute"]),
"different K": lambda x: x.update(actual_k=29),
"missing minute": lambda x: x["minutes"].pop(),
"partial digest": lambda x: x["minutes"][0].update(scope="SimulationDigest"),
"minute SHA": lambda x: x["minutes"][0].update(sha256="f" * 64),
"wrong replacement": lambda x: x["minutes"][0]["replacement"].update(event_id="other"),
"two replacements": lambda x: x["minutes"][0].update(replacement_count=2),
"after window": lambda x: x["minutes"][6].update(replacement=x["io"][-1]["event"], replacement_count=1),
"excluded path": lambda x: x["minutes"][0].update(excluded_paths=["clock"]),
"same PID": lambda x: x.update(pid=100),
}, lambda x: check.check_pair(writer, x))
def test_rs4_rs5_arrival_boundary(self):
writer, reader = report("O-writer"), report("O-reader", 101)
check.check_pair(writer, reader)
self.rejects(writer, {
"boundary only": lambda x: x["boundary"].update(guest_auto_count=0),
"null arrival": lambda x: x.update(boundary=None),
"unprocessed": lambda x: x["boundary"].update(processed_arrival_minutes=[]),
"not first tick": lambda x: x["ui_ticks"].insert(0, copy.deepcopy(x["boundary"])),
"duplicate guest": lambda x: x["boundary"]["guest_auto_ids"].append("guest-auto-00000001"),
"next missing": lambda x: x["next_boundary"].update(processed_once=False),
"next duplicate": lambda x: x["next_boundary"]["after"].append(420),
"cap exceeded": lambda x: x.update(start_minute=200),
}, check.check_report)
def test_d1_import_closure(self):
check.check_dependencies()
check.check_modules()
for name in ("restart_pair.py", "check_restart.py", "test_restart.py"):
check.check_imports((check.HERE / name).read_text())
for text in ("import forbidden_project_module", "from tools.strict_test_markers import main",
"importlib.util.spec_from_file_location('x', 'tools/evil.py')",
"__import__('tools.evil')", "exec('import tools.evil')"):
with self.subTest(text=text), self.assertRaises(ValueError):
check.check_imports(text)
with self.assertRaises(ValueError):
check.check_modules({"bad": SimpleNamespace(__file__=str(check.ROOT / "tools/evil.py"))})
for path in check.DEPENDENCIES:
original = check.sha
with self.subTest(pin=path), patch.object(check, "sha", side_effect=lambda p:
"0" * 64 if Path(p) == check.ROOT / path else original(p)), self.assertRaises(ValueError):
check.check_dependencies()
def test_d2_order_and_check_plan(self):
collector, _, _ = driver.load_dependencies()
commands = driver.plan(collector, Path("/engine"), Path("/out"))
self.assertEqual(len(commands), 7)
self.assertIn("--import", commands[0])
self.assertIn("--check-only", commands[1])
self.assertIn("--check-only", commands[2])
rows = []
for _, label in driver.schedule(rows):
rows.append({"phase": label, "passed": True, "report": {"stop_reason": "complete"}})
self.assertEqual([r["phase"] for r in rows], check.PHASES)
rows = []
iterator = driver.schedule(rows)
for _ in range(4):
_, label = next(iterator)
rows.append({"phase": label, "passed": True, "report": {"stop_reason": "complete"}})
rows[-1]["passed"] = False
with self.assertRaises(ValueError):
next(iterator)
rows[-1]["passed"] = True
rows[-1]["report"]["stop_reason"] = "failed"
iterator = driver.schedule([])
next(iterator)
with self.assertRaises(ValueError):
next(iterator)
def test_d3_transfer_and_fresh_profiles(self):
with tempfile.TemporaryDirectory(dir=check.HERE) as temporary:
base = Path(temporary)
source = base / "source"
source.mkdir()
(source / "world.bin").write_bytes(b"0123456789")
(source / "manifest.json").write_text("{}")
target = base / "transfer"
manifest = driver.create_transfer(source, target, write)
driver.check_transfer(target, manifest)
self.assertEqual((target / "tampered/world.bin").read_bytes(), b"0123446789")
driver.fresh_profile(base / "new-profile")
with self.assertRaises(ValueError):
driver.fresh_profile(source)
self.rejects(manifest, {
"SHA": lambda x: x["files"]["good"]["world.bin"].update(sha256="f" * 64),
"offset": lambda x: x["tamper"].update(offset=0),
"xor": lambda x: x["tamper"].update(xor=2),
}, lambda x: driver.check_transfer(target, x))
(target / "tampered/manifest.json").write_text("changed")
with self.assertRaises(ValueError):
driver.check_transfer(target, manifest)
def test_d4_d5_d7_probe_static(self):
text = (check.HERE / "restart_probe.gd").read_text()
check.check_probe(text)
snippets = ["GameClock.advance_minutes(1)", "GameClock.set_paused(false)",
"x.clear()", "spawn_guest()", "SimulationRuntime.new()", "initialize()",
"enable_production_checkpoint()", "main._toggle_pause()", "GameClock.set_paused(true)",
"var elapsed_us = 1", "Time.get_ticks_usec()"]
for snippet in snippets:
with self.subTest(snippet=snippet), self.assertRaises(ValueError):
check.check_probe(text + "\nfunc bad():\n\t" + snippet + "\n")
for replacement in ("GameClock.advance_minutes(2)", "Time.get_ticks_usec()",
"GameClock.advance_one_minute()"):
with self.subTest(step=replacement), self.assertRaises(ValueError):
check.check_probe(text.replace("GameClock.advance_minutes(1)", replacement))
def test_rs7_rs8_full_receipt_rejections(self):
with tempfile.TemporaryDirectory(dir=check.HERE) as temporary:
out = Path(temporary)
receipt = synthetic_receipt(out)
check.check_receipt(receipt, out)
self.rejects(receipt, {
"source map": lambda x: x["source_after"].pop("0"),
"ordinary player": lambda x: x["default_player_after"].update(changed=True),
"guard": lambda x: x["phases"][3]["guard"].update(profile_writes_before_guard=1),
"raw SHA": lambda x: x["phases"][3]["raw"]["stdout.log"].update(sha256="f" * 64),
"PID": lambda x: x["phases"][3]["capture"].update(actual_captured_pids=[999]),
"owned write": lambda x: x["owned_writes"].append("R-writer/report.json"),
"cleanup": lambda x: x["cleanup"][0].update(engine_ended=False),
"stage": lambda x: x["phases"][1].update(stage_after="f" * 64),
"collector": lambda x: x["collector_after"].pop("measure_p28_01.gd"),
"dependency": lambda x: x["dependencies_after"].update({"tools/run_all_tests.py": "f" * 64}),
"cross-cutting": lambda x: x["cross_cutting"].pop(),
"source count": lambda x: (x["source_after"].pop("1"), x["source_before"].pop("1")),
"phase order": lambda x: x["phases"].reverse(),
"foreign signal": lambda x: x["phases"][0]["capture"].update(foreign_process_or_group_signals=1),
}, lambda x: check.check_receipt(x, out))
(out / "R-reader-states/B.bin").write_bytes(b"changed")
with self.assertRaises(ValueError):
check.check_receipt(receipt, out)
def test_d6_raw_report_and_audio(self):
with tempfile.TemporaryDirectory(dir=check.HERE) as temporary:
out = Path(temporary)
receipt = synthetic_receipt(out)
row = receipt["phases"][3]
folder = out / "R-writer"
original = (folder / "stdout.log").read_text()
for text in ("", original + original, original.split("P28_RESTART_AUDIO_CLOSE=")[0],
original.replace('"owned_engine_pid": 103', '"owned_engine_pid": 999')):
(folder / "stdout.log").write_text(text)
row["raw"]["stdout.log"] = check.pin(folder / "stdout.log")
with self.assertRaises(ValueError):
check.bound_raw(folder, row)
def test_d2_d6_real_owned_outer_fake_child(self):
collector, _, outer = driver.load_dependencies()
child = (
"import os,sys,json,time,pathlib\n"
"mode=sys.argv[1]\n"
"identity={'pid':os.getpid(),'ppid':os.getppid(),'pgid':os.getpgrp(),"
"'args':' '.join([sys.executable]+sys.argv)}\n"
"path=pathlib.Path(sys.argv[0]).parent/(mode+'-identity')\n"
"tmp=path.with_suffix('.tmp'); tmp.write_text(json.dumps(identity)); tmp.replace(path)\n"
"pathlib.Path(sys.argv[sys.argv.index('--log-file')+1]).write_text('native raw\\n')\n"
"print('Godot Engine v4.7.2.stable.official.ed1daf0bf - https://godotengine.org',flush=True)\n"
"time.sleep(0.15)\n"
"if mode=='timeout': time.sleep(5)\n"
"sys.exit(3 if mode=='exit' else 0)\n")
with tempfile.TemporaryDirectory(dir=check.HERE) as temporary:
base = Path(temporary)
script = base / "fake-child"
script.write_text(child)
def diagnostic(command, folder, timeout, env, **kwargs):
def probe(pid):
path = base / (command[2] + "-identity")
deadline = time.monotonic() + 2
while time.monotonic() < deadline:
if path.exists():
identity = json.loads(path.read_text())
if identity["pid"] == pid:
return identity
time.sleep(.005)
raise RuntimeError("fake child identity unavailable")
return outer.native.run_owned_diagnostic(command, folder, timeout, env, probe_fn=probe, **kwargs)
for mode in ("ok", "exit", "timeout"):
folder = base / mode
def run_phase(command, destination, limit, env, **kwargs):
self.assertEqual(limit, 1800)
return outer.run_phase(command, destination, .3 if mode == "timeout" else 5,
env, diagnostic_fn=diagnostic, **kwargs)
def guarded_write(path, value):
self.assertFalse(Path(path).is_relative_to(folder), "driver writes owned folder")
write(path, value)
def real_phase(*args, **kwargs):
kwargs["dump_fn"] = write
return run_phase(*args, **kwargs)
with patch.object(collector, "live_engines", return_value=[]), \
patch.object(collector, "write", side_effect=guarded_write):
row = collector.run_engine(SimpleNamespace(run_phase=real_phase),
[sys.executable, str(script), mode], folder, {})
row["phase"] = "import"
self.assertEqual(row["passed"], mode == "ok", row)
self.assertTrue(row["capture"]["all_captured_ended"])
if mode == "ok":
check.bound_raw(folder, row)
else:
with self.assertRaises(ValueError):
check.bound_raw(folder, row)
_, errors = outer.finish_captured(outer.OWNED)
self.assertEqual(errors, [])
if __name__ == "__main__":
unittest.main(verbosity=2)