Files
ARCADIA-Studio/harness/experiments/text-navigation-native-20260911/build/native_v2.py
T

129 lines
6.6 KiB
Python

"""Prepare/launch the identified Windows GUI and preserve native trial checkpoints.
All actual GUI interaction is performed separately with Computer Use.
"""
import argparse
import datetime
import hashlib
import json
import os
from pathlib import Path
import shutil
import subprocess
root = Path(__file__).resolve().parents[2]
evidence = Path(__file__).resolve().parent
trial = root / ".local/text-navigation-native-20260911"
fixtures = root / ".local/text-navigation-native-20260911-preparation"
runtime = root / ".local/toolchain/llvm-mingw-20260826-ucrt-x86_64/bin/libunwind.dll"
oracle = root / ".local/staged-source-oracle-20260911/staged-source-authoring-contract.exe"
runtime_sha = "f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4"
oracle_sha = "967d9ba3c6fb3a7efb410833903ec94556ca2d48a67d5072c8bb160f40a00f9c"
fixture_manifest_sha = "edd4157fc31823f4494dee91862728b87f12ef51c49b8d1fd86a954bc9ecc1ed"
def sha(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def now():
return datetime.datetime.now(datetime.timezone.utc).isoformat()
def record(path, value):
with path.open("x", encoding="utf-8", newline="\n") as out:
json.dump(value, out, indent=2, ensure_ascii=False)
out.write("\n")
def inventory(folder):
result = {}
for path in folder.rglob("*"):
assert not path.is_symlink() and not (getattr(path.lstat(), "st_file_attributes", 0) & 0x400), path
if path.is_file():
result[path.relative_to(folder).as_posix()] = sha(path)
return result
def environment():
parent = {key.upper(): value for key, value in os.environ.items()}
names = ("SystemRoot", "SystemDrive", "ProgramData", "ALLUSERSPROFILE", "PUBLIC")
standard = {name: parent[name.upper()] for name in names}
assert all("%" not in value for value in standard.values())
system = standard["SystemRoot"]
return {**standard, "WINDIR": system, "PATH": system + "\\System32;" + system,
"USERPROFILE": str(trial / "profile"), "LOCALAPPDATA": str(trial / "profile/local"),
"APPDATA": str(trial / "profile/roaming"), "TEMP": str(trial / "tmp"), "TMP": str(trial / "tmp")}
def verify_fixtures():
manifest = fixtures / "fixture-manifest.json"
assert sha(manifest) == fixture_manifest_sha
for entry in json.loads(manifest.read_text(encoding="utf-8")):
path = (fixtures / entry["path"]).resolve()
assert path.is_relative_to(fixtures) and path.stat().st_size == entry["bytes"]
assert sha(path) == entry["sha256"], entry["path"]
parser = argparse.ArgumentParser()
parser.add_argument("action", choices=("launch", "check", "closed"))
parser.add_argument("--label")
parser.add_argument("--model", choices=("valid", "release1"), default="valid")
args = parser.parse_args()
if args.action == "launch":
verify_fixtures()
receipt_path = evidence / "gui-build-v2.json"
receipt = json.loads(receipt_path.read_text(encoding="utf-8"))
preserved = json.loads((evidence / "gui-preserved.json").read_text(encoding="utf-8"))
assert preserved["buildReceiptSha256"] == sha(receipt_path)
binary = Path(preserved["binary"]).resolve()
assert binary == evidence / "gui-v2.exe"
assert preserved["sha256"] == receipt["sha256"]
assert sha(binary) == receipt["sha256"]
assert sha(runtime) == runtime_sha and sha(oracle) == oracle_sha
trial.mkdir(exist_ok=False)
for folder in ("bin", "profile/local", "profile/roaming", "profile/Desktop", "tmp", "screenshots"):
(trial / folder).mkdir(parents=True)
shutil.copy2(binary, trial / "bin/arcadia-studio.exe")
shutil.copy2(runtime, trial / "bin/libunwind.dll")
for source, destination in (("valid-baseline", "valid"), ("release1-baseline", "release1"),
("ambiguous-open", "ambiguous"), ("missing-include-open", "missing-include")):
shutil.copytree(fixtures / source, trial / destination)
before = inventory(trial / "bin")
record(trial / "binary-before.json", before)
record(trial / "source-before.json", {name: inventory(trial / name) for name in ("valid", "release1", "ambiguous", "missing-include")})
env = environment()
startup = subprocess.STARTUPINFO()
startup.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startup.wShowWindow = 0
with (trial / "stdout.log").open("xb") as out, (trial / "stderr.log").open("xb") as err:
process = subprocess.Popen([str(trial / "bin/arcadia-studio.exe")], cwd=trial / "bin",
env=env, stdout=out, stderr=err, startupinfo=startup)
launch = {"atUtc": now(), "pid": process.pid, "binarySha256": receipt["sha256"],
"buildReceiptSha256": sha(receipt_path), "source": str(trial / "valid/project.arcadia"),
"environment": env, "scope": "Identified local Windows GUI; native verification pending"}
record(trial / "launch.json", launch)
print(json.dumps(launch, ensure_ascii=False))
elif args.action == "check":
verify_fixtures()
assert args.label and args.label.replace("-", "").isalnum()
assert inventory(trial / "bin") == json.loads((trial / "binary-before.json").read_text(encoding="utf-8"))
assert sha(oracle) == oracle_sha
checkpoint = trial / (args.label + "-sources")
shutil.copytree(trial / args.model, checkpoint)
env = environment()
env["PATH"] = str(trial / "bin") + ";" + env["PATH"]
command = [str(oracle), "same", str(fixtures / (args.model + "-baseline") / "project.arcadia"), str(checkpoint / "project.arcadia")]
result = subprocess.run(command, capture_output=True, env=env)
(trial / (args.label + ".stdout.log")).write_bytes(result.stdout)
(trial / (args.label + ".stderr.log")).write_bytes(result.stderr)
receipt = {"atUtc": now(), "command": command, "exitCode": result.returncode,
"oracleSha256": oracle_sha, "files": inventory(checkpoint),
"binaryFilesUnchanged": True, "scope": "Complete saved Project/identity, reachable sources and physical Arcadia equality"}
record(trial / (args.label + ".json"), receipt)
print(result.stdout.decode("utf-8", errors="replace"))
print(json.dumps({"label": args.label, "exitCode": result.returncode}))
if result.returncode:
print(result.stderr.decode("utf-8", errors="replace"))
raise SystemExit(result.returncode)
else:
# The caller must observe process closure independently before this readback.
before = json.loads((trial / "binary-before.json").read_text(encoding="utf-8"))
after = inventory(trial / "bin")
record(trial / "binary-after-close.json", {"atUtc": now(), "files": after, "unchanged": before == after})
assert before == after
print(json.dumps({"binaryFilesUnchanged": True, "files": len(after)}))