// Row 125 (#1619) mutants: one per check, each an exact-once text edit in one candidate file, // each named with the test files that must fail on it. Driven by mutants.sh; run from the worktree. // node mutants.mjs list namefiletest files (space separated) // node mutants.mjs check read only: each old text matches once, each test file exists // node mutants.mjs apply NAME apply NAME's edit; exit 3 unless the old text matches exactly once import { readFileSync, writeFileSync } from 'node:fs'; const L = 'packages/bus/src/limits.mjs', LT = 'packages/bus/tests/limit-checks.test.mjs'; const TL = 'packages/plugins/limit-check/token-limit/index.mjs', TT = 'packages/plugins/tests/token-limit.test.mjs'; const B = 'packages/bus/src/broker.mjs', ST = 'packages/bus/tests/limit-stop.test.mjs', RT = 'packages/harness/tests/runner.test.mjs'; const LA = 'packages/cli/src/launcher.mjs', LCT = 'packages/cli/tests/launch-limit-checks.test.mjs', THT = 'packages/cli/tests/thread-launch.test.mjs'; const LS = 'packages/business/src/launch-settings.mjs', TS = 'packages/business/tests/token-limit-settings.test.mjs'; const LM = 'packages/business/src/launch.mjs', BU = 'packages/business/src/business.mjs'; const K = 'packages/plugins/src/kinds.mjs', KT = 'packages/plugins/tests/kinds.test.mjs', LOT = 'packages/plugins/tests/loader.test.mjs'; const SV = 'packages/webui/src/serve.mjs', WRT = 'packages/webui/tests/limits-route.test.mjs'; const BJ = 'packages/webui/src/public/bus.js', WBT = 'packages/webui/tests/limits-browser.test.mjs'; const CL = 'packages/cli/src/limits.mjs', CLT = 'packages/cli/tests/limits-tokens.test.mjs'; const RU = 'packages/bus/src/runtime.mjs', RUT = 'packages/bus/tests/limits-runtime.test.mjs'; const PR = 'packages/bus/src/process.mjs', VW = 'packages/bus/src/views.mjs'; const HS = 'packages/harness/src/tools.mjs', HT = 'packages/harness/tests/tools.test.mjs'; const U = 'packages/bus/src/usage.mjs', UT = 'packages/bus/tests/usage.test.mjs'; const PRT = 'packages/bus/tests/process.test.mjs'; const M = [ // core: packages/bus/src/limits.mjs ['L02-decide-skips-failed-check', L, [LT], "if (code) return halt({ check: check.name, code });", "if (code) continue;"], ['L03-unreadable-state-goes', L, [LT], "return halt({ check: null, code: e.code });", "return { [key]: go };"], ['L04-hit-any-path', L, [LT], "leaves.some(l => same(l.path, hit.path) && l.limit === hit.limit) && ", ""], ['L05-reason-dropped', L, [LT], "reason: named ? named.e.notEnforced : null", "reason: null"], ['L06-report-skips-failed-check', L, [LT], "if (code) return failedAll(check.name, code);", "if (code) continue;"], ['L07-decide-no-not-loaded', L, [LT], " if (!st.checks) {\n log({ event: 'limit-check-failed', business, check: st.missing, export: name, code: 'not-loaded' });\n return halt({ check: st.missing, code: 'not-loaded' });\n }\n", ""], ['L08-report-no-not-loaded', L, [LT], "if (!st.checks) return failedAll(st.missing, 'not-loaded');", "if (!st.checks) return [];"], ['L09-answer-not-validated', L, [LT], " if (!valid(answer)) throw new Failure('malformed-answer');\n", ""], ['L10-own-code-lost', L, [LT], "typeof e?.code === 'string' && CODE.test(e.code) ? e.code : 'check-threw'", "'check-threw'"], ['L11-entry-limit-unchecked', L, [LT], "if (!leaf || e.limit !== leaf.limit || e.since !== leaf.since) return false;", "if (!leaf) return false;"], ['L12-reach-not-logged', L, [LT], " log({ event: 'limit-reached', business, check: check.name, export: name, subject, ...answer.hit });\n", ""], ['L13-decide-no-refresh', L, [LT], " await refresh(business);\n let st;\n", " let st;\n"], ['L14-subject-account-dropped', L, [LT], "=> ({ instance, provider, model, account });", "=> ({ instance, provider, model });"], ['L16-leaves-any-depth', L, [LT], "export function limitLeaves(limits) {\n", "export function limitLeaves(limits) { return anyLeaves(limits); }\nfunction anyLeaves(limits, path = []) {\n const out = [];\n if (!plainObject(limits)) return out;\n for (const [key, value] of Object.entries(limits)) {\n if (own(value, 'limit')) out.push({ path: [...path, key], limit: value.limit, since: value.since });\n else if (plainObject(value)) out.push(...anyLeaves(value, [...path, key]));\n }\n return out;\n}\nfunction depthLeaves(limits) {\n"], // round 2 (Filbert f3): each check gets its own copy of the args ['L17-args-shared', L, [LT], "check.module[name](structuredClone(args))", "check.module[name](args)"], ['L15-thenable-unguarded', L, [LT], " if (answer !== null && (typeof answer === 'object' || typeof answer === 'function') && typeof answer.then === 'function') {\n Promise.resolve(answer).catch(() => {});\n throw new Failure('malformed-answer');\n }\n", ""], // the shipped check: packages/plugins/limit-check/token-limit/index.mjs ['T01-over-not-at', TL, [TT, LT], "if (count >= BigInt(leaf.entry.limit))", "if (count > BigInt(leaf.entry.limit))"], ['T02-since-ignored', TL, [TT, LT], "if (!leaf.match(r) || time(r.at) < since) continue;", "if (!leaf.match(r)) continue;"], ['T03-unknown-not-counted', TL, [TT], "else unknown.add(JSON.stringify([r.run, r.turn]));", "else void 0;"], ['T04-unknown-counted-as-known', TL, [TT, LT], "if (r.status === 'known') for", "if (r.status !== null) for"], ['T05-key-always-enforced', TL, [TT], "if (leaf.key && !named)", "if (false)"], ['T06-instance-matches-all', TL, [TT], "match: x => x.instance === n });", "match: x => true });"], ['T07-provider-matches-all', TL, [TT], "match: x => x.provider === p });", "match: x => true });"], ['T08-model-ignores-model', TL, [TT], "match: x => x.provider === p && x.model === m });", "match: x => x.provider === p });"], ['T09-key-matches-all', TL, [TT], "match: x => x.account === k, key: true });", "match: x => true, key: true });"], ['T10-big-counts-lossy', TL, [TT], "const plain = n => n <= SAFE ? Number(n) : String(n);", "const plain = n => Number(n);"], ['T11-bad-records-pass', TL, [TT], "const amount = value => Number.isSafeInteger(value) && value >= 0 ? BigInt(value) : refuse('bad-record');", "const amount = value => BigInt(value ?? 0);"], // round 2 (Filbert's no-source note): an unknown turn adds nothing and stops nothing ['T12-unknown-turn-stops', TL, [RUT], "if (count >= BigInt(leaf.entry.limit))", "if (count + BigInt(unknownTurns) >= BigInt(leaf.entry.limit))"], // broker: the end-of-turn stop ['B01-throw-goes-on', B, [ST], "answer = { stop: true, check: null, code: 'check-threw' };", "answer = { stop: false };"], ['B02-malformed-kept', B, [ST], "answer?.stop === true ? answer : { stop: true, check: null, code: 'malformed-answer' }", "answer"], ['B03-no-stop-on-state', B, [ST], "(request.verb === 'message.receive' || request.verb === 'state')", "request.verb === 'message.receive'"], ['B04-by-run-not-limit-check', B, [ST, RT], "this.#claimEnd(s, h, s.thread ? 'release' : 'revoke', null, reason, 'limit-check');", "this.#claimEnd(s, h, s.thread ? 'release' : 'revoke', null, reason);"], ['B05-stop-before-result', B, [ST, RT], "if (phase === 'end') await this.#limitCheck(s);", "if (phase === 'end') { await this.#limitCheck(s); if (this.#stops.has(s.business + ':' + s.run)) this.#limitStop(s); }"], ['B06-endlaunch-no-limit', B, [ST], "return { released, ...(limit ? { limit } : {}) };", "return { released };"], ['B07-thread-revoked', B, [ST], "this.#claimEnd(s, h, s.thread ? 'release' : 'revoke', null, reason, 'limit-check');", "this.#claimEnd(s, h, 'revoke', null, reason, 'limit-check');"], ['B08-reason-no-count', B, [ST, RT], " limit ${stop.hit.limit} count ${stop.hit.count}`", " limit ${stop.hit.limit}`"], ['B09-org-keys-no-reset', B, [HT], "export const ORGANIZATION_KEYS = ['revision', 'limits', 'models', 'instances', 'reset'];", "export const ORGANIZATION_KEYS = ['revision', 'limits', 'models', 'instances'];"], // launcher ['A01-op-failure-allows', LA, [LCT], "throw new Refusal(\"limit-check-failed\", `bus: ${e.code ?? \"launch-op-refused\"}`);", "may = { allow: true };"], ['A02-only-false-refuses', LA, [LCT], "if (may?.allow !== true) {", "if (may?.allow === false) {"], ['A03-hit-not-limit-reached', LA, [LCT], "if (may?.hit) { const error", "if (false) { const error"], ['A04-end-record-no-limit', LA, [THT], ", ...(ended?.limit ? { limit: ended.limit } : {}) });", " });"], // business: the writer and the schema ['S01-since-overwrite-accepted', LS, [TS], " if (own(entry, \"since\") && entry.since !== kept) throw new OrganizationError(\"invalid-request\");\n", ""], ['S02-change-restamps', LS, [TS], "entry.since = kept ?? now;", "entry.since = now;"], ['S03-reset-no-move', LS, [TS], " leaf.entry.since = now;\n", ""], ['S04-reset-any-path', LS, [TS], "const leaf = leaves.find(l => l.path.length === path.length && l.path.every((part, i) => part === path[i]));", "const leaf = { entry: at(limits, path) };"], ['S05-settings-keys-no-reset', LS, [TS, HT], "export const SETTINGS_KEYS = Object.freeze([\"revision\", \"limits\", \"models\", \"instances\", \"reset\"]);", "export const SETTINGS_KEYS = Object.freeze([\"revision\", \"limits\", \"models\", \"instances\"]);"], ['S06-reset-alone-refused', LS, [TS], "if (![\"limits\", \"models\", \"instances\", \"reset\"].some(", "if (![\"limits\", \"models\", \"instances\"].some("], // round 2 (Filbert change 1): token limits only at schema depth; each mutant puts r1's any-object-with-limit walk back in one place ['S08-stamp-any-depth', LS, [TS], "for (const { path, entry } of tokenLeaves(next)) {", "for (const { path, entry } of (function any(v, p = []) { return plain(v) ? (own(v, \"limit\") && p.length ? [{ path: p, entry: v }] : Object.entries(v).flatMap(([k, x]) => any(x, [...p, k]))) : []; })(next)) {"], ['S09-reset-any-depth', LS, [TS], " const leaves = tokenLeaves(limits);\n", " const leaves = (function any(v, p = []) { return plain(v) ? (own(v, \"limit\") && p.length ? [{ path: p, entry: v }] : Object.entries(v).flatMap(([k, x]) => any(x, [...p, k]))) : []; })(limits);\n"], ['S07-reset-empty-accepted', LS, [TS], "if (!Array.isArray(paths) || !paths.length) throw", "if (!Array.isArray(paths)) throw"], ['D01-since-no-roundtrip', LM, [TS], "new Date(since).toISOString() !== since) refuse", "false) refuse"], ['D02-limit-unchecked', LM, [TS], "return { limit: count(value.limit, `${at}.limit`), since };", "return { limit: value.limit, since };"], ['D03-leaf-extra-keys', LM, [TS], " rejectUnknownKeys(value, [\"limit\", \"since\"], at);\n", ""], ['D04-token-extra-kinds', LM, [TS], " rejectUnknownKeys(value, [\"instances\", \"providers\", \"models\", \"keys\"], where);\n", ""], ['D05-instance-undeclared', BU, [TS], " for(const instance of Object.keys(limits.tokens?.instances??{}))if(!Object.hasOwn(roles,instance))refuse(", " for(const instance of [])if(!Object.hasOwn(roles,instance))refuse("], // plug-in contract ['K01-version-1', K, [KT, LOT], "kind: \"limit-check\", interfaceVersion: 2,", "kind: \"limit-check\", interfaceVersion: 1,"], ['K02-no-report-export', K, [KT, LOT], "exports: Object.freeze([\"mayLaunch\", \"mustStop\", \"report\"])", "exports: Object.freeze([\"mayLaunch\", \"mustStop\"])"], // Console server, page and reader ['W01-no-tokens-check', SV, [WRT], "if (!value || !Array.isArray(value.tokens)) throw new BusReadError('invalid-response');", "if (!value) throw new BusReadError('invalid-response');"], ['W02-writable-no-reset', SV, [WRT], "const writable = ['limits', 'models', 'instances', 'reset'];", "const writable = ['limits', 'models', 'instances'];"], ['W03-limits-takes-query', SV, [WRT], " if (search) return json(res, 400, { error: 'invalid-request', message: 'limits takes no arguments' });\n", ""], ['W04-whole-document-out', SV, [WRT], "return json(res, 200, { rows: value.tokens, at: new Date().toISOString() });", "return json(res, 200, { ...value, rows: value.tokens, at: new Date().toISOString() });"], ['W05-view-not-limits', VW, [WRT], "limits: () => client.call('limits'),", "limits: () => client.call('usage'),"], ['W06-page-save-drops-tokens', BJ, [WBT], " if(v.limits?.tokens)limits.tokens=structuredClone(v.limits.tokens);\n", ""], ['W07-page-reason-dropped', BJ, [WBT], "`not enforced${t.reason ? `: ${txt(t.reason)}` : ''}`", "'not enforced'"], ['W08-page-reset-ungated', BJ, [WBT], "data-limit-reset=\"${esc(JSON.stringify(t.path || []))}\"${can ? '' : ' disabled'}>", "data-limit-reset=\"${esc(JSON.stringify(t.path || []))}\">"], ['W09-page-check-unescaped', BJ, [WBT], "`check failed: ${txt(t.code)}${t.check ? ` (${txt(t.check)})` : ''}`", "`check failed: ${txt(t.code)}${t.check ? ` (${t.check})` : ''}`"], ['W10-page-refused-shows-none', BJ, [WBT], "limitsRefused = true; }", "}"], // round 2 (Filbert f4): a reset under a dirty draft keeps the snapshot the draft was typed on ['W11-reset-replaces-draft-snapshot', BJ, [WBT], "if(!settingsInputs)settingsSnapshot=result;", "settingsSnapshot=result;"], // CLI ['C01-cli-reason-dropped', CL, [CLT], "return `${head}, not enforced${t.reason ? `: ${t.reason}` : \"\"}`;", "return `${head}, not enforced`;"], ['C02-reset-path-short', CL, [CLT], "reset: [parsed.positional.slice(1)]", "reset: [parsed.positional.slice(2)]"], ['C03-reset-no-path-sent', CL, [CLT], "(verb === \"reset\" && parsed.positional.length < 2)) {", "false) {"], ['C04-cli-tokens-in-json', CL, [CLT], "io.stdout.write(JSON.stringify(rest, null, 2)", "io.stdout.write(JSON.stringify(result, null, 2)"], // bus runtime and its process ['R01-no-tokens', RU, [RUT], "const withTokens = (id, result) => ({ ...result, tokens: limits.report(id) });", "const withTokens = (id, result) => result;"], ['R02-maylaunch-allows', RU, [RUT], "mayLaunch: ({ business, instance, provider, model }) => limits.mayLaunch(business, { instance, provider, model }),", "mayLaunch: async () => ({ allow: true }),"], ['R03-broker-no-limits', RU, [RUT], "\n limits,\n", "\n"], ['R04-reset-not-recounted', RU, [RUT], "if(verb==='limits.set')return withTokens(id,settings.set(id,args,actor));", "if(verb==='limits.set')return settings.set(id,args,actor);"], // round 2 (Filbert f2): plugins.set reloads the limit checks ['R05-plugins-set-no-refresh', RU, [RUT], " limits.refresh(id).catch(e=>limitLog({event:'limit-refresh-failed',business:id,message:e.message}));\n", ""], ['P01-op-not-awaited', PR, [PRT], "result: await launchOp(message)", "result: launchOp(message)"], ['P02-op-missing', PR, [PRT], " if (m.op === 'mayLaunch') return runtime.mayLaunch(m);\n", ""], // harness tool ['H01-tool-keys-no-reset', HS, [HT], "export const ORGANIZATION_KEYS = Object.freeze([\"revision\", \"limits\", \"models\", \"instances\", \"reset\"]);", "export const ORGANIZATION_KEYS = Object.freeze([\"revision\", \"limits\", \"models\", \"instances\"]);"], // usage records the checks count ['U01-begin-row-known', U, [UT, LT], "const known = row.phase === 'end' && row.status === 'known';", "const known = row.status === 'known';"], ]; const [verb, name] = process.argv.slice(2); if (verb === 'list') { const seen = new Set(); for (const [n, file, tests] of M) { if (seen.has(n)) { console.error(`duplicate ${n}`); process.exit(2); } seen.add(n); console.log(`${n}\t${file}\t${tests.join(' ')}`); } } else if (verb === 'check') { // Read only: every mutant's old text must match exactly once, and every named test file must exist. let bad = 0; for (const [n, file, tests, before] of M) { const count = readFileSync(file, 'utf8').split(before).length - 1; if (count !== 1) { bad++; console.log(`${n}\t${file}\tmatches ${count}`); } for (const t of tests) try { readFileSync(t); } catch { bad++; console.log(`${n}\tmissing test ${t}`); } } console.log(`${M.length} mutants, ${bad} problems`); process.exit(bad ? 1 : 0); } else if (verb === 'apply') { const m = M.find(x => x[0] === name); if (!m) { console.error(`no mutant ${name}`); process.exit(2); } const [, file, , before, after] = m; const text = readFileSync(file, 'utf8'), n = text.split(before).length - 1; if (n !== 1) { console.error(`${name}: expected one match in ${file}, found ${n}`); process.exit(3); } writeFileSync(file, text.replace(before, () => after)); } else { console.error('usage: mutants.mjs list | apply NAME'); process.exit(2); }