From f4ae02815451bfb75a41d8404867ee51b4c666a3 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 18:36:44 +0000 Subject: [PATCH] evidence: digest reads DFG Re20 result.json of schema 1 or 2 final_physical_digest.py indexed [0] on the bare-list (schema 1) native receipt. v4_cases() accepts schema 1 and the versioned schema-2 object and rejects others. Existing outputs and final-physical-digest.json are unchanged; that digest records the previous postprocessor hash, so a rerun with this version is a new digest. Co-Authored-By: Claude Opus 5.5 --- .../physical-750e8a4-20261020/final_physical_digest.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/validation/evidence/physical-750e8a4-20261020/final_physical_digest.py b/docs/validation/evidence/physical-750e8a4-20261020/final_physical_digest.py index c7a2677..3c0dcd5 100644 --- a/docs/validation/evidence/physical-750e8a4-20261020/final_physical_digest.py +++ b/docs/validation/evidence/physical-750e8a4-20261020/final_physical_digest.py @@ -13,6 +13,11 @@ R = Path('/home/frank14f/CelerisLab-release-prep-20261020') SHA = '750e8a483ec47dcf472e528edc1cf2936f0e7e83' def read(p): return json.loads(p.read_text()) +def v4_cases(data): + """DFG Re20 result.json: schema 1 is a bare case list, schema 2 is {schema, provenance, cases}.""" + if isinstance(data, list): return data + if isinstance(data, dict) and data.get('schema') == 2: return data['cases'] + raise ValueError('unknown DFG Re20 result schema') def sha(p): return hashlib.sha256(p.read_bytes()).hexdigest() def cmd(*a): @@ -50,7 +55,7 @@ for label,sj in state['jobs'].items(): c['external_manifest_hash_matches']=r['external_manifest_sha256']==sha(Q/'manifest.json') c['hashes']={str(z.relative_to(p)):sha(z) for z in [p/'command.json',p/'result.json',*sorted((p/'output').rglob('*.json')),*sorted((p/'output').rglob('*.csv')),*sorted((p/'output').rglob('*.jsonl'))]} if label.startswith('v4'): - n=read(p/'output/result.json')[0]; rows=n['rows'] + n=v4_cases(read(p/'output/result.json'))[0]; rows=n['rows'] captured=r['native_results'][0] c['native_receipt_matches_external_result']=all(n[k]==z for k,z in captured.items() if k not in ['last_row','rows_count']) and captured['last_row']==rows[-1] and captured['rows_count']==len(rows) c['external_receipt_history_handling']='V4 external receipt stores row count and last row; full native rows are separately hashed'