import datetime
import json
import os
from pathlib import Path
import re
import subprocess

os.umask(0o077)
R=Path('/workspace/contextra-analytics-runtime-lifecycle-refactor')
H=Path(__file__).parent
JOB='20260920-contextra-fall-ready-r01'
D=Path('/tmp/codex/ltb1021/git-closeout')/JOB
D.mkdir(mode=0o700)
def cmd(args):
    p=subprocess.run(args,capture_output=True,text=True,timeout=90)
    assert p.returncode==0,p.stdout+p.stderr
    return p.stdout

assert cmd(['git','-C',str(R),'rev-parse','HEAD']).strip()=='f2a12013b7521d7f7960b58dd0d04a6974ffd214'
assert not cmd(['git','-C',str(R),'diff','--cached','--name-only']).strip()
paths=sorted(set(cmd(['git','-C',str(R),'diff','--name-only']).splitlines()+
    cmd(['git','-C',str(R),'ls-files','--others','--exclude-standard']).splitlines()))
assert 'docs/scenarios/FALL_TEST_RESULTS.json' in paths
cmd(['git','-C',str(R),'diff','--check'])
for p in paths:
    if p.endswith('.md'):
        for link in re.findall(r'\]\(([^)]+)\)',(R/p).read_text()):
            if '://' not in link and not link.startswith('#'):
                assert ((R/p).parent/link.split('#')[0]).exists(),(p,link)
receipt={'schema_version':'ltb1021.codex_hub.report_receipt.v1','receipt_id':JOB,
 'timestamp':datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9))).isoformat(),
 'session_alias':'work-session','task':'Contextra PR8 FALL 통합 및 Web 시험 준비',
 'results':['Pose 하나와 Tracking 한 번을 공유하는 FALL/침입/배회 연결 구현',
 '독립 ROI·Event와 누락 전달, Stop 도중 수락 Frame의 Event 인계 보완',
 'Analytics/Sidecar image와 Gateway 반영, 카메라 stopped 및 Artifact On 유지'],
 'verify':['Python3.12 FALL core96 및 Pose/연결/설정/CAS/수명 표적검증 PASS; fixture 계측 오류 교정 후 worker3 PASS',
 'Medium/Pose/Fire READY, WSS bootstrap4개/heartbeat2회, 과거원장 및 비대상서비스 보존, source17파일hash 일치'],
 'commits':[], 'remaining':['브라우저 FALL 단독 및 침입/배회 공존 시험 대기','실제 낙상 정확도와 Person/Pose+Fire 복수모델은 미검증/보류']}
rp=H/'report-receipt.json'
rp.write_text(json.dumps(receipt,ensure_ascii=False,indent=2))
print(cmd(['codex-report-receipt','--profile','ltb1021','--session-alias','work-session','--receipt',str(rp)]))
mf=D/'manifest.json'
args=['codex-git-closeout','prepare','--repository',str(R),'--output',str(mf),'--job-id',JOB,
 '--profile','ltb1021','--session-alias','work-session',
 '--title','feat(fall): Pose 공유 시나리오와 Web 실행 경로 연결',
 '--message','Why=FALL core를 기존 Pose/Tracking과 연결하면서 침입·배회를 중복 추론 없이 함께 실행하도록 한다.',
 '--message','What=FALL ROI/Settings/Web projection, Pose 의존성, Loitering provider 확장, NMS objectRef 매핑, selected/drop gap과 Stop 중 Event 인계, 공통 허용목록 및 표적 테스트를 추가했다.',
 '--message','Impact=test 대상3서비스만 반영했다. 21카메라 stopped와 Artifact On, 모델·SSIMS·공유서비스·과거원장 보존. 동일 채널 Person/Pose+Fire는 보류다.',
 '--message','Verify=Python3.12 core96 및 Pose/연결/CAS/수명 검증 PASS. fixture 계측 오류 교정 뒤 worker3 PASS. nginx -t, 모델3종 READY, WSS bootstrap4개/heartbeat2회, source17파일hash 일치. FALL 실제영상/카메라/Event시험 대기.',
 '--message','Rollback=자동 rollback 없음. 기존 image와 private 설정백업을 보존하며 필요시 별도 승인 후 대상서비스만 Stop/reap하고 복구한다. 원장/파일 삭제 및 Event재전송 금지.',
 '--message','Related=PR8 eb342d3; FALL-WIRE-01 extends SCENARIO-FANOUT-01',
 '--verify','Python3.12 FALL core96 and targeted wiring/protection PASS; fixture harness corrected; final worker3 PASS',
 '--verify','Medium/Pose/Fire READY; WSS bootstrap4 heartbeat2; 21 stopped; Artifact On; ledger and 15 other containers unchanged',
 '--verify','Running source hashes17 PASS; nginx -t PASS; git diff --check and Markdown links PASS; no camera test',
 '--expires-in-seconds','86400']
for p in paths:
    args+=['--path',p]
print(cmd(args))
batch=D/'batch.json'
print(cmd(['codex-git-closeout','batch-prepare','--output',str(batch),'--batch-id',JOB,
 '--profile','ltb1021','--session-alias','work-session','--expected-group-count','1',
 '--repository-label','contextra-analytics','--manifest',str(mf),
 '--expect-plan-hash',json.loads(mf.read_text())['plan_hash'],'--expires-in-seconds','86400']))
h=json.loads(batch.read_text())['plan_hash']
print(cmd(['codex-git-closeout','batch-plan','--batch-manifest',str(batch),'--expect-batch-plan-hash',h]))
print(cmd(['codex-git-closeout','batch-present','--batch-manifest',str(batch),'--expect-batch-plan-hash',h]))
