"""음성/역사 증거/원본 출처/현재 dist를 독립 SHA-256으로 대조한다.""" import hashlib import json from pathlib import Path import subprocess ROOT = Path(__file__).resolve().parents[2] EVIDENCE = Path(__file__).resolve().parent def sha(path): """파일은 읽기만 한다. 큰 바이너리도 전체 바이트를 해시하여 일부 변경을 놓치지 않는다.""" with Path(path).open('rb') as stream: return hashlib.file_digest(stream, 'sha256').hexdigest() baseline = json.loads((EVIDENCE / 'baseline.json').read_text()) for group in ('immutable', 'voiceWavs'): for path, expected in baseline[group].items(): assert sha(ROOT / path) == expected, path assert subprocess.check_output(['git','rev-parse','HEAD'],cwd=ROOT,text=True).strip() == baseline['commit'] plan = json.loads((ROOT / '.agent/AMBIENCE-SOURCES-v0.2.3.json').read_text()) manifest = json.loads((ROOT / 'public/ambience/manifest.json').read_text()) originals = [] for asset in plan['assets']: suffix = '.mp3' if asset['environmentId'] in ('theater','department') else '.flac' if asset['environmentId']=='rain' else '.ogg' source = Path('/tmp/findword-ambience-sources') / (asset['environmentId']+suffix) assert source.stat().st_size == asset['sourceBytes'] and sha(source) == asset['expectedSourceSha256'] entry = next(f for f in manifest['files'] if f['environmentId']==asset['environmentId']) assert all(entry[k] == value for k,value in asset.items()) originals.append({'environmentId':asset['environmentId'],'sourceSha256':sha(source),'sourceBytes':source.stat().st_size}) for source in (ROOT / 'public').rglob('*'): if source.is_file(): assert sha(source) == sha(ROOT / 'dist' / source.relative_to(ROOT / 'public')) report = {'headUnchanged':True,'head':baseline['commit'],'priorAgentFilesUnchanged':len(baseline['immutable']), 'voiceWavsUnchanged':len(baseline['voiceWavs']),'originals':originals,'allPublicBytesMatchDist':True, 'sourcePlanUnchanged':True,'noCommitPushMergeTagDeploy':True} (EVIDENCE / 'preservation.json').write_text(json.dumps(report,indent=2)+'\n') print(json.dumps({k:v for k,v in report.items() if k!='originals'},indent=2))