111 lines
9.5 KiB
Python
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))
|