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-scenario-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()=='0c0f25c2dfd8d13f506f43d69c38caab77269b9c'
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 'tests/fixtures/loitering_legacy_wire.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 PR6/PR7 통합 및 Web 시나리오 시험 준비',
 'results':['Medium Tracking 공유 INTRUSION+LOITERING fan-out 및 독립 ROI/Event 구현',
 'FIRE 단독 Web 전환·설정·Artifact와 세 시나리오 허용목록 통합',
 'Analytics/Sidecar image와 Gateway 반영, 모든 카메라 stopped와 Artifact On 유지'],
 'verify':['Python3.12 주요 표적270 PASS, 낡은 FIRE 거부 기대값 보정 후 관련/추가11 PASS(중복 포함); nginx fixture1 SKIP은 실제 nginx -t로 보완',
 'Medium/Fire READY, WSS bootstrap4개와 heartbeat5회, 원장/비대상컨테이너 보존, running source18파일hash 일치'],
 'commits':[], 'remaining':['브라우저3종 채널 Start와 자연 Event/영상 수용 대기','Person/Pose+Fire multi-provider Runtime 및 FALL 연결은 후속']}
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(scenarios): Loitering 공존과 FIRE 단독 Web 실행 준비',
 '--message','Why=Loitering 단일 슬롯 제한을 해소하고 FIRE 선개발 연결부를 실제 task와 통합한다.',
 '--message','What=공유 Tracking 순차 fan-out, 독립 ROI·상태·EventID, FIRE 모델/category 전환, 공통 Metadata·Artifact·Sidecar·Gateway 연결과 표적 테스트를 보완했다.',
 '--message','Impact=test 대상3서비스만 반영했다. 카메라19개 stopped, Artifact On/HTTP On, 모델·SSIMS·공유서비스와 과거 Event 원장 보존. FALL 및 복수모델 Runtime은 미연결이다.',
 '--message','Verify=Python3.12 주요 표적270 PASS와 후속11 PASS(중복 포함), 실제 nginx 설정검사, Medium/Fire READY, WSS bootstrap4개/heartbeat5회, image source18파일hash 일치. 실제채널/영상/Event시험은 대기.',
 '--message','Rollback=자동 rollback 없음. 기존 image와 private 설정백업을 보존하며 필요시 별도 승인 후 대상서비스만 Stop/reap하고 복구한다. 원장/파일 삭제 및 Event재전송 금지.',
 '--message','Related=PR6 a095c95, PR7 367c62a; SCENARIO-FANOUT-01',
 '--verify','Targeted Python3.12 fixtures PASS after expectation correction; process CAS/restart/reap PASS; no camera test',
 '--verify','Medium/Fire READY; WSS bootstrap4 heartbeat5; 19 stopped; Artifact On; ledger and 15 other containers unchanged',
 '--verify','Running source hashes18 PASS; nginx -t PASS; git diff --check and Markdown links PASS',
 '--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]))
