Establish InterfaceCanon with Statehub integration and FIAM-derived model
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a087be-d842-7252-81be-2105c4279c87
This commit is contained in:
parent
af74f742de
commit
ee0df5c931
21 changed files with 937 additions and 2 deletions
60
tools/check.py
Normal file
60
tools/check.py
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
"""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')
|
||||
Loading…
Add table
Add a link
Reference in a new issue