diff --git a/research/p104-integer-error-replay/BENCHMARK.json b/research/p104-integer-error-replay/BENCHMARK.json new file mode 100644 index 0000000000..ae2a7c2245 --- /dev/null +++ b/research/p104-integer-error-replay/BENCHMARK.json @@ -0,0 +1,124 @@ +{ + "platform": "macOS-26.6.1-arm64-arm-64bit", + "python": "3.12.14 (main, Aug 25 2026, 13:50:33) [Clang 22.1.3 ]", + "code_sha256": { + "integer.py": "82a73e4bcf1835bfdd510c7ad5538625c1b380c559b671e6a67be490615a743a", + "contract.py": "ecf1f000e4aef11d5d5e1191a8e6fa44518ba8736e14505071f4ebc32d1409ce", + "verify.py": "aaa392ca9aa9aead0eee0ac6c64ff44ea66e1f251b0a79d18cd60baa946261a1", + "benchmark.py": "59c451bd1fcb1e8fcaebe5d6d7002bb85ccbd68c2f0f8d14e70de8fbcf949c69", + "inputs/source.tar": "f8252a90dc504ce8a77d781e558691fe8d21014b0f3f720d8dfd5274b5e8aadc" + }, + "order": [ + "baseline", + "candidate", + "candidate", + "baseline", + "baseline", + "candidate" + ], + "runs": [ + { + "mode": "baseline", + "wall_seconds": 31.14853112504352, + "internal_seconds": 31.07993, + "receipt": { + "coordinates": 799, + "events": 25984, + "guards": { + "owned": 22, + "prefix": 26, + "public": 23 + }, + "source_controls": 6, + "source_result_sha256": "cc7739703ed23532d187949c8507d09c13ac4c152e07a930f0087cde0d7a3d98" + } + }, + { + "mode": "candidate", + "wall_seconds": 9.81022679200396, + "internal_seconds": 9.748412, + "receipt": { + "coordinates": 799, + "events": 25984, + "guards": { + "owned": 22, + "prefix": 26, + "public": 23 + }, + "source_controls": 6, + "source_result_sha256": "cc7739703ed23532d187949c8507d09c13ac4c152e07a930f0087cde0d7a3d98" + } + }, + { + "mode": "candidate", + "wall_seconds": 3.2594693331047893, + "internal_seconds": 3.213043, + "receipt": { + "coordinates": 799, + "events": 25984, + "guards": { + "owned": 22, + "prefix": 26, + "public": 23 + }, + "source_controls": 6, + "source_result_sha256": "cc7739703ed23532d187949c8507d09c13ac4c152e07a930f0087cde0d7a3d98" + } + }, + { + "mode": "baseline", + "wall_seconds": 24.090562083059922, + "internal_seconds": 24.048647, + "receipt": { + "coordinates": 799, + "events": 25984, + "guards": { + "owned": 22, + "prefix": 26, + "public": 23 + }, + "source_controls": 6, + "source_result_sha256": "cc7739703ed23532d187949c8507d09c13ac4c152e07a930f0087cde0d7a3d98" + } + }, + { + "mode": "baseline", + "wall_seconds": 19.79097054200247, + "internal_seconds": 19.715903, + "receipt": { + "coordinates": 799, + "events": 25984, + "guards": { + "owned": 22, + "prefix": 26, + "public": 23 + }, + "source_controls": 6, + "source_result_sha256": "cc7739703ed23532d187949c8507d09c13ac4c152e07a930f0087cde0d7a3d98" + } + }, + { + "mode": "candidate", + "wall_seconds": 4.3006944169756025, + "internal_seconds": 4.239551, + "receipt": { + "coordinates": 799, + "events": 25984, + "guards": { + "owned": 22, + "prefix": 26, + "public": 23 + }, + "source_controls": 6, + "source_result_sha256": "cc7739703ed23532d187949c8507d09c13ac4c152e07a930f0087cde0d7a3d98" + } + } + ], + "baseline_median_seconds": 24.090562083059922, + "candidate_median_seconds": 4.3006944169756025, + "speedup": 5.601551690808398, + "wall_reduction_fraction": 0.8214780376585825, + "cold_processes": true, + "parallel_replays": false, + "scope": "Complete contract with source authentication, extraction, all original endpoint/error/guard/control checks. Local shared-host timings; no Linux or hosted speed claim. Qualification is a separate cost." +} diff --git a/research/p104-integer-error-replay/CONTRACT-EDITS.json b/research/p104-integer-error-replay/CONTRACT-EDITS.json new file mode 100644 index 0000000000..b24bbda3d9 --- /dev/null +++ b/research/p104-integer-error-replay/CONTRACT-EDITS.json @@ -0,0 +1,18 @@ +[ + [ + "def authenticate():", + "def authenticate(P=P):" + ], + [ + "def calculate():", + "def calculate(P=P, backend=None):" + ], + [ + " authenticate()", + " authenticate(P)" + ], + [ + " a = module('noisy_precision', P / 'precision.py')", + " a = backend if backend is not None else module('noisy_precision', P / 'precision.py')" + ] +] diff --git a/research/p104-integer-error-replay/PROOF.md b/research/p104-integer-error-replay/PROOF.md new file mode 100644 index 0000000000..62d256bb06 --- /dev/null +++ b/research/p104-integer-error-replay/PROOF.md @@ -0,0 +1,66 @@ +# Exact dyadic scale and complete replay equivalence + +The frozen source is PR5826 at58f93cf41751920f3d6766a39caccc2974ce18b0. +Its official source export is retained unchanged. The candidate changes the +arithmetic representation of sparse coefficients. It changes no native word, +error allowance, operation count, endpoint, guard inequality or scientific claim. + +For each row, keep an upper bound d on the exponent of two in the denominator +of any coefficient. Start d=0. A reset sets d=0. MOVE leaves it unchanged. +SCALE by n/2^k sets d to d+k. ADD or additive COPY from s into r sets +`d_r=max(d_r,d_s+k)`. Injection of a new unit defect requires only exponent0. +The same rule on the transposed word uses `d_s=max(d_s,d_r+k)`. These bounds +ignore cancellations and powers of two in n; thus they can be conservative. +Take E to be the largest bound on either traversal and S=2^E. + +Induction on each event proves that every exact forward and transposed +coefficient is an integer multiple of 1/S. Store its numerator at this common +scale as a Python arbitrary-size integer. Multiplication by n/2^k is exact: +first multiply by n, then divide by 2^k. The implementation checks divisibility +before every division. It does not truncate, use a modulus or accept a floating +comparison. A nondyadic coefficient is rejected. The same scale covers ideal +input coefficients; it is a representation bound, not a numerical precision +or magnitude promise for the physical caller. + +Choose separate power-of-two scales A and C for the nonnegative local weights +a_j and c_j. If a coefficient is z/S, its three exact score contributions are + +`abs(z)*a_j*A/(S*A)`, `abs(z)*c_j*C/(S*C)`, and `abs(z)/S`. + +The numerator updates use the signed sum z_old+q*z_source before taking its +absolute value. Thus a cancellation changes the score by +`abs(z_new)-abs(z_old)`, including negative changes. The candidate does not +replace signed transfer by an absolute-value propagation bound. All score units +are fixed, so comparisons, tie choices and Pareto order agree with the rational +reference. Fraction conversion occurs only at the result and hash boundaries. +Canonical reduced numerator/denominator encodings preserve all source hashes. + +The transposed pass uses a separate traversal with the same proved scale. It +checks every final signed coefficient and score. The original literal scalar +execution and reverse-cut oracles remain unchanged. The original contract's +complete ideal endpoint, native frame/ownership checks, guards, attaining +fixtures and six rejection controls remain active. + +`contract.py` is an explicit adapted copy of the source runner. Its four declared +edits add source-directory and backend parameters. Its obsolete direct CLI is +removed; this package supplies its own CLI. The checker reconstructs these +exact edits before loading the runner. This is ordinary dependency injection; +no module, global function, source file or import hook is patched at runtime. + +Qualification compares the complete baseline and candidate results, all actual +signed witness rows, and120 separate finite dyadic error words with cancellation, +scaling, reset, COPY and varied defect sites. It also compares each finite +transposed result and rejects nondyadic scalars, nondyadic weights and inexact +integer divisions. The frozen source and package code remain distinct. + +The default command runs the fast complete source contract and compares the +entire result to the frozen expected record. `--qualify` additionally runs the +slow reference and the extra cross-checks; its cost is not hidden in the speedup. +`--baseline` runs the unmodified source contract for a matched timing. Timings +include source authentication, archive extraction and full original controls. +No cache is shared between processes. Local measurements are not a promise +about another machine or hosted CI. + +The original precision theorem remains conditional. Native unitary gauges, +physical resets, exact owned return, compiler, precision-tape and analytic +admission retain their prior status. Faster verification is not a new kappa. diff --git a/research/p104-integer-error-replay/README.md b/research/p104-integer-error-replay/README.md new file mode 100644 index 0000000000..2e46190110 --- /dev/null +++ b/research/p104-integer-error-replay/README.md @@ -0,0 +1,69 @@ +# Exact integer replay for the complete P104 precision contract + +The complete PR5826 precision replay now has an exact integer backend for its +ideal input, signed error and transposed passes. A proved common dyadic scale +replaces Fraction operations in the inner loops. Every division is checked; +no floating or modular result decides acceptance. The word, full799-coordinate +endpoint, all25,984 events, signed coefficients, guards and six source rejection +controls remain unchanged. See [PROOF.md](PROOF.md). + +The local complete-process median falls **24.091 to 4.301 seconds** +(**5.60x**, 82.1% less wall time). The host was not reserved: +baseline runs range from 19.791 to 31.149 seconds; candidate runs range +from 3.259 to 9.810 seconds. `BENCHMARK.json` records six serial fresh-process runs of the complete baseline +and candidate, with code hashes, all receipts and the local timing limits. +The candidate still authenticates and extracts the frozen source, executes the +literal attaining oracles, and compares the full source result. It has no +persistent cache. Qualification is a separate cost and is not hidden in the +claimed runtime. + +Run the fast complete replay: + +```sh +python3 -I -B research/p104-integer-error-replay/verify.py +``` + +Run the full independent arithmetic qualification: + +```sh +python3 -B scripts/research_package.py --package research/p104-integer-error-replay +``` + +Qualification compares the entire original/candidate result, every actual +signed witness row, the ideal metadata and all local weights. It also checks120 +separate dyadic words with4,240 events, varied defect sites, reset, COPY and +cancellation, plus three integer-contract rejection controls. The source's six +controls remain active in every normal replay. `RESULT.json` records this scope. + +To repeat timings with Python3.12, run `benchmark.py --output /tmp/p104-error-times.json` +from this package directory. Six complete processes run in sequence. Local +macOS timings do not establish Linux or hosted CI performance. Python3.12 and +the standard library suffice. The source export and source NOTICE files are +retained unchanged. Registered, source-corruption and read-only Git-free +optimized checks are separate from timing. + +`contract.py` adds four explicit parameter edits to the source runner and removes +its old CLI. `CONTRACT-EDITS.json` authenticates that adaptation. The backend is +passed as an ordinary argument. No runtime monkeypatch or import hook is used. +The candidate keeps the original literal scalar and reverse-cut oracles. + +## Open next questions + +- Reproduce the complete six-process measurement on an idle Linux runner. Keep + all endpoint, error and failure checks, and report qualification cost separately. +- If a later word needs new operations or nondyadic coefficients, prove a new + representation contract before using this backend. This package rejects them. +- Bind actual native/rounded child and reset implementations to the retained + precision theorem. A faster replay does not discharge those assumptions. + +Source PR5826 at58f93cf41751920f3d6766a39caccc2974ce18b0. Credit PR5819, +Adam Gall, ywz65, PR2878, Mason Hale/PR5811 and all retained P104/native/compiler +sources. New backend and qualification by Ali Romman with substantial OpenAI +Codex assistance; no subagents. Apache-2.0. No new kappa, frontier change, +physical supplier admission or independent human acceptance is claimed. + +The source manifest pins replay inputs to copies in this publication history. +Its `retained_predecessors` records keep each original head, path, blob and +SHA256, plus its exact retained file or archive member. Each source was checked +byte-for-byte before this rebind. This removes an unavailable side-commit +dependency from clean CI; no source bytes or scientific checks are changed. diff --git a/research/p104-integer-error-replay/RESULT.json b/research/p104-integer-error-replay/RESULT.json new file mode 100644 index 0000000000..1952424ccc --- /dev/null +++ b/research/p104-integer-error-replay/RESULT.json @@ -0,0 +1,22 @@ +{ + "all_actual_witness_rows_equal": true, + "coefficient_scale_bits": 55, + "coordinates": 799, + "events": 25984, + "finite_fixtures": { + "cases": 120, + "controls": [ + "nondyadic scalar", + "inexact scale division", + "nondyadic weight" + ], + "events": 4240 + }, + "guards": { + "owned": 22, + "prefix": 26, + "public": 23 + }, + "source_controls": 6, + "source_result_sha256": "cc7739703ed23532d187949c8507d09c13ac4c152e07a930f0087cde0d7a3d98" +} diff --git a/research/p104-integer-error-replay/SOURCES.json b/research/p104-integer-error-replay/SOURCES.json new file mode 100644 index 0000000000..2bd2ce4b37 --- /dev/null +++ b/research/p104-integer-error-replay/SOURCES.json @@ -0,0 +1,80 @@ +{ + "schema_version": 1, + "base_commit": "de5ec2e7a6074068452f45b1a191b5c72832df4a", + "files": [ + { + "path": "certificates/selected-result.json", + "usage": "provenance", + "git_blob": "581f8d87008354eb5b869f7ed9de1317ad837ce8", + "sha256": "2a0220e866417774606e0f991e41b9c1d26b1f43b97857238cf478dbc7d330b5" + }, + { + "path": "docs/research/agent-task-catalog.json", + "usage": "provenance", + "git_blob": "3404fbdd5e7529a0674eef26114b5071e4586cec", + "sha256": "ead6cdc9d0af6d58e8a49744d8ec8c7c15e13d2ba93c276c434bcf9b51b93a81" + }, + { + "path": "research/p104-integer-error-replay/inputs/source.tar", + "usage": "input", + "origin": { + "commit": "58f93cf41751920f3d6766a39caccc2974ce18b0", + "package": "research/p104-noisy-copy-reset", + "format": "official research-package export" + }, + "git_blob": "8f17461fcdbef4bc896bd45f2481ac3bad758d0d", + "sha256": "f8252a90dc504ce8a77d781e558691fe8d21014b0f3f720d8dfd5274b5e8aadc", + "pin": "head:55394bd623bc9db68ac5d49999481185ba9b82dd:research/p104-integer-error-replay/inputs/source.tar" + } + ], + "retained_predecessors": [ + { + "path": "research/p104-noisy-copy-reset/SOURCES.json", + "usage": "provenance", + "pin": "head:58f93cf41751920f3d6766a39caccc2974ce18b0:research/p104-noisy-copy-reset/SOURCES.json", + "git_blob": "081ab1db527ad481b475585ed73d8d7c042f1cf9", + "sha256": "da233c5ea2b28a58d6026cebbd266ea373716afd11b2013f655265d536b8b787", + "retained_source": { + "path": "research/p104-integer-error-replay/inputs/source.tar", + "pin": "head:55394bd623bc9db68ac5d49999481185ba9b82dd:research/p104-integer-error-replay/inputs/source.tar", + "member": "research/p104-noisy-copy-reset/SOURCES.json" + } + }, + { + "path": "research/p104-noisy-copy-reset/VERIFY.json", + "usage": "provenance", + "pin": "head:58f93cf41751920f3d6766a39caccc2974ce18b0:research/p104-noisy-copy-reset/VERIFY.json", + "git_blob": "b5a196e0e71cfafbb88f457ff7c94b63a9a7087a", + "sha256": "b84a00c78d77b82e87050516ef19199fc30a5bae50846f588e09dd5f2a5ed52d", + "retained_source": { + "path": "research/p104-integer-error-replay/inputs/source.tar", + "pin": "head:55394bd623bc9db68ac5d49999481185ba9b82dd:research/p104-integer-error-replay/inputs/source.tar", + "member": "research/p104-noisy-copy-reset/VERIFY.json" + } + }, + { + "path": "research/p104-noisy-copy-reset/RESULT.json", + "usage": "provenance", + "pin": "head:58f93cf41751920f3d6766a39caccc2974ce18b0:research/p104-noisy-copy-reset/RESULT.json", + "git_blob": "66a891b4fa7d5860b5a08b67f50e988f15e4a520", + "sha256": "ac7f1922f777667f51bf666538eb36bdca97f55cb0313210c60ffe29fef77cd2", + "retained_source": { + "path": "research/p104-integer-error-replay/inputs/source.tar", + "pin": "head:55394bd623bc9db68ac5d49999481185ba9b82dd:research/p104-integer-error-replay/inputs/source.tar", + "member": "research/p104-noisy-copy-reset/RESULT.json" + } + }, + { + "path": "research/p104-noisy-copy-reset/verify.py", + "usage": "provenance", + "pin": "head:58f93cf41751920f3d6766a39caccc2974ce18b0:research/p104-noisy-copy-reset/verify.py", + "git_blob": "55beda7b0518b9e458b610ee4e16ee2a7ad43f74", + "sha256": "4896c85513f0e15e96fdcb9df9245055bf252dc6be99be5e2a037420a795bb34", + "retained_source": { + "path": "research/p104-integer-error-replay/inputs/source.tar", + "pin": "head:55394bd623bc9db68ac5d49999481185ba9b82dd:research/p104-integer-error-replay/inputs/source.tar", + "member": "research/p104-noisy-copy-reset/verify.py" + } + } + ] +} diff --git a/research/p104-integer-error-replay/TASK.json b/research/p104-integer-error-replay/TASK.json new file mode 100644 index 0000000000..87b843050e --- /dev/null +++ b/research/p104-integer-error-replay/TASK.json @@ -0,0 +1,67 @@ +{ + "schema_version": 1, + "task_id": "replay-runtime", + "priority_revision": 8, + "base_commit": "de5ec2e7a6074068452f45b1a191b5c72832df4a", + "original_card": { + "id": "replay-runtime", + "question": "Can one measured replay bottleneck be sped up without dropping coverage?", + "starting_points": [ + "scripts/run_isolated_tests.py", + "scripts/verify_replay_coverage.py", + "certificates/selected-result.json" + ], + "methods": [ + "Profile one isolated replay and optimize the measured bottleneck.", + "Design source-bound reuse with explicit dependency invalidation and negative controls." + ], + "acceptance_gate": "Report before/after wall time on the same fixture, retained checks and invalidation behavior.", + "first_step": { + "command": [ + "python3", + "-B", + "scripts/research_selected_baseline.py" + ], + "expected": "PASS_RESEARCH_BASELINE_BINDINGS_ONLY", + "inspect": "Resolve the current selected review, separate complex/ordinary BIT/admitted BIT/outer budgets, and compare the card against live handoffs before starting. Historical ceilings require exact source/model equivalence.", + "scope": "Source-binding orientation only; not a scientific replay or all-size proof. Materialize selected inputs if this checkout is sparse." + }, + "first_experiment": "Measure one unchanged fixture, preserve check coverage, and test one optimization with invalidation controls.", + "bottleneck_link": "Faster trustworthy replays increase review throughput and let mechanism candidates be checked sooner.", + "allowed_variations": [ + "Profile one stage or design one source-bound cache boundary." + ], + "scope_change_triggers": [ + "Skipping coverage or comparing different fixtures to claim speedup." + ], + "overlap_terms": [ + "replay runtime", + "cache invalidation", + "isolated tests" + ] + }, + "scope_status": "refined", + "actual_scope": "Accelerate the complete PR5826 precision replay with proved exact dyadic integer arithmetic, retaining all source coverage.", + "scope_change_reason": "The Fraction inner loops are measured in the full current precision contract; changed words and physical arithmetic remain outside this runtime task.", + "bottleneck_link": "Reduces the cost of complete arbitrary-input, signed-error and transpose checks when evaluating P104 precision candidates.", + "budget_minutes": 90, + "stop_rule": "Require exact complete result and witness equality, semantic controls and a measured whole-process gain.", + "outcome": "completed", + "evidence": "PROOF.md, RESULT.json and BENCHMARK.json; original full contract and additional exact arithmetic qualification.", + "author": "Ali Romman with substantial OpenAI Codex assistance", + "ai_assistance": "Substantial OpenAI Codex assistance in proof, implementation, measurement and checks. No subagents.", + "open_questions": [ + { + "question": "Does the complete replay speedup reproduce on Linux?", + "why_it_matters": "Local shared-host timings do not establish hosted performance.", + "first_step": "Run the six serial cold-process benchmark with the exact committed code and Python3.12.", + "blocked_by": "An idle Linux measurement is not supplied here." + }, + { + "question": "Can actual native and reset machines meet the precision allowances?", + "why_it_matters": "Faster checking is not numerical supplier admission.", + "first_step": "Bind a paid completed child and reset to the source contract, retaining all owned return obligations.", + "blocked_by": "The five framework assumptions keep their status." + } + ] +} diff --git a/research/p104-integer-error-replay/VERIFY.json b/research/p104-integer-error-replay/VERIFY.json new file mode 100644 index 0000000000..674756ac8c --- /dev/null +++ b/research/p104-integer-error-replay/VERIFY.json @@ -0,0 +1,21 @@ +{ + "schema_version": 1, + "status": "ready", + "python": "3.12", + "requirements": [], + "inputs": [ + "research/p104-integer-error-replay/inputs/source.tar" + ], + "commands": [ + { + "argv": [ + "{python}", + "-B", + "{package}/verify.py", + "--qualify" + ], + "timeout_seconds": 180, + "checks": "Complete baseline/candidate precision result, actual signed witness rows, ideal weights,120 dyadic words and source plus integer rejection controls" + } + ] +} diff --git a/research/p104-integer-error-replay/benchmark.py b/research/p104-integer-error-replay/benchmark.py new file mode 100644 index 0000000000..280f163fb0 --- /dev/null +++ b/research/p104-integer-error-replay/benchmark.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +"""Measure complete baseline and candidate runs in six serial fresh processes.""" +import argparse +from hashlib import sha256 +import json +from pathlib import Path +import platform +import statistics +import subprocess +import sys +import time + +P=Path(__file__).resolve().parent + + +def measure(): + rows=[] + order=['baseline','candidate','candidate','baseline','baseline','candidate'] + for mode in order: + start=time.perf_counter() + run=subprocess.run([sys.executable,'-I','-B',str(P/'verify.py'),'--'+mode],cwd=P.parent.parent,capture_output=True,text=True,check=True,timeout=120) + elapsed=time.perf_counter()-start + payload=json.loads(run.stdout.splitlines()[-1]) + rows.append(dict(mode=mode,wall_seconds=elapsed,internal_seconds=payload['seconds'],receipt=payload['receipt'])) + print(f'{mode}: {elapsed:.3f} seconds',file=sys.stderr,flush=True) + if len({json.dumps(row['receipt'],sort_keys=True) for row in rows})!=1:raise ValueError('benchmark coverage differs') + baseline=statistics.median(r['wall_seconds'] for r in rows if r['mode']=='baseline') + candidate=statistics.median(r['wall_seconds'] for r in rows if r['mode']=='candidate') + files=['integer.py','contract.py','verify.py','benchmark.py','inputs/source.tar'] + return dict(platform=platform.platform(),python=sys.version,code_sha256={f:sha256((P/f).read_bytes()).hexdigest() for f in files},order=order,runs=rows, + baseline_median_seconds=baseline,candidate_median_seconds=candidate,speedup=baseline/candidate,wall_reduction_fraction=1-candidate/baseline, + cold_processes=True,parallel_replays=False,scope='Complete contract with source authentication, extraction, all original endpoint/error/guard/control checks. Local shared-host timings; no Linux or hosted speed claim. Qualification is a separate cost.') + + +if __name__=='__main__': + parser=argparse.ArgumentParser();parser.add_argument('--output',type=Path);args=parser.parse_args();text=json.dumps(measure(),indent=2)+'\n' + if args.output:args.output.write_text(text) + else:print(text,end='') diff --git a/research/p104-integer-error-replay/contract.py b/research/p104-integer-error-replay/contract.py new file mode 100644 index 0000000000..5b10dcac38 --- /dev/null +++ b/research/p104-integer-error-replay/contract.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +"""Replay the declared completed-operation precision contract.""" +from fractions import Fraction as F +from hashlib import sha256 +import copy +import gzip +import importlib.util +import json +from pathlib import Path +import sys + +P = Path(__file__).resolve().parent + + +def need(ok, message): + if not ok: + raise ValueError(message) + + +def authenticate(P=P): + for row in json.loads((P / 'SOURCES.json').read_text())['files']: + if row['usage'] == 'input': + data = (P.parent.parent / row['path']).read_bytes() + need(sha256(data).hexdigest() == row['sha256'], 'source hash') + original = (P / 'inputs/prior-analysis.py').read_text() + for before, after in json.loads((P / 'PRECISION-EDITS.json').read_text()): + need(original.count(before) == 1, 'unique declared edit') + original = original.replace(before, after) + need(original == (P / 'precision.py').read_text(), 'declared edit bytes') + + +def module(name, path): + spec = importlib.util.spec_from_file_location(name, path) + value = importlib.util.module_from_spec(spec) + spec.loader.exec_module(value) + return value + + +def guards(J, K, scores): + # A strict inequality gives error less than B at t=0, and less than + # B*2^-t when each unit roundoff has t more bits. + prefix = next(g for g in range(100) if J + K < 2**g) + def valid(g): + u = F(1, 2**g) + return u*K < 1 and all(u*(a+b*u*J/(1-u*K)) < 1 for a,b,_ in scores) + final = next(g for g in range(100) if valid(g)) + need(not valid(final-1), 'previous final guard must fail this formula') + return dict(prefix=prefix, final=final) + + +def rejection(label, action): + try: + action() + except (ValueError, AssertionError): + return label + raise ValueError('accepted negative control: ' + label) + + +def calculate(P=P, backend=None): + authenticate(P) + a = backend if backend is not None else module('noisy_precision', P / 'precision.py') + word = json.loads(gzip.decompress((P / 'inputs/WORD.json.gz').read_bytes())) + source = json.loads((P / 'inputs/caller-RESULT.json').read_text()) + need(a.digest(word) == source['canonical_word_sha256'], 'caller word binding') + ideal, weights = a.ideal(word) + rounded = set(ideal['counts']) + trace, witnesses = a.error_trace(word, weights, rounded) + scores, hashes = a.reverse_all(word, weights, rounded) + need(scores == trace['final_scores'], 'all final transpose scores') + need(hashes == trace['final_coefficient_hashes'], 'all final signed coefficients') + for k, peak in enumerate(trace['peaks']): + _, row = a.reverse_cut(word, rounded, peak['cut'], peak['role']) + need(row == witnesses[k], 'prefix signed transpose') + errors = {j:(1 if v > 0 else -1) for j,v in row.items()} + value = a.execute(word, {}, errors, peak['cut'])[peak['role']] + need(value == sum(map(abs, row.values()), F(0)), 'literal aligned defects') + if k == 2: + need(value == peak['value'], 'attained independent defect gain') + peak = ideal['input_peak'] + initial = {r:(1 if v > 0 else -1) for r,v in peak['row'].items()} + need(a.execute(word, initial, {}, peak['cut'])[peak['role']] == peak['value'], + 'attained input gain') + J,K = [v['value'] for v in trace['peaks'][:2]] + public = word['public'] + gp = guards(J,K,scores[:public]) + go = guards(J,K,scores[public:]) + need(gp['prefix'] == go['prefix'], 'one uniform prefix guard') + + # A reset residual at the destination survives the next additive COPY. + j = next(i for i,e in enumerate(word['program']) if e[0] == 'COPY') + r = word['program'][j][1] + reset = max(i for i,e in enumerate(word['program'][:j]) if e[1] == r) + need(word['program'][reset][0] == 'INIT', 'copy fixture starts at reset') + actual = a.execute(word, {}, {reset:F(1)}, j+1)[r] + prior = module('prior_precision', P / 'inputs/prior-analysis.py') + overwrite = prior.execute(word, {}, {reset:F(1)}, j+1)[r] + need(actual == 1 and overwrite == 0, 'overwrite extension loses reset residual') + _, error_row = a.reverse_cut(word, rounded, j+1, r) + need(error_row[reset] == 1, 'transpose retains reset residual') + + controls = [] + bad = copy.deepcopy(word) + bad['program'].insert(j, ['ADD', r, word['program'][j][2], 1, word['program'][j][-1]]) + controls.append(rejection('nonzero ideal COPY destination', lambda:a.ideal(bad))) + bad = copy.deepcopy(word) + bad['program'][j][-2] = '1/3' + controls.append(rejection('nondyadic COPY scalar', lambda:a.ideal(bad))) + bad = copy.deepcopy(word) + bad['program'][j][2] = r + controls.append(rejection('self COPY', lambda:a.ideal(bad))) + controls.append(rejection('overwrite extension to residual reset', + lambda:need(actual == overwrite, 'residual omitted'))) + last_clear = max(i for i,e in enumerate(word['program']) if e[0] == 'CLEAR') + owned_role = word['program'][last_clear][1] + residual = a.execute(word, {}, {last_clear:F(1)}, len(word['program']))[owned_role] + need(residual == 1, 'final reset residual fixture') + controls.append(rejection('exact owned zero from bounded residual', + lambda:need(residual == 0, 'owned residual'))) + g = gp['prefix'] + controls.append(rejection('one fewer uniform guard bit', lambda:need(J+K < 2**(g-1), 'guard'))) + public_peak = [max((scores[r][k],r) for r in range(public)) for k in range(3)] + owned_peak = [max((scores[r][k],r) for r in range(public,len(scores))) for k in range(3)] + return a.encode(dict( + source_pr=5811, source_head='33d7760d0a6fea6394aefd014eea68f0af02a92e', + contract='Additive COPY; bounded reset residual; all completed operations have defects', + ideal=ideal, weights_sha256=a.digest(weights), trace=trace, + public_final_peaks=public_peak, owned_final_peaks=owned_peak, + sufficient_guard_bits=dict(prefix=gp['prefix'], public=gp['final'], owned=go['final']), + copy_residual_control=dict(reset=reset, copy=j, role=r, additive=actual, overwrite=overwrite), + rejected_controls=controls, + limits=['Unitary decoded native maps remain a premise.', + 'This contract does not prove exact owned zero return.', + 'No implementation or fee for a residual reset is supplied.', + 'The guards are sufficient for this formula, not optimal physical precision.', + 'No kappa or selected frontier change.'])) + diff --git a/research/p104-integer-error-replay/inputs/source.tar b/research/p104-integer-error-replay/inputs/source.tar new file mode 100644 index 0000000000..8f17461fcd Binary files /dev/null and b/research/p104-integer-error-replay/inputs/source.tar differ diff --git a/research/p104-integer-error-replay/integer.py b/research/p104-integer-error-replay/integer.py new file mode 100644 index 0000000000..22394de087 --- /dev/null +++ b/research/p104-integer-error-replay/integer.py @@ -0,0 +1,184 @@ +"""Exact integer inner loops for signed dyadic error transfer. + +Ali Romman with substantial OpenAI Codex assistance. Apache-2.0. +The literal scalar and reverse-cut oracles stay in the authenticated reference. +""" +from collections import Counter +from fractions import Fraction as F +from hashlib import sha256 + + +def need(ok,message): + if not ok: raise ValueError(message) + + +def dyadic(value): + value=F(value);d=value.denominator + need(d & (d-1)==0,'dyadic value required') + return value.numerator,d.bit_length()-1 + + +def scaled(value,scale): + n,k=dyadic(value);need(scale%(1<>k) + + +def multiply(value,n,k): + value*=n + need(not k or value%(1<>k + + +def coefficients(word): + n=word['public']+word['owned_fields'];ops=[] + for event in word['program']: + op,r=event[:2];need(op in ('INIT','CLEAR','MOVE','ADD','COPY','SCALE'),'known operation') + need(0<=r=0 and c>=0 for a,c in values),'nonnegative weights') + return n,ops,scale,values,(scale*aw,scale*cw,scale) + + +def add_row(dst,src,n,k): + for i,v in src.items(): + value=dst.get(i,0)+multiply(v,n,k) + if value:dst[i]=value + else:dst.pop(i,None) + + +def update_hash(h,event,value,scale): + q=F(value,scale) + h.update(f'{event}:{q.numerator}/{q.denominator};'.encode()) + + +class Engine: + def __init__(self,reference): + self.binary_span=reference.binary_span + self.digest=reference.digest + self.encode=reference.encode + self.execute=reference.execute + self.reverse_cut=reference.reverse_cut + + def ideal(self,word): + n,ops,S=coefficients(word);public=word['public'] + rows=[{i:S} for i in range(n)];norms=[S]*n + frames=list(word['initial'])+[None]*(n-public) + spaces=[self.binary_span(v) for v in word['frames']] + live=set();peak_live=0;peak=dict(value=S,cut=0,role=0,row={0:S}) + weights=[];erasures=[];counts=Counter();stream=sha256() + for j,(op,r,a,k,s) in enumerate(ops): + event=word['program'][j];counts[op]+=1 + weight,feedback=F(norms[r],S),F(1) + if op=='MOVE': + _,_,start,end,private=event + need(frames[r]==start and private==(r in live),'Native source and owner') + need(spaces[end]=public and r not in live,'Owned INIT');live.add(r);peak_live=max(peak_live,len(live));rows[r]={};frames[r]=event[2] + elif op=='CLEAR': + need(r in live and frames[r]==event[2],'Owned CLEAR') + erasures.append(dict(event=j,role=r,ideal_norm=F(norms[r],S),ideal_zero=not rows[r])) + live.remove(r);rows[r]={};frames[r]=None + else: + q=F(a,1<peak['value']:peak=dict(value=norms[r],cut=j+1,role=r,row=dict(rows[r])) + need(not live and frames[:public]==word['final'],'Public and owned exits') + active=set(word['live']) + for r,row in enumerate(rows): + expected={r:S} if rpeaks[c]['value']: + peaks[c]=dict(value=value,cut=j+1,role=r);witness[c]=dict(rows[r]) + pareto=[];maximum=-1 + for a,b,cut,r in sorted(pairs,reverse=True): + if b>maximum:pareto.append([F(a,units[0]),F(b,units[1]),cut,r]);maximum=b + final=[] + for c in range(3): + value,r=max((scores[r][c],r) for r in range(word['public'])) + final.append(dict(value=F(value,units[c]),cut=len(ops),role=r));witness['final'+str(c)]=dict(rows[r]) + hashes=[] + for row in rows: + h=sha256() + for j in sorted(row,reverse=True):update_hash(h,j,row[j],S) + hashes.append(h.hexdigest()) + for c,peak in enumerate(peaks):peak['value']=F(peak['value'],units[c]) + witness={key:{j:F(v,S) for j,v in row.items()} for key,row in witness.items()} + return dict(rounded=sorted(rounded),sites=sites,peaks=peaks,pareto=pareto,final_peaks=final, + final_scores=[[F(v,u) for v,u in zip(row,units)] for row in scores], + final_coefficient_hashes=hashes,score_stream_sha256=stream.hexdigest()),witness + + def reverse_all(self,word,weights,rounded): + n,ops,S,w,units=setup(word,weights);columns=[{i:S} for i in range(n)];scores=[[0]*3 for _ in range(n)];hashes=[sha256() for _ in range(n)] + for j in reversed(range(len(ops))): + op,r,a,k,s=ops[j] + if op in rounded: + for i,v in columns[r].items(): + scores[i][0]+=abs(v)*w[j][0];scores[i][1]+=abs(v)*w[j][1];scores[i][2]+=abs(v) + update_hash(hashes[i],j,v,S) + if op in ('INIT','CLEAR'):columns[r]={} + elif op=='SCALE':columns[r]={i:multiply(v,a,k) for i,v in columns[r].items()} + elif op in ('ADD','COPY'):add_row(columns[s],columns[r],a,k) + return [[F(v,u) for v,u in zip(row,units)] for row in scores],[h.hexdigest() for h in hashes] diff --git a/research/p104-integer-error-replay/verify.py b/research/p104-integer-error-replay/verify.py new file mode 100644 index 0000000000..b7022c4dba --- /dev/null +++ b/research/p104-integer-error-replay/verify.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +"""Run the complete frozen contract through a declared exact backend.""" +from contextlib import contextmanager +from fractions import Fraction as F +from hashlib import sha256 +import importlib.util +import json +from pathlib import Path +import random +import sys +import tarfile +import tempfile +import time + +P=Path(__file__).resolve().parent + + +def need(ok,message): + if not ok: raise ValueError(message) + + +def load(name,path): + spec=importlib.util.spec_from_file_location(name,path);value=importlib.util.module_from_spec(spec);sys.modules[name]=value;spec.loader.exec_module(value);return value + + +def authenticate(): + for row in json.loads((P/'SOURCES.json').read_text())['files']: + if row['usage']=='input':need(sha256((P.parent.parent/row['path']).read_bytes()).hexdigest()==row['sha256'],'source hash') + + +@contextmanager +def source(): + authenticate() + with tempfile.TemporaryDirectory(prefix='p104-integer-error-') as tmp: + root=Path(tmp) + with tarfile.open(P/'inputs/source.tar') as archive: + need(all(x.isfile() and not x.name.startswith('/') and '..' not in Path(x.name).parts for x in archive),'regular source archive') + archive.extractall(root,filter='data') + p=root/'research/p104-noisy-copy-reset' + original=(p/'verify.py').read_text() + for before,after in json.loads((P/'CONTRACT-EDITS.json').read_text()): + need(original.count(before)==1,'unique backend seam');original=original.replace(before,after) + original=original[:original.index("\nif __name__ == '__main__':")] + need(original==(P/'contract.py').read_text(),'declared contract edits') + reference=load('frozen_contract',p/'verify.py');reference.authenticate() + precision=load('frozen_precision',p/'precision.py') + yield p,reference,precision + + +def fixtures(reference,engine,integer): + rng=random.Random(5826);cases=120;comparisons=0 + for case in range(cases): + n=6;program=[] + for _ in range(5+case%70): + op=rng.choice(['INIT','CLEAR','MOVE','ADD','COPY','SCALE']);r=rng.randrange(n) + if op in ('INIT','CLEAR'):event=[op,r,0] + elif op=='MOVE':event=[op,r,0,0,False] + else: + q=rng.choice(['1/2','-3/8','2','-4','5/16','1','-1']) + if op=='SCALE':event=[op,r,q,0] + else: + s=rng.choice([i for i in range(n) if i!=r]);event=[op,r,s,q,0] + program.append(event) + word=dict(public=4,owned_fields=2,program=program) + weights=[(F(rng.randrange(10),2**rng.randrange(8)),F(rng.randrange(10),2**rng.randrange(8))) for _ in program] + rounded={op for op in ['INIT','CLEAR','MOVE','ADD','COPY','SCALE'] if rng.randrange(2)} + a,wa=reference.error_trace(word,weights,rounded);b,wb=engine.error_trace(word,weights,rounded) + need(a==b and wa==wb,'complete finite trace and signed witness equality') + need(reference.reverse_all(word,weights,rounded)==engine.reverse_all(word,weights,rounded),'finite transpose equality') + comparisons+=len(program) + controls=[] + for label,fn in [('nondyadic scalar',lambda:integer.dyadic(F(1,3))),('inexact scale division',lambda:integer.multiply(1,1,1)),('nondyadic weight',lambda:integer.scaled(F(1,3),8))]: + try:fn() + except ValueError:controls.append(label) + else:raise ValueError('accepted invalid integer contract') + return dict(cases=cases,events=comparisons,controls=controls) + + +def run(mode): + started=time.perf_counter() + with source() as (p,original,reference): + expected=json.loads((p/'RESULT.json').read_text()) + if mode=='baseline':result=original.calculate() + else: + contract=load('explicit_contract',P/'contract.py');integer=load('integer_backend',P/'integer.py');engine=integer.Engine(reference) + result=contract.calculate(p,engine) + need(result==expected,'complete source result, including original controls') + receipt=dict(source_result_sha256=sha256(json.dumps(result,sort_keys=True,separators=(',',':')).encode()).hexdigest(),events=result['ideal']['events'],coordinates=result['ideal']['n'],source_controls=len(result['rejected_controls']),guards=result['sufficient_guard_bits']) + if mode=='qualify': + baseline=original.calculate();need(result==baseline,'complete baseline/candidate output equality') + # Compare the full witness rows, which are used but not all stored + # in the source receipt. This is a qualification cost, not runtime. + import gzip + word=json.loads(gzip.decompress((p/'inputs/WORD.json.gz').read_bytes()));ideal,weights=reference.ideal(word);rounded=set(ideal['counts']) + need(engine.ideal(word)==(ideal,weights),'full ideal metadata and exact local weights') + a,wa=reference.error_trace(word,weights,rounded);b,wb=engine.error_trace(word,weights,rounded) + need(a==b and wa==wb,'all actual signed witness rows') + receipt['finite_fixtures']=fixtures(reference,engine,integer) + receipt['coefficient_scale_bits']=integer.setup(word,weights)[2].bit_length()-1 + receipt['all_actual_witness_rows_equal']=True + return receipt,time.perf_counter()-started + + +if __name__=='__main__': + args=sys.argv[1:];mode='candidate' if not args else args[0].removeprefix('--') + need(mode in ('candidate','baseline','qualify'),'mode') + receipt,seconds=run(mode) + if mode=='qualify': + if args[1:]==['--write']:(P/'RESULT.json').write_text(json.dumps(receipt,indent=2,sort_keys=True)+'\n') + else:need(not args[1:] and receipt==json.loads((P/'RESULT.json').read_text()),'qualification receipt') + else:need(len(args)<=1,'arguments') + print('PASS_P104_INTEGER_ERROR_REPLAY') + print(json.dumps({'mode':mode,'seconds':round(seconds,6),'receipt':receipt},sort_keys=True)) diff --git a/tests/fixtures/README.md b/tests/fixtures/README.md new file mode 100644 index 0000000000..472a6a502f --- /dev/null +++ b/tests/fixtures/README.md @@ -0,0 +1,41 @@ +# Frozen research routing fixture + +`research-routing-v7.json` retains two exact task cards from priority revision 7: +`portable-replay` and `centre-path-sharing`. They exercise the generic-baseline +and historical-ceiling routes in `choose_current_research_task.py`. The fixture +records the source commit, path and SHA256. The card objects are unchanged. + +The source is commit `553d26d796fde9871ff2661a49ac7732e945410f`, file +`docs/research/agent-task-catalog.json`. The test places these cards in a small +catalogue with one track of weight 100. This carrier is test data, not a change +to research priorities. Current cards are checked separately from the live +catalogue. Every live card must preserve its other fields; each distinct direct +command also passes through both real CLIs. + +The CLI fixture copies the two real scripts. It creates inert reference files +for path validation and copies the current selected record for receipt fields. +It does not execute those references or verify a mathematical claim. Missing +references, sparse omissions, deleted files and staged replacements have separate +negative tests. No Git history, network request or change to the live catalogue +is needed to run the fixture. + +```sh +python3 -B -m unittest discover -s tests -p test_research_current_assignment.py -v +python3 -B -m unittest discover -s tests -p test_research_assignment.py -v +``` + +This fixture repairs the CI handoff in +[Discussion 5722](https://github.com/CrocSwap/integer-mult-bounds/discussions/5722). +The [investigation](https://github.com/CrocSwap/integer-mult-bounds/discussions/5758) +records the failure, checks and publication status. The original scripts, task +catalogue and selected frontier are unchanged. + +On base `de5ec2e7a6074068452f45b1a191b5c72832df4a`, the old suite has eight +failures and two errors. After the repair, all 12 current-assignment tests pass +under Python 3.14.7 and Python 3.12.14, including an isolated optimized run. +All 16 related assignment tests pass under Python 3.12.14. This is test coverage, +not a mathematical replay. See the investigation for hosted-plan status. + +Retain the original catalogue, routing and test authors, including Douglas +Colkitt, Ali Romman and Mihir Srivastava. New fixture and test repair: Ali Romman +with substantial OpenAI Codex assistance. No subagents. Apache-2.0. diff --git a/tests/fixtures/research-routing-v7.json b/tests/fixtures/research-routing-v7.json new file mode 100644 index 0000000000..f5d52ac21d --- /dev/null +++ b/tests/fixtures/research-routing-v7.json @@ -0,0 +1,87 @@ +{ + "source_commit": "553d26d796fde9871ff2661a49ac7732e945410f", + "source_path": "docs/research/agent-task-catalog.json", + "source_sha256": "f7a48bfa2659b88378282a8eee7d9fe52cdf505594124c4d1686b58510fffd72", + "scope": "Two exact revision-7 cards. The test carrier is not a research allocation.", + "cards": [ + { + "id": "centre-path-sharing", + "question": "Can the 64-per-call centre path (eight rank-8 COPY/ERASE totals through the private temporary) be shared across calls or shrunk?", + "starting_points": [ + "docs/research/research-family-status.md", + "research/complex-local-word-ceiling/README.md", + "research/split-centres-global/proofs/PR386-BIT-GEOMETRY.txt", + "certificates/selected-result.json" + ], + "methods": [ + "Derive a fully charged favorable relaxation for sharing one centre path across k calls, including restoration, hosting and precision.", + "Prove a lower bound on copied information per call for the selected decoder contract." + ], + "acceptance_gate": "Show the changed centre charge and copy profile per call with every added restoration or hosting cost charged, and its ceiling via ceiling.ceiling(centre=...). A scoped local result may be retained without a complete supplier. Expanding it into further construction rounds requires the family-level budget and obligation gate in research-family-status.md; an optimistic passing relaxation alone is not acceptance.", + "first_step": { + "command": [ + "python3", + "-B", + "research/complex-local-word-ceiling/verify.py" + ], + "expected": "PASS_COMPLEX_LOCAL_WORD_CEILING", + "inspect": "Read the selected word's per-call local profile, its packing ceiling and the lever table; run levers.py for the ideal budget of the lever you target.", + "scope": "Positive control and planning budget only; this is not a new construction or full cold replay." + }, + "first_experiment": "Write the per-call centre charge and copy children for sharing across k = 2 calls, charge the sharing overhead, and screen it; stop this route if even the ideal sharing gains under 10%. Ideal budgets: free 2.80x, shared by two 1.90x (levers.py).", + "bottleneck_link": "The centre path consumes 64 of about 99.6 unpaid coordinates per call, i.e. about 64% of the raw deficit.", + "allowed_variations": [ + "One sharing pattern across a fixed number of calls.", + "One reduced copied-total contract for the existing decoder.", + "If the coverage law, template or guard changes, derive an applicable fully charged replacement bound instead of applying the incumbent ceiling outside its scope." + ], + "scope_change_triggers": [ + "Removing copies without a replacement contract for the decoder's copied totals.", + "Continuing a partial cost improvement while leaving the same unpriced consumer, restoration or central-readout obligation unresolved; refresh the family expansion decision first. Local product-retiming evidence has a narrow scope and is not a ban on changed graphs or contracts." + ], + "overlap_terms": [ + "centre path", + "copied totals", + "private temporary", + "shared centre", + "batching" + ] + }, + { + "id": "portable-replay", + "question": "Can one reproducible environment or artifact-comparison failure be repaired?", + "starting_points": [ + "research/gzip-container-reproducibility/README.md", + "docs/ci-verification.md" + ], + "methods": [ + "Reproduce one portability failure and add a minimal cross-environment fix.", + "Audit one canonical-payload comparison for omitted proof-relevant fields." + ], + "acceptance_gate": "Preserve immutable source hashes and proof-relevant content; a different compressed byte stream alone is not a mathematical mismatch.", + "first_step": { + "command": [ + "python3", + "-B", + "scripts/research_baseline.py" + ], + "expected": "PASS_RESEARCH_BASELINE_BINDINGS_ONLY", + "inspect": "Read the gzip-container reproducibility examples and distinguish frozen source bytes from regenerated payloads.", + "scope": "Positive control / source-bound orientation only; this is not a new construction or full cold replay." + }, + "first_experiment": "Reproduce one specific mismatch or audit one canonical comparison for omitted proof-relevant fields.", + "bottleneck_link": "Portable exact checks prevent environment failures from blocking valid contributions.", + "allowed_variations": [ + "One interpreter/version mismatch or one payload field comparison." + ], + "scope_change_triggers": [ + "Removing immutable-source pins because generated compressed bytes differ." + ], + "overlap_terms": [ + "gzip container", + "reproducibility", + "canonical payload" + ] + } + ] +} diff --git a/tests/test_research_current_assignment.py b/tests/test_research_current_assignment.py index 860f6b9b9e..c524c0037f 100644 --- a/tests/test_research_current_assignment.py +++ b/tests/test_research_current_assignment.py @@ -1,9 +1,15 @@ -"""Current orientation must preserve frozen task-selection evidence.""" +"""Current orientation must preserve frozen task-selection evidence. + +Frozen fixtures and live-catalog coverage: Ali Romman with OpenAI Codex +assistance. Retain the original routing and test authors. Apache-2.0. +""" import copy +from contextlib import contextmanager import importlib.util import json from pathlib import Path import subprocess +import shutil import sys import tempfile import unittest @@ -15,16 +21,49 @@ LEGACY = current.LEGACY legacy = current.runpy.run_path(str(LEGACY)) CATALOG = json.loads((ROOT / legacy["CATALOG"]).read_text(encoding="utf-8")) +FROZEN = json.loads((ROOT / "tests/fixtures/research-routing-v7.json").read_text()) +FROZEN_CATALOG = { + "schema_version": 1, "priority_revision": 7, + "priority_log": "docs/research/routing-fixture.md", + "tracks": [{"id": "routing-fixtures", "weight": 100, "tasks": FROZEN["cards"]}], +} + + +@contextmanager +def frozen_cli_tree(): + """Run the real scripts with exact old cards and inert reference files.""" + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + required = {FROZEN_CATALOG["priority_log"], + "scripts/research_selected_baseline.py", "scripts/research_package.py", + "research/historical-complex-ceiling-replay/VERIFY.json", + "research/historical-complex-ceiling-replay/verify.py"} + for card in FROZEN["cards"]: + required.update(card["starting_points"]) + required.add(card["first_step"]["command"][-1]) + for name in required: + path = root / name + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text("Inert reference for CLI validation only.\n") + for name in ("choose_research_task.py", "choose_current_research_task.py"): + shutil.copyfile(ROOT / "scripts" / name, root / "scripts" / name) + path = root / legacy["CATALOG"] + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(FROZEN_CATALOG)) + path = root / "certificates/selected-result.json" + path.parent.mkdir(parents=True, exist_ok=True) + shutil.copyfile(ROOT / "certificates/selected-result.json", path) + yield root class CurrentResearchAssignmentTests(unittest.TestCase): - def test_every_frozen_card_and_other_fields_stay_exact(self): - before = copy.deepcopy(CATALOG) + def check_card_round_trips(self, catalog): + before = copy.deepcopy(catalog) resolved = 0 historical = 0 - for track in CATALOG["tracks"]: + for track in catalog["tracks"]: for task in track["tasks"]: - result = legacy["select"](CATALOG, "a1" * 16, task_id=task["id"]) + result = legacy["select"](catalog, "a1" * 16, task_id=task["id"]) original = copy.deepcopy(result) current.resolve_current_first_steps(result, ROOT) card = result["suggestions"][0] @@ -55,19 +94,25 @@ def test_every_frozen_card_and_other_fields_stay_exact(self): self.assertTrue(card.pop("first_step_resolution")) card["first_step"] = card.pop("catalog_first_step") self.assertEqual(result, original) - self.assertEqual(resolved, 16) - self.assertEqual(historical, 4) - self.assertEqual(CATALOG, before) + self.assertEqual(catalog, before) + return resolved, historical + + def test_frozen_routes_and_other_fields_stay_exact(self): + self.assertEqual(self.check_card_round_trips(FROZEN_CATALOG), (1, 1)) + + def test_every_live_card_and_other_fields_stay_exact(self): + self.assertTrue(CATALOG["tracks"]) + self.check_card_round_trips(CATALOG) def test_only_exact_generic_command_is_resolved(self): - result = copy.deepcopy(legacy["select"](CATALOG, "0" * 32, task_id="portable-replay")) + result = copy.deepcopy(legacy["select"](FROZEN_CATALOG, "0" * 32, task_id="portable-replay")) result["suggestions"][0]["first_step"]["command"].append("--historical-fixture") original = copy.deepcopy(result) self.assertEqual(current.resolve_current_first_steps(result, ROOT), []) self.assertEqual(result, original) def test_missing_current_helper_does_not_change_receipt(self): - result = legacy["select"](CATALOG, "0" * 32, task_id="portable-replay") + result = legacy["select"](FROZEN_CATALOG, "0" * 32, task_id="portable-replay") original = copy.deepcopy(result) with tempfile.TemporaryDirectory() as directory: with self.assertRaisesRegex(ValueError, "missing current first-step script"): @@ -82,7 +127,7 @@ def test_nonexact_scientific_commands_are_not_reinterpreted(self): ): with self.subTest(command=command): result = copy.deepcopy(legacy["select"]( - CATALOG, "0" * 32, task_id="centre-path-sharing")) + FROZEN_CATALOG, "0" * 32, task_id="centre-path-sharing")) result["suggestions"][0]["first_step"]["command"] = command original = copy.deepcopy(result) self.assertEqual(current.resolve_current_first_steps(result, ROOT), []) @@ -102,34 +147,52 @@ def test_missing_historical_reference_leaves_all_cards_unchanged(self): path = root / name path.parent.mkdir(parents=True, exist_ok=True) path.write_text("fixture\n") - result = legacy["select"](CATALOG, "0" * 32, task_id="portable-replay") + result = legacy["select"](FROZEN_CATALOG, "0" * 32, task_id="portable-replay") result["suggestions"] += legacy["select"]( - CATALOG, "0" * 32, task_id="centre-path-sharing")["suggestions"] + FROZEN_CATALOG, "0" * 32, task_id="centre-path-sharing")["suggestions"] original = copy.deepcopy(result) with self.assertRaises(ValueError) as raised: current.resolve_current_first_steps(result, root) self.assertIn(absent, str(raised.exception)) self.assertEqual(result, original) - def cli(self, script, args): - return subprocess.run([sys.executable, "-B", str(script), *args], cwd=ROOT, + def cli(self, script, args, root=ROOT): + return subprocess.run([sys.executable, "-B", str(root / "scripts" / Path(script).name), *args], cwd=root, capture_output=True, text=True, encoding="utf-8") def test_cli_preserves_legacy_output_and_emits_both_commands(self): - args = ["--seed", "0" * 32, "--task", "portable-replay"] - original = self.cli(LEGACY, args) - output = self.cli(ROOT / "scripts/choose_current_research_task.py", args) - self.assertEqual(original.returncode, 0) - self.assertEqual(output.returncode, 0) - old, new = json.loads(original.stdout), json.loads(output.stdout) - card = new["suggestions"][0] - self.assertEqual(card["first_step"]["command"], - ["python3", "-B", "scripts/research_selected_baseline.py"]) - self.assertEqual(card.pop("catalog_first_step"), old["suggestions"][0]["first_step"]) - self.assertTrue(card.pop("first_step_resolution")) - card["first_step"] = old["suggestions"][0]["first_step"] - self.assertEqual(new, old) - self.assertEqual(self.cli(LEGACY, args).stdout, original.stdout) + with frozen_cli_tree() as root: + args = ["--seed", "0" * 32, "--task", "portable-replay"] + original = self.cli(LEGACY, args, root) + output = self.cli(ROOT / "scripts/choose_current_research_task.py", args, root) + self.assertEqual(original.returncode, 0) + self.assertEqual(output.returncode, 0) + old, new = json.loads(original.stdout), json.loads(output.stdout) + card = new["suggestions"][0] + self.assertEqual(card["first_step"]["command"], + ["python3", "-B", "scripts/research_selected_baseline.py"]) + self.assertEqual(card.pop("catalog_first_step"), old["suggestions"][0]["first_step"]) + self.assertTrue(card.pop("first_step_resolution")) + card["first_step"] = old["suggestions"][0]["first_step"] + self.assertEqual(new, old) + self.assertEqual(self.cli(LEGACY, args, root).stdout, original.stdout) + + def test_live_cli_preserves_each_unmapped_command(self): + checked = {tuple(card["first_step"]["command"]) for card in FROZEN["cards"]} + for track in CATALOG["tracks"]: + for task in track["tasks"]: + command = tuple(task["first_step"]["command"]) + if command in checked: + continue + checked.add(command) + with self.subTest(task=task["id"]): + args = ["--seed", "0" * 32, "--task", task["id"]] + original = self.cli(LEGACY, args) + output = self.cli(ROOT / "scripts/choose_current_research_task.py", args) + self.assertEqual(original.returncode, 0, original.stderr) + self.assertEqual(output.returncode, 0, output.stderr) + self.assertEqual(json.loads(output.stdout), json.loads(original.stdout)) + self.assertEqual(self.cli(LEGACY, args).stdout, original.stdout) def test_help_and_error_preserve_child_status_and_output(self): for args, code in ((["--help"], 0), (["--unknown-option"], 2), (["--seed", "bad"], 2)): @@ -139,23 +202,24 @@ def test_help_and_error_preserve_child_status_and_output(self): self.assertEqual((output.stdout, output.stderr), (original.stdout, original.stderr)) def test_cli_distinguishes_current_orientation_from_historical_control(self): - args = ["--seed", "0" * 32, "--task", "complex-new-helper-inventory"] - original = self.cli(LEGACY, args) - output = self.cli(ROOT / "scripts/choose_current_research_task.py", args) - self.assertEqual(original.returncode, 0, original.stderr) - self.assertEqual(output.returncode, 0, output.stderr) - old, new = json.loads(original.stdout), json.loads(output.stdout) - card = new["suggestions"][0] - self.assertEqual(card["first_step"]["command"][2], "scripts/research_selected_baseline.py") - self.assertEqual(card["first_step"]["expected"], "PASS_RESEARCH_BASELINE_BINDINGS_ONLY") - follow_up, = card.pop("follow_up_steps") - self.assertEqual(follow_up["command"][-1], "research/historical-complex-ceiling-replay") - self.assertIn("historical", follow_up["scope"].lower()) - self.assertIn("cost law", follow_up["inspect"]) - self.assertTrue(card.pop("first_step_resolution")) - card["first_step"] = card.pop("catalog_first_step") - self.assertEqual(new, old) - self.assertEqual(self.cli(LEGACY, args).stdout, original.stdout) + with frozen_cli_tree() as root: + args = ["--seed", "0" * 32, "--task", "centre-path-sharing"] + original = self.cli(LEGACY, args, root) + output = self.cli(ROOT / "scripts/choose_current_research_task.py", args, root) + self.assertEqual(original.returncode, 0, original.stderr) + self.assertEqual(output.returncode, 0, output.stderr) + old, new = json.loads(original.stdout), json.loads(output.stdout) + card = new["suggestions"][0] + self.assertEqual(card["first_step"]["command"][2], "scripts/research_selected_baseline.py") + self.assertEqual(card["first_step"]["expected"], "PASS_RESEARCH_BASELINE_BINDINGS_ONLY") + follow_up, = card.pop("follow_up_steps") + self.assertEqual(follow_up["command"][-1], "research/historical-complex-ceiling-replay") + self.assertIn("historical", follow_up["scope"].lower()) + self.assertIn("cost law", follow_up["inspect"]) + self.assertTrue(card.pop("first_step_resolution")) + card["first_step"] = card.pop("catalog_first_step") + self.assertEqual(new, old) + self.assertEqual(self.cli(LEGACY, args, root).stdout, original.stdout) def test_sparse_historical_references_require_unchanged_head_entries(self): with tempfile.TemporaryDirectory() as directory: @@ -178,12 +242,12 @@ def git(*args): git("update-index", "--skip-worktree", *omitted) for name in omitted: (root / name).unlink() - result = legacy["select"](CATALOG, "0" * 32, task_id="coverage-law-change") + result = legacy["select"](FROZEN_CATALOG, "0" * 32, task_id="centre-path-sharing") self.assertEqual(current.resolve_current_first_steps(result, root), sorted(omitted)) # Ordinary deletion must not acquire the deliberate-sparse exemption. changed = omitted[-1] git("update-index", "--no-skip-worktree", changed) - result = legacy["select"](CATALOG, "0" * 32, task_id="coverage-law-change") + result = legacy["select"](FROZEN_CATALOG, "0" * 32, task_id="centre-path-sharing") original = copy.deepcopy(result) with self.assertRaises(ValueError): current.resolve_current_first_steps(result, root) @@ -208,9 +272,9 @@ def git(*args): git("-c", "user.name=Fixture", "-c", "user.email=fixture@example.invalid", "-c", "core.hooksPath=/dev/null", "commit", "-qm", "helper") git("update-index", "--skip-worktree", name); path.unlink() - result = legacy["select"](CATALOG, "0" * 32, task_id="portable-replay") + result = legacy["select"](FROZEN_CATALOG, "0" * 32, task_id="portable-replay") self.assertEqual(current.resolve_current_first_steps(result, root), [name]) git("update-index", "--no-skip-worktree", name) - result = legacy["select"](CATALOG, "0" * 32, task_id="portable-replay") + result = legacy["select"](FROZEN_CATALOG, "0" * 32, task_id="portable-replay") with self.assertRaisesRegex(ValueError, "missing current first-step script"): current.resolve_current_first_steps(result, root)