"""Phase 4AK orchestration only: call hash-pinned, unchanged AJ functions. No LIF, APL, queue, or input-generation equation is implemented here. Fresh processes resolve AJ's one unqualified import to the pinned AJ timing_queue. The D18 control adapter compares evidence; it never changes a case to D0. """ from __future__ import annotations import hashlib import importlib.util import json from pathlib import Path import re import sys import traceback AJ_PINS = { 'gpu_timing.py': 'af1975ba5e5e700208c0a598d3cbe648a3cf10885c6231239b6e4b28f76d173b', 'oracle_timing.py': '39d8c47c94bc2faddf016864a43e7715907fb987f56f7b27098060a88851f6be', 'compare_timing.py': '9010f070fba9b7c9cf2d86187ee2abc57f3ac606ebc08a42baad4f3922b51734', 'timing_queue.py': '54df6d4818e2a0c26e96fb0aaf5d9b71cd964981706da937562d48af79aca14e', } COMPONENT_SHA256 = '5cfc9a4505da6b8fb4e7d2617cdaedaa8475ed46a11ec9bdf0d4d25b1d22c2d3' COHORTS = ('seed_validation', 'novel_to_aj') GROUPS = ('controls',) + COHORTS STAGES = ('controls', 'seed-probes', 'seed-validation', 'novel-probes', 'novel-validation') _BUDGET_KEYS = frozenset(('budget_root', 'output_budget_bytes')) _LOADED = {} def require(ok, message): if not ok: raise ValueError(message) def read(path): return json.loads(Path(path).read_text(encoding='utf-8')) def digest(path): result = hashlib.sha256() with Path(path).open('rb') as stream: for block in iter(lambda: stream.read(8 << 20), b''): result.update(block) return result.hexdigest() def save(path, report): if path is None: return path = Path(path) path.parent.mkdir(parents=True, exist_ok=True) with path.open('x', encoding='utf-8') as stream: json.dump(report, stream, ensure_ascii=False, indent=2, sort_keys=True, allow_nan=False) stream.write('\n') def load_checked(name, path, expected_hash): """Execute the verified bytes with importlib metadata, without old pyc writes.""" path = Path(path).absolute() data = path.read_bytes() require(hashlib.sha256(data).hexdigest() == expected_hash, 'Sealed source SHA mismatch: ' + str(path)) if name in sys.modules: existing = sys.modules[name] require(Path(getattr(existing, '__file__', '')).absolute() == path and _LOADED.get(name) == (path, expected_hash, existing), 'Refusing an existing unverified module name: ' + name) return existing spec = importlib.util.spec_from_file_location(name, path) require(spec is not None and spec.loader is not None, 'Cannot create sealed module specification') module = importlib.util.module_from_spec(spec) sys.modules[name] = module try: exec(compile(data, str(path), 'exec'), module.__dict__) except BaseException: sys.modules.pop(name, None) raise # Keep verification metadata here, not in the sealed module's globals. _LOADED[name] = (path, expected_hash, module) return module def sealed(filename, aj_model=None): require(filename in AJ_PINS, 'Unregistered AJ module') if aj_model is None: from context import AJ_MODEL aj_model = AJ_MODEL directory = Path(aj_model) if filename == 'gpu_timing.py': load_checked('timing_queue', directory / 'timing_queue.py', AJ_PINS['timing_queue.py']) name = 'phase4ak_sealed_aj_' + Path(filename).stem return load_checked(name, directory / filename, AJ_PINS[filename]) def case_spec(case_dir): spec = read(Path(case_dir) / 'spec.json') require(isinstance(spec.get('name'), str) and re.fullmatch(r'[A-Za-z0-9_-]+', spec['name']), 'Unsafe case name') require(type(spec.get('apl_delay_steps')) is int and spec['apl_delay_steps'] == 18, 'AK permits fixed D18 only') require(spec.get('steps') == 1000 and type(spec['steps']) is int and spec.get('dt_ms') == .1 and spec.get('duration_ms') == 100.0 and spec.get('record_history') is False, 'AK requires the unchanged fullgraph 100 ms window') require(spec.get('training') is False and spec.get('poi_used', False) is False and spec.get('precision') == 'float64' and spec.get('apl_precision') == 'float32', 'Frozen no-learning / no-poi / precision scope changed') require(spec.get('apl') == {'tau_ms': 20, 'gain': .01, 'normalization': 1}, 'Frozen APL parameters changed') for field in ('graph_dir', 'apl_bundle_dir', 'coefficients_path', 'science_python', 'legacy_apl_worker'): require(field in spec and Path(spec[field]).is_absolute(), 'Missing absolute path: ' + field) require(isinstance(spec.get('model'), dict), 'Missing original author parameter dictionary') return spec def _new_output(case_dir, out_dir): spec = case_spec(case_dir) output = Path(out_dir).absolute() budget_root = Path(spec['budget_root']).absolute() require(type(spec.get('output_budget_bytes')) is int and spec['output_budget_bytes'] > 0, 'Missing positive output budget') require(output.is_relative_to(budget_root) and output != budget_root and not output.exists(), 'Output must be a new exclusive child of this case budget root') return spec def _receipt(output, backend, filename, aj_model): module = sealed(filename, aj_model) require(digest(module.__file__) == AJ_PINS[filename], 'Sealed runtime changed during invocation') receipt = {'backend': backend, 'sealed_runtime': str(module.__file__), 'sealed_runtime_sha256': AJ_PINS[filename], 'apl_delay_steps': 18, 'old_module_scientific_globals_changed': False, 'dynamics_copied_or_reimplemented': False, 'scope': 'Unchanged AJ runtime called with new case/output/build arguments; not a new numerical model'} save(Path(output) / 'runtime-bridge.json', receipt) def run_gpu(case_dir, out_dir, build_root, *, aj_model=None): spec = _new_output(case_dir, out_dir) build_root = Path(build_root).absolute() require(build_root.is_relative_to(Path(spec['budget_root']).absolute()) and build_root != Path(spec['budget_root']).absolute(), 'Build cache must stay in the new experiment') module = sealed('gpu_timing.py', aj_model) result = module.run_gpu(Path(case_dir), Path(out_dir), build_root) _receipt(out_dir, 'gpu', 'gpu_timing.py', aj_model) return result def run_oracle(case_dir, out_dir, *, aj_model=None): _new_output(case_dir, out_dir) require('torch' not in sys.modules, 'Pinned Brian process must not import Torch') module = sealed('oracle_timing.py', aj_model) result = module.run_case(Path(case_dir), Path(out_dir)) _receipt(out_dir, 'oracle', 'oracle_timing.py', aj_model) return result def compare(case_dir, gpu_dir, cpu_dir, out_json=None, *, aj_model=None): case_spec(case_dir) return sealed('compare_timing.py', aj_model).compare(case_dir, gpu_dir, cpu_dir, out_json) def compare_gpu_repeats(first, second, out_json=None, *, aj_model=None): require(all(read(Path(path) / 'summary.json').get('apl_delay_steps') == 18 for path in (first, second)), 'Repeat evidence must be fixed D18') return sealed('compare_timing.py', aj_model).compare_gpu_repeats(first, second, out_json) def component(output, bundle, out_json, *, ag_model=None): if ag_model is None: from context import AG_MODEL ag_model = AG_MODEL module = load_checked('phase4ak_sealed_ag_components', Path(ag_model) / 'validate_components.py', COMPONENT_SHA256) return module.validate(Path(output), Path(bundle), Path(out_json)) def control_inputs(newcase, oldcase): """Only budget metadata may differ; all saved request/drive bytes are identical.""" newcase, oldcase = Path(newcase), Path(oldcase) fresh, old = case_spec(newcase), case_spec(oldcase) require({k: v for k, v in fresh.items() if k not in _BUDGET_KEYS} == {k: v for k, v in old.items() if k not in _BUDGET_KEYS}, 'D18 control scientific specification changed beyond the two budget fields') evidence = {str(folder / 'spec.json'): digest(folder / 'spec.json') for folder in (newcase, oldcase)} files = ['requests.npz', 'pn-drive.npz'] require((newcase / 'arrays.npz').exists() == (oldcase / 'arrays.npz').exists(), 'Control initial-state presence changed') if (newcase / 'arrays.npz').exists(): files.append('arrays.npz') for name in files: first, second = digest(newcase / name), digest(oldcase / name) require(first == second, 'Control input byte mismatch: ' + name) evidence[str(newcase / name)], evidence[str(oldcase / name)] = first, second return fresh, old, evidence def control_identity(newdir, olddir, newcase, oldcase, out_json=None, *, aj_model=None): """D18 scientific identity using unchanged AJ comparison primitives. Unlike the AJ D0-vs-AG adapter, this checks real D18 applied/pending files. Input SHA fields are checked against each real spec, not compared after deleting metadata. Only build/cache paths and the two budget fields are permitted to differ; all numerical/timing/component evidence stays exact. """ require(out_json is None or not Path(out_json).exists(), 'Refusing existing control report') aj = sealed('compare_timing.py', aj_model) checks, differences, used = {}, {}, set() report = {'kind': 'fixed-D18 same-input sealed-AJ control identity', 'pass_all': False, 'new_output': str(newdir), 'old_output': str(olddir), 'new_case': str(newcase), 'old_case': str(oldcase), 'apl_delay_steps': 18, 'tolerance': None, 'signed_zero_included': True, 'dynamics_copied_or_reimplemented': False, 'old_globals_patched': False, 'independent_CPU_all_components_claim': False, 'allowed_case_metadata_differences': sorted(_BUDGET_KEYS), 'build_and_wall_metadata_compared': False} try: spec, old_spec, inputs = control_inputs(newcase, oldcase) used.update(Path(path) for path in inputs) checks['control_input_spec_and_bytes'] = True steps, n, edges = spec['steps'], 166700, 25582938 left = aj._load_run(Path(newdir), 'fresh_gpu', n, edges, steps, False, spec['name'], 18, used, checks) right = aj._load_run(Path(olddir), 'sealed_gpu', n, edges, steps, False, spec['name'], 18, used, checks) aj._pair_outputs(left, right, checks, differences, n, steps, False) for role, run, case, case_input in (('fresh_gpu', left, Path(newcase), spec), ('sealed_gpu', right, Path(oldcase), old_spec)): aj._timing_sequence_checks(run['apl']['inhibition'], run['apl']['applied'], run['pending'], 18, role, checks, differences) aj._timing_metadata_checks(run, role, 18, steps, checks) expected = {name: digest(case / name) for name in ('spec.json', 'requests.npz')} if (case / 'arrays.npz').exists(): expected['arrays.npz'] = digest(case / 'arrays.npz') weight, coeff = Path(case_input['graph_dir']) / 'weights.npy', Path(case_input['coefficients_path']) used.update((weight, coeff)) expected.update({'base_weights.npy': digest(weight), 'coefficients.json': digest(coeff)}) checks[role + '.actual_input_hashes'] = run['summary'].get('input_sha256') == expected stable = ('case', 'complete', 'pass_all', 'steps', 'completed_ticks', 'nodes', 'edges', 'precision', 'apl_precision', 'state_units', 'total_spikes', 'requested', 'accepted_requests', 'rejected_requests', 'arriving_edges', 'accepted_arriving_edges', 'counts_match_sparse_events', 'all_finite', 'record_history', 'apl_fast_edges_zeroed', 'kc_targets_present', 'effective_weights_raw_sha256', 'apl_schedule', 'apl_si_conversion', 'sealed_ae_backend_sha256', 'learning', 'apl_delay_steps', 'apl_timing', 'source_provenance', 'pending_generated_count', 'pending_initial_zero_prefix', 'pending_tail_shape', 'future_generated_decay_tail_included') for key in stable: checks['summary.' + key] = key in left['summary'] and key in right['summary'] and left['summary'][key] == right['summary'][key] checks['timing_audit.parsed_exact'] = left['timing'] == right['timing'] report.update(case=spec['name'], steps=steps, nodes=n, edges=edges, scope='Six state arrays, every tick state/queue/request, generated/applied/pending APL, component and timing metadata') except Exception as error: report['error'] = {'type': type(error).__name__, 'message': str(error)} return aj._finish(report, checks, differences, used, out_json) def inventories(exp, cfg): """Validate static cells before choosing any dynamic case; no old dynamic read.""" exp = Path(exp) controls = read(exp / 'protocol/control-cases.json') records = read(exp / 'protocol/validation-cases.json') require(isinstance(controls, list) and len(controls) == 2 and [row['name'] for row in controls] == cfg['control_cases'], 'Unexpected control inventory') for row in controls: require(re.fullmatch(r'[A-Za-z0-9_-]+', row['name']) and all(Path(row[key]).is_absolute() for key in ('source_case', 'source_output')), 'Invalid control identity/path') design = cfg['validation'] require(design['cohorts'] == list(COHORTS) and len(design['seeds']) == 5 and len(set(design['seeds'])) == 5 and design['modes'] == ['physiological', 'matched_energy'] and design['odor_count'] == 6 and design['repeat_first_seed_each_odor'] is True, 'Fixed validation design changed') require(isinstance(records, list) and len(records) == 120, 'Expected 120 predeclared cases') cells, names = set(), set() for row in records: key = (row['cohort'], row['mode'], row['odor_index'], row['seed']) require(re.fullmatch(r'[A-Za-z0-9_-]+', row['name']) and row['name'] not in names and key not in cells, 'Duplicate/unsafe case identity') require(type(row['odor_index']) is int and type(row['seed']) is int, 'Noninteger odor/seed') cells.add(key); names.add(row['name']) expected = {(cohort, mode, oi, seed) for cohort in COHORTS for mode in design['modes'] for oi in range(6) for seed in design['seeds']} require(cells == expected, 'Missing or substituted validation cell') return controls, records def cohort_rows(records, cfg, cohort): require(cohort in COHORTS, 'Unknown validation cohort') design = cfg['validation'] return sorted((row for row in records if row['cohort'] == cohort), key=lambda row: (design['modes'].index(row['mode']), row['odor_index'], design['seeds'].index(row['seed']))) def probe_rows(records, cfg, cohort): rows = [row for row in cohort_rows(records, cfg, cohort) if row['odor_index'] == 0 and row['seed'] == cfg['validation']['seeds'][0]] require(len(rows) == 2 and [row['mode'] for row in rows] == cfg['validation']['modes'], 'Fixed probe selection changed') return rows def ensure_unstopped(exp): markers = sorted((Path(exp) / 'audit').glob('stopped-*.json')) require(not markers, 'A prior stop marker forbids all new scientific actions: ' + ', '.join(path.name for path in markers)) def guarded_action(exp, backend, group, name, repeat, action): """Persist failure after invoking an authorized scientific action, even standalone. The caller performs input authorization and external idle preflight first. A failure here is an execution/audit failure, not evidence of instability. """ require(backend in ('gpu', 'oracle', 'component') and group in GROUPS and isinstance(name, str) and re.fullmatch(r'[A-Za-z0-9_-]+', name) and type(repeat) is int and repeat in (0, 1), 'Invalid guarded action identity') ensure_unstopped(exp) try: return action() except Exception as error: save(Path(exp) / 'audit' / f'stopped-case-{backend}-{group}-{name}-r{repeat}.json', { 'stage': 'case', 'backend': backend, 'group': group, 'case': name, 'repeat': repeat, 'type': type(error).__name__, 'message': str(error), 'traceback': traceback.format_exc(), 'stop_kind': 'execution_or_numerical_failure', 'scientific_action_invoked': True, 'numerical_instability_inferred_from_execution_error': False, 'later_stages_not_permitted': True, 'partial_outputs_preserved': True, 'parameters_changed': False, 'replacement_seeds_or_odors_selected': False}) raise def validate_summary_coverage(summary, exp, cfg, cohort, *, representation=False): """Bind the unlock to this cohort's exact 60 base / 12 repeat / 72 timing cells.""" _, records = inventories(exp, cfg) rows = cohort_rows(records, cfg, cohort) expected = {row['name'] for row in rows} repeated = {row['name'] for row in rows if row['seed'] == cfg['validation']['seeds'][0]} timing = {(name, 0) for name in expected} | {(name, 1) for name in repeated} require(summary.get('cohort') == cohort and summary.get('phase') == '4AK' and summary.get('apl_delay_steps') == 18 and summary.get('seeds') == cfg['validation']['seeds'] and summary.get('duration_ms') == 100.0, 'Validation summary identity/window differs') require(summary.get('expected_base_trials') == summary.get('valid_base_trials') == 60 and summary.get('expected_repeats') == summary.get('checked_repeats') == 12 and summary.get('checked_timing_trials') == 72, 'Incomplete validation coverage') base_rows, repeat_rows, timing_rows = (summary.get(key, []) for key in ('runs', 'repeats', 'timing_checks')) require(len(base_rows) == 60 and {row['case'] for row in base_rows} == expected and all(row.get('cohort') == cohort and row.get('validation_cohort') == cohort for row in base_rows), 'Base-trial identities differ from the fixed cohort inventory') require(len(repeat_rows) == 12 and {row['case'] for row in repeat_rows} == repeated and all(row.get('pass_all') is True for row in repeat_rows), 'Exact-repeat identities/coverage failed') require(len(timing_rows) == 72 and {(row['case'], row['repeat']) for row in timing_rows} == timing and all(type(row['repeat']) is int and row.get('pass_all') is True and row.get('apl_delay_steps') == 18 and row.get('steps') == 1000 for row in timing_rows), 'Timing identities/coverage failed') proof = summary.get('input_output_provenance', []) require(len(proof) == 72 and {(row['case'], row['repeat']) for row in proof} == timing, 'Per-run evidence provenance does not cover exactly the registered cells') require(summary.get('prerequisite_input_sha256', {}).get('validation_inventory') == digest(Path(exp) / 'protocol/validation-cases.json'), 'Summary was derived from a different validation inventory') require(summary.get('execution_gate_pass') is True and summary.get('numerical_integrity_pass') is True and summary.get('exact_repeat_pass') is True, 'Validation execution/numerical/repeat gate failed') require(set(summary.get('modes', {})) == set(cfg['validation']['modes']), 'Incomplete mode summaries') if representation: require(summary.get('representation_gate_pass') is True and summary.get('pass_all') is True and all(summary['modes'][mode].get('gate') is True for mode in cfg['validation']['modes']), 'Seed representation summary does not authorize novel dynamics') def gate_pass(exp, stage, *, representation=False, cfg=None): report = read(Path(exp) / 'audit' / (stage + '-gate.json')) require(report.get('stage') == stage and report.get('pass_all') is True, 'Required execution gate failed: ' + stage) if representation: require(stage == 'seed-validation', 'Only the complete seed block unlocks novel trajectories') require(report.get('execution_gate_pass') is True and report.get('representation_gate_pass') is True, 'Complete seed representation/execution gate required before novel dynamics') if cfg is None: from context import CFG cfg = CFG path = Path(exp) / 'validation/seed_validation/summary.json' require(report.get('cohort') == 'seed_validation' and report.get('summary') == 'validation/seed_validation/summary.json' and report.get('summary_sha256') == digest(path), 'Seed gate is not bound to this exact summary') require(report.get('validation_inventory_sha256') == digest(Path(exp) / 'protocol/validation-cases.json'), 'Seed gate inventory digest differs') validate_summary_coverage(read(path), exp, cfg, 'seed_validation', representation=True) return report def authorize_case(exp, cfg, group, name, backend, repeat): ensure_unstopped(exp) require(group in GROUPS and backend in ('gpu', 'oracle', 'component') and type(repeat) is int and repeat in (0, 1), 'Invalid runtime action') controls, records = inventories(exp, cfg) rows = controls if group == 'controls' else cohort_rows(records, cfg, group) selected = [row for row in rows if row['name'] == name] require(len(selected) == 1, 'Case not in the registered group inventory') row = selected[0] if group == 'controls': require(repeat == 0 and backend in ('gpu', 'component'), 'Controls are single fresh GPU identity runs') else: gate_pass(exp, 'controls') if group == 'novel_to_aj': gate_pass(exp, 'seed-validation', representation=True, cfg=cfg) probes = {item['name'] for item in probe_rows(records, cfg, group)} probe_stage = 'seed-probes' if group == 'seed_validation' else 'novel-probes' if name not in probes or repeat: gate_pass(exp, probe_stage) if backend in ('oracle', 'component'): require(name in probes and repeat == 0, 'Oracle/component scope is the two fixed probes per cohort') if repeat: require(backend == 'gpu' and row['seed'] == cfg['validation']['seeds'][0], 'Only declared first-seed repeats') return row