From f8ef14235d9f6ebb71c54a1bd7359fa7f3ef768a Mon Sep 17 00:00:00 2001 From: Dylan Roy <249851642+d6g8k5htny-coder@users.noreply.github.com> Date: Tue, 6 Oct 2026 06:52:53 -0700 Subject: [PATCH] Third-order replay: authenticate complete child reports and exact mutant inventory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dylan Roy — delegated AI engineering. Actual author OpenAI / GPT-6 Astra Pro, pr349-followthrough-third-order-protocol-20261006-r4; Math-#322 pickup6017164236. Scientific effect NONE; organizational-independence credit0. Three-file test-first repair. Original numerical/proof/RESULTS/SOURCES/dependencies and 7056-byte source preflight/137-byte postflight are preserved. Full146-case suites pass in both Python modes; exact predecessor yields98 intended failures/mode. Separate nonauthor source review, fresh applicable hosted execution and nonauthor/nonreader guarded integration remain required; no self-merge. --- .github/workflows/third-order-rate.yml | 71 ++++- .../third_order_protocol_20261006/SCOPE.md | 132 ++++++++ tests/test_third_order_rate_workflow.py | 300 ++++++++++++++++++ 3 files changed, 493 insertions(+), 10 deletions(-) create mode 100644 reviews/third_order_protocol_20261006/SCOPE.md create mode 100644 tests/test_third_order_rate_workflow.py diff --git a/.github/workflows/third-order-rate.yml b/.github/workflows/third-order-rate.yml index 85859c3ca..2c89975d8 100644 --- a/.github/workflows/third-order-rate.yml +++ b/.github/workflows/third-order-rate.yml @@ -5,6 +5,7 @@ name: Third-order rate 3/7 exact controls paths: - 'frontiers/third_order_rate_20261001/**' - '.github/workflows/third-order-rate.yml' + - 'tests/test_third_order_rate_workflow.py' # bound merged sources (consumed in SOURCES.json) and the pinned unmerged dependencies - 'frontiers/three_fronts_20260924/LIFETIME_REMAINDER.md' - 'imports/lifetime_parent_20260925/UNIFORM_MATRIX_CAP_AND_LIFETIME.md' @@ -37,6 +38,13 @@ jobs: - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 with: python-version: '3.11.16' + - name: Verify complete child contracts in both Python modes + env: + PYTHONDONTWRITEBYTECODE: '1' + run: | + set -euo pipefail + python -B -S -m unittest discover -s tests -p test_third_order_rate_workflow.py -v + python -B -O -S -m unittest discover -s tests -p test_third_order_rate_workflow.py -v - name: Verify sources, replay both modes, reject mutants env: PYTHONDONTWRITEBYTECODE: '1' @@ -142,19 +150,62 @@ jobs: now = current(entry) if kind == 'consumed_unmerged' else None print(json.dumps({kind + '_bound': entry['path'], 'git_blob': entry['git_blob'], 'head': entry['head'], 'via': where, 'current_ref_checked': now})) + # Complete typed JSON fingerprints measured from unchanged r37_check.py + # d521770d56d7c59604a949645332820091d69b8e in both Python modes. + # Fixed expectations, never learned from the child executing in this job. + mutants = ['M1', 'M2', 'M3', 'M4', 'M5', 'M6', 'M7', 'M8'] + if manifest.get('mutants') != mutants: + raise ValueError('third-order mutant inventory differs from the exact contract') + report_sha256 = { + 'M1': '1c8ee21898e24815a92e25d857eb9ebd1e37e986c2b8e05cde8c517fc85fd783', + 'M2': 'ca947f31b05b42df18711d83ae41b093f0364518daaa05197cc12e2f19139ed4', + 'M3': '1cd08221647a8e4d09103ac4f062041fa87a986a34600d9833389e373d9d3b91', + 'M4': 'ccb57cb465ef6fc3eddddc44152563d8cd60fad742cc58fc6c44b4fe6fca2f82', + 'M5': '359b1200abec0231f68302fb88c62d4df026e4a28fc94e176aada0959a21bc53', + 'M6': '6ef4e23a74fd63529b3482c634626f2cf32529c099ed717bee3556345b29bfc8', + 'M7': 'c18c8977019114dce23b6659d3d82e881bbaafa9c2ddcca2f09de1f67dba8340', + 'M8': '2b6da63037130a77ab2bd87d520b2b724dc28653001d01ee006f829e3ca8b682', + } + def unique_fields(pairs): + result = {} + for key, value in pairs: + if key in result: + raise ValueError('duplicate third-order report field: ' + key) + result[key] = value + return result + + def invalid_number(value): + raise ValueError('floating/nonfinite third-order report number: ' + value) + + def report_digest(raw): + value = json.loads(raw.decode('utf-8'), object_pairs_hook=unique_fields, + parse_float=invalid_number, parse_constant=invalid_number) + canonical = json.dumps(value, sort_keys=True, separators=(',', ':'), + ensure_ascii=False, allow_nan=False).encode('utf-8') + return hashlib.sha256(canonical).hexdigest() + script = str(root / 'r37_check.py') expected = (root / 'RESULTS.json').read_bytes() 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)) - for m in manifest['mutants']: - rc = subprocess.run([sys.executable, *flags, script, '--mutant', m], capture_output=True, timeout=600).returncode - if rc != 1: - raise RuntimeError('mutant ' + m + ' not rejected') - rc = subprocess.run([sys.executable, *flags, script, '--mutant', 'M9'], capture_output=True, timeout=600).returncode - if rc != 2: - raise RuntimeError('unknown mutant label not rejected with exit 2') + for mutant in (None, *mutants, 'M9'): + args = [sys.executable, *flags, script] + if mutant is not None: + args.extend(['--mutant', mutant]) + process = subprocess.run(args, capture_output=True, timeout=600) + if mutant is None: + valid = process.returncode == 0 and not process.stderr and process.stdout == expected + elif mutant == 'M9': + valid = (process.returncode == 2 and process.stdout == b'' + and process.stderr == b'unknown mutant label\n') + else: + valid = (process.returncode == 1 and not process.stderr + and report_digest(process.stdout) == report_sha256[mutant]) + if not valid: + raise RuntimeError('third-order child contract mismatch: ' + str(mutant)) + print(json.dumps({'flags': flags, 'mutant': mutant, 'returncode': process.returncode, + 'stdout_sha256': hashlib.sha256(process.stdout).hexdigest(), + 'stderr_sha256': hashlib.sha256(process.stderr).hexdigest(), + 'scientific_effect': 'NONE'}, sort_keys=True)) print(json.dumps({'scientific_effect': 'NONE', 'mathematical_acceptance': False})) PY git diff --exit-code diff --git a/reviews/third_order_protocol_20261006/SCOPE.md b/reviews/third_order_protocol_20261006/SCOPE.md new file mode 100644 index 000000000..2c889ab65 --- /dev/null +++ b/reviews/third_order_protocol_20261006/SCOPE.md @@ -0,0 +1,132 @@ +# Third-order rate workflow: complete child-result contracts + +Dylan Roy — delegated AI engineering. Author/executor: OpenAI / GPT-6 Astra Pro, +`pr349-followthrough-third-order-protocol-20261006-r4`, Math-#322 pickup6017164236. +Scientific effect NONE; source/report exposed; organizational-independence credit0. +No author self-merge. This repairs execution validation, not a mathematical proof. + +## Frozen predecessor and exact scope + +Source cut: `37bd017be7b1a48455fe651977fedc5b7105d188`. + +| Source | Bytes | Git blob | +|---|---:|---| +| .github/workflows/third-order-rate.yml |9922|85859c3ca46faf9b6302427de28aa488e1e13aee| +| frontiers/third_order_rate_20261001/r37_check.py |19191|d521770d56d7c59604a949645332820091d69b8e| +| frontiers/third_order_rate_20261001/RESULTS.json |2234|8a2921cfb82deb8962bd45923ba7559401f020e7| +| frontiers/third_order_rate_20261001/SOURCES.json |11327|1cccc27e4aea99a2a861bc32ebb556eba4a2408e| + +The original workflow accepts named mutants solely by exit1, unknown M9 solely +by exit2, and ignores baseline stderr. The earlier #322 list identified this +path as a candidate; this work supplies its specific executable counterexample +and repair. No scientific packet, proof, original checker, RESULTS, source map, +formal source, theorem annotation, dependency or scientific register changes. + +Only the existing workflow, one new root regression, and this scope are changed. +The 7056-byte original dependency/packet preflight and 137-byte final report/Git +postflight remain byte-identical. The tracked-tree regular-file, source hash, +commit/path/blob API, live dependency-PR drift and untracked-path guards are +retained without alteration. Action pins, Python3.11.16, read-only permissions, +600-second numerical-child and10-minute job budgets are unchanged. + +## Original checker characterization + +Twenty actual native commands completed under local CPython3.13.5: baseline, +M1–M8 and M9 in each normal/optimized Python mode. All ten mode-paired stdout AND +stderr streams are byte-identical. Baseline exactly matches the native RESULTS +blob/SHA256 `7f877f034baaa5b7c8a879a76dd2b825e57869478fdc396897cde1da9bf10ade`. +Named mutants exit1 with empty stderr and full JSON. M9 exits2 with empty stdout +and exactly `unknown mutant label` followed by one LF on stderr. + +| Mutant | Failed check; other checks remain present | +|---|---| +| M1, M2, M6, M8 |R1_exponent_ledger (four different complete reports)| +| M3 |R2_lemma_L (also changes equality_instances to41)| +| M4 |R3_refined_B4| +| M5 |R4_typed_window| +| M7 |R6_C_plus_CE_plus_bookkeeping| + +The repair compares the entire report, not just that failed-check column. +Its eight fixed canonical JSON SHA256 values were measured from the authenticated +unchanged checker before implementation and cross-checked in both modes. They +are not learned from a currently running child. Duplicate keys, floating or +nonfinite numbers, extra/missing/type-confused fields, trailing JSON, wrong +mutant/reason/info and unrelated failures reject. JSON whitespace and key order +are semantically immaterial under canonical serialization. The baseline keeps +its stricter original byte-equality rule and now requires empty stderr. Every +named output also requires empty stderr and exit1; M9 requires its complete +three-part exit/stdout/stderr contract. Manifest mutants must equal the exact +ordered M1–M8 list before the first child. No predicate relies on Python assert. + +The verifier prints each successfully validated child's original stream hashes +and exit; it stops at the first invalid result. Timeout/spawn failures propagate +rather than being mistaken for intended rejection. It is not a hostile-child +sandbox: a malicious executable can print the expected report. Source identity, +process output, mathematical validity and independent alignment remain distinct. +Output-memory/durable-artifact/process-group guarantees are not introduced. + +## Test-first whole-shell evidence + +The same final ten-method test source was run against the exact original and +repaired workflow in BOTH outer Python modes. Each completed suite runs146 +isolated real Git/Bash/Python shell scenarios; no subprocess is mocked. Each +fixture retains a nonempty consumed and cited tracked source, coherent packet +hashes, all20 ordered child slots and final Git-diff validation. Actual children +are synthetic carriers for independently fixed native reports, not numerical +proof computations. Four independent fixtures may execute concurrently; they +share no repo, HOME, trace or process environment. Assertion evaluation remains +in the parent test thread. Optional evidence output is outside fixture repos. + +Predecessor: **98 intended assertion failures, zero test errors, each mode**: +94 undesired report admissions, three incomplete/reordered/duplicate mutant +inventories, and one missing regression-wiring assertion. These are finite +protocol counterexamples, not98 independent mathematical defects. Seventeen +valid/formatting cases and the inherited rejecting exit/source/postflight +controls retain their intended outcomes. + +Candidate: **10/10 methods,146/146 expected outcomes each mode**, no failures, +errors or skips. Seventeen valid/formatting admissions and129 rejections. All +failed targets stop at the precise ordered prefix; preflight guards run before +children and dirty-tree rejection occurs after all20. Full normal/optimized +suite elapsed times were50.490s/41.571s including launch. The two completed +predecessor calls took79.245s/84.323s. Four completed suites retain584 scenario +records,1168 raw shell streams and8144 child trace entries. These counts overlap +and do not represent independent proofs. + +A separate execution of the exact repaired numerical-loop slice ran all20 +GENUINE original checker commands again. Every exit and stdout/stderr hash +matches the pre-repair characterization; final acceptance annotation stays +false. That execution deliberately does not claim the original dependency/API +preflight ran locally. Fresh hosted full workflow execution is still required. + +An initial sequential full-suite tool call stopped after17 completed cases; +there is no final suite summary, and it is excluded. A streaming-execution tool +attempt was unavailable and launched nothing. The independent fixtures were +parallelized without removing any case or weakening a product timeout. Final +complete paired runs use identical test bytes. Initial partial records and the +local execution-driver histories remain in the owner's evidence delivery. + +## Reproduction and integration boundary + +```sh +python -B -S -m unittest discover -s tests -p test_third_order_rate_workflow.py -v +python -B -O -S -m unittest discover -s tests -p test_third_order_rate_workflow.py -v +``` + +The new root-test path triggers the dedicated workflow, which runs both commands +unconditionally before its original verification step. There is no new imported +helper/package-resolution dependency. Local development used an authenticated +selected-source repository after direct GitHub DNS failed, not a full upstream +checkout. The local whole-shell fixtures cover merged-source preflights, not +successful live API retrieval; that code is byte-preserved and the hosted job +must supply its own full-source/current-runtime evidence. No local Lean or new +whole-mathematics review is claimed. + +Source remains frozen for one actual nonauthor engineering read. Required scope: +exact reports and canonical parser, whole original/new shell and source guard +preservation, meaningful counterfactual outcomes and actual hosted bindings. +A distinct eligible nonauthor/nonreader integrator must later reconcile live +refs/queue/full diff/reviews/threads/dependencies and every applicable check, +use ordinary expected-head merge, and read back the landed tree/push evidence. +This author will not merge. #322 remains open for its broader affected class; +#312, soft-fold, equal-height, factorial budget and all other owners keep scope. diff --git a/tests/test_third_order_rate_workflow.py b/tests/test_third_order_rate_workflow.py new file mode 100644 index 000000000..bd73275e0 --- /dev/null +++ b/tests/test_third_order_rate_workflow.py @@ -0,0 +1,300 @@ +"""Whole original verification shell, real Git/Bash/Python; numerical child is a fixture. + +Frozen report deltas were measured from unchanged checker d521770d at37bd017b +in both Python modes BEFORE the repair. They are not learned from a test child. +Successful remote API retrieval is not simulated or claimed by this suite; +all original remote source checks remain in the executed shell unchanged. +""" +from pathlib import Path +import copy +from concurrent.futures import ThreadPoolExecutor +import threading +import hashlib +import json +import os +import shutil +import subprocess +import sys +import tempfile +import unittest + +ROOT = Path(__file__).resolve().parents[1] +WORKFLOW = ROOT / '.github/workflows/third-order-rate.yml' +PACKET = 'frontiers/third_order_rate_20261001' +MUTANTS = tuple('M'+str(i) for i in range(1, 9)) +SEQUENCE = [(mode, m) for mode in (0, 1) for m in ('baseline', *MUTANTS, 'M9')] +BASELINE_SHA = '7f877f034baaa5b7c8a879a76dd2b825e57869478fdc396897cde1da9bf10ade' +# None below deletes the sole obsolete M2 exponent entry; no native delta sets null. +DELTAS = {'M1': [(('checks', 'R1_exponent_ledger', 'candidate_least'), '4/11'), + (('checks', 'R1_exponent_ledger', 'candidate_terms', 'F: l rho_f^-2 (finite part)'), '5/11'), + (('checks', 'R1_exponent_ledger', 'candidate_terms', 'F: rho_f^2'), '6/11'), + (('checks', 'R1_exponent_ledger', 'candidate_terms', 'F: rho_f^5/l (Lemma F layer)'), '4/11'), + (('checks', 'R1_exponent_ledger', 'candidate_terms', 'K: Lemma C+ r^1 kappa^1'), '5/11'), + (('checks', 'R1_exponent_ledger', 'candidate_terms', 'K: rho_f^5/l (Lemma O tail)'), '4/11'), + (('checks', 'R1_exponent_ledger', 'elder_least'), '4/11'), + (('checks', 'R1_exponent_ledger', 'elder_terms', 'F: l rho_f^-2 (finite part)'), '5/11'), + (('checks', 'R1_exponent_ledger', 'elder_terms', 'F: rho_f^2'), '6/11'), + (('checks', 'R1_exponent_ledger', 'elder_terms', 'F: rho_f^5/l (Lemma F layer)'), '4/11'), + (('checks', + 'R1_exponent_ledger', + 'elder_terms', + 'F: rho_f^5/l (rejected fold mass, [C7-K] (K2))'), + '4/11'), + (('checks', 'R1_exponent_ledger', 'elder_terms', 'K: Lemma CE+ r^1 kappa^1'), '5/11'), + (('checks', 'R1_exponent_ledger', 'elder_terms', 'K: Lemma CE+ r^2 kappa^3'), '6/11'), + (('checks', 'R1_exponent_ledger', 'elder_terms', 'K: Lemma CE+ r^3/2 kappa^2'), '1/2'), + (('checks', 'R1_exponent_ledger', 'elder_terms', "K: rho_f^5/l (Lemma O/O' tail)"), '4/11'), + (('checks', 'R1_exponent_ledger', 'passed'), False), + (('checks', 'R1_exponent_ledger', 'rho_f'), 'l^3/11'), + (('mutant',), 'M1'), + (('passed',), False)], + 'M2': [(('checks', 'R1_exponent_ledger', 'candidate_least'), '2/7'), + (('checks', 'R1_exponent_ledger', 'candidate_terms', 'K: Lemma C+ r^1 kappa^1'), None), + (('checks', 'R1_exponent_ledger', 'candidate_terms', 'K: Lemma C+ r^1 kappa^2'), '2/7'), + (('checks', 'R1_exponent_ledger', 'passed'), False), + (('checks', 'R1_exponent_ledger', 'remark1_maxmin', 'drop_linear_cand'), '4/11'), + (('mutant',), 'M2'), + (('passed',), False)], + 'M3': [(('checks', 'R2_lemma_L', 'equality_instances'), 41), + (('checks', 'R2_lemma_L', 'passed'), False), + (('mutant',), 'M3'), + (('passed',), False)], + 'M4': [(('checks', 'R3_refined_B4', 'passed'), False), (('mutant',), 'M4'), (('passed',), False)], + 'M5': [(('checks', 'R4_typed_window', 'passed'), False), (('mutant',), 'M5'), (('passed',), False)], + 'M6': [(('checks', 'R1_exponent_ledger', 'a'), 'l^2/15'), + (('checks', 'R1_exponent_ledger', 'elder_least'), '2/5'), + (('checks', 'R1_exponent_ledger', 'elder_terms', 'I: [a,r0*] l^(2/3) a^-2'), '2/5'), + (('checks', 'R1_exponent_ledger', 'elder_terms', 'I: [a,r0*] l^(5/3) a^-7'), '11/15'), + (('checks', 'R1_exponent_ledger', 'elder_terms', 'I: [rho_c,a] a^4 (log)'), '8/15'), + (('checks', 'R1_exponent_ledger', 'intermediate_least'), '2/5'), + (('checks', 'R1_exponent_ledger', 'passed'), False), + (('mutant',), 'M6'), + (('passed',), False)], + 'M7': [(('checks', 'R6_C_plus_CE_plus_bookkeeping', 'passed'), False), + (('mutant',), 'M7'), + (('passed',), False)], + 'M8': [(('checks', 'R1_exponent_ledger', 'candidate_least'), '1/4'), + (('checks', 'R1_exponent_ledger', 'candidate_terms', 'J2: rho_c^3'), '3/4'), + (('checks', 'R1_exponent_ledger', 'candidate_terms', 'J3: l rho_c^-2'), '1/2'), + (('checks', 'R1_exponent_ledger', 'candidate_terms', 'J3: l^2 rho_c^-7'), '1/4'), + (('checks', 'R1_exponent_ledger', 'candidate_terms', 'J4: l^2 rho_c^-7'), '1/4'), + (('checks', 'R1_exponent_ledger', 'candidate_terms', 'K: Lemma C+ r^1 kappa^0'), '1/2'), + (('checks', 'R1_exponent_ledger', 'candidate_terms', 'K: l^2 rho_c^-7 (upper tail)'), '1/4'), + (('checks', 'R1_exponent_ledger', 'elder_least'), '1/4'), + (('checks', 'R1_exponent_ledger', 'elder_terms', 'I: [rho_c,a] l^3 rho_c^-11 (log)'), '1/4'), + (('checks', 'R1_exponent_ledger', 'elder_terms', 'J4: l^2 rho_c^-7'), '1/4'), + (('checks', 'R1_exponent_ledger', 'elder_terms', 'K: Lemma CE+ r^1 kappa^0'), '1/2'), + (('checks', 'R1_exponent_ledger', 'elder_terms', 'K: l^2 rho_c^-7 (upper tail)'), '1/4'), + (('checks', 'R1_exponent_ledger', 'hypotheses'), False), + (('checks', 'R1_exponent_ledger', 'intermediate_least'), '1/4'), + (('checks', 'R1_exponent_ledger', 'passed'), False), + (('checks', 'R1_exponent_ledger', 'rho_c'), 'l^1/4'), + (('mutant',), 'M8'), + (('passed',), False)]} + + +def identity(path, raw): + return dict(path=path, bytes=len(raw), sha256=hashlib.sha256(raw).hexdigest(), + git_blob=hashlib.sha1(b'blob '+str(len(raw)).encode()+b'\0'+raw).hexdigest()) + + +def native_reports(): + raw = (ROOT/PACKET/'RESULTS.json').read_bytes() + if hashlib.sha256(raw).hexdigest() != BASELINE_SHA: + raise ValueError('test reference baseline changed') + baseline = json.loads(raw) + reports = {'baseline': raw.decode()} + for name, changes in DELTAS.items(): + obj = copy.deepcopy(baseline) + for keys, value in changes: + node = obj + for key in keys[:-1]: + node = node[key] + if value is None: + del node[keys[-1]] + else: + node[keys[-1]] = value + reports[name] = json.dumps(obj, indent=1, sort_keys=True)+'\n' + return reports + + +def verification_shell(): + # Deliberately restricted to this workflow's uniquely named literal run block. + text = WORKFLOW.read_text() + name = ' - name: Verify sources, replay both modes, reject mutants\n' + if text.count(name) != 1: + raise ValueError('verification step not unique') + tail = text.split(name, 1)[1] + block = tail.split(' run: |\n', 1)[1] + return ''.join(line[10:] if line.startswith(' ') else line + for line in block.splitlines(keepends=True)) + + +def fixture_script(reports): + return ('import os,sys,json\nfrom pathlib import Path\nREPORTS = '+repr(reports)+'\n'+r""" +m = sys.argv[2] if len(sys.argv)>2 else 'baseline' +mode = int(sys.flags.optimize) +with open(os.environ['TRACE'], 'a') as f: + f.write(json.dumps([mode,m])+'\n') +out = b'' if m == 'M9' else REPORTS[m].encode() +err = b'unknown mutant label\n' if m == 'M9' else b'' +rc = 2 if m == 'M9' else (0 if m == 'baseline' else 1) +case = os.environ['CASE'] +if m == os.environ['TARGET'] and mode == int(os.environ['TARGET_MODE']): + if case == 'crash': raise RuntimeError('unrelated synthetic failure') + elif case == 'silence': out = err = b'' + elif case == 'stderr': err += b'unexpected warning\n' + elif case == 'wrong_exit': rc = 0 if m != 'baseline' else 1 + elif case == 'wrong_report': out = REPORTS['M2' if m != 'M2' else 'M1'].encode() + elif case == 'wrong_baseline': out += b' ' + elif case == 'unknown_reason': err = b'wrong failure reason\n' + elif case == 'unknown_stdout': out = b'noise\n' + elif case == 'unknown_exit': rc = 1 + else: + obj = json.loads(out) + if case == 'format': + out = (' \n'+json.dumps(obj,ensure_ascii=False,sort_keys=False)+' \n').encode() + elif case == 'duplicate': out = out.replace(b'"passed": false', b'"passed": false, "passed": false', 1) + elif case == 'trailing': out += b'{}' + elif case == 'nonfinite': out = out.replace(b'"passed": false', b'"passed": NaN', 1) + elif case == 'float': out = out.replace(b'6108', b'6108.0', 1) + elif case == 'int_bool': + obj['passed']=0; out=json.dumps(obj).encode() + elif case == 'missing': + del obj['checks']['R5_W_plus_bookkeeping']; out=json.dumps(obj).encode() + elif case == 'extra': + obj['extra']=True; out=json.dumps(obj).encode() + elif case == 'wrong_info': + obj['checks']['R2_lemma_L']['instances']+=1; out=json.dumps(obj).encode() + elif case == 'wrong_mutant': + obj['mutant']='M8'; out=json.dumps(obj).encode() + elif case == 'not_json': out=b'FAILED some other check\n' +if case == 'dirty' and mode==1 and m=='M9': + Path('consumed.txt').write_text('changed after preflight\n') +sys.stdout.buffer.write(out);sys.stderr.buffer.write(err);sys.exit(rc) +""") + + +class ThirdOrderProtocolTests(unittest.TestCase): + counter = 0 + evidence_lock = threading.Lock() + + def run_case(self, case='valid', target='M1', mode=0, guard=None): + with tempfile.TemporaryDirectory(prefix='third-order-test-') as tmp: + home=Path(tmp);repo=home/'repo';repo.mkdir();packet=repo/PACKET;packet.mkdir(parents=True) + reports=native_reports() + (packet/'r37_check.py').write_text(fixture_script(reports)) + (packet/'RESULTS.json').write_text(reports['baseline']) + for name in ('consumed.txt','cited.txt'): + (repo/name).write_text('fixed '+name+'\n') + manifest={'files':[identity(p.name,p.read_bytes()) for p in sorted(packet.iterdir())], + 'mutants':list(MUTANTS),'consumed':[identity('consumed.txt',(repo/'consumed.txt').read_bytes())], + 'cited_only':[identity('cited.txt',(repo/'cited.txt').read_bytes())], + 'consumed_unmerged':[],'cited_unmerged':[]} + if guard=='missing_mutant': manifest['mutants'].pop() + if guard=='duplicate_mutant': manifest['mutants'].append('M1') + if guard=='reordered_mutant': manifest['mutants'].reverse() + (packet/'SOURCES.json').write_text(json.dumps(manifest)) + env={k:v for k,v in os.environ.items() if not k.startswith('GIT_')} + env.update(HOME=str(home),XDG_CONFIG_HOME=str(home/'xdg'),GIT_CONFIG_NOSYSTEM='1', + PYTHONDONTWRITEBYTECODE='1',GH_TOKEN='synthetic-no-network-token', + GH_REPOSITORY='fixture/repo',TRACE=str(home/'trace'),CASE=case, + TARGET=target,TARGET_MODE=str(mode)) + for cmd in (['git','init','-q'],['git','add','.'], + ['git','-c','user.name=Fixture','-c','user.email=f@example.invalid', + '-c','commit.gpgsign=false','commit','-qm','fixture']): + subprocess.run(cmd,cwd=repo,env=env,capture_output=True,check=True,timeout=15) + if guard=='packet_tamper': (packet/'r37_check.py').write_text('# changed\n') + if guard=='packet_missing': (packet/'RESULTS.json').unlink() + if guard=='packet_extra': (packet/'extra.txt').write_text('extra') + if guard=='packet_symlink': + (packet/'r37_check.py').rename(home/'saved.py');(packet/'r37_check.py').symlink_to(home/'saved.py') + if guard=='consumed_tamper': (repo/'consumed.txt').write_text('changed') + if guard=='cited_missing': (repo/'cited.txt').unlink() + if guard=='cited_symlink': + (repo/'cited.txt').rename(home/'saved.txt');(repo/'cited.txt').symlink_to(home/'saved.txt') + shell=verification_shell() + result=subprocess.run(['bash','-c',shell],cwd=repo,env=env,capture_output=True,timeout=45) + trace=[tuple(json.loads(s)) for s in (home/'trace').read_text().splitlines()] if (home/'trace').exists() else [] + # Optional external evidence is never committed or used to choose an expectation. + destination=os.environ.get('THIRD_ORDER_TEST_EVIDENCE') + if destination: + folder=Path(destination);folder.mkdir(parents=True,exist_ok=True) + with type(self).evidence_lock: + type(self).counter+=1;stem=f'{type(self).counter:04d}' + meta={'case':case,'target':target,'mode':mode,'guard':guard, + 'returncode':result.returncode,'trace':trace, + 'stdout_sha256':hashlib.sha256(result.stdout).hexdigest(), + 'stderr_sha256':hashlib.sha256(result.stderr).hexdigest()} + for suffix,raw in [('json',(json.dumps(meta,sort_keys=True)+'\n').encode()), + ('stdout',result.stdout),('stderr',result.stderr)]: + with (folder/f'{stem}.{suffix}').open('xb') as f:f.write(raw) + return result,trace + + def check_cases(self, cases): + # Each case owns its repo, HOME, process environment and external trace. + # Parallelism changes only fixture scheduling, not any verifier command. + with ThreadPoolExecutor(max_workers=4) as pool: + executions = list(pool.map(lambda c: self.run_case(*c[:3]), cases)) + for (case,target,mode,valid), (p,trace) in zip(cases,executions): + with self.subTest(case=case,target=target,mode=mode): + self.assertEqual(p.returncode,0 if valid else 1,(case,target,mode,p.stderr)) + expected=SEQUENCE if valid else SEQUENCE[:SEQUENCE.index((mode,target))+1] + self.assertEqual(trace,expected,(case,target,mode)) + if valid: + self.assertIn(b'"mathematical_acceptance": false',p.stdout) + self.assertEqual(p.stderr,b'') + + def test_valid_and_formatting(self): + self.check_cases([('valid','M1',0,True)]+[ + ('format',target,mode,True) for mode in (0,1) for target in MUTANTS]) + + def test_crash_silence_wrong_report_each_mutant(self): + self.check_cases([(case,target,mode,False) for mode in (0,1) + for target in MUTANTS for case in ('crash','silence','wrong_report')]) + + def test_stderr_and_exit_each_mutant(self): + self.check_cases([(case,target,mode,False) for mode in (0,1) + for target in MUTANTS for case in ('stderr','wrong_exit')]) + + def test_complete_typed_report_and_json(self): + self.check_cases([(case,'M1',mode,False) for mode in (0,1) + for case in ('duplicate','trailing','nonfinite','float','int_bool', + 'missing','extra','wrong_info','wrong_mutant','not_json')]) + + def test_baseline_exact_exit_stdout_and_stderr(self): + self.check_cases([(case,'baseline',mode,False) for mode in (0,1) + for case in ('stderr','wrong_exit','wrong_baseline','silence')]) + + def test_unknown_exact_contract(self): + self.check_cases([(case,'M9',mode,False) for mode in (0,1) + for case in ('silence','unknown_reason','unknown_stdout','unknown_exit','stderr')]) + + def test_source_guards_before_children(self): + for guard in ('packet_tamper','packet_missing','packet_extra','packet_symlink', + 'consumed_tamper','cited_missing','cited_symlink'): + with self.subTest(guard=guard): + p,trace=self.run_case(guard=guard) + self.assertEqual(p.returncode,1);self.assertEqual(trace,[]) + self.assertNotIn(b'"mathematical_acceptance": false',p.stdout) + + def test_exact_mutant_inventory_before_children(self): + for guard in ('missing_mutant','duplicate_mutant','reordered_mutant'): + with self.subTest(guard=guard): + p,trace=self.run_case(guard=guard) + self.assertEqual(p.returncode,1);self.assertEqual(trace,[]) + + def test_postflight_mutation_is_rejected(self): + p,trace=self.run_case('dirty') + self.assertEqual(p.returncode,1);self.assertEqual(trace,SEQUENCE) + self.assertIn(b'diff --git',p.stdout) + + def test_unconditional_two_mode_wiring(self): + text=WORKFLOW.read_text() + self.assertIn(" - 'tests/test_third_order_rate_workflow.py'",text) + for flags in ('-B -S','-B -O -S'): + self.assertIn('python '+flags+' -m unittest discover -s tests -p test_third_order_rate_workflow.py -v',text) + + +if __name__=='__main__': + unittest.main()