"""Read-only scope audit against reviewed baselines; writes evidence in this directory.""" from pathlib import Path import hashlib, json, re HERE = Path(__file__).resolve().parent ROOT = HERE.parents[2] UP = HERE.parent def sha(path): return hashlib.sha256(path.read_bytes()).hexdigest().upper() def read(path): return json.loads(path.read_text(encoding='utf-8-sig')) def changed(entries): return [p for p,h in entries.items() if not (ROOT/p).exists() or sha(ROOT/p) != h.upper()] inventory = read(UP/'B3DesignAudit/source-hashes.json') design = read(UP/'阶段4B3方案受审基线-2026-09-24.json') b2 = read(UP/'KVGameLoadTests/checkpoint4-delivery-hashes.json') pf = 'Client/Assets/Scripts/Core/PlayFabMgr.cs' current = (ROOT/pf).read_bytes() begin = b'\r\n // BEGIN KV explicit fixed-handle access\r\n' end = b' // END KV explicit fixed-handle access\r\n' assert current.count(begin) == current.count(end) == 1 start = current.index(begin) finish = current.index(end) + len(end) old_equal = current[:start] + current[finish:] == (HERE/'PlayFabMgr.before.txt').read_bytes() callers = {} for path in (ROOT/'Client/Assets/Scripts').rglob('*.cs'): # Identifier-only scan: Latin-1 preserves ASCII in both legacy GBK and UTF-8 sources. lines = [i for i,s in enumerate(path.read_bytes().decode('latin-1').splitlines(),1) if re.search(r'\b(?:ReadKV|WriteKV|FlushKVAsync|WaitForKVTicketAsync)\s*\(',s)] if lines: callers[path.relative_to(ROOT).as_posix()] = lines new = sorted(p.relative_to(ROOT).as_posix() for p in (ROOT/'Client/Assets/Scripts').rglob('*.cs') if p.relative_to(ROOT).as_posix() not in inventory) result = dict(sourceBaselineCount=len(inventory), sourceChanged=changed(inventory), newScriptSources=new, oldPlayFabBytesUnchanged=old_equal, designMaterialChanged=changed({f['path']:f['sha256'] for f in design['files']}), oldB2DeliveryChanged=changed({f['path']:f['sha256'] for f in b2['files']}), facadeReferences=callers, limitation='Scope is reviewed baselines, not all pre-existing working tree changes. Static/conditional probes do not prove Unity Player linking or stripping.') assert result['sourceChanged'] == [pf], result assert result['designMaterialChanged'] == [pf], result assert result['oldB2DeliveryChanged'] == [pf], result assert old_equal assert len(new) == 2 assert set(callers) == {pf,'Client/Assets/Scripts/Services/PlayerData/PlayerKVSaveRouteEditorValidationHost.cs'} (HERE/'checkpoint4-boundary-audit.json').write_text(json.dumps(result,ensure_ascii=False,indent=2)+'\n',encoding='utf-8') print(json.dumps(result,ensure_ascii=False,indent=2))