tracegrpo / scripts /verify_phase1_layout.py
Dhruv1000's picture
Publish approved Phase 1: scientific README/figures, canonical audit paths and preserved historical navigation
2ab3846 verified
Raw History Blame Contribute Delete
3.49 kB
#!/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)