frontend-patterns/scripts/research_admission.py

103 lines
4.8 KiB
Python
Raw Permalink Normal View History

"""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)