"""Finalize bounded evidence and publish the additive L01 catalog, not runtime code."""
import ast
import json
from pathlib import Path
from run_l01 import ROOT, BASELINE, REMOTE, save, digest, scp, checked, checkpoint, exclusive_gpu

def main():
    catalog=json.loads((ROOT/'metadata/catalog.json').read_text())
    parts=catalog['L01']['parts'];assert [p['observations'] for p in parts]==[1044,7261]
    before=json.loads((ROOT/'reports/production_before.json').read_text())
    after=json.loads((ROOT/'reports/production_after.json').read_text())
    operations=dict(container_identity_unchanged=set(before['containers'])==set(after['containers']),
        config_hash_unchanged=before['config_sha256']==after['config_sha256'],
        production_GPU_pids_unchanged=set(before['gpu_owner']['gpu_pids'])==set(after['gpu_owner']['gpu_pids']),
        performance='not measured; no claim of zero operating impact')
    resources=[before['resources'],after['resources']]
    for p in (ROOT/'reports').glob('*_resources.json'):resources.extend(json.loads(p.read_text()))
    minimum=dict(root_free_bytes=min(r['root_free'] for r in resources),
        storage_free_bytes=min(r['storage_free'] for r in resources),ram_available_bytes=min(r['ram_available'] for r in resources),
        gpu_free_mib=min(r['gpu_free_mib'] for r in resources if 'gpu_free_mib' in r),
        scope='bounded observations, not exact peak measurement')
    query_count=0;rgb_checks=0;pose_wall=0
    for p in parts:
        root=ROOT/p['cache']
        q=json.loads((root/'reports/compatibility.json').read_text());v=json.loads((root/'reports/cache_validation.json').read_text())
        assert q['status']==v['status']=='PASS'
        query_count+=q['raw_queries'];rgb_checks+=len(v['rgb_checks'])
        pose_wall+=json.loads((root/'reports/pose_summary.json').read_text())['wall_seconds']
    assert query_count==830500 and rgb_checks==6
    assert digest(ROOT/'helpers/pose.py')==digest(BASELINE/'helpers/pose.py')
    alignment=json.loads((ROOT/'clips/L01_P02/reports/sampling_alignment.json').read_text())
    assert alignment['status']=='PASS' and alignment['samples']==7261 and not alignment['model_started']
    for p in (ROOT/'helpers').glob('*.py'):ast.parse(p.read_text(),filename=str(p))
    owner=exclusive_gpu()
    save(ROOT/'reports/final_validation.json',dict(status='PASS',parts=2,observations=8305,expected=[1044,7261],
        prefilter_queries=query_count,rgb_source_pixel_checks=rgb_checks,remote_rgb_files=8305,local_rgb_files=0,
        operations=operations,minimum_observed_resources=minimum,remaining_gpu_owner=owner,
        engine_or_event_executed=False,legacy_pose_helper_unchanged=True,pose_trial_wall_seconds_sum=round(pose_wall,3),
        source_gap_seconds=1.016668,selected_observation_gap_seconds=1.057729,
        source_frame='L01 input 1-based',seconds='L01 actual elapsed time',sample='part-local 0-based',
        raw_transfer_sha256='all transferred cache inputs and reports matched remote per-part inventories'))
    handoff='''# 메인 세션 전달문

자체촬영 원본01의 L01을 원본 영상 하나 그대로 유지하면서 두 파트의 Pose/RGB/Tracking cache로 준비했습니다.
이 run의 00_README_ko.md, metadata/catalog.json, metadata/locations.json을 먼저 읽어주세요.
새 catalog는 기존 C01~C22의 검증된 cache를 참조하며 재추론하거나 변경하지 않았습니다.
L01_P01은1044관측, L01_P02는7261관측입니다. RGB는 원격에만 있습니다.
source_frame과 seconds는 L01 전체 입력 기준이고 sample만 파트별0부터입니다. 기존 사용자 검토Frame은 대응표로 찾으세요.
원본 경계 간격1.016668초와 선택 관측 사이 간격1.057729초를 구분하세요. 시간 압축이나 가짜 Frame은 없습니다.
두 파트의 Tracker는 독립적입니다. (part_id,T ID)로 구분하고 두 pose.json을 그대로 이어 붙이지 마세요.
metadata/action_links.json에 L01과 겹치는 사용자 행동 참고57개를 연결했습니다. 원문 a/b 보정·대략적 anchor·우선도와 O20 판단 대기를 유지하세요.
Cxx와 L01의 중첩 관측은 독립 검증 사례가 아닙니다. 정확한 Frame GT나 새 Event 판정으로 읽지 마세요.
L01의 과거 DEFERRED 기록은 보존했으며, 이번 새 catalog가 두 파트 준비 완료의 최신 근거입니다.
메인 reader의 JSON/hash 계약만 검증했습니다. 실제 엔진 reset·Event 종료 정책·renderer·RTSP 시험은 별도 요청 범위입니다.
메인 Owner와 기존 evidence는 수정하지 않았습니다.
'''
    (ROOT/'HANDOFF_ko.md').write_text(handoff)
    checkpoint('CACHE_AND_CATALOG_VERIFIED_PUBLISHING')
    for folder in ('metadata','main_reader','helpers'):
        scp(ROOT/folder,'si@58.233.207.31:'+REMOTE+'/')
    for name in ('00_README_ko.md','HANDOFF_ko.md'):
        scp(ROOT/name,'si@58.233.207.31:'+REMOTE+'/')
    for name in ('final_validation.json','catalog_validation.json','sampling_recovery.json','source_fingerprints.json'):
        scp(ROOT/'reports'/name,'si@58.233.207.31:'+REMOTE+'/reports/')
    targets=[p for folder in ('metadata','main_reader','helpers') for p in (ROOT/folder).rglob('*') if p.is_file() and '__pycache__' not in p.parts]
    targets += [ROOT/'00_README_ko.md',ROOT/'HANDOFF_ko.md']
    expected={str(p.relative_to(ROOT)):digest(p) for p in targets}
    code="import pathlib,json,hashlib;root=pathlib.Path("+repr(REMOTE)+");names="+repr(list(expected))+";print(json.dumps({n:hashlib.sha256((root/n).read_bytes()).hexdigest() for n in names}))"
    actual=json.loads(checked(['python3','-c',code],timeout=120))
    assert actual==expected
    save(ROOT/'reports/metadata_transfer.json',dict(status='PASS',files=len(expected),sha256_match=True))
    checkpoint('COMPLETE_L01_TWO_PARTS')
    scp(ROOT/'checkpoint.json','si@58.233.207.31:'+REMOTE+'/')
    scp(ROOT/'reports/metadata_transfer.json','si@58.233.207.31:'+REMOTE+'/reports/')
    files={str(p.relative_to(ROOT)):dict(bytes=p.stat().st_size,sha256=digest(p)) for p in sorted(ROOT.rglob('*'))
           if p.is_file() and p.name!='manifest.json' and '__pycache__' not in p.parts}
    save(ROOT/'manifest.json',dict(schema='contextra.l01-pose-run.v1',lifecycle='durable-large',retention='manual-review',files=files))
    scp(ROOT/'manifest.json','si@58.233.207.31:'+REMOTE+'/local_inventory_manifest.json')
    print(json.dumps(dict(status='PASS',parts=2,observations=8305,metadata_and_helpers_verified=len(expected),operations=operations,minimum=minimum)),flush=True)

if __name__=='__main__':main()
