"""Check document references, work-record identity, and immutable source provenance.""" import argparse import hashlib import json from pathlib import Path import re import subprocess ROOT = Path(__file__).resolve().parents[1] parser = argparse.ArgumentParser() parser.add_argument('--require-sources', action='store_true') args = parser.parse_args() errors = [] for path in ROOT.rglob('*.md'): if '.git' in path.parts: continue for link in re.findall(r'\[[^\]]*\]\(([^)]+)\)', path.read_text()): if '://' in link or link.startswith('#'): continue target = link.split('#')[0] if target and not (path.parent / target).exists(): errors.append(f'{path.relative_to(ROOT)}: missing link {target}') ids = set() for path in sorted((ROOT / 'workplans').glob('*.md')): text = path.read_text() for identifier in re.findall(r'^id: (INTERFACE-WP-[^\s]+)$', text, re.M): if identifier in ids: errors.append(f'duplicate work-record id: {identifier}') ids.add(identifier) for status in re.findall(r'^status: (\S+)$', text, re.M): if status not in {'proposed', 'ready', 'active', 'blocked', 'backlog', 'finished', 'archived', 'wait', 'todo', 'progress', 'done', 'cancel'}: errors.append(f'{path.name}: invalid status {status}') for path in [ROOT / 'canon.yaml', ROOT / 'infospace/interfaces/federation.yaml']: for target in re.findall(r'^\s*(?:path|manifest): (\S+)$', path.read_text(), re.M): if target == 'infospace/schemas/interface-card.schema.yaml': continue # Explicitly owned upstream. if not (ROOT / target).is_file(): errors.append(f'{path.name}: missing artifact {target}') for path in sorted((ROOT / 'infospace').rglob('*.json')): manifest = json.loads(path.read_text()) revision = manifest['source_commit'] if not re.fullmatch('[0-9a-f]{40}', revision): errors.append(f'{path}: invalid source commit') source = ROOT.parent / manifest['source_repository'] records = manifest.get('sources', manifest.get('imports', [])) if not records: errors.append(f'{path}: empty source manifest') for record in records: if not re.fullmatch('[0-9a-f]{64}', record['sha256']): errors.append(f'{path}: invalid SHA-256') if not (source / '.git').exists(): if args.require_sources: errors.append(f'missing source checkout: {source}') continue result = subprocess.run(['git', '-C', str(source), 'show', f'{revision}:{record["path"]}'], capture_output=True) if result.returncode or hashlib.sha256(result.stdout).hexdigest() != record['sha256']: errors.append(f'{path.name}: source integrity failure for {record["path"]}') if errors: raise SystemExit('\n'.join(errors)) print(f'PASS: document links, artifact paths, {len(ids)} work-record IDs, and available pinned Git sources')