"""Prepare a new disposable canvas trial; retain the real GUI process exit code.""" from pathlib import Path from datetime import datetime, timezone import hashlib import json import os import shutil import subprocess import sys root = Path(__file__).resolve().parent.parent trial = root / '.local/native-qa/canvas-followup-20260912-01' mode = sys.argv[1] if mode == 'prepare': assert not trial.exists(), 'Never overwrite a native trial' sources = { 'compact-baseline': root / '.local/native-qa/delete-20260912/compact-baseline-v2', 'compact-work': root / '.local/native-qa/delete-20260912/compact-baseline-v2', 'bound-baseline': root / 'harness/experiments/canvas-editing-followup-20260912/bound-nested-fixture-r1-20260912', 'bound-work': root / 'harness/experiments/canvas-editing-followup-20260912/bound-nested-fixture-r1-20260912', 'draft-work': root / 'harness/experiments/canvas-editing-followup-20260912/nested-call-fixture-20260912', 'process-work': root / 'harness/fixtures/source7-native/base', } assert all(path.is_dir() for path in sources.values()) trial.mkdir() rows = [] for label, source in sources.items(): shutil.copytree(source, trial / label) for path in sorted(source.rglob('*')): if path.is_file(): relative = path.relative_to(source) raw = path.read_bytes() assert (trial / label / relative).read_bytes() == raw rows.append({'path': f'{label}/{relative.as_posix()}', 'source': str(path.relative_to(root)), 'bytes': len(raw), 'sha256': hashlib.sha256(raw).hexdigest()}) for label in ['profile/Desktop', 'profile/Documents', 'profile/AppData/Local', 'profile/AppData/Roaming', 'temp', 'evidence']: (trial / label).mkdir(parents=True, exist_ok=True) (trial / 'preparation.json').write_text(json.dumps({'utc': datetime.now(timezone.utc).isoformat(), 'files': rows}, indent=2) + '\n', encoding='utf-8', newline='\n') print(json.dumps({'trial': str(trial), 'identical_fixture_files': len(rows)})) elif mode == 'launch': assert (trial / 'preparation.json').is_file() assert not (trial / 'launch.json').exists() stage = Path(sys.argv[2]).resolve() assert stage.is_relative_to(root / '.local/native-qa') record = json.loads((stage / 'manifest.json').read_bytes()) for gate in ['full', 'clippy', 'gui']: result = record['results'][gate] assert result['exit'] == 0 and not result['changed_inputs'] assert result['source'] == record['source'], 'Require same-source gates for this production correction' for name in ['arcadia-studio.exe', 'libunwind.dll']: raw = (stage / name).read_bytes() assert len(raw) == record['artifacts'][name]['bytes'] assert hashlib.sha256(raw).hexdigest() == record['artifacts'][name]['sha256'] env = {key: os.environ[key] for key in ['SystemRoot', 'WINDIR', 'SystemDrive', 'ProgramData', 'ALLUSERSPROFILE', 'PUBLIC', 'COMSPEC', 'PATHEXT', 'NUMBER_OF_PROCESSORS', 'PROCESSOR_ARCHITECTURE', 'OS'] if key in os.environ} profile = trial / 'profile' windows = Path(env['SystemRoot']) env.update({'PATH': os.pathsep.join(map(str, [stage, windows / 'System32', windows])), 'USERPROFILE': str(profile), 'LOCALAPPDATA': str(profile / 'AppData/Local'), 'APPDATA': str(profile / 'AppData/Roaming'), 'TEMP': str(trial / 'temp'), 'TMP': str(trial / 'temp')}) with (trial / 'stdout.log').open('xb') as out, (trial / 'stderr.log').open('xb') as err: process = subprocess.Popen([str(stage / 'arcadia-studio.exe')], cwd=trial, env=env, stdin=subprocess.DEVNULL, stdout=out, stderr=err, creationflags=subprocess.CREATE_NO_WINDOW) launch = {'utc': datetime.now(timezone.utc).isoformat(), 'pid': process.pid, 'source': record['source'], 'binary': str(stage / 'arcadia-studio.exe'), 'sha256': record['artifacts']['arcadia-studio.exe']['sha256'], 'private_profile': str(profile)} (trial / 'launch.json').write_text(json.dumps(launch, indent=2) + '\n', encoding='utf-8', newline='\n') print(json.dumps(launch), flush=True) code = process.wait() closure = {'utc': datetime.now(timezone.utc).isoformat(), 'pid': process.pid, 'exit': code} (trial / 'closure.json').write_text(json.dumps(closure, indent=2) + '\n', encoding='utf-8', newline='\n') print(json.dumps(closure), flush=True) sys.exit(code) else: raise SystemExit('Expected prepare or launch STAGED_DIRECTORY')