Files
stack/agents/dewey/work/queue-69-review/r1/mutants.py
T
2026-10-12 18:16:28 -05:00

39 lines
3.0 KiB
Python

# Row 69 round 1 review mutants (Dewey): each swaps one exact string in a scratch copy of the
# candidate, runs every packages/cli test in its own process group (killed after 300 s, which
# counts as a kill), and restores the file. The first five are the packet's own sites, rerun.
import subprocess, sys, os, signal
M = os.path.expanduser('~/dewey-scratch/r69mut')
env = dict(os.environ, TMPDIR=os.path.expanduser('~/dewey-scratch/tmp'))
S = 'packages/cli/src/screens.mjs'; A = 'packages/cli/src/app.mjs'
muts = [
('shown-no-min-cols', S, 'return cols >= MIN_COLS && rows >= MIN_ROWS && o?.kind', 'return rows >= MIN_ROWS && o?.kind'),
('n9-17000-end-off', S, '(n >= 0x17000 && n <= 0x18d08)', '(n >= 0x17000 && n <= 0x18cff)'),
('cut-drops-choice', S, 'const tail = b.slice(-2);', 'const tail = b.slice(-1);'),
('hidden-no-way-out', S, ', "Resize, or use mosaic decide."]', ']'),
('hidden-status-promises-y', A, 'const state = hidden ? {', 'const state = false ? {'),
('hidden for any overlay', A, 'const hidden = o?.kind === "decide" && !decideShown', 'const hidden = !decideShown'),
('hidden status written into state', A, 'hidden ? { ...this.state, status: "Too small to confirm here.", statusStyle: "warning" } : this.state', 'hidden ? Object.assign(this.state, { status: "Too small to confirm here.", statusStyle: "warning" }) : this.state'),
('hidden only while asking', A, 'o?.kind === "decide" && !decideShown(o, this.cols, this.rows)', 'o?.kind === "decide" && o.asking && !decideShown(o, this.cols, this.rows)'),
('hidden status muted', A, 'status: "Too small to confirm here.", statusStyle: "warning"', 'status: "Too small to confirm here.", statusStyle: "muted"'),
('way out only while asking', S, ', "Resize, or use mosaic decide."]', ', ...(o.asking ? ["Resize, or use mosaic decide."] : [])]'),
('draw too-small from 30 columns', S, 'if (cols < MIN_COLS || rows < MIN_ROWS) return tooSmall', 'if (cols < 30 || rows < MIN_ROWS) return tooSmall'),
]
tests = sorted('packages/cli/tests/' + f for f in os.listdir(os.path.join(M, 'packages/cli/tests')) if f.endswith('.test.mjs'))
for name, f, old, new in muts:
p = os.path.join(M, f); src = open(p).read()
n = src.count(old)
if n != 1: print(f'BAD-ANCHOR ({n}) {name}', flush=True); continue
open(p, 'w').write(src.replace(old, new))
proc = subprocess.Popen(['node', '--test', *tests], cwd=M, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, start_new_session=True)
try:
out, _ = proc.communicate(timeout=300)
except subprocess.TimeoutExpired:
os.killpg(proc.pid, signal.SIGKILL); proc.communicate()
out = None
finally:
open(p, 'w').write(src)
if out is None:
print(f'killed {name} (hung: killed after 300 s)', flush=True); continue
fails = [l for l in out.splitlines() if l.startswith('not ok')]
print(('killed ' if proc.returncode else 'SURVIVED') + f' {name}' + (f' ({len(fails)} failing: ' + ' | '.join(l[7:90] for l in fails) + ')' if fails else ''), flush=True)