"""Read-only portable receipt and frame verification, without archived execution.""" from pathlib import Path, PurePosixPath from datetime import datetime import hashlib,json,zipfile ROOT=Path(__file__).resolve().parent def pin(b):return {'bytes':len(b),'sha256':hashlib.sha256(b).hexdigest()} def need(ok,msg): if not ok:raise ValueError(msg) def main(): files=json.loads((ROOT/'files.json').read_bytes()) for n,p in files.items():need(pin((ROOT/n).read_bytes())==p,'outer hash '+n) manifest=json.loads((ROOT/'manifest.json').read_bytes()) with zipfile.ZipFile(ROOT/'evidence.zip') as z: names=z.namelist() need(len(names)==len(set(names)) and set(names)==set(manifest['members']),'exact ZIP membership') need(all(not PurePosixPath(n).is_absolute() and '..' not in PurePosixPath(n).parts and '\\' not in n and ':' not in n for n in names),'safe members') for n,p in manifest['members'].items():need(pin(z.read(n))==p,'member hash '+n) def obj(n):return json.loads(z.read(n)) input_names={n[len('native/input/'):]:n for n in names if n.startswith('native/input/')} reference=None;frame_count=0;image_count=0 for run,spec in manifest['runs'].items(): prefix='native/'+run+'/' start=obj(prefix+'started.json');result=obj(prefix+'result.json');process=obj(prefix+'process.json') need(all(result[k]==v for k,v in start.items()),run+' start/result relationship') need(result['exitCode']==spec['exitCode'],run+' exit code') need(isinstance(process['pid'],int) and process['pid']>0,run+' process ID') need(datetime.fromisoformat(start['start'])<=datetime.fromisoformat(process['observed'])<=datetime.fromisoformat(result['finish']),run+' process time order') need(result['helper']==pin(z.read(spec['helper'])),run+' launch helper') need(result['inputs']==pin(z.read(prefix+'inputs.json')) and result['inputUnchanged'] is True,run+' input receipt') inputs=obj(prefix+'inputs.json') need(set(inputs)==set(input_names),run+' input inventory') for n,p in inputs.items():need(pin(z.read(input_names[n]))==p,run+' input bytes '+n) if reference is None:reference=inputs need(inputs==reference,run+' all trials identical fixture') for stream in ['stdout','stderr']: b=z.read(prefix+stream+'.log');need(not b and result[stream]==pin(b),run+' empty stream '+stream) build=obj(spec['build']) need(result['buildReceipt']==pin(z.read(spec['build'])) and result['buildHead']==build['head'],run+' build receipt') need(build['exitCode']==0 and build['inputsUnchanged'] is True,run+' build status') need(datetime.fromisoformat(build['start'])<=datetime.fromisoformat(build['finish'])<=datetime.fromisoformat(start['start']),run+' build before launch') gui=[a for a in build['artifacts'] if a['cargo']['target']['name']=='arcadia-studio'] need(len(gui)==1,run+' unique GUI artifact') need(result['binary']==result['binaryAfter']==gui[0]['readback'],run+' unchanged GUI pin') need(result['command']==[gui[0]['cargo']['executable']],run+' exact launch command') images=[] for metadata in spec['captures']: frame=obj(metadata) need(datetime.fromisoformat(start['start'])<=datetime.fromisoformat(frame['time'])<=datetime.fromisoformat(result['finish']),metadata+' capture interval') need(frame['window']['app'].replace('\\','/')=='process:'+result['command'][0].replace('\\','/'),metadata+' window application path') for image in frame['images']: n=prefix+image['file'];need(n in manifest['members'] and z.read(n).startswith(b'\xff\xd8'),metadata+' JPEG reference') need(image['width']>0 and image['height']>0,metadata+' frame dimensions') images.append(n) actual_images=[n for n in names if n.startswith(prefix) and n.endswith('.jpg')] need(len(images)==len(set(images)) and set(images)==set(actual_images),run+' all images referenced once') actual_metadata=[n for n in names if n.startswith(prefix) and n.endswith('.json') and 'images' in obj(n)] need(set(actual_metadata)==set(spec['captures']) and len(actual_metadata)==len(spec['captures']),run+' all capture metadata') frame_count+=len(actual_metadata);image_count+=len(images) print(run,'exit',result['exitCode'],'captures',len(actual_metadata),'images',len(images)) need(frame_count==manifest['captureCount'] and image_count==manifest['imageCount'],'aggregate frame counts') print('PASS:',len(names),'raw members;',len(reference),'unchanged fixture files;',frame_count,'capture records;',image_count,'original JPEGs.') print('Build source inventories are external. Frames prove recorded states, not action execution or PID attestation. Capture helper was not start-pinned.') if __name__=='__main__':main()