diff --git a/reviews/legacy_tty_diagnostic_20261006/SCOPE.md b/reviews/legacy_tty_diagnostic_20261006/SCOPE.md new file mode 100644 index 000000000..11c6bcdb8 --- /dev/null +++ b/reviews/legacy_tty_diagnostic_20261006/SCOPE.md @@ -0,0 +1,102 @@ +# Legacy replay: match the captured child's diagnostic width + +Dylan Roy — delegated AI engineering. Actual author: OpenAI / GPT-6 Astra Pro, +`legacy-tty-followon-20261006`. Scientific effect **NONE**; shared-account +organizational-independence credit **0**. This is a narrow successor to #330, +not a new review of that repair or a scientific checker change. The original +#330 author and integrator retain their own attribution. + +## Source and placement + +The predecessor is Math-#330 reviewed head +`f220b70da3c8b21131116d8e91473f5dbf65e446`. Its helper is Git blob +`7f10e0c549c850ab151b55e982eb9fa827a8f8c0`, its test file is +`3dd05f3b26361cab2299b3f646157c9db3de21c9`, and its reference JSON is +`349fc3276ce02ba80aecd7ecc897e37349c65007`. That source review +[6008784871](https://github.com/d6g8k5htny-coder/Math-/pull/330#issuecomment-6008784871) +reported the terminal mismatch as nonblocking for the verified hosted workflow. +This work was separately claimed in +[6008915837](https://github.com/d6g8k5htny-coder/Math-/pull/330#issuecomment-6008915837). +It does not reopen that verdict or write to the parent's branch. Integration is +a separately reviewed follow-on after #330, never a merge into its author branch. + +## Reproduced cause + +The parent `_unknown_stderr` reconstructs the fixed C6 argparse grammar; the +actual checker is launched with `capture_output=True`. CPython's HelpFormatter +uses terminal columns minus two unless an explicit width is supplied. The +terminal-size helper first accepts a positive integer `COLUMNS`, otherwise it +queries `sys.__stdout__`, and falls back to 80 columns for a pipe. + +Thus a parent connected to a 30- or 220-column real terminal can render different +usage bytes from its captured child when `COLUMNS` is absent or invalid. Merely +using the same interpreter does not make their output devices identical. This +is a false rejection of a valid unknown-label diagnostic, not acceptance of an +unrelated crash and not a mathematical counterexample. + +Primary references: +- [Python 3.13 terminal-size semantics](https://docs.python.org/3.13/library/shutil.html#shutil.get_terminal_size). +- [Pinned CPython 3.11.16 HelpFormatter](https://github.com/python/cpython/blob/v3.11.16/Lib/argparse.py). + +## Smallest repair + +Only the C6 grammar renderer now supplies an explicit HelpFormatter width. It +uses the inherited positive integer `COLUMNS`, or 80 when absent, invalid or +nonpositive, then subtracts two just as CPython 3.11–3.13 does. It does not change +the environment, the actual checker, its flags, or its captured output. It does +not execute the checker to learn an expectation, strip usage text, accept a +substring, or relax full-byte stderr comparison. + +All other helper functions, original twelve test methods, reference reports, +scientific scripts/RESULTS/manifests/proofs, and workflow source guards remain +unchanged. The added `os` import has no mutation side effect. No new dependency, +workflow, timeout, permission, action pin, or scientific flag is introduced. +The existing three workflow path filters and direct test-file invocations +already discover the new tests. + +## Test-first evidence + +Actual local runtime: CPython 3.13.5, standard library, Linux. The three new +methods use real pseudo-terminals, not terminal/subprocess mocks. Every case +checks the parent really has a TTY at the requested width and invokes the +unchanged authentic C6 script, Git blob +`91f485f529e09bebcd92f5a8f41d87bf01efc6d6`, with `--mutant unknown`. This exits +before the script's mathematical checks; it is diagnostic execution only. + +The cases cover widths 30, 80 and 220 with no override; positive overrides +35, 80, 220 and whitespace-padded 110; and empty, zero, negative and nonnumeric +overrides. Parent and checker each run normally and optimized. That is 38 PTY +cases per outer suite run. All child exits must be 2, stdout empty, and stderr +byte-identical to the independently constructed parent expectation. + +Before changing production code, these three methods failed in exactly twenty +subcases in each outer mode, with assertion failures and no errors. After the +fix they passed all 38 cases per mode. The final complete test file passed +**15/15 methods in each outer mode**: the original 201 whole-shell synthetic +fixtures plus 38 new PTY cases, 478 cases across both final runs. Original +crash/silence/wrong-reason/source-tamper rejection and harmless formatting +acceptance remain exercised. Repeated focused runs are not new unique tests. + +```sh +python -B -S tests/test_legacy_json_workflows.py +python -B -O -S tests/test_legacy_json_workflows.py +``` + +The PTY class is explicitly skipped on non-POSIX systems; no Windows PTY result +is claimed. File descriptors close in `finally`, and real subprocesses have +finite timeouts. The unchanged whole-shell fixtures still inherit the parent's +ambient Git-signing limitation; that separate observation is not repaired here. + +## Assurance boundary + +Local inputs were reconstructed from authenticated public blobs because direct +GitHub DNS was unavailable. This is not a full repository checkout, a genuine +complete ancestor-manifest replay, local Lean execution, or a hosted 3.11.16 +receipt. The full-shell fixtures are synthetic; only the new invalid-label +child uses the authentic scientific script. Applicable fresh hosted checks and +a separate nonauthor delta review are required before eligible integration. + +The width calculation targets the documented/pinned CPython behavior, not an +unreviewed future argparse implementation. No theorem review, numerical +certificate, new error budget, formal alignment, or whole-program audit closure +follows. The old #330 review does not automatically approve these changed bytes. diff --git a/tests/test_legacy_json_workflows.py b/tests/test_legacy_json_workflows.py index 3dd05f3b2..ef2fa5850 100644 --- a/tests/test_legacy_json_workflows.py +++ b/tests/test_legacy_json_workflows.py @@ -1,8 +1,9 @@ -"""Whole-shell regression tests; every repository and checker here is synthetic. +"""Synthetic whole-shell regressions and authentic child-diagnostic PTY checks. -The fixture commits source/manifest/upstream bytes, then runs the actual workflow -verification block with real subprocesses. Child stdout uses the independently -measured report fixtures. This is protocol coverage, not a field calculation. +The workflow fixture commits synthetic source/manifest/upstream bytes, then runs +the actual verification block with real subprocesses and measured report fixtures. +LegacyTerminalTests separately invokes the authentic C6 invalid-label command. +This is protocol coverage, not a field calculation. """ from __future__ import annotations import hashlib @@ -226,6 +227,77 @@ def test_baseline_bytes_remain_exact(self): for mode in ['normal','optimized']: with self.subTest(family=fam,mode=mode):self.rejected(fam,'baseline',mode,'format') +@unittest.skipUnless(os.name == 'posix', 'real PTY regression requires POSIX') +class LegacyTerminalTests(unittest.TestCase): + """Compare the real captured checker with a parent attached to a real PTY.""" + + def diagnostic_case(self, width, columns, optimized): + import fcntl + import pty + import struct + import termios + script = ROOT/'frontiers/c6_cluster_law_20260929/cluster_law_check.py' + self.assertEqual(blob(script.read_bytes()), + '91f485f529e09bebcd92f5a8f41d87bf01efc6d6') + env = os.environ.copy() + env.pop('COLUMNS', None) + env.pop('LINES', None) + if columns is not None: + env['COLUMNS'] = columns + flags = ['-B', '-O', '-S'] if optimized else ['-B', '-S'] + code = ''' +import importlib.util, json, os, subprocess, sys +spec = importlib.util.spec_from_file_location('legacy_parent', sys.argv[1]) +helper = importlib.util.module_from_spec(spec) +spec.loader.exec_module(helper) +expected = helper._unknown_stderr('c6-cluster', 'cluster_law_check.py') +child_flags = ['-B', '-O', '-S'] if sys.flags.optimize else ['-B', '-S'] +child = subprocess.run([sys.executable, *child_flags, sys.argv[2], '--mutant', 'unknown'], + capture_output=True, timeout=15) +print(json.dumps({'tty':sys.stdout.isatty(), 'width':os.get_terminal_size(1).columns, + 'returncode':child.returncode, 'stdout':child.stdout.hex(), + 'actual':child.stderr.hex(), 'expected':expected.hex()}), file=sys.stderr) +''' + master, slave = pty.openpty() + try: + fcntl.ioctl(slave, termios.TIOCSWINSZ, + struct.pack('HHHH', 24, width, 0, 0)) + run = subprocess.run( + [sys.executable, *flags, '-c', code, + str(SOURCE/'tools/legacy_json_replay.py'), str(script)], + stdout=slave, stderr=subprocess.PIPE, env=env, timeout=30) + finally: + os.close(slave) + os.close(master) + self.assertEqual(run.returncode, 0, run.stderr.decode(errors='replace')) + record = json.loads(run.stderr) + self.assertTrue(record['tty']) + self.assertEqual(record['width'], width) + self.assertEqual(record['returncode'], 2) + self.assertEqual(record['stdout'], '') + self.assertEqual(record['actual'], record['expected'], record) + + def test_unknown_diagnostic_uses_child_pipe_width(self): + for width in (30, 80, 220): + for optimized in (False, True): + with self.subTest(width=width, optimized=optimized): + self.diagnostic_case(width, None, optimized) + + def test_columns_override_matches_captured_child(self): + for width in (30, 220): + for columns in ('35', '80', '220', ' 110 '): + for optimized in (False, True): + with self.subTest(width=width, columns=columns, optimized=optimized): + self.diagnostic_case(width, columns, optimized) + + def test_invalid_columns_use_pipe_fallback(self): + for width in (30, 220): + for columns in ('', '0', '-3', 'not-a-number'): + for optimized in (False, True): + with self.subTest(width=width, columns=columns, optimized=optimized): + self.diagnostic_case(width, columns, optimized) + + ORDERS={'local-pairing':['M1','M2','M3','M4'],'far-elder':['M1','M2','M3','M4'],'c6-cluster':['pins-not-critical','wrong-pin-hessian','drop-sigma-jacobian','three-extra-points','shear-drop-cubic','s-bound-constant','cross-term-not-small','window-closed','index-sign']} # BASELINES inserted below from native RESULTS, independently hash-bound above. BASELINES = {'c6-cluster': '{\n "groups": [\n "NF",\n "RS",\n "EX",\n "BD",\n "EU",\n "SH",\n "TW",\n "LG"\n ],\n "ledger": {\n "cross_term_exponent": "9/2",\n "hadamard_row_factors": {\n "3": 6,\n "4": 8\n },\n "soft_slice_exponent": 3,\n "ui_tail": "1/(M-1)"\n },\n "mathematical_acceptance": false,\n "object": "CL-C6-CLUSTER-LAW-20260929-v1.2",\n "passed": true,\n "q2_terms": 12,\n "schema": 1,\n "scientific_effect": "NONE",\n "scope": "exact identities, exact configurations and exponent bookkeeping only",\n "typed_window": {\n "family_size": 201720,\n "typed_in_window": 2590\n }\n}\n', 'far-elder': '{\n "checks": {\n "B1_barrier_polynomial": {\n "info": {\n "K": "22",\n "R": "1/11",\n "grid_points_in_ball": 1793,\n "lambda": "2"\n },\n "passed": true\n },\n "B2_algebra_and_constants": {\n "info": {\n "L=1": {\n "a_L": "1/4",\n "c_L": "1/48"\n },\n "L=2": {\n "a_L": "1/2",\n "c_L": "1/12"\n },\n "L=24": {\n "a_L": "1",\n "c_L": "1/3"\n },\n "L=4": {\n "a_L": "1",\n "c_L": "1/3"\n }\n },\n "passed": true\n },\n "I1_split_integral": {\n "info": {\n "q=2,u=1/100": "149/1000000",\n "q=2,u=1/4": "5/64",\n "q=2,u=1/9": "25/1458",\n "q=4,u=1/100": "299999999/4000000000000",\n "q=4,u=1/4": "767/16384",\n "q=4,u=1/9": "9841/1062882"\n },\n "passed": true\n },\n "I2_exponent_ledger": {\n "info": {\n "count exponent": "5/3",\n "inverse moment threshold": "-5/3",\n "u^2 exponent of ell": "2/3"\n },\n "passed": true\n },\n "N1_near_consistency": {\n "info": {\n "6*sqrt(6 c_L) < 12 for c_L <= 1/3": true\n },\n "passed": true\n }\n },\n "object": "CL-FAR-ELDER-RATE-20260930-v1",\n "passed": true,\n "scientific_effect": "NONE"\n}\n', 'local-pairing': '{\n "checks": {\n "I0_I3_identities": {\n "info": 144,\n "passed": true\n },\n "N1_n0_grid": {\n "info": {\n "n0_points_with_U_hypothesis": 0,\n "n0_typed_grid_points": 135\n },\n "passed": true\n },\n "P1_path_cases": {\n "info": {\n "cases": {\n "U": 2160,\n "V": 4680,\n "V_at_other_root": 2160\n },\n "min_margin": "1/1200",\n "samples": 6840\n },\n "passed": true\n },\n "P2_elder_witness": {\n "info": {\n "Z1_squared": "15/8",\n "n": 2,\n "saddle_height": "-7/32",\n "u0": "-3/4",\n "vertical_line_constant": true\n },\n "passed": true\n }\n },\n "object": "CL-LOCAL-PAIRING-20260930-v1",\n "passed": true,\n "scientific_effect": "NONE"\n}\n'} diff --git a/tools/legacy_json_replay.py b/tools/legacy_json_replay.py old mode 100644 new mode 100755 index 7f10e0c54..30b1194d6 --- a/tools/legacy_json_replay.py +++ b/tools/legacy_json_replay.py @@ -14,6 +14,7 @@ import hashlib import io import json +import os from pathlib import Path import re import subprocess @@ -106,10 +107,19 @@ def _contract(family: str) -> dict[str, Any]: def _unknown_stderr(family: str, script_name: str) -> bytes: if family != 'c6-cluster': return b'unknown mutant label\n' - # Reproduce only argparse's frozen grammar in the parent process. This does - # NOT execute or learn from the checker. Its version-specific usage wrapping - # matches the child because both use the exact same Python interpreter. - parser = argparse.ArgumentParser(prog=script_name) + # Reproduce the frozen grammar, not output learned from the checker. The + # child has captured stdout, so shutil's terminal-width query cannot use the + # parent's TTY. Match its positive COLUMNS override or 80-column pipe fallback. + try: + columns = int(os.environ.get('COLUMNS', '0')) + except ValueError: + columns = 0 + if columns <= 0: + columns = 80 + # CPython 3.11--3.13 HelpFormatter subtracts two from the terminal columns. + parser = argparse.ArgumentParser( + prog=script_name, + formatter_class=lambda prog: argparse.HelpFormatter(prog, width=columns - 2)) parser.add_argument('--mutant', choices=C6_CHOICES) stream = io.StringIO() try: