"""Publish additive catalog metadata and summarize bounded technical evidence."""
import ast
import json
from pathlib import Path
from control import ROOT, REMOTE, digest, save, scp, checked, exclusive_gpu, checkpoint, resources

def main():
    catalog=json.loads((ROOT/'metadata/catalog.json').read_text())
    assert len(catalog['clips'])==22 and catalog['observations']==7060
    before=json.loads((ROOT/'reports/production_before.json').read_text())
    after=json.loads((ROOT/'reports/production_after.json').read_text())
    state=dict(container_identity_unchanged=before['containers']==after['containers'],
               config_hash_unchanged=before['config_sha256']==after['config_sha256'],
               production_pids_unchanged=set(before['process_pids'])==set(after['process_pids']),
               performance='not measured; no assertion of zero runtime impact')
    assert state['container_identity_unchanged'] and state['config_hash_unchanged']
    ownership=exclusive_gpu()
    samples=[before['resources'],after['resources']]
    for p in (ROOT/'reports').glob('*_resources.json'):samples.extend(json.loads(p.read_text()))
    stats=dict(min_observed_root_free=min(r['root_free'] for r in samples),
               min_observed_storage_free=min(r['storage_free'] for r in samples),
               min_observed_ram_available=min(r['ram_available'] for r in samples),
               min_observed_gpu_free_mib=min(r['gpu_free_mib'] for r in samples if 'gpu_free_mib' in r),
               scope='bounded samples; not exact peak resource measurement',per_trial_cpu_limit=2,per_trial_ram_gib=8)
    raw_queries=0;rgb_checks=0;wall=0
    for c in catalog['clips']:
        root=ROOT/c['cache']
        extra=json.loads((root/'reports/compatibility.json').read_text())
        check=json.loads((root/'reports/cache_validation.json').read_text())
        raw_queries+=extra['raw_queries'];rgb_checks+=len(check['rgb_checks'])
        wall+=json.loads((root/'reports/pose_summary.json').read_text())['wall_seconds']
    assert raw_queries==706000 and rgb_checks==66
    for p in (ROOT/'helpers').glob('*.py'):ast.parse(p.read_text(),filename=str(p))
    save(ROOT/'reports/final_validation.json',dict(status='PASS',clips=22,observations=7060,prefilter_queries=raw_queries,
        rgb_source_pixel_checks=rgb_checks,remote_rgb_files=7060,local_rgb_files=0,
        engine_or_event_executed=False,pose_trial_wall_seconds_sum=round(wall,3),operation_observations=state,
        resource_samples=stats,remaining_gpu_owner_check=ownership,deferred=['L01 sampler gap policy'],
        transfer_verification='all transferred input/report files checked against remote per-clip SHA256 inventories'))
    handoff='''# 메인 세션 전달문

자체촬영 원본01 C01~C22의 Pose/RGB/Tracking cache를 준비했습니다.
먼저 이 run의 00_README_ko.md와 metadata/catalog.json, metadata/action_cases.json을 읽어주세요.
22클립·7060관측이며 20개 관찰을 59개 행동 참고 항목으로 연결했습니다.
실제 local/remote 경로는 metadata/locations.json입니다. RGB는 원격 cache에만 있습니다.
source_frame은 클립 내부1-based 번호입니다. 사용자 검토Frame은 metadata/observation_maps/Cxx.csv로 연결하세요.
주체 a/b와 T/q를 동일시하지 말고 대략적인 anchor를 정확한 Event 정답 경계로 쓰지 마세요.
기존 raw 관절score와 prefilter를 보존했습니다. valid_joints는 기존0.3 집계이므로 별도 기준에는 raw score를 사용하세요.
클립별 Tracking은 처음부터 독립 실행했습니다. 중첩 context·동일 검토Frame도 독립 검증 사례로 합산하지 마세요.
O20/C22 싸움은 사용자 판단 대기입니다. 쓰러짐 전체 비감지 참고와 명시 우선도는 원문을 따르세요.
L01은 PTS 간격과 기존 sampler의 중복선택 방지로 보류했습니다. 자동 보간이나 재샘플링을 하지 않았습니다.
main_reader/Cxx/data는 기존 JSON/hash/selected_samples 읽기 계약용입니다. 엔진·Event·renderer 실행은 검증하지 않았습니다.
메인 Owner와 기존 snapshot은 변경하지 않았습니다. 실제 엔진 개발·평가는 사용자의 별도 요청 범위에서 진행하세요.
'''
    (ROOT/'HANDOFF_ko.md').write_text(handoff)
    checkpoint('COMPLETE_CACHE_AND_CATALOG',[c['clip_id'] for c in catalog['clips']])
    checked(['mkdir','-p',REMOTE+'/reports'])
    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','checkpoint.json'):
        scp(ROOT/name,'si@58.233.207.31:'+REMOTE+'/')
    for c in catalog['clips']:
        scp(ROOT/c['cache']/'reports/cache_identity.json','si@58.233.207.31:'+REMOTE+'/'+c['cache']+'/reports/')
    for name in ('final_validation.json','catalog_validation.json'):
        scp(ROOT/'reports'/name,'si@58.233.207.31:'+REMOTE+'/reports/'+name)
    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 not in ('manifest.json','metadata_transfer.json') and '__pycache__' not in p.parts}
    save(ROOT/'manifest.json',dict(schema='contextra.self-filming-pose-run.v1',lifecycle='durable-large',retention='manual-review',files=files))
    targets=[rel for rel in files if rel.startswith(('metadata/','main_reader/')) or rel in ('00_README_ko.md','HANDOFF_ko.md','checkpoint.json')]
    code="import pathlib,json,hashlib;root=pathlib.Path("+repr(REMOTE)+");names="+repr(targets)+";print(json.dumps({n:hashlib.sha256((root/n).read_bytes()).hexdigest() for n in names}))"
    remote_hashes=json.loads(checked(['python3','-c',code],timeout=120))
    assert all(remote_hashes[n]==files[n]['sha256'] for n in targets)
    save(ROOT/'reports/metadata_transfer.json',dict(status='PASS',files=len(targets),sha256_match=True))
    scp(ROOT/'manifest.json','si@58.233.207.31:'+REMOTE+'/local_inventory_manifest.json')
    print(json.dumps(dict(status='PASS',clips=22,observations=7060,metadata_transferred=len(targets),resources=stats)),flush=True)

if __name__=='__main__':main()
