from live import *

plan=json.loads((R/'plan.json').read_text());result=json.loads((R/'live.json').read_text())
assert result['stage']=='FAIL'
current=ops.inspect('contextra-analytics-1')
assert not current['State']['Running'] and current['HostConfig']['RestartPolicy']['Name']=='no'
compose=ops.C/'compose.yaml'
old=yaml.safe_load(Path('/tmp/codex/ltb1021/2026-09-15_contextra-redis-final_r01/compose.before').read_text())
new=yaml.safe_load(compose.read_text())
assert new['services']['analytics']['restart']=='no'
new['services']['analytics']['restart']=old['services']['analytics']['restart'];assert new==old
for p,h in plan['config_hashes'].items():
    if p!=str(compose):assert ops.g.sha(Path(p))==h
assert ops.inspect('contextra-redis-1')['Id']==plan['redis_id']
nowothers=ops.inventory()
assert all(nowothers[k]==v for k,v in plan['others'].items() if k!='/contextra-analytics-1')
send=sender();assert send['executionId']==plan['sender']['executionId'] and send['state']=='STREAMING'
reg=yaml.safe_load(Path('/data/contextra/runtime-state/streams_runtime.yaml').read_text())
cfg=next(x for x in reg['added'] if x['stream_id']==TARGET);assert cfg==plan['registration']
assert query('reader','EXISTS',plan['key'])==0
summary=dict(status='BLOCKED_NOT_READY',start=1,stop_request=1,normal_cleanup='NOT_COMPLETED_WITHIN40_SECONDS',analytics_safety_stop=True,analytics_running=False,analytics_restart_actual='no',analytics_restart_persistent='no',prior_restart_policy=old['services']['analytics']['restart'],configuration_changed='Only safety restart policy; camera/ROI/Anchor unchanged',rtsp_error='503 Service Unavailable at initial request, before decoded Frame',first_frame_timeout_seconds=40,decoded_frames=0,result_fps=None,redis_fps=None,queue_drop='NOT_MEASURED',rtp_metadata='NOT_PRODUCED',frame_key_exists=False,sender_retained=True,sender_restarted=False,redis_unchanged=True,other_containers_preserved=True,model_change=False,capacity_errors=0,helper_cleanup='Completed; no owned receivers created',gpu_final=gpu(),evidence_scope='No new Web reader Frame test; previous PASS not revoked or promoted',next='Identify RTSP request returning503 using bounded non-AI receiver and sender observation; fix before another AI Start. Restore normal Analytics restart policy when resuming healthy stopped service.')
save('summary.json',summary)
with (R/'summary.csv').open('w') as f:
    w=csv.writer(f);w.writerow(['item','result','scope']);w.writerows([['Web readiness','BLOCKED','RTSP503 before first decoded Frame'],['Start','1','no retry'],['Stop','INCOMPLETE','40-second cleanup budget'],['Analytics','SAFE_STOPPED','actual/persistent restart=no'],['Sender/Redis','PRESERVED','same execution/container'],['FPS/RTP','NOT_MEASURED','no Frame']])
save('report-receipt.json',dict(schema_version='ltb1021.codex_hub.report_receipt.v1',receipt_id='contextra-web-ready-20260915-113000',timestamp='2026-09-15T11:30:00+09:00',session_alias='work-session',task='Contextra 단일 INTRUSION Web Redis 시험 기동 및 안전 종료',results=['기존 H264 sender와 CLI Anchor 및 Medium/INTRUSION/ROI 설정 정합 확인','AI Start1회에서 RTSP503 및 decoded Frame 미도달 확인','Stop cleanup 미완료로 다른 채널 stopped 확인 후 Analytics 안전 중지','actual/persistent restart=no 적용;sender/Redis/모델/다른 서비스 보존'],verify=['설정 patch0;Start1/Stop1;기동40초 및 cleanup40초 제한 적용','timestamp 로그로 RTSP503이 Stop 요청 이전 발생한 사실 확인','Analytics stopped 및 GPU 사용량 감소, sender 실행과 Redis container 보존','새 Frame key 없음;처리량/시간정보/Web 수신 미검증'],commits=[],remaining=['RTSP request 단계503 원인 판별과 작은 교정','건강한 Analytics stopped 복구 및 정상 restart 정책 복원 후 제한 Start 재개','실제 Web 수신 및 동기화 확인']))
for p in R.iterdir():
    if p.is_file():p.chmod(0o600)
print(json.dumps(summary,ensure_ascii=False))
