Files

80 lines
4.6 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Row 119 (#1613): role files for the chief of staff and the assistant.
# Each check must fail on a mutant. For each mutant this runs one named test file against the
# real source (control) and against the source with one exact-once text edit, in the given worktree,
# one test file at a time, and restores every source file on exit.
# Usage: mutants.sh <worktree> <outdir>
set -u
WT=$(cd "$1" && pwd); OUT=$(mkdir -p "$2" && cd "$2" && pwd)
cd "$WT" || exit 2
BAK=$(mktemp -d "$OUT/.bak.XXXXXX")
declare -A SAVED
save() { [ -n "${SAVED[$1]:-}" ] || { mkdir -p "$BAK/$(dirname "$1")"; cp "$1" "$BAK/$1"; SAVED[$1]=1; }; }
restore() { for f in "${!SAVED[@]}"; do cp "$BAK/$f" "$f"; done; rm -rf "$BAK"; }
trap restore EXIT
edit() { # file old new: exact-once replace
save "$1"
node -e '
const fs = require("fs"); const [f, a, b] = process.argv.slice(1);
const s = fs.readFileSync(f, "utf8"); const n = s.split(a).length - 1;
if (n !== 1) { console.error("expected one match, found " + n); process.exit(3); }
fs.writeFileSync(f, s.replace(a, () => b));
' "$1" "$2" "$3"
}
run() { # name testfile
setsid -w timeout -k 10 400 node --test "$2" > "$OUT/$1.out" 2>&1; echo "$?" > "$OUT/$1.rc"
echo "$1: exit $(cat "$OUT/$1.rc"); $(grep -E '^ℹ (tests|pass|fail)' "$OUT/$1.out" | tr '\n' ' ')"
grep -E '^✖' "$OUT/$1.out" | sort -u | sed 's/ ([0-9.]*ms)$//' | sed "s/^/ failed: /"
}
mutant() { # name testfile file old new
edit "$3" "$4" "$5" || { echo "$1: edit failed"; return; }
diff -u "$BAK/$3" "$3" > "$OUT/$1.patch"
run "$1" "$2"
cp "$BAK/$3" "$3"
}
BT=packages/business/tests; HT=packages/harness/tests; CT=packages/cli/tests; WT_=packages/webui/tests
COS=$BT/chief-of-staff.test.mjs; AST=$BT/assistant.test.mjs; ROLE=$BT/role.test.mjs; BUS=$BT/business.test.mjs
TOOLS=$HT/tools.test.mjs; BUNDLE=$HT/bundle.test.mjs; LAUNCH=$CT/launcher.test.mjs; READS=$WT_/reads.test.mjs
C=roles/chief-of-staff.json; P=roles/pm.json; A=roles/assistant.json; V=packages/business/src/business.mjs
for t in "$COS" "$AST" "$ROLE" "$BUS" "$TOOLS" "$BUNDLE" "$LAUNCH" "$READS"; do run "control-$(basename "$t" .test.mjs)" "$t"; done
# m1: the chief of staff loses role.launch (REQ-COS-1, decision 139)
for pair in "a $COS" "b $TOOLS" "c $BUNDLE" "d $LAUNCH" "e $READS" "f $BUS"; do
set -- $pair
mutant "m1$1-chief-drops-launch" "$2" "$C" '"withinRole": ["message.send", "role.launch"]' '"withinRole": ["message.send"]'
done
# m2: the PM regains role.launch (REQ-LAUNCH-1)
for pair in "a $COS" "b $ROLE" "c $TOOLS" "d $BUNDLE" "e $READS"; do
set -- $pair
mutant "m2$1-pm-regains-launch" "$2" "$P" '"task.close", "message.send"]' '"task.close", "message.send", "role.launch"]'
done
# m3: the assistant gains role.launch
mutant m3-assistant-launch "$AST" "$A" '"withinRole": ["message.send"]' '"withinRole": ["message.send", "role.launch"]'
# m4: the assistant gains a write tool
mutant m4-assistant-write "$AST" "$A" '"tools": ["read", "grep", "find", "ls"]' '"tools": ["read", "write", "grep", "find", "ls"]'
# m5: the assistant gains a cross-role action
mutant m5-assistant-crossrole "$AST" "$A" '"crossRole": []' '"crossRole": ["task.priority.change"]'
# m6: the assistant gains a credential
mutant m6-assistant-credential "$AST" "$A" '"credentials": []' '"credentials": [{"service": "gitea", "scopes": ["read:user"]}]'
# m7: the chief of staff gains a write tool
mutant m7-chief-write "$COS" "$C" '"tools": ["read", "grep", "find", "ls"]' '"tools": ["read", "write", "grep", "find", "ls"]'
# m8: the chief of staff gains a credential
mutant m8-chief-credential "$COS" "$C" '"credentials": []' '"credentials": [{"service": "gitea", "scopes": ["read:user"]}]'
# m9: the validator stops requiring role.launch of the launcher
for pair in "a $COS" "b $AST" "c $BUS"; do
set -- $pair
mutant "m9$1-validator-accepts-any-launcher" "$2" "$V" 'if (!launcher.authority.withinRole.includes("role.launch")) {' 'if (false) {'
done
# m10: the PM contract launches again
mutant m10-pm-contract-launches "$COS" roles/pm.md "You don't launch sessions." "Launch coders and reviewers when a task is ready."
# m11: the chief of staff contract talks to the user again
mutant m11-chief-contract-talks "$COS" roles/chief-of-staff.md "You never talk to the user." "You never talk to the user, but you are the main point of interaction."
# m12: the assistant contract directs agents again
mutant m12-assistant-contract-directs "$AST" roles/assistant.md "You don't direct" "You direct"
# m13: a shipped role file names a contract that isn't there (every shipped role loads)
for pair in "a $ROLE" "b $AST"; do
set -- $pair
mutant "m13$1-assistant-contract-missing" "$2" "$A" '"contract": "assistant.md"' '"contract": "missing.md"'
done
echo done