Files
jason.woltjeandClaude Opus 5.5 80797a5398 docs: add Dewey row 131 r2 packet (plug-in loader and setter fixes)
Round 2 for #1626 per sage-0314: F1a setter refuses a non-plain prototype,
F1b organization writer validates and logs JSON.parse(bytes), F2 loader
refuses unreadable paths, README top-level-code limit, control run.
Gate 591/0, 0 skipped; controls pass; mutants 47/47 killed (slot sage-0316).
Candidate manifest sha256 466c835f7dce79d20c7ab74c197a52b70e7fdeb0efae0456222dc6d0a633f219,
14 paths on base 0bdd573ff; no commit in base..1376caab9 touches them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 11:44:04 -05:00

12 KiB

Row 131 mutants (round 2)

One fault per check. mutants.py makes each edit to the candidate, runs only the listed test files with node --test in its own process group (whole-group kill at 300 s), records the result and restores the file. mutants.py --dry checks that each mutation site matches. Per-mutant output is in mutants/<id>.log; mutants/results.json holds every result. Run: 2026-10-15T16:42:28Z to 16:42:52Z, in the slot Sage gave in sage-0316.

Control

Before any mutant, each distinct test set runs on the unmutated candidate; one failure stops the run (round 2, sage-0314). Logs: mutants/control-*.log, mutants/control.json.

Control Test files Exit Tests Pass Fail Skipped
control-01 plugins/tests/kinds.test.mjs, plugins/tests/loader.test.mjs 0 21 21 0 0
control-02 plugins/tests/kinds.test.mjs 0 5 5 0 0
control-03 plugins/tests/loader.test.mjs 0 16 16 0 0
control-04 business/tests/plugin-settings.test.mjs, plugins/tests/loader.test.mjs 0 20 20 0 0
control-05 business/tests/plugin-settings.test.mjs 0 4 4 0 0
control-06 cli/tests/stack-copy.test.mjs 0 12 12 0 0

All pass with 0 skipped, so the chmod tests (run as a normal user) ran.

Mutants

Result: 47 of 47 cases run; 47 KILLED, 0 SURVIVED, 0 TIMEOUT.

Mutant Check Result Test files First failing test
01-name-pattern kinds: names must match ^[a-z][a-z0-9-]*$ KILLED (exit 1) plugins/tests/kinds.test.mjs, plugins/tests/loader.test.mjs REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates
02-list-cardinality kinds: a list kind refuses a non-list KILLED (exit 1) plugins/tests/kinds.test.mjs, plugins/tests/loader.test.mjs REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything
03-one-cardinality kinds: a one-module kind refuses a non-string KILLED (exit 1) plugins/tests/kinds.test.mjs REQ-PLUG-1 a kind that switches on one module takes one name, not a list
04-no-duplicates kinds: a name twice refuses KILLED (exit 1) plugins/tests/kinds.test.mjs, plugins/tests/loader.test.mjs REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates
05-known-kind kinds: an unknown kind refuses KILLED (exit 1) plugins/tests/kinds.test.mjs REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates
06-plugins-object kinds: plugins must be a non-array object KILLED (exit 1) plugins/tests/kinds.test.mjs REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates
07-registry-exports registry: limit-check exports mayLaunch and mustStop KILLED (exit 1) plugins/tests/kinds.test.mjs, plugins/tests/loader.test.mjs REQ-PLUG-1 REQ-LIMIT-4 the registry: usage-source and limit-check at interface version 1, each a list
08-registry-version registry: usage-source is interface version 1 KILLED (exit 1) plugins/tests/kinds.test.mjs, plugins/tests/loader.test.mjs REQ-PLUG-1 REQ-LIMIT-4 the registry: usage-source and limit-check at interface version 1, each a list
09-missing-directory loader: an absent path refuses as missing KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting
10-component-type loader: a component of the wrong type refuses KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting
11-symlink-test loader: a symlinked component refuses KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting
12-plugins-dir-checked loader: packages/plugins is checked KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it)
13-kind-dir-checked loader: is checked KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting
14-name-dir-checked loader: is checked KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting
15-index-checked loader: index.mjs is checked KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting
16-realpath-before-import loader: realpath(index.mjs) equals the expected path just before import KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check
17-load-failure loader: a module that fails to load refuses KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a module that fails to load refuses and keeps the old setting
18-export-present loader: every export is present KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a missing export refuses and keeps the old setting
19-export-function loader: the kind's exports are functions KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a missing export refuses and keeps the old setting
20-interface-version loader: interfaceVersion must equal the kind's KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a different interface version refuses and keeps the old setting
21-version-strict loader: the version comparison is strict (string "1" refuses) KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a different interface version refuses and keeps the old setting
22-keep-old-setting loader: a refusal keeps the last good modules KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting
23-first-load-nothing-on loader: a kind refused on first load has nothing on KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name
24-refusal-logged loader: every refusal calls log KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting
25-log-names-module loader: the log names the refused module KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting
26-log-kept loader: the log says what stays on KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting
27-whole-setting-object loader: an array setting refuses every kind KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything
28-unknown-kind-logged loader: an unknown kind refuses and is logged KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything
29-absent-key-off loader: an absent key switches the kind off KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a path that becomes unreadable after the lstat checks refuses at either realpath
30-loads-serialized loader: loads run one at a time KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 loads run one at a time, so the last requested setting wins
31-log-required loader: createLoader needs a log callback KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 the loader needs a log callback
32-schema-key schema: plugins is an allowed top-level key KILLED (exit 1) business/tests/plugin-settings.test.mjs, plugins/tests/loader.test.mjs REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names
33-schema-check schema: plugins is checked with checkPlugins KILLED (exit 1) business/tests/plugin-settings.test.mjs REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names
34-schema-returned schema: plugins is returned KILLED (exit 1) business/tests/plugin-settings.test.mjs, plugins/tests/loader.test.mjs REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names
35-schema-copy schema: the returned value is a frozen copy, the document stays mutable KILLED (exit 1) business/tests/plugin-settings.test.mjs REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names
36-setter-strict-keys setter: only revision and plugins are accepted KILLED (exit 1) business/tests/plugin-settings.test.mjs REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing
37-setter-revision-format setter: revision must be 64 hex KILLED (exit 1) business/tests/plugin-settings.test.mjs REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing
38-setter-checks-names setter: plugins is checked before the writer runs KILLED (exit 1) business/tests/plugin-settings.test.mjs REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing
39-setter-actor-logged setter: the actor is logged KILLED (exit 1) business/tests/plugin-settings.test.mjs REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names
40-setter-writes-plugins setter: plugins is written KILLED (exit 1) business/tests/plugin-settings.test.mjs REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names
41-copy-ships-plugins stack-copy: packages/plugins ships in the pinned copy KILLED (exit 1) cli/tests/stack-copy.test.mjs shared copy: npm ci, pushed reachability, readonly/existing copy, webui delegation
42-modules-known-kind loader: modules() refuses an unknown kind KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything
43-unreadable-refused loader: a read error other than ENOENT/ENOTDIR refuses as unreadable KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a path that can't be read refuses as unreadable, is logged and keeps the old setting
44-realpath-index-mapped loader: realpath(index.mjs) errors become refusals KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a path that becomes unreadable after the lstat checks refuses at either realpath
45-realpath-root-mapped loader: realpath(packages/plugins) errors become refusals KILLED (exit 1) plugins/tests/loader.test.mjs REQ-PLUG-1 a path that becomes unreadable after the lstat checks refuses at either realpath
46-setter-plain-prototype setter: plugins must have prototype Object.prototype or null KILLED (exit 1) business/tests/plugin-settings.test.mjs REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing
47-writer-checks-bytes writer: validates JSON.parse of the bytes it writes KILLED (exit 1) business/tests/plugin-settings.test.mjs REQ-PLUG-1 REQ-ROLE-3 the writer checks the bytes it writes: a Date in plugins is refused there too

New in round 2: 43, 44 and 45 (F2), 46 (F1a), 47 (F1b). 43 to 45 fail because load() rejects on the raw EACCES instead of logging a refusal; 46 lets a Date through the setter, written as a date string; 47 lets the writer take a Date without a BusinessError. Mutant 09 now sits in the shared fsCall helper: without the ENOENT mapping a missing path refuses as unreadable, not missing. Mutant 17's log shows a SyntaxError: it is the fixture module's deliberate syntax error, which the mutant rethrows instead of refusing. changes(old, written) has no mutant; see BUILD.md, round 2.