Files
ARCADIA-Studio/harness/experiments/canvas-native-dd39e1f-20260913/tools/archive-canvas-native-dd39e1f-20260913.py
T

111 lines
9.5 KiB
Python

"""Archive only the closed dd39 trial; no compiler/UI and no root source changes."""
from pathlib import Path
from datetime import datetime,timezone
import hashlib,json,struct,subprocess
R=Path(__file__).resolve().parent.parent;T=R/'.local/native-qa/lifecycle-palette-20260913-01';S=R/'.local/native-qa/studio-dd39e1f'
D=R/'harness/experiments/canvas-native-dd39e1f-20260913';SOURCE='dd39e1fdc13f04f18975a70736d2ed7f752a6df2';FULL='e44f07565cc7599d5c5a15a6b5599b4f5d105718'
def ident(p):
b=p.read_bytes();return {'bytes':len(b),'sha256':hashlib.sha256(b).hexdigest()}
def read(p):return json.loads(p.read_bytes())
def verify(p,x):
a=ident(p);assert a['sha256']==x['sha256'],str(p)
if 'bytes' in x:assert a['bytes']==x['bytes'],str(p)
def new(p,b):
p.parent.mkdir(parents=True,exist_ok=True)
with p.open('xb') as f:f.write(b)
def dt(s):return datetime.fromisoformat(s.replace('Z','+00:00'))
def jpeg_dimensions(b):
assert b[:2]==b'\xff\xd8';i=2
while i<len(b):
assert b[i]==255
while b[i]==255:i+=1
marker=b[i];i+=1
if marker in [0xd8,0xd9] or 0xd0<=marker<=0xd7:continue
size=int.from_bytes(b[i:i+2],'big');assert size>=2
if marker in [0xc0,0xc1,0xc2,0xc3,0xc5,0xc6,0xc7,0xc9,0xca,0xcb,0xcd,0xce,0xcf]:
h,w=struct.unpack('>HH',b[i+3:i+7]);return w,h
i+=size
raise AssertionError('JPEG frame dimensions absent')
assert not D.exists()
binding=read(R/'.local/native-dd39-source-binding-20260913.json')
assert binding['source']==SOURCE and binding['full_source']==FULL and binding['core_inputs_verified']==110 and binding['exact_test_only_delta_verified']
m=read(S/'manifest.json');verify(S/'manifest.json',{'sha256':binding['stage_manifest_sha256']})
l=read(T/'launch.json');c=read(T/'closure.json');prepared=read(T/'preparation.json');top=read(T/'topology-preparation.json')
assert l['source']==SOURCE and l['full_test_source']==FULL and l['full_suite_reuse']==m['full_suite_reuse']
assert l['pid']==c['pid'] and c['exit']==0 and dt(prepared['utc'])<dt(l['utc'])<dt(c['utc'])
for name in ['arcadia-studio.exe','libunwind.dll']:verify(S/name,m['artifacts'][name])
assert l['sha256']==m['artifacts']['arcadia-studio.exe']['sha256']
assert (T/'stdout.log').stat().st_size==(T/'stderr.log').stat().st_size==0
assert len(prepared['files'])==12
for x in prepared['files']:
verify(T/x['path'],x);verify(R/x['source'],x)
base_names=['compact-baseline','lifecycle-baseline','topology-baseline'];base_records=[x for x in prepared['files'] if x['path'].split('/')[0] in base_names]
assert len(base_records)==6
for x in top['files']:
verify(R/x['path'],x);verify(R/x['source']['path'],x['source'])
assert len(top['files'])==4
toprun=T/'topology-admission-42511f0-r1';admission=read(toprun/'run.json')
assert admission['exit']==0 and admission['inputs_unchanged'] and admission['oracle_sha256']==top['oracle']['sha256']
for x in top['provenance']+[top['oracle'],top['runtime'],top['helper'],top['original_preparation']]:verify(R/x['path'],x)
for stream in ['stdout','stderr']:verify(R/admission[stream]['path'],admission[stream])
oracle=R/top['oracle']['path'];oracleid=ident(oracle)
core=read(toprun/'verified-current-core-config-inputs.json');assert len(core)==110
# The completed source binding permits the parent to release current source holds.
# Compare frozen stage/core inventories, not later mutable root core files.
by_path={x['path']:x for x in m['inputs']}
assert all(x==by_path[x['path']] for x in core)
frames=sorted((T/'evidence').glob('[0-9][0-9]-*.json'));assert len(frames)==41
for i,p in enumerate(frames,1):
assert p.name.startswith(f'{i:02}-');x=read(p);png=p.with_suffix('.png');verify(png,x)
b=png.read_bytes();assert jpeg_dimensions(b)==(x['width'],x['height'])
assert dt(l['utc'])<=dt(x['utc'])<=dt(c['utc'])
assert x['window']['app']=='process:'+l['binary']
assert {p.with_suffix('.png') for p in frames}==set((T/'evidence').glob('*.png'))
names=['lifecycle-after-save','lifecycle-after-reopen','compact-after-save','topology-after-save','topology-after-close'];snapshots=set();oracle_rows=[]
for name in names:
p=T/'evidence'/(name+'.json');x=read(p);assert x['exit']==0 and x['gui_source']==SOURCE and x['gui_sha256']==l['sha256'] and x['verified_core_inputs']==110
assert x['oracle_sha256']==oracleid['sha256'];snapshot=T/'checkpoints'/name;assert Path(x['snapshot']).resolve()==snapshot.resolve()
fixture=name.split('-after-')[0];entry='lifecycle-native.arcadia' if fixture=='lifecycle' else 'project.arcadia';base=T/(fixture+'-baseline')
assert x['command']==[str(oracle),'same',str(base/entry),str(snapshot/entry)]
if name=='topology-after-close':assert dt(x['start'])>dt(c['utc'])
else:assert dt(l['utc'])<dt(x['start'])<=dt(x['end'])<dt(c['utc'])
declared={snapshot/y['path'] for y in x['files']};assert declared=={q for q in snapshot.rglob('*') if q.is_file()}
for y in x['files']:
q=snapshot/y['path'];assert q.resolve().is_relative_to(snapshot.resolve());verify(q,y);assert y['baseline_equal'] and q.read_bytes()==(base/y['path']).read_bytes()
log=T/'evidence'/(name+'.log');assert b'PASS same: complete Project and exact SourceBundle restored' in log.read_bytes()
snapshots.update(declared);oracle_rows.append({'receipt':p.relative_to(T).as_posix(),'snapshot_files':len(declared),'log':ident(log),'after_close':name=='topology-after-close'})
assert snapshots=={p for p in (T/'checkpoints').rglob('*') if p.is_file()}
critic=R/'.local/lifecycle-palette-native-independent-review-20260913.md';verify(critic,{'bytes':9110,'sha256':'3869d71bb6f13276de84bf1ab2eaff2424ce865999206cb42b82fb95ae289de6'})
payload=[]
def add(p,relative):
assert p.is_file() and not p.is_symlink();payload.append((p,relative))
for n in ['preparation.json','topology-preparation.json','launch.json','closure.json','stdout.log','stderr.log']:add(T/n,n)
for n in ['evidence','checkpoints','topology-admission-42511f0-r1',*base_names]:
for p in sorted((T/n).rglob('*')):
if p.is_file():add(p,p.relative_to(T).as_posix())
for p in sorted(S.iterdir()):
if p.is_file() and p.suffix.lower() not in ['.exe','.dll']:add(p,'stage/'+p.name)
helpers=['lifecycle-palette-native-session.py','lifecycle-palette-native-testfix-session.py','lifecycle-palette-checkpoint.py','stage-palette-gui.py','stage-palette-gui-testfix.py','prepare-topology-native-20260913.py','delete-native-oracle.rs','verify-native-dd39-source-20260913.py','archive-canvas-native-dd39e1f-20260913-png-assumption.py',Path(__file__).name]
for n in helpers:add(R/'.local'/n,'tools/'+n)
add(R/'.local/native-dd39-source-binding-20260913.json','source-binding.json')
add(critic,'review/lifecycle-palette-native-independent-review-20260913.md')
add(R/'.local/palette-testfix-staging-review-20260913.md','review/palette-testfix-staging-review-20260913.md')
for x in top['provenance']:
p=R/x['path'];add(p,'provenance/'+p.relative_to(R).as_posix().removeprefix('.local/'))
assert len({x for _,x in payload})==len(payload)
assert not any(x.split('/')[0] in ['profile','temp','compact-work','lifecycle-work','topology-work'] for _,x in payload)
before={str(p):ident(p) for p,_ in payload};D.mkdir();rows=[]
for p,relative in payload:
new(D/relative,p.read_bytes());verify(D/relative,before[str(p)]);rows.append({'path':relative,'source_path':p.relative_to(R).as_posix(),**before[str(p)]})
for p,_ in payload:verify(p,before[str(p)])
external={'scope':'Large executable/runtime are excluded hashed inputs; no newly executed runtime validation.','source':SOURCE,'full_suite_source':FULL,'reuse':m['full_suite_reuse'],'gui':{'local_path':l['binary'],**m['artifacts']['arcadia-studio.exe']},'runtime':{'local_path':str(S/'libunwind.dll'),**m['artifacts']['libunwind.dll']},'oracle':{'local_path':str(oracle),**oracleid},'excluded':['profile','temp','compact-work','lifecycle-work','topology-work','large executable/target payloads'],'original_preparation_rows':12,'archived_baseline_files':8}
b=(json.dumps(external,indent=2)+'\n').encode();new(D/'external-bindings.json',b);rows.append({'path':'external-bindings.json','source_path':'generated from verified stage/launch/oracle records','bytes':len(b),'sha256':hashlib.sha256(b).hexdigest()})
manifest={'utc':datetime.now(timezone.utc).isoformat(),'source':SOURCE,'full_suite_source':FULL,'closed_exit':0,'screenshots':41,'screenshot_byte_format':'All original .png-named files contain JPEG/JFIF bytes; sidecar SHA and JPEG frame dimensions verified; filenames and bytes unchanged. Initial PNG-signature assumption failed before archive creation and its helper is retained.','checkpoints':oracle_rows,'original_preparation_rows_verified':12,'topology_preparation_rows_verified':4,'archived_baseline_files':8,'source_binding':'source-binding.json','files':rows}
new(D/'manifest.json',(json.dumps(manifest,indent=2)+'\n').encode())
# All earlier root attributes are staged; append only the new archive rule.
attr=R/'.gitattributes';current=attr.read_bytes();index=subprocess.check_output(['git','show',':.gitattributes'],cwd=R)
assert current.decode().splitlines()==index.decode().splitlines(),'Coordinate existing unstaged attribute edits first'
rule=b'harness/experiments/canvas-native-dd39e1f-20260913/** -text whitespace=-blank-at-eol,-blank-at-eof,cr-at-eol';assert rule not in current
with attr.open('ab') as f:f.write((b'' if current.endswith(b'\n') else b'\n')+rule+b'\n')
print(json.dumps({'payloads_before_readme':len(rows),'screenshots':41,'checkpoints':len(oracle_rows),'baseline_files':8,'critic_relative_path':'review/lifecycle-palette-native-independent-review-20260913.md','sources_unchanged':True},indent=2))