review-industry-intake.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. """Review a provided intake against a sealed release. No network, collection or writes to data."""
  2. import argparse
  3. from datetime import datetime, timezone
  4. from decimal import Decimal
  5. import hashlib
  6. import json
  7. from pathlib import Path, PurePosixPath
  8. import sys
  9. import zipfile
  10. def sha(raw): return hashlib.sha256(raw).hexdigest()
  11. def checked_zip(path, expected):
  12. raw=path.read_bytes()
  13. if sha(raw)!=expected: raise ValueError('INTAKE_SHA_MISMATCH')
  14. with zipfile.ZipFile(path) as z:
  15. names=z.namelist()
  16. if len(names)!=len(set(names)) or any(PurePosixPath(n).is_absolute() or '..' in PurePosixPath(n).parts for n in names):
  17. raise ValueError('UNSAFE_ARCHIVE_MEMBERS')
  18. files={n:z.read(n) for n in names}
  19. verified=set()
  20. for line in files['FILES.sha256'].decode('utf-8-sig').splitlines():
  21. digest,name=line.split(' ',1)
  22. if name in verified or name not in files or sha(files[name])!=digest: raise ValueError('INTAKE_MANIFEST_MISMATCH')
  23. verified.add(name)
  24. if verified!=set(files)-{'FILES.sha256'}: raise ValueError('INTAKE_MANIFEST_INCOMPLETE')
  25. return files
  26. def main():
  27. p=argparse.ArgumentParser(description=__doc__)
  28. p.add_argument('--intake',type=Path,required=True);p.add_argument('--sha256',required=True)
  29. p.add_argument('--release',type=Path,required=True);p.add_argument('--out',type=Path,required=True)
  30. a=p.parse_args();package=a.release.resolve()
  31. if a.out.exists(): raise ValueError('REVIEW_OUTPUT_ALREADY_EXISTS')
  32. files=checked_zip(a.intake,a.sha256)
  33. for line in (package/'FILES.sha256').read_text('utf-8').splitlines():
  34. digest,name=line.split(' ',1)
  35. if sha((package/name).read_bytes())!=digest:raise ValueError('SEALED_RELEASE_CHANGED')
  36. sys.path.insert(0,str(package/'server/voc-library'))
  37. from monitoring_policy import standard_facts
  38. from monitoring_dispatch import batch_preflight
  39. intake=json.loads(files['intake-report.json'])
  40. pricing=json.loads(files['pricing-voc-20261107.json'])
  41. price=[v for v in pricing['data']['pricing']['items'] if v['model']=='voc-e-commerce']
  42. if len(price)!=1 or Decimal(str(price[0]['unitPriceCny']))!=Decimal('0.30'):raise ValueError('UNIT_PRICE_CHANGED')
  43. scope=json.loads((package/'handoff/collector-scope.json').read_bytes());generation=scope['generation']
  44. old=package/'evidence/r9-policy-sources'
  45. assets=package/'data/monitoring-registry/assets'
  46. metadata=json.loads((old/'standard-page-receipt.json').read_bytes())['rows']
  47. pdfs=json.loads((old/'pdf-receipt.json').read_bytes())['rows']
  48. local=[]
  49. for row in pdfs:
  50. raw=(assets/row['rawFile']).read_bytes()
  51. page=next(x for x in metadata if x['documentId']==row['documentId'])
  52. html=(old/page['rawFile']).read_bytes()
  53. if sha(raw)!=row['sha256'] or not raw.startswith(b'%PDF-') or not row['matchesArchivedSha256']:
  54. raise ValueError('STANDARD_PDF_NOT_VERIFIED')
  55. if sha(html)!=page['sha256'] or page['sha256']!=row['sourcePageSha256']:raise ValueError('STANDARD_PAGE_CHANGED')
  56. facts=standard_facts({'record_id':row['documentId']},html)
  57. local.append({'documentId':row['documentId'],'pdfSha256':sha(raw),'pdfBytes':len(raw),
  58. 'pdfPathInRelease':'data/monitoring-registry/assets/'+row['rawFile'],
  59. 'metadataPathInRelease':'evidence/r9-policy-sources/'+page['rawFile'],
  60. 'metadataSha256':sha(html),'originalObservationTime':page['checkedAt'],
  61. 'requestMethod':page['method'],'officialStatusAtThatObservation':facts['officialStatus']})
  62. fetched=[json.loads(line) for line in files['policy-fetch/record.jsonl'].decode('utf-8-sig').splitlines() if line]
  63. shell=[v for v in fetched if str(v['httpCode'])=='200'];blocked=[v for v in fetched if str(v['httpCode'])=='412']
  64. if len(shell)!=9 or len(blocked)!=16 or len({(r['sha256'],r['bytes']) for r in shell})!=1:raise ValueError('POLICY_FETCH_COUNTS_CHANGED')
  65. shell_baseline=Path(__file__).resolve().parents[1]/'work/industry-policy-verification-20261107-r1'/(shell[0]['sha256']+'.raw')
  66. shell_raw=shell_baseline.read_bytes()
  67. if sha(shell_raw)!=shell[0]['sha256'] or len(shell_raw)!=625:raise ValueError('SHELL_BASELINE_CHANGED')
  68. if b'form1' not in shell_raw or b'task' not in shell_raw or b'index' not in shell_raw:raise ValueError('SHELL_CLASSIFICATION_UNVERIFIED')
  69. candidates=[{'endpointId':'jd.get_item_comments_v1','params':{'itemId':x['productId'].split(':',1)[1],'page':page},
  70. 'generation':generation,'window':'intake-quote-review-no-dispatch'} for x in scope['primaryProducts'] for page in [1,2]]
  71. before=sha((package/'data/monitoring-registry/monitoring.sqlite').read_bytes())
  72. preflight=batch_preflight(package/'data/monitoring-registry',candidates,generation)
  73. if sha((package/'data/monitoring-registry/monitoring.sqlite').read_bytes())!=before:raise ValueError('AUXILIARY_CHANGED')
  74. paid=sum('REQUEST_ALREADY_CHARGED_NO_AUTOMATIC_REPLAY' in r['blockingReasons'] for r in preflight['rows'])
  75. response=sum(bool(r['existingResponses']) for r in preflight['rows'])
  76. r11=package.parent/'ksf-industry-monitor-20261107-r11.zip'
  77. output={'schema':'ksf.industry-intake-review.v1','checkedAt':datetime.now(timezone.utc).isoformat(),
  78. 'generation':generation,'inputSha256':a.sha256,'inputManifestFilesVerified':len(files)-1,
  79. 'release':{'name':r11.name,'sha256':sha(r11.read_bytes()),'bytes':r11.stat().st_size,'manifestVerified':True,
  80. 'deploymentStillUnconfirmed':True},
  81. 'policy':{'thisIntake':{'shell200':len(shell),'challenged412':len(blocked),'newBodies':0,
  82. 'shellSha256':shell[0]['sha256'],'shellBytes':625},
  83. 'existingVerifiedPdfCount':len(local),'existingVerifiedPdfs':local,
  84. 'currentStatusAsOfThisIntake':None,'historicalEvidenceInvalidatedByShell':False,
  85. 'recommendation':'reuse_verified_release_assets_without_network; latest fetch failure remains a separate observation'},
  86. 'pricing':{'unitPriceMinor':30,'currency':'CNY','billingMode':price[0]['billingMode'],
  87. 'cachePolicy':price[0]['cachePolicy'],'pricingVersion':price[0].get('pricingVersion'),
  88. 'accountFieldsOmitted':True,'worstCase7050Accepted':False,
  89. 'reason':'no per-product missing-page plan or verified current page limits; listing page estimate disagrees with archived receipts',
  90. 'failureBillingVerifiedFromPricingReceipt':False,'requestIdBillingIdempotenceVerified':False,
  91. 'ledgerStillRequiredForFailuresAndUnknownOutcomes':True},
  92. 'scope':{'shops':[{k:x[k] for k in ['shopId','historicalProducts','historicalPages','currentLibraryProducts']} for x in scope['shops']],
  93. 'historicalListingPages':sum(x['historicalPages'] for x in scope['shops']),
  94. 'historicalListingProducts':sum(x['historicalProducts'] for x in scope['shops']),
  95. 'currentLibraryShopProducts':sum(x['currentLibraryProducts'] for x in scope['shops']),
  96. 'primaryProducts':165,'existingPrimaryDetailResponses':41,'existingPrimaryDetailTextProducts':12,
  97. 'primaryProductsWithoutDetailResponse':124,
  98. 'hypotheticalTwoPageReviewSlots':len(candidates),'hypotheticalSlotsWithKnownCharges':paid,
  99. 'hypotheticalSlotsWithRegisteredResponse':response,
  100. 'hypotheticalSlotsWithoutKnownCharge':len(candidates)-paid,
  101. 'twoPagesIsVerifiedCurrentLimit':False,'hypotheticalSlotsAreDispatchQueue':False},
  102. 'mappingCorrections':{'appendObjectIsSupported':True,'correctAppendPointer':'/afterComment',
  103. 'mainDatePointer':'/commentDate','mainContentPointer':'/commentData',
  104. 'unknownDefaultAndRatingScaleRemainUnknown':True,
  105. 'numericIdsNeedLosslessParsingOrExplicitSkipReason':True},
  106. 'ranking':{'intakeCatalogSupport':False,'catalogSha256Provided':bool(intake['collector']['catalogSha256']),
  107. 'allProvidersUnsupported':False,'realAlternativeStillRequired':True},
  108. 'dispatchAllowed':False,'paidCalls':0,'productionWrites':0,'chargedMinor':4620,'protectedMinor':380,
  109. 'newBudgetAuthorizedMinor':0}
  110. a.out.parent.mkdir(parents=True,exist_ok=True);a.out.write_text(json.dumps(output,ensure_ascii=False,indent=2)+'\n','utf-8')
  111. print(json.dumps({'ok':True,'inputManifestVerified':True,'existingPdfVerified':len(local),
  112. 'historicalListingPages':output['scope']['historicalListingPages'],'knownPaidReviewSlots':paid,
  113. 'worstCase7050Accepted':False,'paidCalls':0,'productionWrites':0},ensure_ascii=False))
  114. if __name__=='__main__':main()