Files
stack/agents/darkwing/work/queue-113/r1/mutants.py
T
jason.woltjeandClaude Opus 5.5 d6ea568312 docs: Darkwing row 113 round 1 packet (#1607)
Ledger skips T3 reasoning summaries and reads issue pages with a
10-page ceiling. Candidate manifest, patch against cc48b124f, mutants
(both killed) and a passing full gate.

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

54 lines
2.6 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 113 (#1607): the brief's two mutants, one per fix.
# m1 drops the id prefix check in t3.mjs, so every role "reasoning" row is
# skipped, including one whose id doesn't start "reasoning:".
# m2 stops the issue read after page 1, as if it were still one call.
# Each runs the FULL ledger suite (node --test tests/*.test.mjs in
# packages/ledger) 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/ledger/src/t3.mjs',
"if (m.role === 'reasoning' && typeof m.message_id === 'string' && m.message_id.startsWith('reasoning:')) {",
"if (m.role === 'reasoning') {"),
'm2': ('packages/ledger/src/ledger.mjs',
" if (rows.length < PAGE_SIZE) return { issues: issues.filter(i => !i.pull_request), pages: page };\n",
" return { issues: issues.filter(i => !i.pull_request), pages: page };\n"),
}
os.makedirs(LOGS, exist_ok=True)
env = dict(os.environ, TMPDIR=os.path.expanduser('~/darkwing-scratch/r113/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/ledger'),
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')