Files
stack/agents/darkwing/work/slice1-s2b-review/mutations-r2.py
T
jason.woltjeandClaude Opus 5.5 bba75b4a4f review(bus): darkwing S2b round 2, approve (row 43, #1525)
Verdict comment 26769, queue rev 146. R1, R2 and both lead decision 64
rulings are in; round 1 probes now refuse. 55/55 on Node 24 and 26,
nine of ten mutants killed, the survivor equivalent.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 17:43:09 -05:00

35 lines
2.6 KiB
Python

#!/usr/bin/env python3
# S2b round 2: Darkwing's mutants, repository layout, each compared to a clean baseline.
import pathlib,tempfile,shutil,subprocess,json,re,sys
cases=[
('drop-consumed-check','broker.mjs',"fail('decision-consumed');",";"),
('gated-only-again','broker.mjs',"result.decision &&\n this.#store.get(","result.decision && this.#decision(s, result.decision).class === 'gated' &&\n this.#store.get("),
('drop-class-match','broker.mjs',"d.class !== cls ||",""),
('within-role-ignores-decision','broker.mjs',"cls === 'within-role' && !decision","cls === 'within-role'"),
('message-send-check-only','broker.mjs',"this.#consumeAuthority(s, 'message.send',","this.#checkAuthority(s, 'message.send',"),
('role-revoke-check-only','broker.mjs',"this.#consumeAuthority(s, 'role.revoke',","this.#checkAuthority(s, 'role.revoke',"),
('is-not-to-ne','broker.mjs',"IS NOT 'decision.raise' LIMIT 1","!= 'decision.raise' LIMIT 1"),
('count-raise-event','broker.mjs',"AND json_extract(body,'$.operation') IS NOT 'decision.raise' LIMIT 1","LIMIT 1"),
('event-drops-decision','broker.mjs',"{ action, ...result, target: context.target ?? null }","{ action, class: result.class, target: context.target ?? null }"),
('authorize-outside-transaction','broker.mjs',"return this.#store.transaction(() => this.#consumeAuthority(s, action, context));","return this.#consumeAuthority(s, action, context);"),
]
source=pathlib.Path(sys.argv[1])
def run(dest):
r=subprocess.run(['node','--test',*[str(p) for p in sorted((dest/'tests').glob('*.test.mjs'))]],capture_output=True,text=True,timeout=300)
tests=set(l[2:].rsplit(' (',1)[0] for l in r.stdout.splitlines() if l.startswith('✖ ') and not l.startswith('✖ failing tests'))
return r.returncode,tests
with tempfile.TemporaryDirectory(prefix='dw-s2b2-mut-') as root:
dest=pathlib.Path(root)/'packages'/'bus';shutil.copytree(source,dest,ignore=shutil.ignore_patterns('dw'))
code,base=run(dest);print(json.dumps({'baseline_exit':code,'baseline_failures':sorted(base)}),flush=True)
code,f=run(dest);print(json.dumps({'mutation':'no-op','exit':code,'killed':bool(f-base)}),flush=True)
out=[]
for name,file,old,new in cases:
p=dest/'src'/file;b=p.read_text();pat=r'\s*'.join(re.escape(c) for c in old if not c.isspace());n=len(re.findall(pat,b))
if n!=1: print(json.dumps({'mutation':name,'error':f'{n} matches'}),flush=True);continue
p.write_text(re.sub(pat,lambda m:new,b,count=1))
try: code,f=run(dest)
finally: p.write_text(b)
x=sorted(f-base);out.append({'mutation':name,'exit':code,'killed':bool(x),'new_failures':x})
print(json.dumps(out[-1]),flush=True)
print('killed',sum(x['killed'] for x in out),'of',len(out))