Files
jason.woltje 068d868a99 lead: commit rocko's untracked evidence (4471 files: queue-124 queue-129 queue-171 queue-178 queue-280 queue-31 queue-84 queue-85 queue-94)
Unclaimed files left in the canonical checkout. Inventory: canonical-cleanup-inventory.md (commit-as-evidence groups). detect-secrets scan clean on 2026-11-02: hits are sha256 pins, git object ids, test sentinels already in tracked tests and unit-file lines.
2026-11-02 15:05:15 -06:00

62 lines
5.4 KiB
Python

#!/usr/bin/env python3
"""Each mutant runs focused behavioral tests, restoring the candidate in finally."""
import pathlib,subprocess,json,sys,os,signal
root=pathlib.Path.cwd();out=pathlib.Path(sys.argv[1]);out.mkdir(parents=True,exist_ok=True)
if root == pathlib.Path('/mnt/storage/src/mosaic-stack'):raise SystemExit('refuses canonical checkout')
if os.environ.get('DOCKER_HOST') != 'unix:///nonexistent.sock':raise SystemExit('decision 127 requires disabled Docker endpoint')
if subprocess.check_output(['git','branch','--show-current'],text=True).strip():raise SystemExit('requires detached HEAD')
bus='packages/bus/src/broker.mjs';cli='packages/cli/src/cli.mjs';web='packages/webui/src/public/bus.js';serve='packages/webui/src/serve.mjs';views='packages/bus/src/views.mjs'
btest=['packages/bus/tests/messages.test.mjs'];ctest=['packages/cli/tests/follow.test.mjs'];wtest=['packages/webui/tests/messages.test.mjs']
mutants=[
('no-reader-read',bus,"'replies', 'messages'].includes(request.verb)","'replies'].includes(request.verb)",btest),
('role-can-read',bus,"case 'messages': {\n if (!s.human && !s.reader) fail('human-required');","case 'messages': {",btest),
('limit-default',bus,'const limit = a.limit ?? 50;','const limit = a.limit ?? 49;',btest),
('limit-cap',bus,'a.limit <= 200','a.limit <= 201',btest),
('ignore-cursor',bus,'cursor?.seq ?? null, cursor?.seq ?? null','null, null',btest),
('oldest-first',bus,'ORDER BY m.seq DESC LIMIT ?','ORDER BY m.seq ASC LIMIT ?',btest),
('read-state-lost',bus,'read: !!m.read','read: false',btest),
('unread-page-only',bus,'return { messages, ...counts, next:','return { messages, ...counts, unread: messages.length, next:',btest),
('no-next-page',bus,'next: rows.length > limit ? messages.at(-1).id : null','next: null',btest),
('foreign-business',bus,"WHERE business=? AND to_role='human' AND id=?", "WHERE ? IS NOT NULL AND to_role='human' AND id=?",btest),
('include-roles',bus,"FROM messages m WHERE business=? AND to_role='human' AND (?", "FROM messages m WHERE business=? AND (?",btest),
('view-drops-paging',views,"client.call('messages', args)","client.call('messages', {})",btest),
('follow-stops-at-reply',cli,'if (!follow && answered) return;','if (answered) return;',ctest),
('no-message-read',cli,'await call("message.read", { id: m.id });','/* read removed */',ctest),
('inbox-first-page-only',cli,'after = page.next;','after = null;',ctest),
('inbox-consumes',cli,'const decisions = await call("inbox");','await call("message.receive"); const decisions = await call("inbox");',ctest),
('json-break',cli,'if (json) return print(decisions, formatInbox);','if (json) return print({decisions}, formatInbox);',ctest),
('no-interrupt-handler',cli,'if (follow) process.on("SIGINT", stop);','/* SIGINT handler removed */',ctest),
('no-route',serve,"'credentials', 'replies', 'messages'];","'credentials', 'replies'];",wtest),
('accept-bad-limit',serve,"Number(query.get('limit')) > 200", "Number(query.get('limit')) > 201",wtest),
('accept-bad-after',serve,"!subjectOk(query.get('after'))","false",wtest),
('drop-metadata',serve,'{ rows, ...metadata, at:','{ rows, at:',wtest),
('no-message-view',web,"if (a === 'messages' && n === 1)","if (a === 'never-messages' && n === 1)",wtest),
('no-poll',web,'timer = setTimeout(() => render(false), 10000)','timer = setTimeout(() => render(false), 100000)',wtest),
('message-not-escaped',web,'<p class="s1-message">${txt(m.body)}</p></li>','<p class="s1-message">${m.body}</p></li>',wtest),
('page-count',web,'messageCount = r.unread','messageCount = r.rows.length',wtest),
('no-older-link',web,'${page.next ? `<a data-older','${false ? `<a data-older',wtest),
('delivered-read-flag',bus,"d.op='read') AS read FROM","d.op IN ('delivered','read')) AS read FROM",btest),
('delivered-unread-count',bus,"d.op='read')),0) AS unread","d.op IN ('delivered','read'))),0) AS unread",btest),
('inbox-include-read',cli,'messages.push(...page.messages.filter(m => !m.read));','messages.push(...page.messages);',ctest),
('plain-talk-intercepts-sigint',cli,'if (follow) process.on("SIGINT", stop);','process.on("SIGINT", stop);',ctest),
('no-nonmessages-pre-read',web,"if (path !== '/messages') {",'if (false) {',wtest),
('refusal-keeps-count',web,'inboxCount = null; messageCount = null; }','inboxCount = null; }',wtest),
('board-ignores-count',web,"if (messages.status === 'fulfilled' && !refused) messageCount = messages.value.unread;",'',wtest),
('swallow-preread-403',web,"if (err.status === 403 || err.code === 'signed-out') throw err;","if (err.code === 'signed-out') throw err;",wtest),
]
results=[]
for n,(name,file,before,after,tests) in enumerate(mutants,1):
p=root/file;original=p.read_text()
if original.count(before)!=1:raise SystemExit(f'{name}: replacement count {original.count(before)}')
try:
p.write_text(original.replace(before,after,1))
with (out/f'mutant-{n:02d}-{name}.txt').open('w') as log:
child=subprocess.Popen(['node','--test',*tests],stdout=log,stderr=subprocess.STDOUT,start_new_session=True)
try:code=child.wait(timeout=20);status='killed' if code else 'survived'
except subprocess.TimeoutExpired:
os.killpg(child.pid,signal.SIGTERM);child.wait();code=124;status='timeout'
row={'n':n,'name':name,'file':file,'exit':code,'status':status};results.append(row)
(out/'mutants.json').write_text(json.dumps(results,indent=2)+'\n');print(json.dumps(row),flush=True)
finally:p.write_text(original)
sys.exit(any(r['status']!='killed' for r in results))