prj-canon-federation/docs/evidence/2026-09-05-counterparty-validate.py
tegwick 49b03943c8 Complete G7 reciprocal canon interfaces and source-review evidence
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a070b5-4994-7271-bd8b-7c3dbcedec4b
2026-09-06 01:04:34 +02:00

73 lines
4 KiB
Python

"""Reproduce T06 ledger/import/provenance checks; requires Python 3 and PyYAML."""
import argparse
import hashlib
import json
from pathlib import Path
import re
import subprocess
import yaml
parser = argparse.ArgumentParser()
parser.add_argument('--commerce-repo', type=Path, required=True)
parser.add_argument('--info-tech-repo', type=Path, required=True)
args = parser.parse_args()
commerce = args.commerce_repo.resolve()
upstream = args.info_tech_repo.resolve()
evidence = Path(__file__).resolve().parent
project = evidence.parents[1]
ledger = json.loads((project / 'ledger/concept-ownership.json').read_text())
model_path = commerce / 'infospace/models/counterparty/CommerceCanonCounterpartyModel.md'
model = model_path.read_text()
fm = yaml.safe_load(model.split('---')[1])
assigned = [t for e in ledger['entries'] for t in e['targets'] if t['owner']['canon'] == 'commerce-canon']
owned = {t['concept'] for t in assigned if t['kind'] == 'concept'}
assert set(fm['owned_concepts']) == owned
assert len(fm['owned_concepts']) == len(owned) == 18
assert set(fm['convenience_terms']) == {t['concept'] for t in assigned if t['kind'] == 'convenience_term'}
assert set(re.findall(r'^### 2\.\d+ (.+)$', model, re.M)) == owned
registry = yaml.safe_load((commerce / 'canon.yaml').read_text())
assert len(registry['models']) == 1
entry = registry['models'][0]
assert commerce / entry['path'] == model_path
for key in ['id', 'version', 'status']: assert entry[key] == fm[key]
manifest = json.loads(model_path.with_name('imports.json').read_text())
assert set(fm['imports']) == {row['model'] for row in manifest['imports']}
imports = set()
upstream_drift = []
for row in manifest['imports']:
data = (upstream / row['path']).read_bytes()
historical = subprocess.check_output(['git', 'show', manifest['source_commit'] + ':' + row['path']], cwd=upstream)
assert hashlib.sha256(historical).hexdigest() == row['sha256'], 'reviewed commit differs: ' + row['path']
if data != historical:
upstream_drift.append(row['path'])
text = historical.decode()
if text.startswith('---\n'):
upstream_fm = yaml.safe_load(text.split('---')[1])
upstream_owned = set(upstream_fm.get('owned_concepts', []))
# Organization frontmatter records only its incremental social extension.
# Include explicit legacy definitions, never bare imported anchors.
upstream_owned.update(re.findall(r'^(?:An? )?\*\*(.+?)\*\* is\b', text, re.M))
else:
upstream_owned = set(re.findall(r'^## \d+\.\d+[a-z]? (.+)$', text, re.M))
assert set(row['concepts']) <= upstream_owned, set(row['concepts']) - upstream_owned
imports.update(row['concepts'])
assert owned.isdisjoint(imports)
for concept in ['Evidence', 'Evidence Source', 'Adjudication Outcome', 'Evidence strength', 'Identifier', 'Scoped Identifier']:
assert concept in imports and concept not in owned
protected = json.loads((evidence / '2026-09-05-counterparty-protected-hashes.json').read_text())
for path, digest in protected.items():
assert hashlib.sha256((commerce / path).read_bytes()).hexdigest() == digest, path
links = 0
for relative in ['README.md', 'docs/RepositoryLayout.md', 'infospace/kernel/CommerceCanonCore.md',
'infospace/models/README.md', 'infospace/models/counterparty/CommerceCanonCounterpartyModel.md',
'workplans/COMMERCE-WP-0002-counterparty-model.md']:
path = commerce / relative
for link in re.findall(r'\]\(([^)]+)\)', path.read_text()):
if '://' in link or link.startswith('#'): continue
assert (path.parent / link.split('#')[0]).exists(), (relative, link)
links += 1
print(json.dumps({'owned_concepts': len(owned), 'convenience_terms': 2,
'imported_models': len(manifest['imports']), 'imported_concepts': len(imports),
'upstream_commit': manifest['source_commit'], 'upstream_changes_since_review': upstream_drift, 'protected_files_unchanged': len(protected),
'links_checked': links, 'registry_and_boundaries': 'pass'}, indent=2))