Files
ARCADIA-Studio/harness/experiments/rename-scroll-feab202-20260913/verify.py
T

186 lines
14 KiB
Python

"""Portable closed evidence verification; only file/Git reads, no saved code execution."""
from pathlib import Path
import argparse, hashlib, io, json, ntpath, re, subprocess, sys
from datetime import datetime
if sys.flags.optimize:
raise SystemExit('Optimized Python forbidden')
ARCHIVE = Path(__file__).resolve().parent
ROOT = ARCHIVE.parents[2]
SOURCE = 'feab2020d8e5b6bfdb59c86ab4aee161617b8b6f'
BASE = '9e51cd72ba05d37482c5bc258e284804058a1475'
def require(value, message):
if not value:
raise ValueError(message)
def identity(data):
return {'bytes': len(data), 'sha256': hashlib.sha256(data).hexdigest()}
def binding(row):
return {k: row[k] for k in ('bytes', 'sha256')}
def read(path):
return json.loads(path.read_bytes())
def git(*args, data=None):
return subprocess.check_output(['git', '-c', 'core.longpaths=true', *args], cwd=ROOT, input=data)
def blobs(specs):
stream = io.BytesIO(git('cat-file', '--batch', data=('\n'.join(specs) + '\n').encode()))
result = []
for spec in specs:
header = stream.readline().split()
require(len(header) == 3 and header[1] == b'blob', 'Missing blob ' + spec)
data = stream.read(int(header[2]))
require(len(data) == int(header[2]) and stream.read(1) == b'\n', 'Malformed batch blob')
result.append(data)
require(not stream.read(), 'Trailing batch bytes')
return result
parser = argparse.ArgumentParser(description=__doc__)
group = parser.add_mutually_exclusive_group()
group.add_argument('--git-ref')
group.add_argument('--index', action='store_true')
args = parser.parse_args()
m = read(ARCHIVE / 'manifest.json')
require((m['schema'], m['source'], m['publication_base']) == ('rename-scroll-feab-evidence-v1', SOURCE, BASE), 'Wrong manifest pins')
all_rows = m['files'] + m['references']
require(len({r['path'] for r in all_rows}) == len(all_rows), 'Duplicate paths')
originals = {}
for row in all_rows:
p = ROOT / row['path']
require(not Path(row['path']).is_absolute() and p.resolve().is_relative_to(ROOT), 'Escaping archive path')
require(p.is_file() and not p.is_symlink() and identity(p.read_bytes()) == binding(row), 'Payload differs ' + row['path'])
if 'original' in row:
key = row['original'].replace('\\', '/')
require(key not in originals, 'Duplicate original mapping')
originals[key] = p
own = {p.relative_to(ROOT).as_posix() for p in ARCHIVE.rglob('*') if p.is_file()}
manifest_path = (ARCHIVE / 'manifest.json').relative_to(ROOT).as_posix()
require(own == {r['path'] for r in m['files']} | {manifest_path}, 'Archive membership differs')
require(all(Path(n).suffix.lower() not in ('.exe', '.dll', '.rlib', '.rmeta', '.pdb') for n in own), 'Executable/cache payload')
for row, data in zip(m['references'], blobs([r['commit'] + ':' + r['path'] for r in m['references']])):
require(identity(data) == binding(row), 'Published reference differs')
source_rows = [r for r in m['files'] if 'source_commit' in r]
for row, data in zip(source_rows, blobs([r['source_commit'] + ':' + r['source_path'] for r in source_rows])):
require(identity(data) == binding(row), 'Source seam differs')
def original(path):
path = str(path).replace('\\', '/')
prefix = 'C:/Users/User/Documents/Git/ARCADIA-Studio/'
path = path.removeprefix(prefix)
require(path in originals, 'Original unavailable ' + path)
return originals[path]
trial = '.local/native-qa/rename-scroll-feab202-20260913-01/'
gates = '.local/rename-scroll-gates-20260913/'
prepdir = '.local/rename-scroll-gate-preparation-r3-20260913/'
prep = read(original(trial + 'preparation.json'))
launch = read(original(trial + 'launch.json'))
closure = read(original(trial + 'closure.json'))
require(launch['source'] == closure['source'] == prep['source'] == SOURCE, 'Native source differs')
require(launch['pid'] == closure['pid'] == 10456 and closure['exit'] == 0 and closure['stage_unchanged'] is True and closure['exact_disk_bytes_equal'] is True, 'Native closure failed')
require(closure['utc'] == '2026-09-13T12:21:44.165074+00:00', 'Wrong closure')
require(identity(original(trial + 'preparation.json').read_bytes()) == launch['manifest'], 'Launch manifest differs')
require(read(original(trial + 'launch-request.json')).items() <= launch.items(), 'Launch request differs')
require(identity(original('.local/rename-scroll-native-feab202.py').read_bytes()) == launch['helper'], 'Launcher differs')
require(identity(original('.local/rename-scroll-native-feab202.config.json').read_bytes()) == launch['config'], 'Config differs')
require(original(trial + 'preparation.json').read_bytes() == original('.local/native-qa/rename-scroll-stage-feab202-20260913-01/manifest.json').read_bytes(), 'Stage manifest differs')
for name in ('stdout', 'stderr'):
require(identity(original(trial + name + '.log').read_bytes()) == closure[name] and closure[name]['bytes'] == 0, 'Native stream differs')
omitted = {r['original']: r for r in m['omitted'] if 'sha256' in r}
for path, bound in prep['records'].items():
path = path.replace('\\', '/')
if path in omitted:
require(binding(omitted[path]) == bound, 'Omitted binary identity differs')
else:
require(identity(original(path).read_bytes()) == bound, 'Preparation record differs ' + path)
cp = read(original(trial + 'checkpoints/cancel-save/result.json'))
require(cp['before'] == cp['snapshot'] == cp['after'] == cp['expected'] == prep['baseline'] and len(prep['baseline']) == 6, 'Checkpoint map differs')
require(cp['copy_consistent'] is True and cp['baseline_unchanged'] is True and cp['exact_disk_bytes_equal'] is True and cp['stage_unchanged'] is True, 'Checkpoint failed')
require(all(cp[k] is None for k in ('before_error', 'copy_error', 'snapshot_error', 'after_error', 'verification_error')), 'Checkpoint error')
for folder in ('source-baseline/', 'checkpoints/cancel-save/snapshot/'):
for name, bound in prep['baseline'].items():
require(identity(original(trial + folder + name).read_bytes()) == bound, 'Snapshot bytes differ')
frames = sorted(p for key,p in originals.items() if key.startswith(trial + 'evidence/') and key.endswith('.jpg'))
require(len(frames) == 20 and [p.name[:3] for p in frames] == [f'{i:03}' for i in range(1,21)], 'Frame membership differs')
for p in frames:
note = read(p.with_suffix('.json'))
require(p.read_bytes().startswith(b'\xff\xd8') and note['source'] == SOURCE and note['window']['id'] == 6750956 and note['screenshot']['mime'] == 'image/jpeg', 'Image/source binding differs')
expected = read(original(prepdir + 'expected.json'))
require(expected['source'] == SOURCE and len(set(expected['listing_members'])) == 1099, 'Wrong listing configuration')
for p, sha in expected['baseline_and_original_helper_pins'].items():
require(identity(original(p).read_bytes())['sha256'] == sha, 'Baseline helper differs')
gate_inputs = read(original(gates + 'list-feab202.inputs.json'))
gui_inputs = read(original(gates + 'gui-feab202/inputs.json'))
im = {r['path']: r for r in gate_inputs['inputs']}
gm = {r['path']: r for r in gui_inputs['inputs']}
require(len(im) == len(gate_inputs['inputs']) == 581 and len(gm) == len(gui_inputs['inputs']) == 615 and all(gm.get(k) == v for k,v in im.items()), '581/615 input scope differs')
scopes = ['Cargo.toml','Cargo.lock','rust-toolchain.toml','.cargo','build','crates','assets','docs','examples','harness/fixtures']
require(git('ls-tree','-r','--name-only',SOURCE,'--',*scopes).decode().splitlines() == [r['path'] for r in gate_inputs['inputs']], 'Gate input membership differs from Git')
require(git('ls-tree','-r','--name-only',SOURCE,'--',*scopes,'scripts').decode().splitlines() == [r['path'] for r in gui_inputs['inputs']], 'GUI input membership differs from Git')
for row,data in zip(gui_inputs['inputs'], blobs([SOURCE + ':' + r['path'] for r in gui_inputs['inputs']])):
require(identity(data) == binding(row), 'Committed input differs ' + row['path'])
test_artifact = None
for stem in ('list', 'preview', 'rename', 'navigation', 'source-navigation', 'full', 'strict'):
stem += '-feab202'
result = read(original(gates + stem + '.result.json'))
admitted = read(original(gates + stem + '.admission.json'))
require(datetime.fromisoformat(result['end']) >= datetime.fromisoformat(result['start']), 'Gate closure order differs')
require(result['source'] == result['head_after'] == admitted['source'] == SOURCE and result['exit'] == 0 and result['changed_inputs'] == [] and result['status_after'] == '' and admitted['verified_inputs'] == 581, 'Gate not closed cleanly')
require(read(original(gates + stem + '.inputs.json')) == gate_inputs, 'Gate source scope differs')
for suffix,key in (('result.json','result_sha256'),('inputs.json','inputs_sha256'),('log','log_sha256')):
require(identity(original(gates + stem + '.' + suffix).read_bytes())['sha256'] == admitted[key], 'Admission binding differs')
require(admitted['helper_sha256'] == identity(original(prepdir + 'admit-fixed.py').read_bytes())['sha256'] and admitted['config_sha256'] == identity(original(prepdir + 'expected.json').read_bytes())['sha256'] and result['launcher_sha256'] == identity(original(prepdir + 'run-fixed.py').read_bytes())['sha256'], 'Gate tool differs')
log = original(gates + stem + '.log').read_text(encoding='utf-8')
prefix = ['cargo','test','--locked','-p','oa-desktop','--bin','arcadia-studio']
if stem.startswith('strict'):
require(result['command'] == ['cargo','clippy','--locked','-p','oa-desktop','--all-targets','--','-D','warnings'] and result['artifacts'] == [] and 'Finished `dev` profile' in log, 'Strict invocation differs')
continue
launches = re.findall(r'Running unittests [^\r\n]+ \(([^\r\n]+\.exe)\)', log)
require(len(launches) == 1, 'Missing actual test executable launch')
launched = launches[0].replace('\\','/')
if not ntpath.isabs(launched):
launched = ntpath.join('C:/Users/User/Documents/Git/ARCADIA-Studio/.local/rs', launched)
require(ntpath.normcase(ntpath.normpath(launched)) == ntpath.normcase(ntpath.normpath(result['artifacts'][0]['path'])), 'Launched test executable differs')
require(len(result['artifacts']) == 1 and result['artifacts'][0] == admitted['artifact'], 'Test artifact differs')
if test_artifact is None:
test_artifact = admitted['artifact']
require(admitted['artifact'] == test_artifact, 'Mixed test binaries')
if stem.startswith('list'):
names = re.findall(r'^([^\r\n]+): test\r?$', log, re.M)
require(result['command'] == prefix + ['--','--list'] and names == result['listing_members'] and set(names) == set(expected['listing_members']) and len(names) == 1099, 'Listing differs')
else:
names = admitted['names']
ignored = expected['ignored'] if stem.startswith('full') else []
selector = [] if stem.startswith('full') else [result['command'][7]]
require(result['command'] == prefix + selector + ['--','--test-threads=1'], 'Runtime invocation differs')
require(set(names) == set(expected['listing_members'] if not selector else expected['focused_groups'][selector[0]]), 'Expected group differs')
outcomes = re.findall(r'^test ([^\r\n]+) \.\.\. (ok|FAILED|ignored)(?:, [^\r\n]*)?\r?$', log, re.M)
require(len(outcomes) == len(set(n for n,_ in outcomes)) == len(names) and set(n for n,_ in outcomes) == set(names), 'Runtime membership differs')
require(all(v == ('ignored' if n in ignored else 'ok') for n,v in outcomes), 'Runtime outcome differs')
totals = re.findall(r'^test result: ok\. (\d+) passed; (\d+) failed; (\d+) ignored; (\d+) measured; (\d+) filtered out;', log,re.M)
require(totals == [(str(len(names)-len(ignored)),'0',str(len(ignored)),'0',str(1099-len(names)))], 'Runtime totals differ')
gui = read(original(gates + 'gui-feab202/result.json'))
require(gui['source'] == gui['head_after'] == SOURCE and gui['exit'] == 0 and gui['outcome'] == 'passed' and gui['changed_inputs'] == [] and gui['status_after'] == '', 'GUI build failed')
require(Path(gui['command'][0].replace('\\','/')).name.casefold() in ('cargo','cargo.exe') and gui['command'][1:] == ['build','--locked','--offline','-p','oa-desktop','--bin','arcadia-studio','--message-format=json-render-diagnostics'], 'GUI command differs')
require(datetime.fromisoformat(gui['end']) >= datetime.fromisoformat(gui['start']), 'GUI closure order differs')
for name,key in [('inputs.json','inputs'),('environment.json','environment'),('cargo.jsonl','stdout'),('cargo.stderr.log','stderr')]:
require(identity(original(gates + 'gui-feab202/' + name).read_bytes()) == gui[key], 'GUI binding differs')
require(identity(original(prepdir + 'build-gui.py').read_bytes()) == gui['launcher'], 'GUI builder differs')
require(len(gui['artifacts']) == 1 and binding(gui['artifacts'][0]) == prep['gui'] == prep['payloads']['arcadia-studio.exe'], 'GUI/native artifact differs')
artifact = gui['artifacts'][0]['cargo_artifact']
require(artifact in [json.loads(line) for line in original(gates + 'gui-feab202/cargo.jsonl').read_text(encoding='utf-8').splitlines()] and artifact['target']['name'] == 'arcadia-studio' and artifact['profile']['test'] is False and artifact['profile']['opt_level'] == '0' and artifact['profile']['debug_assertions'] is True, 'Cargo profile/artifact differs')
failed = read(original(gates + 'preview-bebf9f5.result.json'))
require(failed['source'] == 'bebf9f594c6722200e95e92be2734acbea0dd700' and failed['exit'] == 101 and failed['changed_inputs'] == [], 'Original failed receipt changed')
require('4 passed; 1 failed;' in original(gates + 'preview-bebf9f5.log').read_text(encoding='utf-8'), 'Original failure absent')
if args.git_ref or args.index:
prefix = args.git_ref + ':' if args.git_ref else ':'
for path,data in zip(sorted(own), blobs([prefix + p for p in sorted(own)])):
require((ROOT / path).read_bytes() == data, 'Committed/index bytes differ ' + path)
command = ('ls-tree','-r','--name-only',args.git_ref,'--',ARCHIVE.relative_to(ROOT).as_posix()) if args.git_ref else ('ls-files','--',ARCHIVE.relative_to(ROOT).as_posix())
require(set(git(*command).decode().splitlines()) == own, 'Committed/index membership differs')
print(json.dumps({'verified':True,'payloads':len(m['files']),'references':len(m['references']),'source':SOURCE,'input_counts':[581,615],'frames':20,'full':[1096,0,3],'native_scope':'UI review separate; six-file disk equality and closed owned process bound'}))