"""Explicit native-v4 launch/checkpoint helper. Never operates GUI or compiles. Launch/check/closed require the corresponding root-observed boundary. Merely copying a snapshot never claims Apply, Save, recovery or model acceptance. """ import argparse from datetime import datetime, timezone import hashlib import json import os from pathlib import Path import re import shutil import subprocess import sys import zipfile ROOT = Path(__file__).resolve().parents[2] BASE = Path(__file__).resolve().parent TRIAL = BASE / 'trial' FIXTURES = BASE / 'fixtures' ORIGINAL = ROOT / '.local/text-navigation-native-20260911-preparation' GATES = ROOT / '.local/text-repair-save-20260911' ZIP_SHA = '213fdb39ea7c3601b4baac674c3ab3ecc863ac44db1b092d922d3e24f26887b7' SOURCE_COMMIT = '33c6d83e9ba9a5b1310b1b627d7c40affcbcf15c' FIXTURE_MANIFEST_SHA = '7bccf622c8e2ff42f9e71ac8e5d08ea27b3493c9ebc689ce5f7fbac9aa63f851' ORIGINAL_MANIFEST_SHA = 'edd4157fc31823f4494dee91862728b87f12ef51c49b8d1fd86a954bc9ecc1ed' DLL_SHA = 'f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4' ORACLE_SHA = '967d9ba3c6fb3a7efb410833903ec94556ca2d48a67d5072c8bb160f40a00f9c' 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' MODELS = ('valid', 'release1', 'ambiguous', 'missing-include', 'recovery') JOURNAL = '.arcadia-save-project.arcadia.journal' COMMANDS = { 'desktop-tests': ['cargo', 'test', '--locked', '--offline', '-p', 'oa-desktop', '--bin', 'arcadia-studio', '--', '--test-threads=1'], 'desktop-clippy': ['cargo', 'clippy', '--locked', '--offline', '-p', 'oa-desktop', '--all-targets', '--', '-D', 'warnings'], 'scoped-fmt': ['rustfmt', '--check', '--edition', '2024', '--config', 'skip_children=true', 'crates/oa-desktop/src/app.rs', 'crates/oa-desktop/src/document.rs', 'crates/oa-desktop/src/app/repair_save_state_tests.rs'], 'diff-check': ['git', 'diff', '--check'], 'gui-build': ['cargo', 'build', '--locked', '--offline', '-p', 'oa-desktop', '--bin', 'arcadia-studio', '--message-format=json-render-diagnostics'], } def now(): return datetime.now(timezone.utc).isoformat() def ordinary(path): path = Path(path).absolute() for p in (path, *path.parents): if p.exists(): assert not p.is_symlink() and not (getattr(p.lstat(), 'st_file_attributes', 0) & 0x400), str(p) return path def sha(path): with ordinary(path).open('rb') as stream: return hashlib.file_digest(stream, 'sha256').hexdigest() def load(path): return json.loads(ordinary(path).read_text(encoding='utf-8')) def record(path, value): ordinary(path) with path.open('x', encoding='utf-8', newline='\n') as out: json.dump(value, out, ensure_ascii=False, indent=2) out.write('\n') def inventory(folder): ordinary(folder) result = {} for p in folder.rglob('*'): ordinary(p) if p.is_file(): result[p.relative_to(folder).as_posix()] = sha(p) assert len({p.casefold() for p in result}) == len(result) return result def copy_exact(source, destination): before = inventory(source) assert not destination.exists() shutil.copytree(source, destination) assert inventory(source) == inventory(destination) == before return before def git(*args): return subprocess.check_output(['git', *args], cwd=ROOT) def fixture_admission(): assert sha(ORIGINAL / 'fixture-manifest.json') == ORIGINAL_MANIFEST_SHA for item in load(ORIGINAL / 'fixture-manifest.json'): path = (ORIGINAL / item['path']).resolve() assert path.is_relative_to(ORIGINAL) assert sha(path) == item['sha256'] and path.stat().st_size == item['bytes'] assert sha(BASE / 'fixture-manifest.json') == FIXTURE_MANIFEST_SHA frozen = load(BASE / 'fixture-manifest.json') expected = {item['path']: item['sha256'] for item in frozen['files']} assert len(expected) == len(frozen['files']) and inventory(FIXTURES) == expected mapping = {'valid': 'valid-baseline', 'release1': 'release1-baseline', 'ambiguous': 'ambiguous-open', 'missing-include': 'missing-include-open'} for destination, original in mapping.items(): assert inventory(FIXTURES / destination) == inventory(ORIGINAL / original) after = FIXTURES / 'recovery-expected-after' before = FIXTURES / 'recovery-expected-before' assert inventory(after) == inventory(FIXTURES / 'valid') a = (after / 'project.arcadia').read_bytes() b = (before / 'project.arcadia').read_bytes() title = '"Navigation — 救援"'.encode('utf-8') old_title = '"Before interrupted save — 救援"'.encode('utf-8') assert a.count(title) == 1 and b == a.replace(title, old_title, 1) assert {p: h for p, h in inventory(before).items() if p != 'project.arcadia'} == {p: h for p, h in inventory(after).items() if p != 'project.arcadia'} recovery = FIXTURES / 'recovery' assert load(recovery / JOURNAL / 'journal.json') == {'version': 1, 'entry': 'project.arcadia', 'files': [{'path': 'project.arcadia', 'before': True, 'write': True}]} assert (recovery / JOURNAL / '0.before').read_bytes() == b assert (recovery / JOURNAL / '0.after').read_bytes() == a assert {p: h for p, h in inventory(recovery).items() if not p.startswith(JOURNAL + '/')} == inventory(after) return frozen def gate_admission(source_commit): assert source_commit == SOURCE_COMMIT assert git('rev-parse', 'HEAD').decode().strip() == source_commit manifest = load(GATES / 'inputs1.json') assert manifest['archiveSha256'] == sha(GATES / 'inputs1.zip') == ZIP_SHA entries = {e['path']: e for e in manifest['files']} assert len(entries) == len(manifest['files']) == 500 selectors = ['crates', '.cargo', 'Cargo.toml', 'Cargo.lock', 'docs', 'examples', 'harness/fixtures'] assert not git('status', '--porcelain', '--', *selectors).strip(), 'Source must be committed before launch.' paths = git('ls-files', '-z', '--cached', '--others', '--exclude-standard', '--', *selectors).decode().split('\0')[:-1] assert sorted(set(paths)) == sorted(entries) with zipfile.ZipFile(GATES / 'inputs1.zip') as archive: assert len(archive.namelist()) == len(entries) and set(archive.namelist()) == set(entries) for name, item in entries.items(): path = (ROOT / name).resolve() assert path.is_relative_to(ROOT) raw = ordinary(path).read_bytes() assert len(raw) == item['bytes'] and hashlib.sha256(raw).hexdigest() == item['sha256'] assert archive.read(name) == raw complete = load(GATES / 'complete.json') assert complete['archiveSha256'] == ZIP_SHA and complete['inputsUnchanged'] is True assert complete['gates'] == list(COMMANDS) and complete['sourceHead'] == manifest['head'] receipts = {} for name, command in COMMANDS.items(): receipt = load(GATES / (name + '-result.json')) assert receipt['exitCode'] == 0 and receipt['inputsUnchanged'] is True and receipt['command'] == command assert sha(GATES / (name + '-stdout.log')) == receipt['stdoutSha256'] assert sha(GATES / (name + '-stderr.log')) == receipt['stderrSha256'] receipts[name] = sha(GATES / (name + '-result.json')) summaries = re.findall(r'test result: ok\. (\d+) passed; (\d+) failed; (\d+) ignored; (\d+) measured; (\d+) filtered out;', (GATES / 'desktop-tests-stdout.log').read_text(encoding='utf-8')) assert summaries == [('755', '0', '1', '0', '0')] build = load(GATES / 'gui-build-result.json')['binary'] artifact = build['artifact'] assert artifact['target']['name'] == 'arcadia-studio' and artifact['profile']['test'] is False assert artifact['profile']['opt_level'] == '0' and artifact['profile']['debug_assertions'] is True assert Path(artifact['executable']).resolve() == Path(build['path']).resolve() assert Path(build['path']).resolve().is_relative_to(ROOT / '.local/target-gnullvm') binary = GATES / 'gui-repair.exe' assert sha(binary) == build['sha256'] and binary.stat().st_size == build['bytes'] assert sha(RUNTIME) == DLL_SHA and sha(ORACLE) == ORACLE_SHA return binary, build, receipts def environment(): parent = {k.upper(): v for k, v in os.environ.items()} names = ('SystemRoot', 'SystemDrive', 'ProgramData', 'ALLUSERSPROFILE', 'PUBLIC') standard = {name: parent[name.upper()] for name in names} assert all(value and '%' 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 launch(args): assert not TRIAL.exists(), 'A previous trial must remain untouched.' frozen = fixture_admission() binary, build, receipts = gate_admission(args.source_commit) ordinary(BASE) TRIAL.mkdir() for name in ('bin', 'profile/local', 'profile/roaming', 'profile/Desktop', 'tmp', 'screenshots', 'checkpoints'): (TRIAL / name).mkdir(parents=True) for source, name, checksum in ((binary, 'arcadia-studio.exe', build['sha256']), (RUNTIME, 'libunwind.dll', DLL_SHA)): with source.open('rb') as src, (TRIAL / 'bin' / name).open('xb') as dst: shutil.copyfileobj(src, dst) assert sha(source) == sha(TRIAL / 'bin' / name) == checksum sources = {model: copy_exact(FIXTURES / model, TRIAL / model) for model in MODELS} record(TRIAL / 'source-before.json', sources) record(TRIAL / 'binary-before.json', inventory(TRIAL / 'bin')) record(TRIAL / 'admission.json', {'atUtc': now(), 'sourceCommit': args.source_commit, 'inputArchiveSha256': ZIP_SHA, 'gateReceipts': receipts, 'completeReceiptSha256': sha(GATES / 'complete.json'), 'fixtureManifestSha256': sha(BASE / 'fixture-manifest.json'), 'originalFixtureManifestSha256': ORIGINAL_MANIFEST_SHA, 'binary': build, 'scope': 'Gate/fixture admission only, not native acceptance.'}) 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) receipt = {'atUtc': now(), 'pid': process.pid, 'sourceCommit': args.source_commit, 'binarySha256': build['sha256'], 'guiBuildReceiptSha256': receipts['gui-build'], 'environment': env, 'modelEntries': {m: str(TRIAL / m / 'project.arcadia') for m in MODELS}, 'scope': 'Explicitly launched identified Windows GUI; native results pending.'} record(TRIAL / 'launch.json', receipt) print(json.dumps(receipt, ensure_ascii=False, indent=2)) def checkpoint(args, check): assert args.label and re.fullmatch('[a-z0-9][a-z0-9-]{0,79}', args.label) if check: assert args.after_visible_save, 'Oracle check requires root-observed successful Save.' fixture_admission() launch_receipt = load(TRIAL / 'launch.json') before_bin = load(TRIAL / 'binary-before.json') assert inventory(TRIAL / 'bin') == before_bin and before_bin['arcadia-studio.exe'] == launch_receipt['binarySha256'] folder = TRIAL / 'checkpoints' / args.label assert not folder.exists() folder.mkdir() source = TRIAL / args.model files = copy_exact(source, folder / 'sources') journals = {p: h for p, h in files.items() if p.split('/')[0].startswith('.arcadia-save-')} state = {'atUtc': now(), 'label': args.label, 'model': args.model, 'files': files, 'arcadiaFiles': sum(p.endswith('.arcadia') for p in files), 'journalFiles': journals, 'pendingJournalExists': (source / JOURNAL).exists(), 'binaryFilesUnchanged': True, 'scope': 'On-disk snapshot only; no accepted graph, Apply, Save or recovery success inferred.'} record(folder / 'snapshot.json', state) if not check: print(json.dumps(state, ensure_ascii=False, indent=2)) return assert not journals, 'Final saved oracle cannot bypass a retained transaction journal.' assert sha(ORACLE) == ORACLE_SHA expected = FIXTURES / ('release1' if args.model == 'release1' else 'recovery-expected-after' if args.model == 'recovery' else 'valid') env = environment() env['PATH'] = str(TRIAL / 'bin') + ';' + env['PATH'] command = [str(ORACLE), 'same', str(expected / 'project.arcadia'), str(folder / 'sources/project.arcadia')] result = subprocess.run(command, capture_output=True, env=env) with (folder / 'oracle.stdout.log').open('xb') as out: out.write(result.stdout) with (folder / 'oracle.stderr.log').open('xb') as err: err.write(result.stderr) receipt = {'atUtc': now(), 'command': command, 'exitCode': result.returncode, 'oracleSha256': ORACLE_SHA, 'afterVisibleSaveConfirmedByCaller': True, 'model': args.model, 'expected': str(expected), 'files': files, 'binaryFilesUnchanged': inventory(TRIAL / 'bin') == before_bin, 'scope': 'Actual saved oracle result; complete Project/identity/reachable and physical Arcadia equality only if exitCode is zero.'} record(folder / 'oracle.json', receipt) print(result.stdout.decode('utf-8', errors='replace')) print(json.dumps(receipt, ensure_ascii=False, indent=2)) assert receipt['binaryFilesUnchanged'] if result.returncode: print(result.stderr.decode('utf-8', errors='replace')) raise SystemExit(result.returncode) def closed(args): assert args.observed_absent, 'Root must first verify the process/window is absent.' before = load(TRIAL / 'binary-before.json') after = inventory(TRIAL / 'bin') record(TRIAL / 'binary-after-close.json', {'atUtc': now(), 'files': after, 'unchanged': before == after, 'absenceObservedByCaller': True, 'scope': 'Binary readback only, not a process/exit-code observation by this helper.'}) assert before == after print(json.dumps({'unchanged': True, 'files': len(after)})) if __name__ == '__main__': sys.stdout.reconfigure(encoding='utf-8') sys.stderr.reconfigure(encoding='utf-8') parser = argparse.ArgumentParser() parser.add_argument('action', choices=('launch', 'snapshot', 'check', 'closed')) parser.add_argument('--source-commit') parser.add_argument('--model', choices=MODELS, default='valid') parser.add_argument('--label') parser.add_argument('--after-visible-save', action='store_true') parser.add_argument('--observed-absent', action='store_true') arguments = parser.parse_args() if arguments.action == 'launch': launch(arguments) elif arguments.action in ('snapshot', 'check'): checkpoint(arguments, arguments.action == 'check') else: closed(arguments)