#!/usr/bin/env python3 """Portable smoke validator: structural and cross-reference checks; not full JSON Schema compliance.""" from pathlib import Path import json, sys ROOT = Path(__file__).resolve().parent.parent errors=[] for path in sorted((ROOT/'examples/dataspaces').glob('*/snapshot.json')): d=json.loads(path.read_text()) kinds={r['id'] for r in d['kinds']}; ids=[r['id'] for r in d['entities']] sources={r['id'] for r in d['sources']} if d['schemaVersion']!='0.1': errors.append(f'{path}: schemaVersion') if len(ids)!=len(set(ids)): errors.append(f'{path}: duplicate entities') for e in d['entities']: if e['kindId'] not in kinds: errors.append(f'{path}: undeclared kind {e["kindId"]}') for sid in e.get('sourceRefs',[]): if sid not in sources: errors.append(f'{path}: missing source {sid}') for r in d['relations']: for field in ('from','to'): if r[field] not in ids: errors.append(f'{path}: dangling {field} {r[field]}') for sid in r.get('sourceRefs',[]): if sid not in sources: errors.append(f'{path}: missing edge source {sid}') manifest=json.loads((path.parent/'manifest.json').read_text()) if manifest['id']!=d['dataspace']['id']: errors.append(f'{path}: manifest ID mismatch') print(f'{d["dataspace"]["id"]}: {len(ids)} entities, {len(d["relations"])} relations, {len(kinds)} kinds') if errors: print('\n'.join(errors),file=sys.stderr);sys.exit(1) print('Smoke validation OK (full JSON Schema and claims pending).')