"""Packaged navigation trial; GUI input and release acceptance belong to root. No execution is admitted until binding.json is completed and its SHA is pinned after independent artifact review. Nothing here operates the GUI or compiles. """ import argparse import ctypes from ctypes import wintypes from datetime import datetime, timezone import hashlib import json import os from pathlib import Path, PurePosixPath import re import shutil import subprocess import sys import zipfile ROOT = Path(__file__).resolve().parents[2] BASE = Path(__file__).resolve().parent TRIAL = BASE / 'trial' BINDING_SHA = '82606e6b3a5fb5dada3e55adcd5cd76792c9f938e6907313539dae7fc5125657' SOURCE = '33c6d83e9ba9a5b1310b1b627d7c40affcbcf15c' NAME = 'ArcadiaStudio-0.2.0-preview.navigation.20260911-x86_64-pc-windows-gnullvm' EXPECTED = { 'arcadia-studio.exe': 'b183ef31f16b3986538b3f2aba8435eab45978635c47f99ca01be6e72b168848', 'arcadia-server.exe': 'efeeb89c0d2e0f93df4472cb3e9f48b2b722a37ae1043c90a5a56f2e845fe665', 'libunwind.dll': 'f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4', } FIXTURES = ROOT / '.local/text-navigation-native-20260911-preparation' FIXTURE_SHA = 'edd4157fc31823f4494dee91862728b87f12ef51c49b8d1fd86a954bc9ecc1ed' ORACLE = ROOT / '.local/staged-source-oracle-20260911/staged-source-authoring-contract.exe' ORACLE_SHA = '967d9ba3c6fb3a7efb410833903ec94556ca2d48a67d5072c8bb160f40a00f9c' 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-sig')) 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 relative(name): p = PurePosixPath(name) assert name == p.as_posix() and not p.is_absolute() and p.parts assert all(x not in ('.', '..') for x in p.parts) assert not any(c in name for c in '\\:\r\n') return name def local(name): p = ordinary(ROOT / relative(name)) assert p.resolve().is_relative_to(ROOT / '.local') return p def inventory(folder): ordinary(folder) found = {} for p in folder.rglob('*'): ordinary(p) if p.is_file(): found[p.relative_to(folder).as_posix()] = sha(p) assert len({p.casefold() for p in found}) == len(found) return found 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 environment(): parent = {k.upper(): v for k, v in os.environ.items()} standard = {k: parent[k.upper()] for k in ('SystemRoot', 'SystemDrive', 'ProgramData', 'ALLUSERSPROFILE', 'PUBLIC')} assert all(v and '%' not in v for v in standard.values()) system = standard['SystemRoot'] return {**standard, 'WINDIR': system, 'PATH': system + '\\System32;' + system, 'USERPROFILE': str(TRIAL / 'profile'), 'APPDATA': str(TRIAL / 'profile/roaming'), 'LOCALAPPDATA': str(TRIAL / 'profile/local'), 'TEMP': str(TRIAL / 'tmp'), 'TMP': str(TRIAL / 'tmp')} def process_identity(pid): """Read-only process query; invalid PID means absent, access errors refuse.""" api = ctypes.WinDLL('kernel32', use_last_error=True) api.OpenProcess.argtypes = [wintypes.DWORD, wintypes.BOOL, wintypes.DWORD] api.OpenProcess.restype = wintypes.HANDLE api.CloseHandle.argtypes = [wintypes.HANDLE] api.GetProcessTimes.argtypes = [wintypes.HANDLE] + [ctypes.POINTER(wintypes.FILETIME)] * 4 api.GetProcessTimes.restype = wintypes.BOOL api.QueryFullProcessImageNameW.argtypes = [wintypes.HANDLE, wintypes.DWORD, wintypes.LPWSTR, ctypes.POINTER(wintypes.DWORD)] api.QueryFullProcessImageNameW.restype = wintypes.BOOL handle = api.OpenProcess(0x1000, False, pid) if not handle: error = ctypes.get_last_error() assert error == 87, f'Process query failed: {error}' return None try: created, exited, kernel, user = (wintypes.FILETIME() for _ in range(4)) assert api.GetProcessTimes(handle, ctypes.byref(created), ctypes.byref(exited), ctypes.byref(kernel), ctypes.byref(user)) buffer, count = ctypes.create_unicode_buffer(32768), wintypes.DWORD(32768) assert api.QueryFullProcessImageNameW(handle, 0, buffer, ctypes.byref(count)) return {'path': str(Path(buffer.value).resolve()), 'creation_filetime': (created.dwHighDateTime << 32) | created.dwLowDateTime} finally: api.CloseHandle(handle) def admission(verify_inventory=True): assert BINDING_SHA and sha(BASE / 'binding.json') == BINDING_SHA, 'Package binding is pending or changed.' binding = load(BASE / 'binding.json') assert binding['status'] == 'READY' and binding['source_commit'] == SOURCE archive, review_path, producer_path, package = (local(binding[k]) for k in ('archive_path', 'review_path', 'producer_readback_path', 'package_path')) assert archive.name == NAME + '.zip' and package.name == NAME assert package.resolve() != (producer_path.parent / 'packages' / NAME).resolve(), 'Use independent extraction, not producer stage.' assert sha(archive) == binding['archive_sha256'] and archive.stat().st_size == binding['archive_bytes'] assert sha(review_path) == binding['review_sha256'] and sha(producer_path) == binding['producer_readback_sha256'] review, producer = load(review_path), load(producer_path) assert review['status'] == producer['status'] == 'PASS' assert review['archive']['sha256'] == producer['archive_sha256'] == binding['archive_sha256'] assert review['archive']['bytes'] == producer['archive_bytes'] == binding['archive_bytes'] assert Path(review['archive']['path']).resolve() == archive.resolve() expected = review['archive']['files'] assert expected == {p: v['sha256'] for p, v in producer['files'].items()} assert review['archive']['file_count'] == producer['file_count'] == len(expected) if verify_inventory: assert inventory(package) == expected for p, h in EXPECTED.items(): assert expected[p] == h zipped = {} with zipfile.ZipFile(archive) as z: for item in z.infolist(): assert item.filename.startswith(NAME + '/') and not item.is_dir() p = relative(item.filename[len(NAME) + 1:]) assert p not in zipped zipped[p] = hashlib.sha256(z.read(item)).hexdigest() assert zipped == expected return binding, package, expected def fixture(): assert sha(FIXTURES / 'fixture-manifest.json') == FIXTURE_SHA expected = {e['path'].removeprefix('valid-baseline/'): e['sha256'] for e in load(FIXTURES / 'fixture-manifest.json') if e['path'].startswith('valid-baseline/')} assert len(expected) == 4 and inventory(FIXTURES / 'valid-baseline') == expected assert sha(ORACLE) == ORACLE_SHA return FIXTURES / 'valid-baseline', expected def launch(): assert not TRIAL.exists(), 'Preserve old trial; never overwrite.' binding, package, expected = admission() baseline, files = fixture() TRIAL.mkdir() for folder in ('profile/local', 'profile/roaming', 'profile/Desktop', 'tmp', 'screenshots', 'checkpoints'): (TRIAL / folder).mkdir(parents=True) assert copy_exact(baseline, TRIAL / 'work') == files record(TRIAL / 'package-before.json', expected) record(TRIAL / 'source-before.json', files) record(TRIAL / 'admission.json', {'atUtc': now(), 'binding_sha256': BINDING_SHA, 'binding': binding, 'fixture_sha256': FIXTURE_SHA, 'scope': 'Artifact/fixture admission, 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(package / 'arcadia-studio.exe')], cwd=package, env=env, stdout=out, stderr=err, startupinfo=startup) # Preserve PID immediately even if a subsequent read-only identity query fails. receipt = {'atUtc': now(), 'pid': process.pid, 'package': str(package), 'environment': env, 'binding_sha256': BINDING_SHA, 'source': str(TRIAL / 'work/project.arcadia'), 'scope': 'Identified packaged GUI launched; native acceptance pending.'} record(TRIAL / 'launch.json', receipt) identity = process_identity(process.pid) assert identity and Path(identity['path']) == (package / 'arcadia-studio.exe').resolve() record(TRIAL / 'process-identity.json', identity) print(json.dumps(receipt, ensure_ascii=False, indent=2)) def package_guard(label): binding, package, expected = admission(verify_inventory=False) launch_receipt = load(TRIAL / 'launch.json') assert launch_receipt['binding_sha256'] == BINDING_SHA and Path(launch_receipt['package']).resolve() == package.resolve() assert load(TRIAL / 'package-before.json') == expected actual = inventory(package) unchanged = actual == expected record(TRIAL / (label + '-package.json'), {'atUtc': now(), 'archive_sha256': binding['archive_sha256'], 'files': actual, 'added': sorted(actual.keys() - expected.keys()), 'removed': sorted(expected.keys() - actual.keys()), 'changed': sorted(p for p in actual.keys() & expected.keys() if actual[p] != expected[p]), 'completeInventoryUnchanged': unchanged}) assert unchanged, 'Package changed; exact delta receipt preserved.' return package def saved(args): assert args.after_visible_save, 'Root must observe successful Save first.' assert re.fullmatch('[a-z0-9][a-z0-9-]{0,79}', args.label) folder = TRIAL / 'checkpoints' / args.label assert not folder.exists() package = package_guard(args.label) baseline, expected = fixture() folder.mkdir() actual = copy_exact(TRIAL / 'work', folder / 'sources') record(folder / 'sources.json', actual) assert actual == expected, 'Saved exact baseline files differ; preserve and inspect.' env = environment() env['PATH'] = str(package) + ';' + env['PATH'] command = [str(ORACLE), 'same', str(baseline / 'project.arcadia'), str(folder / 'sources/project.arcadia')] result = subprocess.run(command, cwd=folder, env=env, capture_output=True) (folder / 'stdout.log').write_bytes(result.stdout) (folder / 'stderr.log').write_bytes(result.stderr) record(folder / 'result.json', {'atUtc': now(), 'command': command, 'exitCode': result.returncode, 'oracle_sha256': ORACLE_SHA, 'scope': 'Saved disk/model oracle after root-observed Save; no independent UI assertion.'}) assert result.returncode == 0 package_guard(args.label + '-after-oracle') print(json.dumps({'action': 'saved', 'oracle_exit': 0, 'source_files': len(actual)})) def closed(args): assert args.observed_absent, 'Root must first observe the GUI/window absent.' launch_receipt = load(TRIAL / 'launch.json') current = process_identity(launch_receipt['pid']) original = load(TRIAL / 'process-identity.json') assert current is None or current['creation_filetime'] != original['creation_filetime'], 'Original GUI process remains present.' package = package_guard('after-close') record(TRIAL / 'closed.json', {'atUtc': now(), 'pid': launch_receipt['pid'], 'originalProcessAbsent': True, 'pidReused': current is not None, 'completePackageUnchanged': True, 'scope': 'Root-observed window absence plus read-only process identity and package check; no numeric exit code observed.'}) print(json.dumps({'action': 'closed', 'package': str(package), 'result': 'PASS'})) if __name__ == '__main__': sys.stdout.reconfigure(encoding='utf-8') sys.stderr.reconfigure(encoding='utf-8') parser = argparse.ArgumentParser() parser.add_argument('action', choices=('launch', 'saved', 'closed')) parser.add_argument('--label') parser.add_argument('--after-visible-save', action='store_true') parser.add_argument('--observed-absent', action='store_true') args = parser.parse_args() if args.action == 'launch': launch() elif args.action == 'saved': saved(args) else: closed(args)