Files
stack/agents/rocko/work/queue-79/build.patch
T
jason.woltjeandClaude Opus 5.5 1735f27d37 rocko: row 79 round 1 packet, BUILD-LOG and SESSIONS (#1573)
Packet agents/rocko/work/queue-79/ (packet manifest b6b11e7b, 42 files,
candidate manifest 16b12387), Rocko's BUILD-LOG and SESSIONS appends.
Committed by Sage for Rocko, who has no credential.

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

112 lines
5.7 KiB
Diff

diff --git a/agents/rocko/work/queue-79/mutants.sh b/agents/rocko/work/queue-79/mutants.sh
new file mode 100755
index 00000000..0d925699
--- /dev/null
+++ b/agents/rocko/work/queue-79/mutants.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+# Row 79 (#1573): rerun the untyped-list mutants against a tree. Usage:
+# mutants.sh <tree>. Each mutant edits untypedActions in
+# packages/harness/src/tools.mjs, runs the harness tools test, and restores
+# the file from a saved copy, so the tree need not be committed. Prints
+# "killed" or "SURVIVED" per mutant; exit 1 if any survives, a sed doesn't
+# change the file, or the unmutated run fails. untyped-within-only stands in
+# for Dewey's mutant of that name (row 73 comment 27506).
+set -euo pipefail
+TREE="$(cd "${1:?usage: mutants.sh <tree>}" && pwd)"
+cd "$TREE"
+SRC=packages/harness/src/tools.mjs
+TESTS=(packages/harness/tests/tools.test.mjs)
+run_tests() { node --test "${TESTS[@]}" >"$1" 2>&1; }
+
+# name | sed expression
+MUTANTS=(
+ 'untyped-within-only|s/^ return \[\.\.\.resolved\.limits\.authority\.withinRole, \.\.\.resolved\.limits\.authority\.crossRole\]\.filter/ return [...resolved.limits.authority.withinRole].filter/'
+ 'untyped-cross-first|s/^ return \[\.\.\.resolved\.limits\.authority\.withinRole, \.\.\.resolved\.limits\.authority\.crossRole\]\.filter/ return [...resolved.limits.authority.crossRole, ...resolved.limits.authority.withinRole].filter/'
+ 'untyped-keeps-typed|s/\.filter((a) => !typed\.has(a));$/.filter((a) => a === "message.send" || !typed.has(a));/'
+)
+
+OUT="${OUT:-$TREE/.mutants}"
+mkdir -p "$OUT"
+run_tests "$OUT/baseline.log" || { echo "baseline run failed: $OUT/baseline.log" >&2; exit 1; }
+echo "baseline: pass"
+status=0
+for m in "${MUTANTS[@]}"; do
+ IFS='|' read -r name expr <<<"$m"
+ cp -p "$SRC" "$OUT/orig"
+ sed -i -e "$expr" "$SRC"
+ if cmp -s "$OUT/orig" "$SRC"; then
+ echo "$name: NOT APPLIED (sed matched nothing)"; status=1; continue
+ fi
+ diff -u "$OUT/orig" "$SRC" >"$OUT/$name.diff" || true
+ if run_tests "$OUT/$name.log"; then
+ echo "$name: SURVIVED"; status=1
+ else
+ echo "$name: killed ($(grep -E '^✖' "$OUT/$name.log" | grep -v 'failing tests' | sed 's/ ([0-9.]*ms)$//' | sort -u | tr '\n' ';'))"
+ fi
+ cp -p "$OUT/orig" "$SRC"
+ cmp -s "$OUT/orig" "$SRC" || { echo "restore failed: $SRC" >&2; exit 1; }
+ rm -f "$OUT/orig"
+done
+exit "$status"
diff --git a/packages/business/README.md b/packages/business/README.md
index 9c3e8d1f..ca372c5e 100644
--- a/packages/business/README.md
+++ b/packages/business/README.md
@@ -34,7 +34,7 @@ in this repository, or `$MOSAIC_ROLES_DIR` if set.
"network": "api-only",
"authority": {
"withinRole": ["task.update.assigned", "git.push.working", "review.request", "message.send"],
- "crossRole": ["task.reassign", "task.scope.change"]
+ "crossRole": ["task.scope.change"]
},
"credentials": [
{ "service": "gitea", "scopes": ["write:issue", "write:repository", "read:user"] },
diff --git a/packages/harness/tests/tools.test.mjs b/packages/harness/tests/tools.test.mjs
index 31606131..3fe7dd1e 100644
--- a/packages/harness/tests/tools.test.mjs
+++ b/packages/harness/tests/tools.test.mjs
@@ -30,9 +30,7 @@ test("a coder gets no launch, no resolve_decision, and no task tools without a t
const tools = typedTools({ resolved: coder });
assert.deepEqual(names(tools), ["send_message", "raise_decision", "list_agents", "inbox", "trail"]);
// Held actions without a tool are listed, so the manifest shows them.
- const untyped = untypedActions(coder, tools);
- assert.ok(untyped.includes("git.push.working"));
- assert.ok(!untyped.includes("message.send"));
+ assert.deepEqual(untypedActions(coder, tools), ["task.update.assigned", "git.push.working", "review.request", "task.scope.change"]);
});
test("a coder with a tracker gets its own task tools and no task_reassign", (t) => {
diff --git a/packages/tasks/README.md b/packages/tasks/README.md
index 61b36664..d47f5de6 100644
--- a/packages/tasks/README.md
+++ b/packages/tasks/README.md
@@ -190,10 +190,10 @@ Details that aren't obvious from the table:
- `task.close` moves the task into the done bucket. `verdict` is a
citation of the review verdict in the queue. The adapter records it
and doesn't check it. Checking it against the queue is a follow-up.
-- A coder role's `crossRole` grant of `task.reassign` can't be used. The
- writer check refuses it before authority is looked at. If a coder
- should hand work on, that's a change to the field table, not to the
- role file.
+- The coder role holds no `task.reassign` grant (row 73). The writer
+ check refuses a non-pm `task.reassign` before authority is looked at
+ anyway. If a coder should hand work on, that's a change to the field
+ table, not to the role file.
Verbs for one business run one at a time, in the same queue as the poll,
so a poll never reads between two writes of a verb.
diff --git a/packages/tasks/live/run.mjs b/packages/tasks/live/run.mjs
index eed1513e..0c8d7e97 100644
--- a/packages/tasks/live/run.mjs
+++ b/packages/tasks/live/run.mjs
@@ -29,7 +29,7 @@ const AUTHORITY = {
withinRole: ['task.create', 'task.assign', 'task.reassign', 'task.schedule', 'task.update.assigned', 'task.close', 'message.send'],
crossRole: ['task.priority.change', 'task.scope.change'],
},
- coder: { withinRole: ['task.update.assigned', 'message.send'], crossRole: ['task.reassign', 'task.scope.change'] },
+ coder: { withinRole: ['task.update.assigned', 'git.push.working', 'review.request', 'message.send'], crossRole: ['task.scope.change'] },
reviewer: { withinRole: ['task.update.assigned', 'message.send'], crossRole: [] },
};
const ref = (x) => (x.file ? { file: x.file, expires: x.expires } : { env: x.env, expires: x.expires });