Files
stack/agents/darkwing/work/queue-115/r1/mutants.py
T
jason.woltjeandClaude Opus 5.5 b3fca1f1a6 docs: row 115 round 1 packet, decision close message (#1609)
Candidate on base 94ece491f: 5 files, manifest f37bc933. Three mutants
killed; gate passed with no flakes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:44:46 -05:00

57 lines
2.7 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# Row 115 (#1609): the brief's three mutants of the decision close message.
# m1 drops the message: the close block never runs.
# m2 addresses the message to route_to instead of the raising role.
# m3 drops the raising-run check, so the raiser's own withdraw sends one too.
# Each runs the FULL bus suite (node --test tests/*.test.mjs in packages/bus)
# and restores the source from the .orig copy whatever happens.
# Usage: mutants.py <worktree> <log dir>
import os, shutil, subprocess, sys, signal
WT, LOGS = sys.argv[1], sys.argv[2]
MUTANTS = {
'm1': ('packages/bus/src/broker.mjs',
" if (op !== 'seen' && (s.human || s.run !== d.raised_by_run)) {\n",
" if (false) {\n"),
'm2': ('packages/bus/src/broker.mjs',
" d.raised_by_role,\n 'DECISION',\n",
" d.route_to,\n 'DECISION',\n"),
'm3': ('packages/bus/src/broker.mjs',
" if (op !== 'seen' && (s.human || s.run !== d.raised_by_run)) {\n",
" if (op !== 'seen') {\n"),
}
os.makedirs(LOGS, exist_ok=True)
env = dict(os.environ, TMPDIR=os.path.expanduser('~/darkwing-scratch/r115/tmp'))
for name, (rel, cur, new) in MUTANTS.items():
src = os.path.join(WT, rel)
text = open(src).read()
if text.count(cur) != 1:
sys.exit(f'STOP {name}: the target text in {rel} is not unique or has moved')
if os.path.exists(src + '.orig'):
sys.exit(f'STOP {rel}.orig already exists')
log_path = os.path.join(LOGS, f'mutant-{name}.log')
shutil.copy2(src, src + '.orig')
try:
open(src, 'w').write(text.replace(cur, new))
with open(log_path, 'w') as out:
p = subprocess.Popen('node --test tests/*.test.mjs', shell=True, cwd=os.path.join(WT, 'packages/bus'),
stdout=out, stderr=subprocess.STDOUT, env=env, start_new_session=True)
try:
rc = p.wait(timeout=600)
except subprocess.TimeoutExpired:
os.killpg(p.pid, signal.SIGKILL)
p.wait()
rc = 'timeout'
finally:
shutil.move(src + '.orig', src)
restored = open(src).read() == text
log = open(log_path).read()
counts = [l for l in log.splitlines() if l.startswith(('ℹ pass', 'ℹ fail', 'ℹ skipped'))]
failed = [l.strip() for l in log.splitlines() if l.lstrip().startswith('✖') and 'failing tests' not in l]
print(f'mutant {name} rc={rc} {" ".join(counts)} restored={restored}')
for f in sorted(set(failed)):
print(' ' + f)
print('KILLED' if rc != 0 else 'SURVIVED')
if not restored:
sys.exit(f'STOP {rel} was not restored')