diff --git a/.github/workflows/side24-d5-closed-form.yml b/.github/workflows/side24-d5-closed-form.yml index 7c3989295..e438b0f8e 100644 --- a/.github/workflows/side24-d5-closed-form.yml +++ b/.github/workflows/side24-d5-closed-form.yml @@ -5,6 +5,10 @@ name: SIDE24 d5 closed form paths: - 'coefficients/side24_d5_closed_form_20260930/**' - '.github/workflows/side24-d5-closed-form.yml' + - 'tools/**' + - 'tools.py' + - 'tests/test_side24_mutant_workflows.py' + - 'tests/test_legacy_workflow_triggers.py' - 'imports/lifetime_parent_20260925/UNIFORM_MATRIX_CAP_AND_LIFETIME.md' - 'coefficients/side24_v1/PROOF.md' workflow_dispatch: @@ -23,6 +27,13 @@ jobs: - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 with: python-version: '3.11.16' + - name: Check trigger coverage and the SIDE24 replay contract in both modes + env: + SIDE24_MUTANT_TEST_FAMILY: 'side24-d5-closed-form' + PYTHONDONTWRITEBYTECODE: '1' + run: | + python -B -S tests/test_side24_mutant_workflows.py + python -B -O -S tests/test_side24_mutant_workflows.py - name: Verify sources and pins, replay both modes, reject mutants env: PYTHONDONTWRITEBYTECODE: '1' @@ -54,27 +65,16 @@ jobs: blob = hashlib.sha1(b'blob %d\0' % len(data) + data).hexdigest() if blob != pin['git_blob']: raise ValueError('pinned source drifted: ' + pin['path'] + ' ' + blob) - script = str(root / 'closed_form_d4.py') expected = (root / 'RESULTS.json').read_bytes() - mutants = ['shift-variance', 'vandermonde', 'erfc-sign', 'jacobian', 'mehta', 'by-parts', 'one-erf', 'two-erf', 'jk-init', 'gauss-half', 'boundary-term', 'atom-pi', 'cumulative-init', 'cumulative-recurrence'] - def mutant_rc(args): - flags, m = args - return m, flags, subprocess.run([sys.executable, *flags, script, '--mutant', m], - capture_output=True, timeout=600).returncode - for flags in (['-B', '-S'], ['-B', '-O', '-S']): - out = subprocess.run([sys.executable, *flags, script], - check=True, capture_output=True, timeout=600).stdout - if out != expected: - raise RuntimeError('output differs from RESULTS.json under ' + ' '.join(flags)) - doc = json.loads(out) - if not (doc['passed'] and len(doc['checks']) == 16 and all(doc['checks'].values())): - raise RuntimeError('certification did not pass under ' + ' '.join(flags)) - if doc['closed_forms']['D_4'] != '6695/54 + -405/32 sqrt(6) + -1375/72 sqrt(21) pi^(-1) + -405/16 sqrt(6) pi^(-1) arctan(1/4 sqrt(14)) + -6695/27 pi^(-1) arctan(1/7 sqrt(21)) + 405/16 sqrt(6) pi^(-1) arctan(1/7 sqrt(14))' or doc['closed_forms']['Z_4'] != '1536 pi^(1)': - raise RuntimeError('closed form differs from the stated one') - with concurrent.futures.ThreadPoolExecutor(max_workers=4) as pool: - for m, fl, rc in pool.map(mutant_rc, [(flags, m) for m in mutants]): - if rc != 1: - raise RuntimeError('mutant ' + m + ' not rejected under ' + ' '.join(fl)) + doc = json.loads(expected) + if not (doc['passed'] and len(doc['checks']) == 16 and all(doc['checks'].values())): + raise RuntimeError('RESULTS.json is not a passing certification') + if doc['closed_forms']['D_4'] != '6695/54 + -405/32 sqrt(6) + -1375/72 sqrt(21) pi^(-1) + -405/16 sqrt(6) pi^(-1) arctan(1/4 sqrt(14)) + -6695/27 pi^(-1) arctan(1/7 sqrt(21)) + 405/16 sqrt(6) pi^(-1) arctan(1/7 sqrt(14))' or doc['closed_forms']['Z_4'] != '1536 pi^(1)': + raise RuntimeError('closed form differs from the stated one') + # Every child: exact exit status, one timing line on stderr, baseline stdout equal + # to RESULTS.json, each mutant's report equal to the measured one (both modes). + from tools.side24_mutant_replay import replay + replay('side24-d5-closed-form', pathlib.Path.cwd(), timeout=600) print(json.dumps({'scientific_effect': 'NONE', 'mathematical_acceptance': False, 'exact_identity_checked': True, 'side24_v1': 'unchanged'})) PY git diff --exit-code diff --git a/.github/workflows/side24-d5-coefficient.yml b/.github/workflows/side24-d5-coefficient.yml index 9f46fffa4..2c50da0ab 100644 --- a/.github/workflows/side24-d5-coefficient.yml +++ b/.github/workflows/side24-d5-coefficient.yml @@ -5,6 +5,10 @@ name: SIDE24 d5 coefficient paths: - 'coefficients/side24_d5_20260930/**' - '.github/workflows/side24-d5-coefficient.yml' + - 'tools/**' + - 'tools.py' + - 'tests/test_side24_mutant_workflows.py' + - 'tests/test_legacy_workflow_triggers.py' - 'imports/lifetime_parent_20260925/UNIFORM_MATRIX_CAP_AND_LIFETIME.md' - 'coefficients/side24_v1/PROOF.md' - 'coefficients/side24_v1/ENCLOSURE.json' @@ -24,6 +28,13 @@ jobs: - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 with: python-version: '3.11.16' + - name: Check trigger coverage and the SIDE24 replay contract in both modes + env: + SIDE24_MUTANT_TEST_FAMILY: 'side24-d5-coefficient' + PYTHONDONTWRITEBYTECODE: '1' + run: | + python -B -S tests/test_side24_mutant_workflows.py + python -B -O -S tests/test_side24_mutant_workflows.py - name: Verify sources and pins, replay both modes, reject mutants env: PYTHONDONTWRITEBYTECODE: '1' @@ -61,25 +72,14 @@ jobs: for d in ('2', '3'): if ('"%s", "%s"' % (enc[d]['lower'], enc[d]['upper'])) not in src: raise ValueError('SIDE24 d = ' + d + ' enclosure quoted in the script differs from ENCLOSURE.json') - script = str(root / 'cone_moment_d5.py') expected = (root / 'RESULTS.json').read_bytes() - mutants = ['shift-variance', 'vandermonde', 'erfc-branch', 'remainder-dropped', 'mehta', 'sphere-area', 'image-shells', 'sandwich-exponent', 'recurrence', 'jacobian', 'cumulative-half'] - def mutant_rc(args): - flags, m = args - return m, flags, subprocess.run([sys.executable, *flags, script, '--mutant', m], - capture_output=True, timeout=1500).returncode - for flags in (['-B', '-S'], ['-B', '-O', '-S']): - out = subprocess.run([sys.executable, *flags, script], - check=True, capture_output=True, timeout=1500).stdout - if out != expected: - raise RuntimeError('output differs from RESULTS.json under ' + ' '.join(flags)) - doc = json.loads(out) - if not (doc['passed'] and len(doc['checks']) == 13 and all(doc['checks'].values())): - raise RuntimeError('certification did not pass under ' + ' '.join(flags)) - with concurrent.futures.ThreadPoolExecutor(max_workers=4) as pool: - for m, fl, rc in pool.map(mutant_rc, [(flags, m) for m in mutants]): - if rc != 1: - raise RuntimeError('mutant ' + m + ' not rejected under ' + ' '.join(fl)) + doc = json.loads(expected) + if not (doc['passed'] and len(doc['checks']) == 13 and all(doc['checks'].values())): + raise RuntimeError('RESULTS.json is not a passing certification') + # Every child: exact exit status, one timing line on stderr, baseline stdout equal + # to RESULTS.json, each mutant's report equal to the measured one (both modes). + from tools.side24_mutant_replay import replay + replay('side24-d5-coefficient', pathlib.Path.cwd(), timeout=1500) print(json.dumps({'scientific_effect': 'NONE', 'mathematical_acceptance': False, 'certified_enclosures': True, 'side24_v1': 'unchanged'})) PY git diff --exit-code diff --git a/.github/workflows/side24-d6-coefficient.yml b/.github/workflows/side24-d6-coefficient.yml index 6059c2ecd..7a851d9a4 100644 --- a/.github/workflows/side24-d6-coefficient.yml +++ b/.github/workflows/side24-d6-coefficient.yml @@ -5,6 +5,10 @@ name: SIDE24 d6 coefficient paths: - 'coefficients/side24_d6_20260930/**' - '.github/workflows/side24-d6-coefficient.yml' + - 'tools/**' + - 'tools.py' + - 'tests/test_side24_mutant_workflows.py' + - 'tests/test_legacy_workflow_triggers.py' - 'imports/lifetime_parent_20260925/UNIFORM_MATRIX_CAP_AND_LIFETIME.md' - 'coefficients/side24_v1/PROOF.md' - 'coefficients/side24_v1/ENCLOSURE.json' @@ -24,6 +28,13 @@ jobs: - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 with: python-version: '3.11.16' + - name: Check trigger coverage and the SIDE24 replay contract in both modes + env: + SIDE24_MUTANT_TEST_FAMILY: 'side24-d6-coefficient' + PYTHONDONTWRITEBYTECODE: '1' + run: | + python -B -S tests/test_side24_mutant_workflows.py + python -B -O -S tests/test_side24_mutant_workflows.py - name: Verify sources and pins, replay both modes, reject mutants env: PYTHONDONTWRITEBYTECODE: '1' @@ -61,25 +72,14 @@ jobs: for d in ('2', '3'): if ('"%s", "%s"' % (enc[d]['lower'], enc[d]['upper'])) not in src: raise ValueError('SIDE24 d = ' + d + ' enclosure quoted in the script differs from ENCLOSURE.json') - script = str(root / 'cone_moment_d6.py') expected = (root / 'RESULTS.json').read_bytes() - mutants = ['shift-variance', 'vandermonde', 'erfc-branch', 'jacobian', 'mehta', 'recurrence', 'layer-init', 'two-erf', 'cumulative-half', 'remainder-dropped', 'erf-inside', 'tail-dropped', 'image-shells', 'sandwich-exponent'] - def mutant_rc(args): - flags, m = args - return m, flags, subprocess.run([sys.executable, *flags, script, '--mutant', m], - capture_output=True, timeout=3000).returncode - for flags in (['-B', '-S'], ['-B', '-O', '-S']): - out = subprocess.run([sys.executable, *flags, script], - check=True, capture_output=True, timeout=3000).stdout - if out != expected: - raise RuntimeError('output differs from RESULTS.json under ' + ' '.join(flags)) - doc = json.loads(out) - if not (doc['passed'] and len(doc['checks']) == 20 and all(doc['checks'].values())): - raise RuntimeError('certification did not pass under ' + ' '.join(flags)) - with concurrent.futures.ThreadPoolExecutor(max_workers=4) as pool: - for m, fl, rc in pool.map(mutant_rc, [(flags, m) for m in mutants]): - if rc != 1: - raise RuntimeError('mutant ' + m + ' not rejected under ' + ' '.join(fl)) + doc = json.loads(expected) + if not (doc['passed'] and len(doc['checks']) == 20 and all(doc['checks'].values())): + raise RuntimeError('RESULTS.json is not a passing certification') + # Every child: exact exit status, one timing line on stderr, baseline stdout equal + # to RESULTS.json, each mutant's report equal to the measured one (both modes). + from tools.side24_mutant_replay import replay + replay('side24-d6-coefficient', pathlib.Path.cwd(), timeout=3000) print(json.dumps({'scientific_effect': 'NONE', 'mathematical_acceptance': False, 'certified_enclosures': True, 'side24_v1': 'unchanged'})) PY git diff --exit-code diff --git a/reviews/side24_mutant_wrappers_20261026/SCOPE.md b/reviews/side24_mutant_wrappers_20261026/SCOPE.md new file mode 100644 index 000000000..df951a1a9 --- /dev/null +++ b/reviews/side24_mutant_wrappers_20261026/SCOPE.md @@ -0,0 +1,179 @@ +# SIDE24 d = 5, 6 mutant wrappers: authenticate the intended rejections + +Dylan Roy — delegated AI engineering. Actual author: Anthropic Claude, session +`session_017Mi3hxjaxV45x6zo6o1ee3` (Claude017), stage +`SIDE24-WRAPPERS-REPAIR-017-20261026`, pickup Math-#322/6427944196. Dylan approved +this stage in the author's session at about 21:30Z on 26 October 2026. This session +wrote the three packets. Scientific effect NONE; shared-account organizational +credit 0. Author-side implementation only: a separate nonauthor engineering review +and a separate integrator are required. No mathematical acceptance is asserted. + +The method is the one C8 used for its own wrappers in #518/#519 +(`reviews/c8_check_mode_wrappers_20261026/SCOPE.md`). The helper, references and +test are new, separate files, and C8's `tools/check_mode_*` and +`tests/test_check_mode_workflows.py` are untouched. + +## Finding and exact source + +01Cz's ledger (Math-#322/6388271177) classifies all three wrappers as +`status_only`. Each mutant was accepted on exit status 1 alone, with its stdout and +stderr discarded. So an unrelated crash, a silent exit 1, or a report that fails +for another reason counted as an intended rejection. The baseline was already +compared byte for byte with `RESULTS.json`, but its stderr was discarded. + +| Object | Identity | +|---|---| +| Math- `main` at characterization and branch base | `7d63d08f6cfadfd9c2ef28039a370d741126f9a0` (the merge of #519) | +| `side24-d5-closed-form.yml` before | blob `7c39892950068e1e7f9c9e0f4c1897cfbdfb3a30` | +| `side24-d5-coefficient.yml` before | blob `9f46fffa48b4162356bbe269e54f68602deef1c6` | +| `side24-d6-coefficient.yml` before | blob `6059c2ecde9e7edf4fda1efeece3ce964483e322` | +| `closed_form_d4.py` (unchanged) | blob `e087f4c0007ae2473d28230771588da57f794aab`, 45,019 B | +| `cone_moment_d5.py` (unchanged) | blob `9d918805865031c8ef9ae5d4d0c3266e1a396a98`, 46,082 B | +| `cone_moment_d6.py` (unchanged) | blob `c5e56694d96c885373a7b70868259eeb9fdfa746`, 68,731 B | +| d=5 closed-form `RESULTS.json` (unchanged) | 16,765 B, SHA-256 `b878c1d3534a1ce72f01bb461a06b8777888bc0a5fdd9413fc1a4f8a565a2bb3` | +| d=5 coefficient `RESULTS.json` (unchanged) | 5,435 B, SHA-256 `81c4bc072b7eab4d2f1725b3d873076dac6bc31ab1a06779d3299d9e81b3cbaf` | +| d=6 coefficient `RESULTS.json` (unchanged) | 8,319 B, SHA-256 `82d1c19f9f503fd9211c6dcf356ec62aee9ec9eda44792bc38be4f3690637832` | + +The three packet trees are `d2e9f088`, `dc7dfac0` and `dcbeffef`. The ledger read +the workflows at `d1f6ad28` as blobs `40fe9334`, `93ac2e5e` and `c45d9a5a`. The +only change since is 2c041de (24 October), which moved each file's +`actions/setup-python` pin from `5fda3b95` to `6d4fd22b`. The mutant logic the +ledger classified is unchanged. The imported `tools/legacy_json_replay.py` (blob +`854db466`) and `tests/test_legacy_workflow_triggers.py` (blob `c2e37a3a`) are +the blobs every local run below used. + +## Repair and preservation + +Nine files: `tools/side24_mutant_replay.py`, the three files in +`tools/side24_mutant_contracts/` and `tests/test_side24_mutant_workflows.py` (all +new), the three workflows, and this note. + +`tools/side24_mutant_replay.py` follows `tools/legacy_json_replay.py` and reuses +that module's strict JSON reader and structural comparison instead of copying +them. For each family it binds the checker's git blob and the `RESULTS.json` size +and digest. It then runs the fixed inventory in both modes, exactly as the old +wrappers did: from the repository root, with the absolute script path, and with +`