#!/usr/bin/env python3 """Check maintained documentation, assets and audit bytes without opening TEST data.""" from pathlib import Path import ast,hashlib,json,re,sys from urllib.parse import unquote ROOT=Path(__file__).resolve().parents[1] def check(): missing=[];historical=[];nlinks=0 files=[ROOT/'README.md'] for name in ['docs','figures','audit','models']: files.extend(p for p in (ROOT/name).rglob('*.md') if not p.is_symlink()) for p in files: for target in re.findall(r'\]\(([^)]+)\)',p.read_text(errors='replace')): target=target.strip().split(' "')[0].strip('<>') if re.match(r'^(?:https?:|mailto:|app:|#|data:)',target):continue target=unquote(target.split('#')[0]);nlinks+=1 if target and not (p.parent/target).exists(): row={'source':p.relative_to(ROOT).as_posix(),'target':target} if 'prompts' in p.parts and re.fullmatch(r'figures/fig[1-5]_[a-z]+\.png',target):historical.append(row) else:missing.append(row) manifest=json.loads((ROOT/'figures/provenance.json').read_text());bad_assets=[] for path,digest in manifest['inputs_sha256'].items(): if hashlib.sha256((ROOT/path).read_bytes()).hexdigest()!=digest:bad_assets.append(path) for path,digest in manifest['outputs_sha256'].items(): if hashlib.sha256((ROOT/'figures'/path).read_bytes()).hexdigest()!=digest:bad_assets.append(path) # Only code files are inspected for imports/syntax. No execution of research entry points. syntax=[];unresolved=[];edges=0 for folder in ['src','scripts','tests']: for p in (ROOT/folder).rglob('*.py'): try:tree=ast.parse(p.read_text()) except SyntaxError as e:syntax.append({'path':str(p.relative_to(ROOT)),'error':str(e)});continue for node in ast.walk(tree): mods=[] if isinstance(node,ast.Import):mods=[a.name for a in node.names] elif isinstance(node,ast.ImportFrom) and node.module and not node.level:mods=[node.module] for mod in mods: if mod.split('.')[0] not in ['src','scripts']:continue path=ROOT.joinpath(*mod.split('.'));edges+=1 if not path.with_suffix('.py').is_file() and not path.is_dir():unresolved.append({'source':str(p.relative_to(ROOT)),'module':mod}) from scripts.check_review31_blinding import check_exported_cards from scripts.analyze_review34_llm_judge import validate_grades deck_text=(ROOT/'audit/human/grader.html').read_text() deck_match=re.search(r'const cards=(\[.*?\]);let i=',deck_text,re.S) if not deck_match:raise ValueError('Accepted blinded deck is missing') cards=check_exported_cards(ROOT/'audit/llm_judge/package/cards',source_cards=json.loads(deck_match.group(1))) grades=validate_grades(ROOT/'audit/llm_judge/grades.jsonl') return {'current_local_links_checked':nlinks,'missing_current_targets':missing,'original_prompt_missing_assets_disclosed':historical,'asset_hash_failures':bad_assets,'internal_import_edges':edges,'unresolved_internal_imports':unresolved,'syntax_errors':syntax,'audit_blinding':cards,'validated_grade_records':len(grades),'test_data_opened':False,'passed':not (missing or bad_assets or syntax or unresolved)} if __name__=='__main__': sys.path.insert(0,str(ROOT));result=check();print(json.dumps(result,indent=2));sys.exit(0 if result['passed'] else 1)