import ast,hashlib,json,shutil,subprocess,tarfile
from datetime import datetime
from pathlib import Path
repo=Path('/workspace/contextra-analytics-runtime-lifecycle-refactor');run=Path(__file__).parent
out=Path('/data/codex-artifacts/contextra/field-maintenance/2026/2026-10-07_ai2-data-preview-fix_r01');out.mkdir(mode=0o700)
def git(*args):return subprocess.check_output(['git','-C',str(repo),*args],text=True,timeout=15)
assert git('rev-parse','HEAD').strip()=='c153c81a588904ac5f252d1004f4a15d3dfaf3d6'
assert git('branch','--show-current').strip()=='task/runtime-lifecycle-refactor'
assert not git('diff','--cached','--name-only').strip()
paths=sorted(set(git('diff','--name-only').splitlines()+git('ls-files','--others','--exclude-standard').splitlines()));assert len(paths)==6,paths
subprocess.run(['git','-C',str(repo),'diff','--check'],check=True,timeout=10)
for p in paths:
    if p.endswith('.py'):ast.parse((repo/p).read_text())
with tarfile.open(out/'source.tar.gz','w:gz') as archive:
    for p in paths:archive.add(repo/p,arcname=p)
(out/'source.json').write_text(json.dumps({p:hashlib.sha256((repo/p).read_bytes()).hexdigest() for p in paths},indent=2)+'\n')
(out/'tracked.diff').write_text(git('diff','--binary'))
for p in run.iterdir():
    if p.is_file() and p.suffix in ('.py','.json'):shutil.copyfile(p,out/p.name)
for p in out.iterdir():p.chmod(0o600)
receipt=dict(schema_version='ltb1021.codex_hub.report_receipt.v1',receipt_id='20261007-ai2-data-preview-fix-r01',timestamp=datetime.now().astimezone().isoformat(timespec='seconds'),session_alias='contextra-data-preview',task='ct 수동 미리보기 worker 그룹 권한 보완',results=['권한제한 worker에 승인 mount GID 전달; 안전 오류 기록 추가','현장 미리보기 성공, 실자료 삭제 없이 서비스 정지 복귀'],verify=['그룹 전달/권한 유지·오류 비밀정보 제거2표적PASS','현장 plan returncode0, 서비스 상태 보존, helper잔존0'],commits=[],remaining=['사용자 Runtime 기동·브라우저·실자료 정리 수용 미실행'])
p=run/'fix-report.json';p.write_text(json.dumps(receipt,ensure_ascii=False,indent=2)+'\n');p.chmod(0o600)
args=['/root/.local/bin/codex-git-closeout','prepare','--repository',str(repo),'--output',str(run/'fix-closeout.json'),'--job-id','20261007-ai2-data-preview-fix-r01','--profile','ltb1021','--session-alias','contextra-data-preview','--title','fix(ct-data): 제한 worker의 데이터 그룹 접근과 오류 진단 보완']
for p in paths:args+=['--path',p]
for m in ['Why=cap-drop ALL worker가 operator 소유 데이터 폴더에 접근하지 못해 현장 plan이 실패했다.','What=승인 mount GID만 보조그룹으로 전달하고 비밀정보 없는 오류 종류/errno/위치를 보존한다.','Impact=chmod/chown/capability 확대 없이 AI2 도구2파일 반영. test 설치본·Runtime·자료는 보존한다.','Verify=표적2PASS; 현장 plan 성공3098묶음/보호462; 실제삭제0; 서비스5exited 복귀/helper0; diff/syntax PASS.','Rollback=AI2 data-tool-updates의 두 설치 영수증/백업으로 검토 후 복구. 자동 rollback 없음.']:args+=['--message',m]
args+=['--verify','Targeted2PASS; plan returncode0; no deletion;5services exited;helper0;git diff/syntax PASS']
subprocess.run(args,check=True,timeout=60)
