Files
stack/agents/darkwing/work/queue-55/out/mutants-round3/run3.mjs
T
jason.woltjeandClaude Opus 5.5 26926d183a docs(queue-55): row 55 round 3 candidate packet, R3 and B1 (darkwing)
One rule for Filbert's R3 and Dewey's B1: y resolves only while the
whole decide box is shown. Adds N9 widths, the start-unguarded and
timeout fixes, and two defects found by the cheap tests (wrap at
graphemes, answer render throw). 51/51 mutants killed; gate on
122b1223 green, cli 110.

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

83 lines
7.1 KiB
JavaScript

// Round 3 mutants: round 2's 30 less overlay-cut-loses-prompt (its line is
// gone), four re-anchored to lines round 3 changed, then round 3's own,
// Dewey's B1 among them. Each edits the source, runs
// the cli suite, restores.
import { readFileSync, writeFileSync } from "node:fs";
import { spawnSync } from "node:child_process";
import { join } from "node:path";
const CLI = process.argv[2];
const M = [
["resolve-ask-skipped-when-note", "cli.mjs", "if (ask && !(await ask(", "if (ask && note === undefined && !(await ask("],
["unknown-space-only", "app.mjs", "/^outcome[ -]unknown/", "/^outcome unknown/"],
["restore-no-altleave", "app.mjs", 'stdout.write("\\x1b[?25h\\x1b[?1049l");', ""],
["restore-no-raw-off", "app.mjs", "stdin.setRawMode?.(false);", ""],
["no-sigterm", "app.mjs", 'const SIGNALS = ["SIGTERM", "SIGINT", "SIGHUP"];', 'const SIGNALS = ["SIGINT", "SIGHUP"];'],
["no-sigint", "app.mjs", 'const SIGNALS = ["SIGTERM", "SIGINT", "SIGHUP"];', 'const SIGNALS = ["SIGTERM", "SIGHUP"];'],
["no-sighup", "app.mjs", 'const SIGNALS = ["SIGTERM", "SIGINT", "SIGHUP"];', 'const SIGNALS = ["SIGTERM", "SIGINT"];'],
["resize-unguarded", "app.mjs", "const onResize = () => guard(() => app.resize(stdout.columns || 80, stdout.rows || 24));", "const onResize = () => app.resize(stdout.columns || 80, stdout.rows || 24);"],
["paint-no-reverse", "screens.mjs", "return l.sel ? `\\x1b[7m${body}\\x1b[27m` : body;", "return body;"],
["cells-no-cjk", "screens.mjs", "(n >= 0x2e80 && n <= 0xa4cf && n !== 0x303f) ||", ""],
["cells-no-emoji-presentation", "screens.mjs", "/\\p{Emoji_Presentation}/u.test(c) ||", ""],
["cells-no-fe0f", "screens.mjs", "if (n === 0xfe0f) return prev && /\\p{Emoji}/u.test(prev) && cells(prev) === 1 ? 1 : 0;", ""],
["b1-cut-keeps-end", "screens.mjs", "return keep >= 1 ? [...head, ...question.slice(0, keep), cutLine(\"question\", w - 4), ...tail] : null;", "return b;"],
["app-host-not-live", "cli.mjs", "const business = state?.live ? state.business : null;", "const business = state?.business ?? null;"],
["quit-no-answer", "app.mjs", " this.cancelAsk?.();\n this.wake?.();", " this.answer?.(\"y\");\n this.wake?.();"],
["refresh-in-overlay", "app.mjs", "s.overlay || s.command !== null ? null : this.#read", "this.#read"],
["refresh-answers", "app.mjs", 'if (k !== "refresh") this.answer(k);', "this.answer(k);"],
["refresh-drops-resolved", "app.mjs", 'this.#read(s.view, s.statusStyle === "success")', "this.#read(s.view)"],
["unknown-esc-stays", "app.mjs", ' if (k === "esc") this.state.overlay = null;', ""],
["trail-task-id", "app.mjs", 'view === "tasks" ? item.task_ref : view === "trail"', 'view === "tasks" ? item.title : view === "trail"'],
["business-keeps-data", "app.mjs", " this.state.data = {};", ""],
["nav-no-brackets", "screens.mjs", "piece(`[${label}]`, \"bold\")", "piece(` ${label} `, \"bold\")"],
["typeahead-kept", "app.mjs", " this.keys = [];\n this.answer", " this.answer"],
["no-arm-delay", "app.mjs", " }, this.askMs);", " }, 0);"],
["other-key-waits", "app.mjs", 'this.answer = (k) => answer((k === "y" || k === "Y") && decideShown(this.state.overlay, this.cols, this.rows));', 'this.answer = (k) => (k === "y" || k === "Y" ? answer(decideShown(this.state.overlay, this.cols, this.rows)) : ["n", "N", "esc", "enter", "q"].includes(k) ? answer(false) : null);'],
["no-capital-y", "app.mjs", '(k === "y" || k === "Y") && decideShown', '(k === "y") && decideShown'],
["command-types-names", "app.mjs", '[...k].length === 1 && k >= " "', 'k >= " "'],
["press-1-1", "app.mjs", 'n === 1 ? "1" : `1-${n}`', "`1-${n}`"],
["tiny-no-guard", "screens.mjs", "if (cols < MIN_COLS || rows < MIN_ROWS) return tooSmall(cols, rows, color, Boolean(state.overlay?.asking));", ""],
// Round 3.
["r3-y-ignores-shown", "app.mjs", '(k === "y" || k === "Y") && decideShown(this.state.overlay, this.cols, this.rows)', '(k === "y" || k === "Y")'],
["r3-shown-is-min-size", "app.mjs", "&& decideShown(this.state.overlay, this.cols, this.rows))", "&& this.cols >= 40 && this.rows >= 10)"],
["r3-shown-at-arm", "app.mjs", 'this.answer = (k) => answer((k === "y" || k === "Y") && decideShown(this.state.overlay, this.cols, this.rows));', 'const shown = decideShown(this.state.overlay, this.cols, this.rows);\n this.answer = (k) => answer((k === "y" || k === "Y") && shown);'],
["r3-no-question-whole-box", "screens.mjs", "if (q < 0 || end < 0) return null;", "if (q < 0 || end < 0) return b;"],
["r3-keep-zero", "screens.mjs", "return keep >= 1 ?", "return keep >= 0 ?"],
["r3-hidden-no-any-key", "screens.mjs", 'o.asking ? "Any key answers No." : ', ""],
["r3-start-unguarded", "app.mjs", "guard(() => void app.start());", "void app.start();"],
["r3-text-never-asking", "screens.mjs", "Boolean(state.overlay?.asking)", "false"],
["r3-text-always-asking", "screens.mjs", "const keys = asking ?", "const keys = true ?"],
["r3-answer-render-unguarded", "app.mjs", "try {\n this.render();\n } catch (e) {\n // The pump sends this to crash; the decide flow ends with No.\n resolve(false);\n throw e;\n }", "this.render();"],
["r3-answer-throw-keeps-yes", "app.mjs", "resolve(false);\n throw e;", "resolve(yes);\n throw e;"],
["r3-wrap-code-points", "screens.mjs", "for (const { segment: c } of graphemes.segment(word))", "for (const c of word)"],
["r3-cells-no-2329", "screens.mjs", " n === 0x2329 ||\n", ""],
["r3-cells-no-232a", "screens.mjs", " n === 0x232a ||\n", ""],
["r3-cells-no-a960", "screens.mjs", " (n >= 0xa960 && n <= 0xa97c) ||\n", ""],
["r3-cells-no-fe10", "screens.mjs", " (n >= 0xfe10 && n <= 0xfe19) ||\n", ""],
["r3-cells-no-fe50", "screens.mjs", " (n >= 0xfe50 && n <= 0xfe6b) ||\n", ""],
["r3-cells-no-16fe0", "screens.mjs", " (n >= 0x16fe0 && n <= 0x16ff1) ||\n", ""],
["r3-cells-no-17000", "screens.mjs", " (n >= 0x17000 && n <= 0x18d08) ||\n", ""],
["r3-cells-no-1aff0", "screens.mjs", " (n >= 0x1aff0 && n <= 0x1b2fb) ||\n", ""],
["r3-cells-1aff0-short", "screens.mjs", "(n >= 0x1aff0 && n <= 0x1b2fb)", "(n >= 0x1aff0 && n <= 0x1b2fa)"],
["r3-cells-no-1f200", "screens.mjs", " (n >= 0x1f200 && n <= 0x1f2ff) ||\n", ""],
];
const only = process.argv.slice(3);
let survived = 0;
for (const [name, file, from, to] of M) {
if (only.length && !only.includes(name)) continue;
const path = join(CLI, "src", file);
const orig = readFileSync(path, "utf8");
const n = orig.split(from).length - 1;
if (n !== 1) { console.log(`${name}: SKIP, found ${n} times`); continue; }
try {
writeFileSync(path, orig.replace(from, to));
const r = spawnSync(process.execPath, ["--test", "--test-timeout=20000", "tests/"], { cwd: CLI, encoding: "utf8", timeout: 180000 });
const fails = (r.stdout.match(/^✖ (.*?) \(/gm) ?? []).slice(0, 2).map((s) => s.slice(2, 60));
const killed = r.status !== 0;
if (!killed) survived++;
console.log(`${name}: ${killed ? "killed" : "SURVIVED"}${killed ? ` by ${[...new Set(fails)].join(" | ")}` : ""}`);
} finally {
writeFileSync(path, orig);
}
}
console.log(`survived ${survived}`);