2026-09-27 11:49:48 +02:00
#!/usr/bin/env python3
""" Build or check the pinned 0.1 candidate reference (not a canonical graph). """
import argparse
import hashlib
import json
2026-09-27 17:20:43 +02:00
from pathlib import Path , PurePosixPath
2026-09-27 11:49:48 +02:00
import re
import subprocess
from classification_coverage import section_values , RESPONSIBILITIES
ROOT = Path ( __file__ ) . resolve ( ) . parents [ 1 ]
def require ( condition , message ) :
if not condition :
raise ValueError ( message )
2026-09-27 17:20:43 +02:00
def git_blob ( root , revision , path ) :
require ( isinstance ( revision , str ) and re . fullmatch ( r ' [0-9a-f] {40} ' , revision ) , ' Full pinned revision required ' )
require ( isinstance ( path , str ) and path and not PurePosixPath ( path ) . is_absolute ( )
and all ( part not in { ' . ' , ' .. ' , ' ' } for part in path . split ( ' / ' ) )
and ' \\ ' not in path and ' : ' not in path , ' Invalid reference: ' + str ( path ) )
try :
return subprocess . check_output ( [ ' git ' , ' show ' , revision + ' : ' + path ] , cwd = root , stderr = subprocess . DEVNULL )
except subprocess . CalledProcessError as exc :
raise ValueError ( ' Missing pinned reference: ' + path ) from exc
2026-09-27 11:49:48 +02:00
2026-09-27 17:20:43 +02:00
def release ( root , version = None ) :
registry = json . loads ( ( root / ' reference/releases.json ' ) . read_text ( ) )
require ( registry . get ( ' schema_version ' ) == 1 , ' Unsupported release registry ' )
version = version or registry [ ' current ' ]
require ( isinstance ( version , str ) and re . fullmatch ( r ' [0-9]+ \ .[0-9]+ ' , version ) , ' Invalid reference version ' )
require ( version in registry [ ' releases ' ] , ' Unknown reference version ' )
pin = registry [ ' releases ' ] [ version ]
base = ' reference/ ' + version + ' / '
manifest_bytes = git_blob ( root , pin [ ' packaging_revision ' ] , base + ' manifest.json ' )
require ( hashlib . sha256 ( manifest_bytes ) . hexdigest ( ) == pin [ ' manifest_sha256 ' ] , ' Release manifest digest mismatch ' )
require ( ( root / base / ' manifest.json ' ) . read_bytes ( ) == manifest_bytes , ' Published manifest changed ' )
for name in ( ' development-cases.json ' , ' README.md ' , ' ReleaseNotes.md ' , ' ReleaseReview.md ' ) :
require ( ( root / base / name ) . read_bytes ( ) == git_blob ( root , pin [ ' packaging_revision ' ] , base + name ) , ' Published artifact changed: ' + name )
manifest = json . loads ( manifest_bytes )
require ( manifest [ ' version ' ] == version , ' Release version mismatch ' )
return base , pin , manifest
def build ( root = ROOT , version = None ) :
base , pin , manifest = release ( root , version )
corpus = manifest [ ' corpus_revision ' ]
read_corpus = lambda path : git_blob ( root , corpus , path )
read_package = lambda path : git_blob ( root , pin [ ' packaging_revision ' ] , path )
2026-09-27 11:49:48 +02:00
for path , digest in manifest [ ' inputs ' ] . items ( ) :
2026-09-27 17:20:43 +02:00
require ( hashlib . sha256 ( read_corpus ( path ) ) . hexdigest ( ) == digest , ' Stale pinned input: ' + path )
profiles = json . loads ( read_corpus ( ' research/evolution/profiles.json ' ) ) [ ' profiles ' ]
classifications = json . loads ( read_corpus ( ' research/catalog/ClassificationCoverage.json ' ) ) [ ' candidates ' ]
2026-09-27 11:49:48 +02:00
classes = { r [ ' path ' ] : r for r in classifications }
2026-09-27 17:20:43 +02:00
vocab = read_corpus ( ' canon/vocabulary.yaml ' ) . decode ( )
2026-09-27 11:49:48 +02:00
ids = [ p [ ' id ' ] for p in profiles ]
selected = manifest [ ' core ' ] + manifest [ ' extensions ' ]
require ( len ( ids ) == len ( set ( ids ) ) and all ( re . fullmatch ( r ' CAND- \ d {4} ' , x ) for x in ids ) , ' Invalid candidate identities ' )
require ( len ( selected ) == len ( set ( selected ) ) and set ( selected ) == set ( ids ) , ' Invalid manifest selection ' )
require ( len ( classes ) == len ( classifications ) == len ( profiles ) , ' Invalid classification coverage ' )
records = [ ]
for p in profiles :
2026-09-27 17:20:43 +02:00
raw = read_corpus ( p [ ' path ' ] )
2026-09-27 11:49:48 +02:00
require ( hashlib . sha256 ( raw ) . hexdigest ( ) == p [ ' sha256 ' ] , ' Stale candidate: ' + p [ ' id ' ] )
front = raw . decode ( ) . split ( ' --- ' ) [ 1 ]
c = classes [ p [ ' path ' ] ]
require ( c [ ' sha256 ' ] == p [ ' sha256 ' ] and c [ ' title ' ] == p [ ' title ' ] , ' Stale classification ' )
require ( p [ ' status ' ] == ' draft ' and p [ ' maturity ' ] == ' hypothesis ' , ' Re-review preview maturity ' )
require ( p [ ' gaps ' ] and p [ ' decision ' ] . strip ( ) and c [ ' rationale ' ] . strip ( ) , ' Missing qualification ' )
record = dict ( p , subset = ' core ' if p [ ' id ' ] in manifest [ ' core ' ] else ' extension ' , classification = c ,
classification_status = ' provisional author-model interpretation ' )
for key in ( ' families ' , ' scales ' ) :
match = re . search ( r ' ^ ' + key + r ' : \ [(.+) \ ]$ ' , front , re . M )
require ( match is not None , ' Missing ' + key )
labels = [ s . strip ( ) for s in match [ 1 ] . split ( ' , ' ) ]
require ( len ( set ( labels ) ) == len ( labels ) and set ( labels ) < = set ( section_values ( vocab , key ) ) , ' Invalid ' + key )
record [ key ] = labels
require ( set ( c [ ' responsibilities ' ] ) < = set ( RESPONSIBILITIES ) , ' Invalid responsibility ' )
for labels in c [ ' forces ' ] . values ( ) :
require ( set ( labels ) < = set ( section_values ( vocab , ' forces ' ) ) , ' Invalid force ' )
for e in p [ ' evidence ' ] :
2026-09-27 17:20:43 +02:00
read_package ( e [ ' ref ' ] )
2026-09-27 11:49:48 +02:00
require ( e [ ' kind ' ] and e [ ' scope ' ] , ' Missing evidence qualification ' )
require ( p [ ' evidence ' ] or p [ ' independent_full_contract_uses ' ] is None , ' Missing evidence/unknown ' )
records . append ( record )
for edge in manifest [ ' relations ' ] :
require ( edge [ ' source ' ] in ids and edge [ ' target ' ] in ids , ' Invalid relation endpoint ' )
require ( edge [ ' type ' ] in section_values ( vocab , ' pattern_relations ' ) , ' Invalid relation type ' )
require ( edge [ ' status ' ] == ' proposed ' and all ( edge [ k ] . strip ( ) for k in ( ' condition ' , ' rationale ' , ' unknown ' , ' evidence_kind ' ) ) , ' Unqualified relation ' )
require ( edge [ ' evidence_refs ' ] , ' Missing relation evidence ' )
for ref in edge [ ' evidence_refs ' ] :
2026-09-27 17:20:43 +02:00
read_package ( ref )
2026-09-27 11:49:48 +02:00
for ref in manifest [ ' entrypoints ' ] . values ( ) :
2026-09-27 17:20:43 +02:00
read_package ( ref )
2026-09-27 11:49:48 +02:00
for task in manifest [ ' open_work ' ] :
2026-09-27 17:20:43 +02:00
require ( task [ ' id ' ] in read_package ( task [ ' path ' ] ) . decode ( ) , ' Missing residual task ' )
cases = json . loads ( read_package ( base + ' development-cases.json ' ) ) [ ' cases ' ]
2026-09-27 11:49:48 +02:00
require ( len ( cases ) > = 6 and len ( { c [ ' id ' ] for c in cases } ) == len ( cases ) , ' Invalid development cases ' )
for case in cases :
require ( case [ ' entrypoint ' ] in manifest [ ' entrypoints ' ] and set ( case [ ' expected_candidates ' ] ) < = set ( ids ) , ' Invalid development reference ' )
require ( all ( case [ k ] . strip ( ) for k in ( ' query ' , ' reviewed_answer ' , ' prohibited_claim ' , ' boundary ' ) ) , ' Incomplete development case ' )
2026-09-27 17:20:43 +02:00
index = dict ( schema_version = 1 , version = manifest [ ' version ' ] , status = ' research-preview ' , corpus_revision = manifest [ ' corpus_revision ' ] ,
2026-09-27 11:49:48 +02:00
candidates = records , relations = manifest [ ' relations ' ] , entrypoints = manifest [ ' entrypoints ' ] ,
relation_semantics = ' Conditional proposals only; no inverse or transitive inference. ' )
2026-09-27 17:20:43 +02:00
catalog = [ ' # Candidate reference — ' + manifest [ ' version ' ] , ' ' , ' All records are draft hypotheses. Classification is provisional; linked candidate prose contains context, alternatives and consequences. Evidence locators describe partial support, not full-contract recurrence. ' , ' ' ]
2026-09-27 11:49:48 +02:00
for subset in ( ' core ' , ' extensions ' ) :
catalog + = [ ' ## ' + subset . title ( ) , ' ' , ' | Candidate / decision | Families; scales | Evidence limits | ' , ' | --- | --- | --- | ' ]
for p in records :
if p [ ' id ' ] not in manifest [ subset ] :
continue
catalog . append ( f " | [ { p [ ' id ' ] } — { p [ ' title ' ] } ](../../ { p [ ' path ' ] } ) — { p [ ' decision ' ] } | { ' , ' . join ( p [ ' families ' ] ) } ; { ' , ' . join ( p [ ' scales ' ] ) } | { ' ' . join ( p [ ' gaps ' ] ) } | " )
catalog + = [ ' ' ]
catalog + = [ ' Extensions are optional research leads. VC-03/06 remain provisional; other planned cases are unsupported. Follow the [starting guide](../../START.md) for the two core contexts. ' , ' ' ]
checks = [ ' # Disclosed development navigation checks ' , ' ' , ' Author-model review; scenarios and answers were authored together. These checks validate navigation and stated boundaries, not measured retrieval or independent usefulness. ' , ' ' ]
for c in cases :
checks + = [ ' ## ' + c [ ' id ' ] + ' — ' + c [ ' query ' ] , ' ' , ' [ ' + c [ ' entrypoint ' ] + ' ](../../ ' + manifest [ ' entrypoints ' ] [ c [ ' entrypoint ' ] ] + ' ) → ' + ( ' , ' . join ( c [ ' expected_candidates ' ] ) or ' clarify / abstain ' ) + ' . ' , ' ' , c [ ' reviewed_answer ' ] , ' ' , ' Boundary: ' + c [ ' boundary ' ] , ' ' , ' Reject: ' + c [ ' prohibited_claim ' ] , ' ' , ' Editorial disposition: ' + c [ ' review_result ' ] + ' . ' , ' ' ]
return { ' index.json ' : json . dumps ( index , indent = 2 ) + ' \n ' , ' Catalog.md ' : ' \n ' . join ( catalog ) , ' DevelopmentChecks.md ' : ' \n ' . join ( checks ) }
def main ( ) :
parser = argparse . ArgumentParser ( description = __doc__ )
parser . add_argument ( ' --check ' , action = ' store_true ' )
2026-09-27 17:20:43 +02:00
parser . add_argument ( ' --version ' , help = ' Immutable published version; default is reference/releases.json current ' )
2026-09-27 11:49:48 +02:00
args = parser . parse_args ( )
2026-09-27 17:20:43 +02:00
base , _ , _ = release ( ROOT , args . version )
for name , content in build ( version = args . version ) . items ( ) :
path = ROOT / base / name
2026-09-27 11:49:48 +02:00
if args . check :
require ( path . is_file ( ) and path . read_text ( ) == content , ' Stale export: ' + name )
else :
path . write_text ( content )
print ( ' Preview identities, pinned candidates, vocabulary, references and development cases verified. ' )
if __name__ == ' __main__ ' :
main ( )