"""Offline inspection/publication only; never sends target requests or publishes raw captures.
python publish.py --private-output runner/data/seleniumbase-public-30-20260930
"""
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 a['browser_version']=='153.0.8010.12' and a['driver_binary_sha256']==run['driver_binary_sha256']
 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 a Quick verification / press-and-hold wall. The saved main-frame DOM had no frozen block-phrase match, so missing_required_content is retained; visual verification challenge is recorded separately. No CAPTCHA or login interaction.'
  a['visual_inspection']={'outcome':'verification_challenge','basis':'Private viewport PNG inspected; no frozen main-frame source-text phrase match','changes_frozen_classification':False}
 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,'performance_log_hashes_and_status_redirects_verified':30,'browser_log_hashes_verified':30,'main_browser_arguments_observed':5}
# Recompute all main-document status/redirect observations from private performance events.
for a in run['attempts']:
 logs_path=args.private_output/(a['target_id']+'-performance.json');logs=json.loads(logs_path.read_text())
 assert sha(logs_path.read_bytes())==a['performance_log_sha256']
 responses=[];redirects=[]
 for log in logs:
  event=json.loads(log['message'])['message'];data=event.get('params',{})
  if data.get('frameId')!=a['main_frame_id'] or data.get('type')!='Document':continue
  if event['method']=='Network.requestWillBeSent' and data.get('redirectResponse'):
   old=data['redirectResponse'];redirects.append({'previous':old['url'],'url':data['request']['url'],'status':int(old['status'])})
  elif event['method']=='Network.responseReceived':responses.append({'url':data['response']['url'],'status':int(data['response']['status'])})
 assert responses==a['document_responses'] and redirects==a['redirect_history'] and responses[-1]['status']==a['status']
 assert sha((args.private_output/(a['target_id']+'-browser.json')).read_bytes())==a['browser_log_sha256']
 observations=json.loads((HERE/'launch-observation.json').read_text())['valid_main_process_observations']
 a['effective_browser_arguments']=observations.get(a['target_id'],{}).get('arguments')
 a['browser_arguments_basis']='Independent read-only worker-descendant observer' if a['effective_browser_arguments'] else 'Actual command-line list unavailable; exact API kwargs, release sources, capabilities and observed navigator recorded. Initial selector chose a short-lived version-query child.'
 if a['effective_browser_arguments']:
  assert '--no-proxy-server' in a['effective_browser_arguments'] and '--no-sandbox' in a['effective_browser_arguments'] and '--headless=new' in a['effective_browser_arguments']
  assert '--user-data-dir='+a['browser_capabilities']['chrome']['userDataDir'] in a['effective_browser_arguments']
run['argument_observation_note']=json.loads((HERE/'launch-observation.json').read_text())['note']
run['argument_observation_url']='/evidence/seleniumbase-public-30-20260929/launch-observation.json'
run['summary']['http_200_soft_blocks']=sum(a['status']==200 and a['classification']=='soft_block' for a in run['attempts'])
run['summary']['http_200_visually_observed_challenges_not_classified_soft_block']=sum(a['status']==200 and a['classification']!='soft_block' and a.get('visual_inspection',{}).get('outcome')=='verification_challenge' 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/seleniumbase-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. No local smoke or failed setup navigation was needed. In total, this SeleniumBase job sent 31 public target navigations, plus ordinary resources and redirects.'
run['latency_basis']='latency_seconds starts immediately before driver.get and ends after navigation, eager page-load wait, 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. Excludes the later performance/browser-log drain. 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)
