Files
jason.woltjeandClaude Opus 5.5 62f354746d agents: Dewey row 139 round 1 packet (#1633)
Candidate on 5661bd233: GATED and GATED_ONLY become PRD 1.5's eight hard
waits (data.delete, network.expose and account.create are new), derived
as ACTIONS minus an explicit ROUTED list (REQ-WAIT-3). A gated raise
citing the current spec goes to the domain arbiter unless it is a hard
wait. land-work refuses an arbiter-resolved merge with not-human.
Mutants 5/5 killed; gate green (bus, business and every scripts/test-*.sh).
Row 137 has not landed, so its REQ-LAND-1 refusal and the host-op
arbiter acceptance are left out, as BUILD.md says.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:33:30 -05:00

51 lines
2.8 KiB
Python

#!/usr/bin/env python3
# Row 139 mutants (slot sage-0518). Each mutates one file, runs the owning suite in its own
# process group (killpg on timeout), restores the file and checks its hash before the next.
import hashlib, os, signal, subprocess, sys, time
W = "/home/jwoltje/dewey-scratch/r139/wt"
O = "/home/jwoltje/dewey-scratch/r139/out/mutants"
os.makedirs(O, exist_ok=True)
ENV = dict(os.environ, TMPDIR="/home/jwoltje/dewey-scratch/tmp", COMPOSE_PROJECT_NAME="dewey-r139")
SUITES = {"bus": ["bash", "-c", "cd packages/bus && npm test"],
"business": ["bash", "-c", "cd packages/business && npm test"]}
MUTANTS = [
("m1-no-spec-check", "packages/bus/src/broker.mjs",
"(cls === 'gated' && spec !== null && !GATED.has(a.action))", "(cls === 'gated' && !GATED.has(a.action))", "bus"),
("m2-no-hard-wait-check", "packages/bus/src/broker.mjs",
"(cls === 'gated' && spec !== null && !GATED.has(a.action))", "(cls === 'gated' && spec !== null)", "bus"),
("m3-new-action-not-hard-wait", "packages/bus/src/broker.mjs",
"const ROUTED = new Set([\n", "const ROUTED = new Set([\n 'account.create',\n", "bus"),
("m4-hard-wait-routed", "packages/business/src/vocabulary.mjs",
"export const ROUTED = Object.freeze([\n", "export const ROUTED = Object.freeze([\n \"spend\",\n", "business"),
("m5-validator-refusal", "packages/business/src/role.mjs",
"if (GATED_ONLY.includes(action)) refuse(", "if (false && GATED_ONLY.includes(action)) refuse(", "business"),
]
sha = lambda p: hashlib.sha256(open(p, "rb").read()).hexdigest()
summary = open(os.path.join(O, "summary.txt"), "w")
for name, rel, old, new, suite in MUTANTS:
path = os.path.join(W, rel)
orig = open(path, "rb").read()
h0 = sha(path)
text = orig.decode()
if text.count(old) != 1:
line = f"{name} SKIP anchor count {text.count(old)}"
print(line); summary.write(line + "\n"); summary.flush(); sys.exit(3)
open(path, "w").write(text.replace(old, new))
assert sha(path) != h0
start = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
with open(os.path.join(O, f"{name}.txt"), "w") as out:
p = subprocess.Popen(SUITES[suite], cwd=W, env=ENV, stdout=out, stderr=subprocess.STDOUT, start_new_session=True)
try:
rc = p.wait(timeout=600)
except subprocess.TimeoutExpired:
os.killpg(p.pid, signal.SIGKILL); p.wait(); rc = "timeout"
open(path, "wb").write(orig)
restored = sha(path) == h0
end = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
verdict = "KILLED" if rc not in (0, "timeout") else ("TIMEOUT" if rc == "timeout" else "SURVIVED")
line = f"{name} {rel} suite={suite} rc={rc} {verdict} restored={restored} start={start} end={end}"
print(line); summary.write(line + "\n"); summary.flush()
if not restored:
sys.exit(4)