#!/usr/bin/env python3 # Row 292 mutants. M1-M5 are Gander's (agents/gander/work/config-checks-any-key/changes.json), with # the same edits on the resolved config_checks; M1 restores the console-only skip (the brief's # required mutant). M6-M9 are the resolution's: row 203's console and federation text. `apply ` # edits the worktree and prints the touched path; each edit asserts its anchor appears exactly once. # `check` prints each anchor's count and kill. K is read from the tests, not run. A restore is `git checkout -- `, then sha256 against the blob in BUILD.md. import sys WT = "/mnt/storage/scratch/gizmo-292/" S = "scripts/stack-release.sh" T = "packages/cli/tests/stack-release.test.mjs" M = { "M1": (S, ' [ "$key" != 1 ] || return 0\n if [ "$key" != 0 ]; then', ' [ "$key" != 1 ] || return 0\n [ "$extra" = \'"console"\' ] || return 0\n if [ "$key" != 0 ]; then'), "M2": (S, 'elif out="$(MOSAIC_CONFIG="$cfg" node "$loader" validate 2>&1 >/dev/null)"; then return 0', 'elif out="$(MOSAIC_CONFIG="$cfg" node "$loader" validate 2>&1 >/dev/null)" || true; then return 0'), "M3": (S, ' [ -n "$extra" ] || return 0', ' return 0 # drop missing-loader later-key refusal'), "M4": (S, ' if [ "$key" != 0 ]; then\n check_refuse config-console-key', ' if [ "$key" = 99 ]; then\n check_refuse config-console-key'), "M5": (S, ' if [ ! -f "$loader" ]; then', ' [ -n "$extra" ] || return 0\n if [ ! -f "$loader" ]; then'), "M6": (S, 'Object.hasOwn(d,"federation")?"federation":', ''), "M7": (S, 'const k=Object.hasOwn(d,"console")?"console":', 'const k='), "M8": (S, ' if [ "$extra" = \'"console"\' ] || [ "$extra" = \'"federation"\' ]; then\n check_refuse', ' if [ "$extra" = \'"console"\' ]; then\n check_refuse'), "M9": (S, '[ "$extra" = \'"federation"\' ]; then out="it has no scripts/mosaic-config.mjs"', '[ "$extra" = \'"federation-off"\' ]; then out="it has no scripts/mosaic-config.mjs"'), } # K: (--test-name-pattern, the test.mjs lines the failing stack must show, the text the failure # must show). Kill rule (sage-3152): the named test fails, its first error's stack names every # listed line, and the log holds the text (no quote characters, which node's inspect may escape). A fail anywhere else, or a crash first, is a STOP to Sage. # A survivor of its targeted run gets one whole-file run before it counts as a survivor. K = { "M1": ('^config-any-key: a non-console target refusal stops activate and rollback before any stop$', [517, 2619], "actual: 0"), "M2": ('^config-any-key: a non-console target refusal stops activate and rollback before any stop$', [517, 2619], "actual: 0"), "M3": ('^config-any-key: missing-loader copies accept only the four oldest keys$', [2670], "actual: 0"), "M4": ('^config-any-key: missing config passes but malformed JSON refuses without calling the loader$', [2684], "actual: 0"), "M5": ('^config-any-key: accepting loaders run validate with the selected config on activate and rollback$', [2654], "actual: []"), "M6": ('^config-any-key: missing-loader copies accept only the four oldest keys$', [2677], "to validate configuration key: "), "M7": ('^config-any-key: missing-loader copies accept only the four oldest keys$', [2677], "to validate configuration key: "), "M8": ('^config-any-key: missing-loader copies accept only the four oldest keys$', [2677], "is refused by old"), "M9": ('^config-any-key: missing-loader copies accept only the four oldest keys$', [2677], "to validate configuration key: "), } cmd = sys.argv[1] if cmd == "list": print(" ".join(M)); sys.exit(0) if cmd == "check": for k, (f, a, b) in M.items(): print(k, f, open(WT + f).read().count(a), "kill:", *K[k]) sys.exit(0) f, a, b = M[sys.argv[2]] s = open(WT + f).read() assert s.count(a) == 1, (sys.argv[2], s.count(a)) open(WT + f, "w").write(s.replace(a, b)) print(f)