"""Offline inspection/publication only; never sends target requests or publishes raw captures.
python publish.py --private-output runner/data/botasaurus-public-30-20260930-retry
"""
import argparse,ast,hashlib,json
from datetime import datetime,timezone
from pathlib import Path
HERE=Path(__file__).resolve().parent
sha=lambda body:hashlib.sha256(body).hexdigest()
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--private-output',type=Path,required=True);args=parser.parse_args()
run=json.loads((args.private_output/'results.json').read_text());manifest=json.loads((HERE.parent/'public-30-v1.json').read_text())
assert run['finished_at'] and not run.get('blocker') and len(run['attempts'])==30
assert run['manifest_sha256']==sha((HERE.parent/'public-30-v1.json').read_bytes())
assert run['script_sha256']==sha((HERE/'run.py').read_bytes())
assert run['requirements_sha256']==sha((HERE/'requirements.txt').read_bytes())
def validator(path):
 tree=ast.parse(path.read_text());return [n for n in tree.body if getattr(n,'name',None) in ['PageText','evaluate','result_class'] or isinstance(n,ast.Assign) and any(isinstance(t,ast.Name) and t.id in ['BLOCK_PHRASES','GATE_PHRASES'] for t in n.targets)]
original=validator(HERE.parent/'patchright-public-30-20260929/run.py');assert [ast.dump(n,include_attributes=False) for n in validator(HERE/'run.py')]==[ast.dump(n,include_attributes=False) for n in original]
namespace={};exec('from html.parser import HTMLParser\nimport re',namespace);exec(compile(ast.Module(body=original,type_ignores=[]),'unchanged-validator','exec'),namespace)
assert [(a['target_id'],a['original_url']) for a in run['attempts']]==[(t['id'],t['url']) for t in manifest['targets']]
samples={};doms=pngs=0
for a,target in zip(run['attempts'],manifest['targets']):
 path=args.private_output/(a['target_id']+'.html');body=path.read_bytes() if path.exists() else b''
 assert namespace['evaluate'](body,target)==a['checks'] and namespace['result_class'](a)==a['classification']
 assert a['usable_content']==(a['classification']=='usable') and a['empty_content']==(a['checks']['text_chars']==0)
 if path.exists():assert sha(body)==a['raw_html_sha256'] and len(body)==a['html_bytes'];doms+=1
 else:assert a['raw_html_sha256'] is None and a['html_bytes']==0
 png=args.private_output/(a['target_id']+'.png')
 if png.exists():assert sha(png.read_bytes())==a['screenshot_sha256'];pngs+=1
 else:assert a['screenshot_sha256'] is None
 readings=json.loads((args.private_output/(a['target_id']+'-pss.json')).read_text());samples[a['target_id']]=readings
 assert a['pss_samples']==len(readings) and a['peak_sampled_tree_pss_mb']==max((s['pss_mb'] for s in readings),default=None)
 assert a['cleanup_surviving_owned_pids']==[] and a['navigation_started_at']
 assert '--no-sandbox' not in a.get('effective_browser_arguments',[]) and '--no-proxy-server' in a.get('effective_browser_arguments',[])
 assert all(s['host_available_mb']>=run['config']['min_host_available_mb'] and s['pss_mb']<=run['config']['max_attempt_tree_pss_mb'] for s in readings)
 a['inspection_note']=('Complete saved DOM met all unchanged required word groups, listing patterns and minimum text; not verified field extraction.' if a['usable_content'] else 'Saved DOM contains checked soft-block phrase(s): '+', '.join(a['checks']['soft_block_checks']['matched']) if a['classification']=='soft_block' else 'HTTP 200 saved no checked text; empty-content failure, not a usable 200.' if a['status']==200 and a['empty_content'] else 'Saved DOM was rechecked against frozen content rules; '+a['classification'].replace('_',' ')+'.')
 if a['target_id']=='amazon':a['inspection_note']+=' Private viewport inspection showed laptop listings with RON prices. The frozen price pattern accepts only $, £ or € and found zero matches; no currency rule was changed.'
 if a['target_id']=='example':a['inspection_note']+=' Private viewport showed documentation-example text. The saved DOM used individual letter spans and an SVG icon; frozen source-text checks did not match the required phrases. No parser or phrase rule was changed.'
 if a['target_id']=='bestbuy':a['inspection_note']+=' Private viewport showed a country-choice page, not the requested laptop listings. No choice was clicked.'
 if a['target_id']=='target':a['inspection_note']+=' Private viewport showed navigation/search UI with no populated listing area at capture time. No additional wait or retry was used.'
 a['driver_log_sha256']=sha((args.private_output/(a['target_id']+'-driver.log')).read_bytes())
 for key in ['raw_html_private_path','screenshot_private_path']:a.pop(key,None)
run['publication_validation']={'checked_at':datetime.now(timezone.utc).isoformat(),'unchanged_validator_ast_verified':True,'dom_hashes_and_checks_verified':doms,'png_hashes_verified':pngs,'pss_peaks_verified':30,'additional_target_requests':0,'surviving_owned_processes':0}
run['summary']['http_200_soft_blocks']=sum(a['status']==200 and a['classification']=='soft_block' for a in run['attempts'])
run['summary']['http_200_empty_content']=sum(a['status']==200 and a['empty_content'] for a in run['attempts'])
run['startup_checks_url']='/evidence/botasaurus-public-30-20260929/startup-checks.json'
run['canary_relation']='One separate successful Web Scraping Dev canary preceded this complete 30-target batch. It is not included in these counts. Initial blocked setup and local blank test sent zero public navigations. In total, this safe retry sent 31 public target navigations, plus ordinary resources and redirects.'
run['latency_basis']='latency_seconds starts immediately before driver.get and ends after navigation, ready-state wait (interactive/complete), fixed two-second settle, final-URL read and UTF-8 DOM serialization. Excludes startup, screenshot and teardown. attempt_seconds includes those stages after Python imports. A watchdog fallback is elapsed worker time, not completed request latency.'
(HERE/'pss-samples.json').write_text(json.dumps(samples,indent=2)+'\n');run['pss_samples_sha256']=sha((HERE/'pss-samples.json').read_bytes())
(HERE/'results.json').write_text(json.dumps(run,indent=2)+'\n')
print('Published 30 rechecked records:',run['summary'],'DOM hashes',doms,'PNG hashes',pngs)
