"""Conservative structural screening; never grants research admission.""" import argparse import hashlib import json from pathlib import Path import re RELATIONS = {'requires', 'enables', 'creates-context-for', 'refines', 'generalizes', 'complements', 'alternative-to', 'conflicts-with', 'commonly-combined-with'} def json_block(text): blocks = re.findall(r'```json\s*\n(.*?)\n```', text, re.S) if len(blocks) == 1: return json.loads(blocks[0]) if blocks or '```' in text: raise ValueError('exactly one JSON block required') # Accept one unambiguous terminal JSON document without Markdown fences. # Never repair malformed JSON or infer data from prose. starts = list(re.finditer(r'(?m)^[ \t]*(?=[{\[])', text)) if len(starts) == 1: return json.loads(text[starts[0].start():].strip()) raise ValueError('exactly one JSON block or terminal document required') def screen(candidates, relations): findings = [] ids = set() try: ledger = json_block(candidates) if not isinstance(ledger, dict) or not isinstance(ledger.get('candidates'), list): raise ValueError('candidate ledger required') for item in ledger['candidates']: if not isinstance(item, dict): raise ValueError('candidate must be an object') cid = item.get('id') if not isinstance(cid, str) or not re.fullmatch(r'P[0-9]{2,}', cid) or cid in ids: raise ValueError('candidate IDs must be unique Pnn identities') ids.add(cid) uses = item.get('known_uses') if not isinstance(uses, list): raise ValueError('known_uses list required') for use in uses: if (not isinstance(use, dict) or not all( isinstance(use.get(k), str) and use[k].strip() for k in ('implementation', 'source_locator'))): findings.append(f'{cid}: known use lacks implementation/source locator') recurrence = item.get('recurrence') if recurrence not in ('unknown', 'supported'): findings.append(f'{cid}: recurrence must be unknown or supported') if recurrence == 'supported': located = {(u.get('implementation'), u.get('source_locator')) for u in uses if isinstance(u, dict) and isinstance(u.get('implementation'), str) and isinstance(u.get('source_locator'), str) and u['source_locator'].strip()} if len({x[0] for x in located}) < 2: findings.append(f'{cid}: recurrence lacks two located implementations') except (ValueError, TypeError) as exc: findings.append(f'candidate contract: {exc}') try: edges = json_block(relations) if not isinstance(edges, list): raise ValueError('relation array required') for edge in edges: if not isinstance(edge, dict): raise ValueError('relation must be an object') for field in ('subject_candidate_id', 'object_candidate_id'): endpoint = edge.get(field) if not isinstance(endpoint, str) or endpoint not in ids: findings.append(f'invalid {field}: {endpoint!r}; must resolve to candidate ledger') if edge.get('relation') not in RELATIONS: findings.append('invalid relation type') for field in ('rationale', 'source_claim_and_locator', 'counterexample'): if not isinstance(edge.get(field), str) or not edge[field].strip(): findings.append(f'relation missing {field}') if edge.get('observation_versus_hypothesis') not in ('observation', 'hypothesis'): findings.append('relation missing observation/hypothesis classification') except (ValueError, TypeError) as exc: findings.append(f'relation contract: {exc}') return {'structural_status': 'blocked' if findings else 'passed', 'admission': 'not_granted', 'semantic_review': 'required', 'findings': findings} def audit(root): root = Path(root) paths = [root / 'artifacts/generated' / name for name in ('candidates.md', 'relation-proposals.md')] result = screen(*(p.read_text() for p in paths)) result['artifact_sha256'] = {str(p.relative_to(root)): hashlib.sha256(p.read_bytes()).hexdigest() for p in paths} return result if __name__ == '__main__': parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('root', type=Path) args = parser.parse_args() try: result = audit(args.root) except (OSError, ValueError) as exc: parser.exit(1, f'{exc}\n') print(json.dumps(result, indent=2)) raise SystemExit(0 if result['structural_status'] == 'passed' else 1)