46 lines
3.3 KiB
Python
46 lines
3.3 KiB
Python
"""Read inventories and byte hashes without exposing private paths or contents."""
|
|
from pathlib import Path
|
|
from collections import Counter
|
|
import json, hashlib
|
|
from datetime import datetime, timezone
|
|
R=Path(__file__).resolve().parent.parent
|
|
P=R/'storage/app/private/investmoz-v2/portal-audit'
|
|
sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
|
|
load=lambda p:json.loads(p.read_text(encoding='utf-8-sig'))
|
|
latest=load(P/'service-followup-backup-restoration-qa.json')
|
|
reports=[]
|
|
for name in ['workflow-upgrade-target-backup-20261011.json', 'workflow-upgrade-backup-restoration-qa-20261011.json',
|
|
'service-followup-backup-restoration-qa.json']:
|
|
proof=load(P/name)
|
|
files=proof.get('file_manifest',proof.get('active_files_manifest',{}))
|
|
counts=dict(Counter(k.split('/')[0] for k in files))
|
|
private={k:v for k,v in files.items() if k.startswith('private/')}
|
|
current_private={k:v for k,v in latest['file_manifest'].items() if k.startswith('private/')}
|
|
common={k:v for k,v in private.items() if k in current_private}
|
|
mismatched=[k for k,v in common.items() if current_private[k]!=v]
|
|
reports.append({'proof':name,'verified_at':proof.get('verified_at'), 'target':proof.get('target'),
|
|
'file_count':proof['file_count'], 'manifest_entry_count':len(files),'groups':counts,
|
|
'private_references':len(private),'same_private_entries_in_latest':len(common),
|
|
'private_entries_not_in_latest':len(private)-len(common), 'common_bytes_changed':len(mismatched),
|
|
'table_count':proof.get('table_count',len(proof.get('tables',{}))),
|
|
'sequence_count':proof.get('sequence_count',len(proof.get('sequences',[])))})
|
|
original=load(P/'workflow-upgrade-target-backup-20261011.json')
|
|
original_private={k:v for k,v in original.get('file_manifest',original.get('active_files_manifest',{})).items() if k.startswith('private/')}
|
|
latest_private={k:v for k,v in latest['file_manifest'].items() if k.startswith('private/')}
|
|
assert original_private and all(latest_private.get(k)==v for k,v in original_private.items())
|
|
fixed=R/'outputs/investmoz-workflow-upgrade-20261011'
|
|
delta=R/'outputs/investmoz-service-followup-20261011'
|
|
plan=R/'outputs/investmoz-workflow-service-plan-20261011'
|
|
before={'at':datetime.now(timezone.utc).isoformat(),
|
|
'fixed_candidate':{p.relative_to(fixed).as_posix():sha(p) for p in fixed.rglob('*') if p.is_file()},
|
|
'delta_reference':{p.relative_to(delta).as_posix():sha(p) for p in delta.rglob('*') if p.is_file()},
|
|
'service_plan':{p.relative_to(plan).as_posix():sha(p) for p in plan.rglob('*') if p.is_file()}}
|
|
(P/'consolidated-preservation-before.json').write_text(json.dumps(before,indent=2),encoding='utf-8')
|
|
result={'at':datetime.now(timezone.utc).isoformat(),'result':'passed','reports':reports,
|
|
'originals_from_actual_destination_in_latest_191_by_name_and_hash':len(original_private),
|
|
'latest_191_groups':dict(Counter(k.split('/')[0] for k in latest['file_manifest'])),
|
|
'all_original_destination_bytes_preserved':True, 'active_writes':0,
|
|
'scope':'Referenced private documents, merged candidate code, production bundle and private QA .env; not the whole historical acervo or live runtime'}
|
|
(P/'consolidated-191-reconciliation.json').write_text(json.dumps(result,indent=2),encoding='utf-8')
|
|
print(json.dumps(result,ensure_ascii=False))
|