Close local review loose ends and record blocked workplan gates
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0e241-8285-7a63-8c0c-51c9cb824dc3
This commit is contained in:
parent
bcdb02323c
commit
7295172f1e
20 changed files with 510 additions and 95 deletions
|
|
@ -3,14 +3,13 @@
|
|||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
from pathlib import Path, PurePosixPath
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
from classification_coverage import section_values, RESPONSIBILITIES
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
BASE = 'reference/0.1/'
|
||||
|
||||
|
||||
def require(condition, message):
|
||||
|
|
@ -18,21 +17,46 @@ def require(condition, message):
|
|||
raise ValueError(message)
|
||||
|
||||
|
||||
def locator(root, value):
|
||||
require(isinstance(value, str) and bool(value), 'Empty reference')
|
||||
path = (root / value).resolve()
|
||||
require(path.is_relative_to(root.resolve()) and path.is_file(), 'Invalid reference: ' + value)
|
||||
return path
|
||||
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
|
||||
|
||||
|
||||
def build(root=ROOT):
|
||||
manifest = json.loads((root / BASE / 'manifest.json').read_text())
|
||||
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)
|
||||
for path, digest in manifest['inputs'].items():
|
||||
require(hashlib.sha256(locator(root, path).read_bytes()).hexdigest() == digest, 'Stale input: ' + path)
|
||||
profiles = json.loads((root / 'research/evolution/profiles.json').read_text())['profiles']
|
||||
classifications = json.loads((root / 'research/catalog/ClassificationCoverage.json').read_text())['candidates']
|
||||
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']
|
||||
classes = {r['path']: r for r in classifications}
|
||||
vocab = (root / 'canon/vocabulary.yaml').read_text()
|
||||
vocab = read_corpus('canon/vocabulary.yaml').decode()
|
||||
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')
|
||||
|
|
@ -40,10 +64,8 @@ def build(root=ROOT):
|
|||
require(len(classes) == len(classifications) == len(profiles), 'Invalid classification coverage')
|
||||
records = []
|
||||
for p in profiles:
|
||||
raw = locator(root, p['path']).read_bytes()
|
||||
raw = read_corpus(p['path'])
|
||||
require(hashlib.sha256(raw).hexdigest() == p['sha256'], 'Stale candidate: ' + p['id'])
|
||||
pinned = subprocess.check_output(['git', 'show', manifest['corpus_revision'] + ':' + p['path']], cwd=root)
|
||||
require(raw == pinned, 'Candidate differs from pinned revision: ' + p['id'])
|
||||
front = raw.decode().split('---')[1]
|
||||
c = classes[p['path']]
|
||||
require(c['sha256'] == p['sha256'] and c['title'] == p['title'], 'Stale classification')
|
||||
|
|
@ -61,7 +83,7 @@ def build(root=ROOT):
|
|||
for labels in c['forces'].values():
|
||||
require(set(labels) <= set(section_values(vocab, 'forces')), 'Invalid force')
|
||||
for e in p['evidence']:
|
||||
locator(root, e['ref'])
|
||||
read_package(e['ref'])
|
||||
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)
|
||||
|
|
@ -71,20 +93,20 @@ def build(root=ROOT):
|
|||
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']:
|
||||
locator(root, ref)
|
||||
read_package(ref)
|
||||
for ref in manifest['entrypoints'].values():
|
||||
locator(root, ref)
|
||||
read_package(ref)
|
||||
for task in manifest['open_work']:
|
||||
require(task['id'] in locator(root, task['path']).read_text(), 'Missing residual task')
|
||||
cases = json.loads((root / BASE / 'development-cases.json').read_text())['cases']
|
||||
require(task['id'] in read_package(task['path']).decode(), 'Missing residual task')
|
||||
cases = json.loads(read_package(base + 'development-cases.json'))['cases']
|
||||
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')
|
||||
index = dict(schema_version=1, version='0.1', status='research-preview', corpus_revision=manifest['corpus_revision'],
|
||||
index = dict(schema_version=1, version=manifest['version'], status='research-preview', corpus_revision=manifest['corpus_revision'],
|
||||
candidates=records, relations=manifest['relations'], entrypoints=manifest['entrypoints'],
|
||||
relation_semantics='Conditional proposals only; no inverse or transitive inference.')
|
||||
catalog = ['# Candidate reference — 0.1', '', '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.', '']
|
||||
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.', '']
|
||||
for subset in ('core', 'extensions'):
|
||||
catalog += ['## ' + subset.title(), '', '| Candidate / decision | Families; scales | Evidence limits |', '| --- | --- | --- |']
|
||||
for p in records:
|
||||
|
|
@ -102,9 +124,11 @@ def build(root=ROOT):
|
|||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--check', action='store_true')
|
||||
parser.add_argument('--version', help='Immutable published version; default is reference/releases.json current')
|
||||
args = parser.parse_args()
|
||||
for name, content in build().items():
|
||||
path = ROOT / BASE / name
|
||||
base, _, _ = release(ROOT, args.version)
|
||||
for name, content in build(version=args.version).items():
|
||||
path = ROOT / base / name
|
||||
if args.check:
|
||||
require(path.is_file() and path.read_text() == content, 'Stale export: ' + name)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -53,7 +53,10 @@ def validate(data, root=ROOT):
|
|||
require(not p['review']['independent_human'] or p['review']['role'] == 'human', 'Model review cannot be human validation')
|
||||
date.fromisoformat(p['review']['assessed_at'])
|
||||
if p['review']['last_substantive_review_date'] is not None:
|
||||
date.fromisoformat(p['review']['last_substantive_review_date'])
|
||||
review = p['review']
|
||||
require(date.fromisoformat(review['last_substantive_review_date']) <= date.fromisoformat(review['assessed_at']), 'Review date exceeds assessment')
|
||||
require(review.get('substantive_review_ref') and review.get('substantive_review_scope', '').strip(), 'Substantive review needs provenance and scope')
|
||||
ref_check(root, review['substantive_review_ref'])
|
||||
require(p['independent_full_contract_uses'] is None or type(p['independent_full_contract_uses']) is int and p['independent_full_contract_uses'] >= 0, 'Invalid independent-use count')
|
||||
require(set(p['superseded_by']) <= set(ids) - {p['id']}, 'Unknown/self successor')
|
||||
for e in p['evidence']:
|
||||
|
|
@ -164,7 +167,7 @@ def render(data, baseline=None):
|
|||
lines += ['## Coherence and maintainability', '',
|
||||
'Profile hashes and local references passed validation when this report was generated. That checks traceability, not source truth or external URL freshness.', '',
|
||||
f'Profiles with unknown last substantive review date: {sum(p["review"]["last_substantive_review_date"] is None for p in profiles)}. '
|
||||
'Assessment dates record this metadata review, not a new substantive validation. No age-based freshness claim is available for those profiles.', '',
|
||||
'Assessment dates record this metadata review, not a new substantive validation. Recorded substantive dates identify scoped author-model reviews, not renewed empirical or external-source freshness.', '',
|
||||
'Coherence metrics (ambiguous selections, unjustified relations and overlap resolved): not measured. Conflict Copy / Explicit Reconciliation overlap remains an explicit open question.', '',
|
||||
'Review backlog and evidence gaps remain in ' + ', '.join(link(r) for r in context['open_work']) + '.', '',
|
||||
'## Pattern profiles', '']
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue