Seal AO interface failure and verified isolated postprocessing diagnosis
This commit is contained in:
@@ -0,0 +1,532 @@
|
||||
"""Read-only AM authority verification and exclusive AN engineering preparation.
|
||||
|
||||
Public prepare(model_manifest_sha, experiment_manifest_sha, final_sha) requires
|
||||
three externally recorded, actual AM evidence hashes. It verifies both complete
|
||||
AM seals once, checks their declared gates, then fixes the first planned r0 case.
|
||||
It creates only templates/requests/overlay descriptors and a preregistration;
|
||||
never a GPU trajectory or fictitious future checkpoint hash. The caller records
|
||||
the returned preregistration SHA outside this experiment before execution.
|
||||
|
||||
read_registration(expected_sha) rehashes the exact used source/input inventory
|
||||
and control anchors without rerunning either scientific gate or full AM seal.
|
||||
Future spec.json/checkpoint receipts are runtime artifacts, not preregistered
|
||||
bytes. No frozen model module globals are modified; no Torch is imported.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
from pathlib import Path, PurePosixPath
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
ROOT = Path('/data/digital-drosophila')
|
||||
MODEL = Path(__file__).resolve().parent
|
||||
EXP = ROOT / 'experiments/phase4an-engineering-validation'
|
||||
AM_MODEL_REL = Path('models/male-cns-lif/wholebrain-plastic-interface-v1')
|
||||
AM_EXP_REL = Path('experiments/phase4am-wholebrain-plastic-interface')
|
||||
AK_REL = Path('experiments/phase4ak-apl-timing-validation')
|
||||
AI_HELPER_REL = Path('models/male-cns-lif/pn-recruitment-audit-v1/publication.py')
|
||||
AI_HELPER_SHA = '7a79c499098b74163e7bb16adabcbc54e7069dafa204d0b99c7f4c03d79f8016'
|
||||
AM_PREREG_SHA = '465886b78ada08092c2c83de278844707499718977fb6dc6a7608391da0e5304'
|
||||
AM_OVERLAY_SHA = '7f891d0ba79b393c96e75fd2dee2eddc51e57d8213eaac2779703bc2c18712b4'
|
||||
FINAL_STATUS = 'PASS-EXACT-WHOLEBRAIN-PLASTIC-INTERFACE-NOT-LEARNING'
|
||||
REG_SCHEMA = 'phase4an-engineering-registration-v1'
|
||||
BUDGET = {'output_budget_bytes': 8 * 1024**3, 'minimum_free_reserve_bytes': 10 * 1024**3}
|
||||
MODULES = ('runtime_light', 'runtime_continuation', 'engineering_inputs', 'engineering_plan',
|
||||
'engineering_compare', 'engineering_queue_audit', 'engineering_prepare', 'engineering_workflow',
|
||||
'engineering_scopes', 'engineering_analysis')
|
||||
SOURCE_FILES = tuple(sorted([name + '.py' for name in MODULES] + ['test_' + name + '.py' for name in MODULES] + ['ENGINEERING_GATE_DRAFT.md']))
|
||||
REFERENCES = ('auditory-10hz', 'auditory-100hz', 'olfactory-50hz', 'mechanosensory-50hz', 'gustatory-50hz')
|
||||
GRAPH_FILES = ('nodes.npy', 'edge_id.npy', 'src.npy', 'dst.npy', 'signed_raw.npy', 'ptr.npy', 'weights.npy')
|
||||
APL_FILES = ('input_matrix.npy', 'output_matrix.npy', 'kc_globals.npy', 'apl_globals.npy')
|
||||
_MODULE_CACHE = {}
|
||||
|
||||
|
||||
def require(ok, message):
|
||||
if not ok:
|
||||
raise ValueError(message)
|
||||
|
||||
|
||||
def digest(path):
|
||||
value = hashlib.sha256()
|
||||
with Path(path).open('rb') as stream:
|
||||
for block in iter(lambda: stream.read(8 << 20), b''):
|
||||
value.update(block)
|
||||
return value.hexdigest()
|
||||
|
||||
|
||||
def sha(value):
|
||||
return type(value) is str and re.fullmatch('[0-9a-f]{64}', value) is not None
|
||||
|
||||
|
||||
def read(path):
|
||||
def unique(pairs):
|
||||
result = {}
|
||||
for key, value in pairs:
|
||||
require(key not in result, 'Duplicate JSON key: ' + key)
|
||||
result[key] = value
|
||||
return result
|
||||
return json.loads(Path(path).read_text(encoding='utf-8'), object_pairs_hook=unique,
|
||||
parse_constant=lambda value: (_ for _ in ()).throw(ValueError('Nonfinite JSON: ' + value)))
|
||||
|
||||
|
||||
def save(path, value):
|
||||
path = Path(path)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with path.open('x', encoding='utf-8') as stream:
|
||||
json.dump(value, stream, sort_keys=True, indent=2, ensure_ascii=False, allow_nan=False)
|
||||
stream.write('\n')
|
||||
|
||||
|
||||
def load_module(name, path, expected):
|
||||
path = Path(path).resolve(strict=True)
|
||||
raw = path.read_bytes()
|
||||
require(hashlib.sha256(raw).hexdigest() == expected, 'Source pin mismatch: ' + str(path))
|
||||
identity = str(path), expected
|
||||
if name in sys.modules:
|
||||
require(_MODULE_CACHE.get(name) == (identity, sys.modules[name]), 'Unverified module namespace collision')
|
||||
return sys.modules[name]
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[name] = module
|
||||
try:
|
||||
exec(compile(raw, str(path), 'exec'), module.__dict__)
|
||||
except BaseException:
|
||||
sys.modules.pop(name, None)
|
||||
raise
|
||||
_MODULE_CACHE[name] = identity, module
|
||||
return module
|
||||
|
||||
|
||||
def source_inventory(model):
|
||||
model = Path(model).resolve(strict=True)
|
||||
result = {}
|
||||
for name in SOURCE_FILES:
|
||||
path = model / name
|
||||
require(path.is_file() and not path.is_symlink() and path.resolve().parent == model, 'Required engineering source missing/linked: ' + name)
|
||||
result[str(path)] = digest(path)
|
||||
return result
|
||||
|
||||
|
||||
def _relative(value):
|
||||
require(type(value) is str and value and '\\' not in value, 'Invalid relative inventory path')
|
||||
path = PurePosixPath(value)
|
||||
require(not path.is_absolute() and str(path) == value and all(part not in ('', '.', '..') and ':' not in part for part in path.parts),
|
||||
'Noncanonical relative inventory path')
|
||||
return path
|
||||
|
||||
|
||||
def _safe_file(root, relative):
|
||||
relative = _relative(relative)
|
||||
root = Path(root).resolve(strict=True)
|
||||
path = root
|
||||
for part in relative.parts:
|
||||
path = path / part
|
||||
require(not path.is_symlink(), 'Linked authority/input file')
|
||||
require(path.is_file() and path.resolve().is_relative_to(root), 'Missing or escaping input file')
|
||||
return path
|
||||
|
||||
|
||||
def _index(rows):
|
||||
require(type(rows) is list and rows, 'Missing sealed inventory rows')
|
||||
result = {}
|
||||
for row in rows:
|
||||
require(type(row) is dict and set(row) == {'path', 'bytes', 'sha256'} and
|
||||
type(row['bytes']) is int and row['bytes'] >= 0 and sha(row['sha256']), 'Invalid sealed inventory row')
|
||||
_relative(row['path'])
|
||||
require(row['path'] not in result, 'Duplicate sealed inventory row')
|
||||
result[row['path']] = row
|
||||
return result
|
||||
|
||||
|
||||
def _bind(inputs, path, expected=None):
|
||||
path = Path(path).resolve(strict=True)
|
||||
actual = digest(path)
|
||||
require(expected is None or actual == expected, 'Authority/input SHA mismatch: ' + str(path))
|
||||
require(str(path) not in inputs or inputs[str(path)] == actual, 'Conflicting authority for input')
|
||||
inputs[str(path)] = actual
|
||||
return path
|
||||
|
||||
|
||||
def _sealed_json(experiment, index, relative):
|
||||
require(relative in index, 'Required authority absent from AM seal: ' + relative)
|
||||
path = _safe_file(experiment, relative)
|
||||
require(digest(path) == index[relative]['sha256'], 'Sealed JSON changed: ' + relative)
|
||||
return read(path)
|
||||
|
||||
|
||||
def validate_final_status(final, scientific, before, after, refs):
|
||||
"""Check actual AM final/summary/report schemas, never infer scientific PASS."""
|
||||
require(final.get('status') == scientific.get('status') == FINAL_STATUS, 'AM final status is not the required completed interface gate')
|
||||
for key in ('zero_overlay_exact', 'static25_repeat_exact', 'old_integrity_pass', 'frozen_five_reference_pass',
|
||||
'frozen_eligibility_and_mask_unchanged', 'base_weights_unchanged'):
|
||||
require(final.get(key) is True, 'AM final gate failed/missing: ' + key)
|
||||
require(final.get('primary_trials_per_stage') == 120 and final.get('repeats_per_stage') == 24 and
|
||||
scientific.get('primary_trials_per_stage') == 120 and scientific.get('technical_repeats_per_stage') == 24,
|
||||
'AM complete coverage counts changed')
|
||||
for value in (final, scientific):
|
||||
require(value.get('training') is False and value.get('learned_candidate') is False, 'AM interface must not claim training/learning')
|
||||
require(scientific.get('zero_overlay_exact') is True and scientific.get('static25_repeat_exact') is True, 'Scientific summary gate mismatch')
|
||||
require(type(before) is dict and type(after) is dict and len(before) == len(after) == 28 and set(before) == set(after),
|
||||
'AM must retain the same complete 28-root old-integrity inventory')
|
||||
for name in before:
|
||||
for report in (before[name], after[name]):
|
||||
require(type(report) is dict and report.get('pass') is True, 'Old-integrity gate failed: ' + name)
|
||||
if 'mismatches' in report:
|
||||
require(report['mismatches'] == [], 'Old-integrity mismatch list is nonempty')
|
||||
for key in ('manifest_sha256', 'checksums_sha256'):
|
||||
if key in before[name] or key in after[name]:
|
||||
require(sha(before[name].get(key)) and before[name][key] == after[name].get(key), 'Old-root control changed: ' + name)
|
||||
require(refs.get('pass_all') is True and type(refs.get('trials')) is list and
|
||||
[row.get('trial') for row in refs['trials']] == list(REFERENCES), 'Five old deterministic references are incomplete or reordered')
|
||||
for row in refs['trials']:
|
||||
require(type(row.get('exact')) is dict and set(row['exact']) == {'step', 'neuron_idx', 'counts', 'v', 'g'} and
|
||||
all(value is True for value in row['exact'].values()), 'Old reference arrays are not exact')
|
||||
|
||||
|
||||
def validate_planned(planned):
|
||||
require(type(planned) is list and len(planned) == 144, 'AM planned coverage must be 144')
|
||||
keys = []
|
||||
for row in planned:
|
||||
require(type(row) is dict and type(row.get('name')) is str and re.fullmatch('[A-Za-z0-9_-]+', row['name']) and
|
||||
row.get('cohort') in ('seed_validation', 'novel_to_aj') and type(row.get('repeat')) is int and row['repeat'] in (0, 1),
|
||||
'Invalid AM planned row identity')
|
||||
keys.append((row['name'], row['repeat']))
|
||||
require(len(set(keys)) == 144 and sum(row['repeat'] == 0 for row in planned) == 120 and sum(row['repeat'] == 1 for row in planned) == 24,
|
||||
'AM primary/repeat set incomplete')
|
||||
require(all((name, 0) in set(keys) for name, repeat in keys if repeat == 1), 'AM repeat lacks primary')
|
||||
return next(dict(row) for row in planned if row['repeat'] == 0)
|
||||
|
||||
|
||||
def validate_stage(experiment, index, stage, planned, root):
|
||||
"""Validate all 144 declared results against the already verified AM seal."""
|
||||
receipt = _sealed_json(experiment, index, 'audit/' + stage + '-complete.json')
|
||||
require(receipt.get('stage') == stage and receipt.get('pass_all') is True and receipt.get('trials') == 144 and
|
||||
receipt.get('primary_trials') == 120 and receipt.get('technical_repeats') == 24 and receipt.get('training') is False,
|
||||
'AM stage is incomplete: ' + stage)
|
||||
completed = receipt.get('completed')
|
||||
require(type(completed) is list and len(completed) == 144, 'AM stage completion inventory truncated')
|
||||
for row, done in zip(planned, completed):
|
||||
require(done.get('name') == row['name'] and done.get('repeat') == row['repeat'], 'AM stage completion order/identity mismatch')
|
||||
prefix = 'gpu/' + stage + '/' + row['name'] + '-r' + str(row['repeat']) + '/'
|
||||
expected = [dict(path=key[len(prefix):], bytes=value['bytes'], sha256=value['sha256']) for key, value in sorted(index.items())
|
||||
if key.startswith(prefix) and key != prefix + 'output-hashes.json']
|
||||
inventory = _sealed_json(experiment, index, prefix + 'output-hashes.json')
|
||||
require(inventory == expected and inventory, 'AM completed output inventory differs from sealed payload')
|
||||
require(done.get('output_hashes_sha256') == index[prefix + 'output-hashes.json']['sha256'] and
|
||||
done.get('comparison_sha256') == index[prefix + 'comparison.json']['sha256'], 'AM stage receipt SHA mismatch')
|
||||
summary = _sealed_json(experiment, index, prefix + 'summary.json')
|
||||
compare = _sealed_json(experiment, index, prefix + 'comparison.json')
|
||||
folder, case = experiment / prefix.rstrip('/'), root / AK_REL / 'cases' / row['cohort'] / row['name']
|
||||
require(summary.get('case') == row['name'] and summary.get('complete') is True and summary.get('pass_all') is True and
|
||||
summary.get('all_finite') is True and summary.get('steps') == summary.get('completed_ticks') == 1000,
|
||||
'AM selected stage contains a failed/truncated trial')
|
||||
require(compare.get('pass_all') is True and compare.get('overlay_name') == stage and compare.get('case_dir') == str(case) and
|
||||
compare.get('right') == str(folder), 'AM comparison identity/gate mismatch')
|
||||
require(type(compare.get('checks')) is dict and compare['checks'] and all(value is True for value in compare['checks'].values()) and
|
||||
compare.get('failed_checks') == [] and 'error' not in compare and 'read_manifest_error' not in compare and
|
||||
compare.get('full_queue_reconstructed') is True, 'AM comparison has a hidden negative/missing numerical gate')
|
||||
mode = 'zero' if stage == 'zero' else ('repeat' if row['repeat'] else 'self')
|
||||
left = root / AK_REL / 'gpu' / row['cohort'] / (row['name'] + '-r' + str(row['repeat'])) if stage == 'zero' else (
|
||||
experiment / 'gpu' / stage / (row['name'] + '-r0') if row['repeat'] else folder)
|
||||
require(compare.get('mode') == mode and compare.get('left') == str(left), 'AM comparison reference/mode mismatch')
|
||||
return receipt
|
||||
|
||||
|
||||
def _layout(root, model, experiment):
|
||||
root, model = Path(root).resolve(strict=True), Path(model).resolve(strict=True)
|
||||
experiment = Path(experiment).resolve(strict=False)
|
||||
require(experiment == root / 'experiments/phase4an-engineering-validation' and model.is_relative_to(root / 'models/male-cns-lif'),
|
||||
'Unexpected project/output layout')
|
||||
require(not experiment.exists() or (experiment.is_dir() and not any(experiment.iterdir())), 'Refusing populated experiment; preserve all partials')
|
||||
return root, model, experiment
|
||||
|
||||
|
||||
def _budget(experiment):
|
||||
experiment = Path(experiment)
|
||||
used = sum(path.stat().st_size for path in experiment.rglob('*') if path.is_file()) if experiment.exists() else 0
|
||||
require(used < BUDGET['output_budget_bytes'], '8 GiB engineering budget exhausted; preserve partials')
|
||||
free = shutil.disk_usage(experiment.parent).free
|
||||
require(free >= BUDGET['minimum_free_reserve_bytes'] + BUDGET['output_budget_bytes'] - used,
|
||||
'Insufficient capacity for remaining 8 GiB budget plus 10 GiB free reserve')
|
||||
return dict(BUDGET, used_bytes=used, free_bytes=free)
|
||||
|
||||
|
||||
def budget():
|
||||
return _budget(EXP)
|
||||
|
||||
|
||||
def verify_authority(anchors):
|
||||
"""Full read-only AM seal recheck for the finalizer; no gate rerun."""
|
||||
require(sys.dont_write_bytecode, 'Use python -B for immutable authority verification')
|
||||
root = ROOT.resolve(strict=True)
|
||||
values = [(root / AM_MODEL_REL, 'am_model_manifest_sha256'), (root / AM_EXP_REL, 'am_experiment_manifest_sha256')]
|
||||
for directory, key in values:
|
||||
require(sha(anchors.get(key)) and digest(directory / 'manifest.json') == anchors[key], 'External AM manifest anchor mismatch')
|
||||
require(sha(anchors.get('am_final_sha256')) and digest(root / AM_EXP_REL / 'audit/final-status.json') == anchors['am_final_sha256'],
|
||||
'External AM final anchor mismatch')
|
||||
require(anchors.get('am_preregistration_sha256') == AM_PREREG_SHA and
|
||||
digest(root / AM_EXP_REL / 'protocol/preregistered.json') == AM_PREREG_SHA, 'AM preregistration anchor mismatch')
|
||||
require(anchors.get('AI_publication_helper_sha256') == AI_HELPER_SHA, 'Publication helper authority changed')
|
||||
publication = load_module('phase4an_pinned_AI_publication', root / AI_HELPER_REL, AI_HELPER_SHA)
|
||||
result = {'model': publication.verify_seal(values[0][0]), 'experiment': publication.verify_seal(values[1][0])}
|
||||
require(result['model']['manifest_sha256'] == anchors['am_model_manifest_sha256'] and
|
||||
result['experiment']['manifest_sha256'] == anchors['am_experiment_manifest_sha256'], 'AM seal changed during verification')
|
||||
return result
|
||||
|
||||
|
||||
def _old_integrity(root, context_sha, expected):
|
||||
"""Reuse the pinned AM read-only integrity chain, not a brain runtime."""
|
||||
context_path = root / AM_MODEL_REL / 'context.py'
|
||||
previous_path = list(sys.path)
|
||||
torch_before = 'torch' in sys.modules
|
||||
try:
|
||||
context = load_module('phase4an_sealed_AM_integrity', context_path, context_sha)
|
||||
require(context.ROOT.resolve() == root.resolve(), 'AM integrity helper belongs to another root')
|
||||
result = context.integrity()
|
||||
finally:
|
||||
sys.path[:] = previous_path
|
||||
require(torch_before or 'torch' not in sys.modules, 'Integrity unexpectedly imported Torch')
|
||||
require(type(result) is dict and len(result) == 28 and set(result) == set(expected) and
|
||||
all(type(row) is dict and row.get('pass') is True for row in result.values()), 'Current 28-root old-integrity gate failed')
|
||||
for name, row in result.items():
|
||||
if 'mismatches' in row:
|
||||
require(row['mismatches'] == [], 'Current old-integrity mismatch list is nonempty')
|
||||
for key in ('manifest_sha256', 'checksums_sha256', 'reference_sha256'):
|
||||
if key in expected[name] or key in row:
|
||||
require(sha(row.get(key)) and row[key] == expected[name].get(key), 'Old integrity authority changed: ' + name)
|
||||
return result
|
||||
|
||||
|
||||
def verify_old_integrity(registration):
|
||||
"""Rehash the pinned 28 old roots/snapshot via original CPU-only code."""
|
||||
require(sys.dont_write_bytecode, 'Use python -B for old-integrity verification')
|
||||
path = ROOT / AM_MODEL_REL / 'context.py'
|
||||
expected_sha = registration['input_sha256'].get(str(path.resolve(strict=True)))
|
||||
require(sha(expected_sha) and digest(path) == expected_sha, 'AM integrity source is not a registered input')
|
||||
before_path = EXP / 'regression/old-integrity-before.json'
|
||||
require(digest(before_path) == registration['input_sha256'].get(str(before_path.resolve(strict=True))), 'Before-integrity evidence changed')
|
||||
return _old_integrity(ROOT, expected_sha, read(before_path))
|
||||
|
||||
|
||||
def _prepare(root, model, experiment, am_model_manifest_sha, am_experiment_manifest_sha, am_final_sha):
|
||||
require(sys.dont_write_bytecode, 'Preparation requires python -B; old sources must remain byte-for-byte read-only')
|
||||
require(all(sha(value) for value in (am_model_manifest_sha, am_experiment_manifest_sha, am_final_sha)), 'Three actual external AM SHA-256 anchors are mandatory')
|
||||
root, model, experiment = _layout(root, model, experiment)
|
||||
_budget(experiment)
|
||||
sources = source_inventory(model)
|
||||
inputs_module = load_module('phase4an_prepare_inputs', model / 'engineering_inputs.py', sources[str(model / 'engineering_inputs.py')])
|
||||
plan_module = load_module('phase4an_prepare_plan', model / 'engineering_plan.py', sources[str(model / 'engineering_plan.py')])
|
||||
runtime = load_module('phase4an_prepare_light', model / 'runtime_light.py', sources[str(model / 'runtime_light.py')])
|
||||
publication = load_module('phase4an_pinned_AI_publication', root / AI_HELPER_REL, AI_HELPER_SHA)
|
||||
am_model, am = root / AM_MODEL_REL, root / AM_EXP_REL
|
||||
require(digest(am_model / 'manifest.json') == am_model_manifest_sha and digest(am / 'manifest.json') == am_experiment_manifest_sha and
|
||||
digest(am / 'audit/final-status.json') == am_final_sha, 'External AM authority mismatch')
|
||||
verified = {'model': publication.verify_seal(am_model), 'experiment': publication.verify_seal(am)}
|
||||
require(verified['model']['manifest_sha256'] == am_model_manifest_sha and verified['experiment']['manifest_sha256'] == am_experiment_manifest_sha,
|
||||
'AM controls changed during full seal verification')
|
||||
idx = _index(verified['experiment']['files']); midx = _index(verified['model']['files'])
|
||||
reg = _sealed_json(am, idx, 'protocol/preregistered.json')
|
||||
require(idx['protocol/preregistered.json']['sha256'] == AM_PREREG_SHA, 'AM preregistration authority differs from the fixed anchor')
|
||||
planned = reg.get('planned'); selected = validate_planned(planned)
|
||||
final = _sealed_json(am, idx, 'audit/final-status.json')
|
||||
scientific = _sealed_json(am, idx, 'audit/scientific-summary.json')
|
||||
require(final.get('scientific_summary_sha256') == idx['audit/scientific-summary.json']['sha256'], 'AM final does not bind scientific summary')
|
||||
before = _sealed_json(am, idx, 'regression/old-integrity-before.json')
|
||||
after = _sealed_json(am, idx, 'regression/old-integrity-after.json')
|
||||
refs = _sealed_json(am, idx, 'regression/frozen-five.json')
|
||||
validate_final_status(final, scientific, before, after, refs)
|
||||
for stage in ('zero', 'static25'):
|
||||
validate_stage(am, idx, stage, planned, root)
|
||||
current_old = _old_integrity(root, midx['context.py']['sha256'], after)
|
||||
inputs = {}
|
||||
anchors = {'am_model_manifest_sha256': am_model_manifest_sha, 'am_experiment_manifest_sha256': am_experiment_manifest_sha,
|
||||
'am_final_sha256': am_final_sha, 'am_preregistration_sha256': AM_PREREG_SHA, 'AI_publication_helper_sha256': AI_HELPER_SHA}
|
||||
for path, expected in ((am_model / 'manifest.json', am_model_manifest_sha), (am / 'manifest.json', am_experiment_manifest_sha),
|
||||
(am / 'audit/final-status.json', am_final_sha), (root / AI_HELPER_REL, AI_HELPER_SHA),
|
||||
(am_model / 'overlay.py', AM_OVERLAY_SHA)):
|
||||
_bind(inputs, path, expected)
|
||||
require(midx.get('overlay.py', {}).get('sha256') == AM_OVERLAY_SHA, 'AM model seal does not contain the pinned overlay helper')
|
||||
for name in ('context.py', 'config.json'):
|
||||
_bind(inputs, am_model / name, midx[name]['sha256'])
|
||||
for path in (am_model / 'checksums.sha256', am / 'checksums.sha256'):
|
||||
_bind(inputs, path)
|
||||
for relative in ('protocol/preregistered.json', 'protocol/prepared-hashes.json', 'audit/scientific-summary.json', 'audit/zero-complete.json', 'audit/static25-complete.json',
|
||||
'regression/old-integrity-before.json', 'regression/old-integrity-after.json', 'regression/frozen-five.json'):
|
||||
_bind(inputs, am / relative, idx[relative]['sha256'])
|
||||
am_inputs = {}
|
||||
for row in reg['inputs']:
|
||||
require(set(row) == {'path', 'sha256'} and sha(row['sha256']), 'Invalid AM registered input row')
|
||||
_relative(row['path']); require(row['path'] not in am_inputs, 'Duplicate AM registered input')
|
||||
am_inputs[row['path']] = row['sha256']
|
||||
def authority(path):
|
||||
path = Path(path).resolve(strict=True)
|
||||
require(path.is_relative_to(root), 'AM input leaves project root')
|
||||
relative = path.relative_to(root).as_posix()
|
||||
if path.is_relative_to(am):
|
||||
expected = idx[path.relative_to(am).as_posix()]['sha256']
|
||||
else:
|
||||
require(relative in am_inputs, 'Dependency absent from AM preregistration: ' + relative)
|
||||
expected = am_inputs[relative]
|
||||
return _bind(inputs, path, expected)
|
||||
original_dir = root / AK_REL / 'cases' / selected['cohort'] / selected['name']
|
||||
authority(original_dir / 'spec.json'); authority(original_dir / 'requests.npz')
|
||||
original = read(original_dir / 'spec.json')
|
||||
require(original['steps'] == 1000 and original['kind'] == 'fullgraph' and original['name'] == selected['name'], 'Fixed selected AM case changed')
|
||||
with np.load(original_dir / 'requests.npz', allow_pickle=False) as archive:
|
||||
require(set(archive.files) == {'tick', 'neuron_idx'} and len(archive.files) == 2, 'Unexpected original request arrays')
|
||||
requests = {key: archive[key].copy() for key in archive.files}
|
||||
inputs_module.validate_requests(requests, 1000)
|
||||
for name in GRAPH_FILES:
|
||||
authority(Path(original['graph_dir']) / name)
|
||||
for name in APL_FILES:
|
||||
authority(Path(original['apl_bundle_dir']) / name)
|
||||
authority(original['coefficients_path'])
|
||||
selected_outputs = {}
|
||||
for stage in ('zero', 'static25'):
|
||||
prefix = 'gpu/' + stage + '/' + selected['name'] + '-r0/'
|
||||
selected_outputs[stage] = str(am / prefix.rstrip('/'))
|
||||
for relative, row in idx.items():
|
||||
if relative.startswith(prefix):
|
||||
_bind(inputs, am / relative, row['sha256'])
|
||||
adapted = {}
|
||||
for stage in ('zero', 'static25'):
|
||||
old_manifest = authority(am / 'overlays' / (stage + '.json'))
|
||||
overlay_file = authority(am / 'overlays' / (stage + '.npz'))
|
||||
declaration = read(old_manifest)
|
||||
require(declaration['overlay_name'] == stage and declaration['overlay_sha256'] == digest(overlay_file), 'AM overlay declaration mismatch')
|
||||
for label in ('mask', 'base_candidate'):
|
||||
require(digest(authority(declaration[label + '_path'])) == declaration[label + '_sha256'], 'Overlay external array authority mismatch')
|
||||
adapted[stage] = inputs_module.adapt_overlay_manifest(declaration, str(am_model / 'overlay.py'))
|
||||
require(adapted[stage]['am_overlay_sha256'] == AM_OVERLAY_SHA, 'Adapted helper pin differs')
|
||||
for relative, expected in runtime.PINS.items():
|
||||
_bind(inputs, model.parent / relative, expected)
|
||||
plan = plan_module.validate_plan(plan_module.build_plan())
|
||||
require(plan['total_calls'] == 32 and plan['total_ticks'] == 11074, 'Engineering plan matrix changed')
|
||||
# No writes have occurred before all authority/source/space checks above.
|
||||
experiment.mkdir(parents=True, exist_ok=True)
|
||||
try:
|
||||
for folder in ('protocol', 'cases', 'overlays', 'audit', 'runs', 'receipts', 'logs', 'regression', 'extension-build'):
|
||||
(experiment / folder).mkdir(exist_ok=False)
|
||||
save(experiment / 'regression/old-integrity-before.json', current_old)
|
||||
_bind(inputs, experiment / 'regression/old-integrity-before.json')
|
||||
for source, expected in sources.items():
|
||||
target = experiment / 'protocol/source-snapshot' / Path(source).name
|
||||
publication._copy_verified(Path(source), target, Path(source).stat().st_size, expected)
|
||||
_bind(inputs, target, expected)
|
||||
save(experiment / 'protocol/AM-seal-verification.json', verified)
|
||||
save(experiment / 'protocol/AM-gate-authority.json', dict(anchors=anchors, all_144_per_stage_declared_gates_pass=True,
|
||||
old_roots=28, old_references=list(REFERENCES), verification_scope='Full AM seals and finalized declared gate evidence; no new scientific execution'))
|
||||
for name in ('AM-seal-verification.json', 'AM-gate-authority.json'):
|
||||
_bind(inputs, experiment / 'protocol' / name)
|
||||
overlays, cases = {}, {}
|
||||
for stage, value in adapted.items():
|
||||
manifest_path = experiment / 'overlays' / (stage + '.json')
|
||||
save(manifest_path, value)
|
||||
_bind(inputs, manifest_path)
|
||||
overlays[stage] = {'overlay_file': str(am / 'overlays' / (stage + '.npz')), 'overlay_manifest': str(manifest_path)}
|
||||
for row in plan['rows']:
|
||||
case = experiment / 'cases' / row['row_id']; case.mkdir(exist_ok=False)
|
||||
template = {'schema': 'phase4an-engineering-case-template-v1', 'row_id': row['row_id'], 'original_spec': original,
|
||||
'start': row['start'], 'stop': row['stop'], 'budget_root': str(experiment), 'output_budget_bytes': BUDGET['output_budget_bytes']}
|
||||
save(case / 'template-spec.json', template)
|
||||
part = inputs_module.slice_requests(requests, 1000, row['start'], row['stop'])
|
||||
with (case / 'requests.npz').open('xb') as stream:
|
||||
np.savez_compressed(stream, **part)
|
||||
_bind(inputs, case / 'template-spec.json'); _bind(inputs, case / 'requests.npz')
|
||||
cases[row['row_id']] = {'case_dir': str(case), 'template_spec_sha256': digest(case / 'template-spec.json'),
|
||||
'requests_sha256': digest(case / 'requests.npz')}
|
||||
require(source_inventory(model) == sources and all(digest(path) == expected for path, expected in inputs.items()), 'Source/input changed during preparation')
|
||||
# Repeat the full AM verification after producing only small new inputs.
|
||||
after_verified = {'model': publication.verify_seal(am_model), 'experiment': publication.verify_seal(am)}
|
||||
require(after_verified == verified, 'AM sealed payload changed during preparation')
|
||||
_budget(experiment)
|
||||
registration = {'schema': REG_SCHEMA, 'plan': plan, 'selected_case': {'row': selected, 'original_dir': str(original_dir), 'AM_outputs': selected_outputs},
|
||||
'source_sha256': sources, 'input_sha256': inputs, 'cases': cases, 'overlays': overlays, 'budget': dict(BUDGET), 'anchors': anchors,
|
||||
'training': False, 'new_brain_runs_during_preparation': 0, 'future_checkpoint_hashes_fabricated': False,
|
||||
'runtime_spec_derivation': 'engineering_inputs.derive_spec(template.original_spec, interval, budget, actual verified parent SHA or None)',
|
||||
'scope': 'Fixed 32-call engineering gate only; not conditioning or learning registration'}
|
||||
save(experiment / 'protocol/preregistered.json', registration)
|
||||
return {'prepared': True, 'registration_path': str(experiment / 'protocol/preregistered.json'),
|
||||
'registration_sha256': digest(experiment / 'protocol/preregistered.json'), 'calls': 32, 'brain_runs': 0}
|
||||
except Exception as error:
|
||||
failure = experiment / 'audit/preparation-failure.json'
|
||||
if not failure.exists():
|
||||
save(failure, {'prepared': False, 'type': type(error).__name__, 'message': str(error), 'partial_outputs_preserved': True})
|
||||
raise
|
||||
|
||||
|
||||
def prepare(am_model_manifest_sha, am_experiment_manifest_sha, am_final_sha):
|
||||
return _prepare(ROOT, MODEL, EXP, am_model_manifest_sha, am_experiment_manifest_sha, am_final_sha)
|
||||
|
||||
|
||||
def row_path(registration, row_id):
|
||||
require(type(row_id) is str and row_id in {row['row_id'] for row in registration['plan']['rows']}, 'Unknown engineering row ID')
|
||||
expected = EXP.resolve(strict=True) / 'cases' / row_id
|
||||
actual = Path(registration['cases'][row_id]['case_dir'])
|
||||
require(actual == expected and actual.resolve(strict=True) == expected and actual.is_dir(), 'Row case path is not its fixed safe mapping')
|
||||
return actual
|
||||
|
||||
|
||||
def read_registration(expected_sha):
|
||||
require(sys.dont_write_bytecode and sha(expected_sha), 'Require python -B and an external preregistration SHA')
|
||||
path = EXP / 'protocol/preregistered.json'
|
||||
require(digest(path) == expected_sha, 'Preregistration differs from external authority')
|
||||
require(not (EXP / 'audit/preparation-failure.json').exists(), 'Preparation failed; preserve partials')
|
||||
registration = read(path)
|
||||
require(registration.get('schema') == REG_SCHEMA and registration.get('training') is False and registration.get('budget') == BUDGET,
|
||||
'Unexpected registration schema/scope/budget')
|
||||
require(registration['source_sha256'] == source_inventory(MODEL), 'Registered engineering sources changed or inventory incomplete')
|
||||
for name, expected in registration['input_sha256'].items():
|
||||
require(Path(name).is_absolute() and sha(expected) and digest(name) == expected, 'Registered input changed: ' + name)
|
||||
plan_module = load_module('phase4an_read_plan', MODEL / 'engineering_plan.py', registration['source_sha256'][str(MODEL / 'engineering_plan.py')])
|
||||
inputs_module = load_module('phase4an_read_inputs', MODEL / 'engineering_inputs.py', registration['source_sha256'][str(MODEL / 'engineering_inputs.py')])
|
||||
plan_module.validate_plan(registration['plan'])
|
||||
selected = validate_planned(read(ROOT / AM_EXP_REL / 'protocol/preregistered.json')['planned'])
|
||||
original_dir = ROOT / AK_REL / 'cases' / selected['cohort'] / selected['name']
|
||||
require(registration['selected_case']['row'] == selected and registration['selected_case']['original_dir'] == str(original_dir),
|
||||
'Registered case is not the fixed first AM planned primary')
|
||||
original = read(original_dir / 'spec.json')
|
||||
with np.load(original_dir / 'requests.npz', allow_pickle=False) as archive:
|
||||
require(set(archive.files) == {'tick', 'neuron_idx'} and len(archive.files) == 2, 'Unexpected original sparse request schema')
|
||||
requests = {key: archive[key].copy() for key in archive.files}
|
||||
inputs_module.validate_requests(requests, 1000)
|
||||
require(set(registration['cases']) == {row['row_id'] for row in registration['plan']['rows']}, 'Registered case inventory differs from plan')
|
||||
for row in registration['plan']['rows']:
|
||||
case = row_path(registration, row['row_id']); entry = registration['cases'][row['row_id']]
|
||||
require(entry['template_spec_sha256'] == digest(case / 'template-spec.json') and entry['requests_sha256'] == digest(case / 'requests.npz'),
|
||||
'Template/request metadata does not bind registered bytes')
|
||||
template = read(case / 'template-spec.json')
|
||||
require(set(template) == {'schema', 'row_id', 'original_spec', 'start', 'stop', 'budget_root', 'output_budget_bytes'} and
|
||||
template['schema'] == 'phase4an-engineering-case-template-v1' and template['original_spec'] == original and
|
||||
template['row_id'] == row['row_id'] and template['start'] == row['start'] and template['stop'] == row['stop'] and
|
||||
template['budget_root'] == str(EXP) and template['output_budget_bytes'] == BUDGET['output_budget_bytes'], 'Template rule differs from canonical row')
|
||||
expected = inputs_module.slice_requests(requests, 1000, row['start'], row['stop'])
|
||||
with np.load(case / 'requests.npz', allow_pickle=False) as archive:
|
||||
require(len(archive.files) == 2 and set(archive.files) == set(expected) and
|
||||
all(archive[key].dtype == expected[key].dtype and archive[key].shape == expected[key].shape and
|
||||
archive[key].tobytes() == expected[key].tobytes() for key in expected), 'Registered request slice is not exact')
|
||||
require(set(registration['overlays']) == {'zero', 'static25'}, 'Registered overlay inventory changed')
|
||||
for stage in ('zero', 'static25'):
|
||||
entry = registration['overlays'][stage]
|
||||
expected_file = ROOT / AM_EXP_REL / 'overlays' / (stage + '.npz')
|
||||
expected_manifest = EXP / 'overlays' / (stage + '.json')
|
||||
require(entry == {'overlay_file': str(expected_file), 'overlay_manifest': str(expected_manifest)}, 'Overlay path mapping changed')
|
||||
original_overlay = read(ROOT / AM_EXP_REL / 'overlays' / (stage + '.json'))
|
||||
require(read(expected_manifest) == inputs_module.adapt_overlay_manifest(original_overlay, str(ROOT / AM_MODEL_REL / 'overlay.py')),
|
||||
'Adapted overlay manifest is not the exact declared transformation')
|
||||
anchors = registration['anchors']
|
||||
for name, relative in (('am_model_manifest_sha256', AM_MODEL_REL / 'manifest.json'),
|
||||
('am_experiment_manifest_sha256', AM_EXP_REL / 'manifest.json'),
|
||||
('am_final_sha256', AM_EXP_REL / 'audit/final-status.json')):
|
||||
require(sha(anchors[name]) and digest(ROOT / relative) == anchors[name], 'AM external anchor changed')
|
||||
require(anchors['am_preregistration_sha256'] == AM_PREREG_SHA and digest(ROOT / AM_EXP_REL / 'protocol/preregistered.json') == AM_PREREG_SHA and
|
||||
anchors['AI_publication_helper_sha256'] == AI_HELPER_SHA and digest(ROOT / AI_HELPER_REL) == AI_HELPER_SHA, 'Fixed AM/helper authority changed')
|
||||
require(digest(path) == expected_sha, 'Registration changed during verification')
|
||||
return registration
|
||||
Reference in New Issue
Block a user