import hashlib,json,os,re,subprocess
from pathlib import Path
os.umask(0o077);R=Path(__file__).parent
repo=Path('/workspace/contextra-analytics-runtime-lifecycle-refactor')
source=json.loads((R/'source.json').read_text())
docs=['docs/profiles/ltb1021/topics/runtime-lifecycle/README.ko.md','docs/profiles/ltb1021/topics/runtime-lifecycle/decision/20260912T195459+0900-decision-bounded-correction-recovery.md','docs/profiles/ltb1021/topics/runtime-lifecycle/runs/20260912T195459+0900-run-flow-boundary-rtp-pass.md']
paths=list(source['hashes'])+docs
def git(repo,*args):return subprocess.check_output(['git','-C',str(repo),*args],text=True).strip()
assert git(repo,'rev-parse','HEAD')==source['base']
assert not git(repo,'diff','--cached','--name-only')
assert set(git(repo,'diff','--name-only').splitlines())|set(git(repo,'ls-files','--others','--exclude-standard').splitlines())==set(paths)
for p,h in source['hashes'].items():assert hashlib.sha256((repo/p).read_bytes()).hexdigest()==h
for path in docs:
    text=(repo/path).read_text();assert not re.search(r'192\.168\.\d+\.\d+',text)
    for target in re.findall(r'\]\(([^)]+)\)',text):
        if '://' not in target and not target.startswith('#'):assert ((repo/path).parent/target.split('#')[0]).exists()
subprocess.run(['git','-C',str(repo),'diff','--check'],check=True)
for p in R.iterdir():
    if p.is_file():os.chmod(p,0o600)
args=['/root/.local/bin/codex-git-closeout','prepare','--repository',str(repo),'--output',str(R/'git-manifest.json'),'--job-id','ltb1021-flow-boundary-20260912','--profile','ltb1021','--session-alias','work-session',
'--title','fix(rtp): 실제 source 초기화 교정과 Frame 시간 전달 검증',
'--message','Why=빈 bin 검증이 실제 source의 상속 signal과 dynamic pad 제약을 놓쳐 Flow/API 중단이 반복됐다.',
'--message','What=public setup probe로 source hook을 초기화하고 Flow 세부 관측과 실제 source worker 회귀를 추가했다. 단계별 예산과 복원 최소화 원칙을 Owner에 통합했다.',
'--message','Impact=별도 Decode/pixel 복사나 SDK patch 없이 RTP를 Frame에 연결한다. 표시0.25·모델·인증/LAN·기존 lifecycle 계약을 유지한다.',
'--message','Verify=관련25 tests·source/image 실제 source worker 및 SDK copy PASS. 실패2/build1/live1에서 first3.499초·독립live30/30·RTP Redis/JPEG3쌍·cleanup2.527초 PASS. 실제 절대시각/품질/표본 trackId는 미검증.',
'--message','Rollback=private CAS 원장과 이전 image를 보존했다. 현재 healthy/stopped·capture Off·restart 복원이며 explicit synthetic Anchor는 공동 연동까지 카메라 설정에 유지한다.',
'--verify','25 targeted tests; actual source worker/image SDK PASS; actual RTP/Frame/Redis3/API30/cleanup PASS; source pin/docs/diff check PASS','--expires-in-seconds','604800']
for p in paths:args+=['--path',p]
subprocess.run(args,check=True)
portfolio=Path('/workspace/users/root/codex-ops-docs')
prefix='servers/test/users/ltb1021/portfolio_logs/'
pp=[prefix+'catalogs/track_index.jsonl',prefix+'entries/contextra-analytics/pipeline__runtime__gpu-first-inference/track.json',prefix+'entries/contextra-analytics/pipeline__runtime__gpu-first-inference/2026/2026-09.jsonl']
assert git(portfolio,'rev-parse','HEAD')=='66b9cb17771fcbe5f3d9d175484b1009e4d30b5a'
assert not git(portfolio,'diff','--cached','--name-only')
assert set(git(portfolio,'diff','--name-only').splitlines())|set(git(portfolio,'ls-files','--others','--exclude-standard').splitlines())==set(pp)
subprocess.run(['git','-C',str(portfolio),'diff','--check'],check=True)
args=['/root/.local/bin/codex-git-closeout','prepare','--repository',str(portfolio),'--output',str(R/'portfolio-manifest.json'),'--job-id','ltb1021-flow-boundary-portfolio-20260912','--profile','ltb1021','--session-alias','work-session','--title','docs(portfolio): RTP Frame 연결 Runtime milestone 기록',
'--message','Why=실제 source 구성 교정과 RTP Metadata/제어 종료 검증 근거를 bounded milestone으로 보존한다.',
'--message','What=기존 Portfolio workflow로 HIGH entry와 track/catalog를 append했다. 원본 영상/식별정보/credential은 포함하지 않았다.',
'--message','Verify=append dry-run 및 safe append PASS; application owner evidence와 대조하고 실제 시간/다중채널 품질 미검증을 명시했다.',
'--verify','safe append dry-run/apply PASS; evidence/files verified; diff check PASS','--expires-in-seconds','604800']
for p in pp:args+=['--path',p]
subprocess.run(args,check=True)
