← back to Carnegie Identity Audit

verify_live.py

76 lines

#!/usr/bin/env python3
"""Independent REST/SQL reader; imports no repair implementation."""
import argparse, datetime, json, os, pathlib, subprocess, time, urllib.error, urllib.request
from collect import token
BASE=pathlib.Path(__file__).resolve().parent
RUN=BASE/'execution-20260911'
PLAN=json.loads((BASE/'verification/repair-plan.json').read_text())
TOKEN=token()
def get(path):
    for attempt in range(5):
        req=urllib.request.Request('https://designer-laboratory-sandbox.myshopify.com/admin/api/2026-07/'+path,headers={'X-Shopify-Access-Token':TOKEN})
        try:
            with urllib.request.urlopen(req,timeout=35) as r:
                result=json.load(r)
                assert 'rel="next"' not in r.headers.get('Link',''), 'Truncated REST response'
            time.sleep(.55);return result
        except urllib.error.HTTPError as e:
            if e.code!=429:raise
            time.sleep(max(1,float(e.headers.get('Retry-After','2'))))
    raise RuntimeError('REST throttle exhausted')
def check_product(p,baseline,target=None):
    numeric=p['id'].split('/')[-1]
    actual=get('products/'+numeric+'.json')['product']
    metafields=get('products/'+numeric+'/metafields.json?limit=250')['metafields']
    for key in ['title','handle','vendor','status']:
        assert str(actual[key]).lower()==str(baseline[key]).lower(),(numeric,key)
    assert actual['body_html']==baseline['descriptionHtml']
    assert actual['product_type']==baseline['productType']
    assert sorted(x.strip() for x in actual['tags'].split(',') if x.strip())==sorted(baseline['tags'])
    av={str(v['id']):(v['sku'],v['title'],v['price'],v['compare_at_price']) for v in actual['variants']}
    bv={v['id'].split('/')[-1]:(v['sku'],v['title'],v['price'],v['compareAtPrice']) for v in baseline['variants']['nodes']}
    assert av==bv,(numeric,'variant or price drift')
    live={(m['namespace'],m['key']):(str(m['id']),m['type'],m['value']) for m in metafields}
    expected={(m['namespace'],m['key']):(m['id'].split('/')[-1],m['type'],m['value']) for m in baseline['metafields']['nodes']}
    if target is not None:
        for ns in ['custom','dwc']:
            ident,typ,_=expected[(ns,'manufacturer_sku')]
            expected[(ns,'manufacturer_sku')]=(ident,typ,target)
    assert live==expected,(numeric,'metafield mismatch')
    return dict(id=p['id'],status=actual['status'],metafields=metafields,variant_prices_unchanged=True,unrelated_fields_unchanged=True)
def main():
    ap=argparse.ArgumentParser();ap.add_argument('phase',choices=['canary','rollback-canary','all','monitor']);args=ap.parse_args()
    before=json.loads((RUN/'preflight.json').read_text())
    if (RUN/'products-baseline.json').exists():before['products']=json.loads((RUN/'products-baseline.json').read_text())['products']
    canary=next((r,p) for r in PLAN['rows'] for p in r['products'] if p['status']=='ACTIVE')
    chosen=[canary] if args.phase in ['canary','rollback-canary'] else [(r,p) for r in PLAN['rows'] for p in r['products']]
    if args.phase=='monitor':
        chosen=[canary,next((r,p) for r in PLAN['rows'] for p in r['products'] if p['status']=='ARCHIVED'),next((r,p) for r in reversed(PLAN['rows']) for p in r['products'] if p['status']=='ACTIVE')]
    results=[]
    for r,p in chosen:
        results.append(check_product(p,before['products'][p['id']],r['before'] if args.phase=='rollback-canary' else r['after']))
        print('REST verified '+p['id'],flush=True)
    extra={r['excluded_grouped_product'] for r in PLAN['rows']}
    for pid in extra:check_product({'id':pid},before['products'][pid])
    ids=','.join(str(r['catalog_id']) for r in PLAN['rows'])
    query=f"SELECT json_build_object('rows',(SELECT json_agg(t) FROM (SELECT * FROM carnegie_catalog WHERE id IN ({ids}) ORDER BY dw_sku) t),'count',(SELECT count(*) FROM carnegie_catalog),'distinct',(SELECT count(DISTINCT mfr_sku) FROM carnegie_catalog),'other_digest',(SELECT md5(string_agg(id::text||':'||dw_sku||':'||mfr_sku,'|' ORDER BY id)) FROM carnegie_catalog WHERE id NOT IN ({ids})));"
    q=subprocess.run(['psql','-X','-q','-d','dw_unified','-At','-v','ON_ERROR_STOP=1','-c',query],capture_output=True,text=True,check=True,env=dict(os.environ,PGOPTIONS='-c default_transaction_read_only=on -c statement_timeout=15000'))
    db=json.loads(q.stdout);expected_rows=before['database']['rows']
    target={r['catalog_id']:r['after'] for r in PLAN['rows']}
    for r in expected_rows:r['mfr_sku']=target[r['id']]
    assert db['rows']==expected_rows, 'Unexpected local column change'
    assert db['count']==db['distinct']==5928
    assert db['other_digest']==before['database']['other_digest']
    canonical_verified=False
    if args.phase in ['all','monitor']:
        remote_sql=f"SELECT json_agg(t) FROM (SELECT id,dw_sku,mfr_sku FROM carnegie_catalog WHERE id IN ({ids}) ORDER BY id) t;"
        remote_cmd="PGOPTIONS='-c default_transaction_read_only=on -c statement_timeout=10000' psql -d dw_unified -At -c '"+remote_sql+"'"
        result=subprocess.run(['ssh','-o','BatchMode=yes','-o','ConnectTimeout=8','kamatera',remote_cmd],capture_output=True,text=True,check=True)
        remote=json.loads(result.stdout)
        assert {(r['id'],r['dw_sku'],r['mfr_sku']) for r in remote}=={(r['catalog_id'],r['dw_sku'],r['after']) for r in PLAN['rows']}
        canonical_verified=True
    proof=dict(verdict='PASS',at=datetime.datetime.now(datetime.timezone.utc).isoformat(),phase=args.phase,reader='Independent REST GET plus read-only SQL; no repair implementation imported',products=len(results),metafields=len(results)*2,database_rows=26,unique_identities=5928,grouped_product_unchanged=True,canonical_unchanged_verified=canonical_verified,all_unrelated_metafields_and_product_fields_unchanged=True,results=results)
    (RUN/('independent-'+args.phase+'.json')).write_text(json.dumps(proof,indent=2)+'\n')
    print(json.dumps({k:v for k,v in proof.items() if k!='results'},indent=2))
if __name__=='__main__':main()