agents: researcher row 119 packet (#1613): chief-of-staff and assistant role files, PM drops role.launch, org-file entry and one-window landing order

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
2026-10-14 19:40:28 -05:00
co-authored by Claude Sonnet 5.5
parent 9e837ce5f0
commit 58643299e0
106 changed files with 5942 additions and 0 deletions
+143
View File
@@ -0,0 +1,143 @@
# Row 119 (#1613): role files for the chief of staff and the assistant
Researcher, 2026-10-15. Brief: `docs/plans/2026-10-14_prd-1.2-build.md`, "Role files for the chief of
staff and the assistant", pinned at 240d25518. Requirements: REQ-COS-1, REQ-ASSIST-1, REQ-ROLE-4,
REQ-LAUNCH-1. Decisions 139 (chief holds `role.launch`), 140 (leftover "main point of interaction"
duties removed), 144 (Jason's org file isn't edited by the stack). Reviewer: Filbert (decision 130,
role files, no sandbox change).
Base: c6570f142 (`row119.patch` applies cleanly to it by `git apply --cached --check`; no commit
between c6570f142 and 9e837ce5f touches a candidate path). No broker, thread or org-file verb changes
(rows 120, 121, 124).
## What changes
Role files (the candidate):
| File | Change |
|---|---|
| `roles/assistant.json` (new) | tools `read grep find ls`; network api-only; `withinRole ["message.send"]`, `crossRole []`; credentials `[]` |
| `roles/assistant.md` (new) | REQ-ASSIST-1: talks with the user, passes work to the chief of staff, directs no agent, creates or assigns no task, launches no session |
| `roles/chief-of-staff.json` | `withinRole ["message.send", "role.launch"]` (decision 139); tools, network and credentials unchanged |
| `roles/chief-of-staff.md` | rewritten to REQ-COS-1; the "main point of interaction" duties are gone (decision 140); never talks to the user |
| `roles/pm.json` | `role.launch` removed from `withinRole` (REQ-LAUNCH-1) |
| `roles/pm.md` | the launch bullet becomes "Take the work the chief of staff passes you. You don't launch sessions."; "resume or launch work" becomes "resume or start work" |
Docs and example: `packages/business/README.md` (Chief-of-staff and assistant section, rewritten) and
`packages/business/examples/mosaic-stack.example.json` (adds `chief` and `assistant`, `launch.by "chief"`).
Tests (fixtures follow the roles; the only source files untouched are the packages' `src/`):
- business: `helpers.mjs` (shared `businessDoc` gains `chief`, `launch.by "chief"`), `business.test.mjs`,
`cli.test.mjs`, `resolve.test.mjs`, `role.test.mjs`, `chief-of-staff.test.mjs`, new `assistant.test.mjs`.
- harness: `tools.test.mjs` (PM has no launch; new chief and assistant tool lists), `bundle.test.mjs`
(PM bundle has no launch; new chief bundle test), `mcp-server.test.mjs`, `pi-session.test.mjs`
(the two symlink-write tests and the climbs loop use the PM, because the chief has no write tool).
- cli: `launcher.test.mjs` (launcher instance renamed pm to chief; the founder-credentials test launches
the coder through a bound chief capability), `trackers-boot.test.mjs` (guards for roles with no
tracker or credentials).
- webui: `reads.test.mjs` (chief `role.launch` within, PM gated, `b.launch.by "chief"`).
- Not changed: `packages/webui/tests/views.test.mjs` (stub fixture, no launch assertion). Correction to
my earlier message to Sage: bus needed no fixture edits. Its suite passes 156/156 untouched because
its fixtures are in memory.
Instance names `chief` and `assistant` are my fixture and example choices. Jason picks the real ones.
## Gate (decision 136, targeted; slot held per sage-0250)
Worktree `/mnt/storage/scratch/researcher/q119` at c6570f142 plus `row119.patch`. Outputs are in `suites/`.
| Run | Result |
|---|---|
| business | 78 / 78 |
| bus | 156 / 156 |
| harness | 84 / 84 (first pass 81 / 84; the three pi-session symlink tests called `write` on the chief; fixed as above) |
| cli | 187 / 187 |
| webui | 108 / 108 |
| tasks | 92 / 92 |
| conversation | 182 / 182 |
| seat | 132 / 132 |
| `scripts/test-task.sh` | 98 passed, 0 failed |
## Mutants (`mutants.sh`, `mutants-run.txt`, `mutants/`)
Eight controls pass. 25 mutants, each one exact-once edit, each run against the named test file:
| Mutant | Edit | Caught by |
|---|---|---|
| m1a-f | chief drops `role.launch` | chief-of-staff, harness tools, harness bundle, cli launcher, webui reads, business |
| m2a, m2b | PM regains `role.launch` | chief-of-staff ("the pm does not hold role.launch", "launch.by pm refused"), role ("shipped authority follows the note's table") |
| m2c, m2d, m2e | same edit | **survive** (harness tools, harness bundle, webui reads); see below |
| m3 | assistant gains `role.launch` | assistant |
| m4 | assistant gains `write` | assistant |
| m5 | assistant gains a crossRole action | assistant |
| m6 | assistant gains a credential | assistant |
| m7 | chief gains `write` | chief-of-staff |
| m8 | chief gains a credential | chief-of-staff |
| m9a-c | validator stops requiring `role.launch` of the launcher | chief-of-staff, assistant, business |
| m10 | `pm.md` launches again | chief-of-staff (contract pin) |
| m11 | `chief-of-staff.md` is the main point of interaction again | chief-of-staff (contract pin) |
| m12 | `assistant.md` directs agents | assistant (contract pin) |
| m13a, m13b | `assistant.json` names a missing contract | role ("every role file in roles/ loads through the validator"), assistant |
Why m2c, m2d and m2e survive: `packages/business/src/resolve.mjs:74` keeps `role.launch` only for the
instance `launch.by` names, and lines 76-77 filter it out of every other instance. The PM is never
`launch.by` in the fixtures, so a PM role file that holds `role.launch` resolves with it gated, and the
harness tools, harness bundle and webui reads see the same effective result as before the edit. Those
tests pin the resolved behaviour, which stays safe. The role file itself is pinned where it is read,
by m2a and m2b in the business package. The broker has the same gate at `packages/bus/src/broker.mjs:305`
(`role.launch` is gated unless `business.launch.by === s.role`). Sage agreed on 2026-10-15 (sage-0255).
## What Jason has to do, and in what order
The stack never writes Jason's org file (decision 144). Row 119 ships the role files; the entry below is
what makes them usable. File: `~/.config/mosaic-dev/businesses/mosaic-stack.json`.
Add under `roles` (names are Jason's choice; `vars` mirror the PM's, and the assistant's harness and model
are his call):
```json
"chief": {
"definition": "chief-of-staff",
"vars": { "harness": "claude-code", "model": "claude-opus-5-5", "thinking": "high" }
},
"assistant": {
"definition": "assistant",
"holder": "mos",
"vars": { "harness": "claude-code", "model": "claude-opus-5-5", "thinking": "high" }
}
```
and change `launch.by` from `"pm"` to `"chief"` (`instances` and `max` stay as they are:
`["coder","reviewer"]`, `{"opus":4,"sonnet":4}`). Neither instance takes `tracker` or `credentials`; the
validator refuses both.
### Landing order: one window, PM idle
- Before the re-pin, Jason can't name a chief instance in `launch.by`: the pinned copy's
`chief-of-staff.json` lacks `role.launch`, so the old copy refuses it.
- After the re-pin, his current file (`launch.by: "pm"`) fails to load, because the new `pm.json` no
longer holds `role.launch`.
- So his edit and the re-pin happen in one window run by Mos, with the PM run idle. This is the same
pattern as decision 145 for row 122. The re-pin is Sage's and lands with rows 104, 106 and 109.
- Until Jason adds the entry, the broker keeps `role.launch` gated for any chief-of-staff session
(`broker.mjs:305`: `launch.by` is still `pm`). That is expected and not a defect.
### After that window
Only a running chief-of-staff session can launch. Until one runs, coders and reviewers don't start.
Mos needs to plan the window with that in mind: start the chief session first, then let it launch the
rest. This ships what decision 139 chose; it is not a defect in the row.
## Out of scope and follow-ups
- Broker send limits for the assistant: row 120 (REQ-ASSIST-2, #1614), mine next.
- Threads, org-file verbs, anything under `packages/bus/src`: rows 121, 124, 128.
- `scripts/test-task.sh` still names `pm cto coder reviewer` in its shipped-roles loop; the node tests
(`role.test.mjs`) load every role file, so I left the script alone.
## Files in this packet
`row119.patch`, `candidate-manifest.sha256` (the 22 post-patch candidate files), `mutants.sh`,
`mutants-run.txt`, `mutants/` (per-mutant output, return code, patch), `suites/` (suite output),
`packet-manifest.sha256` (digests of the packet files themselves).
@@ -0,0 +1,22 @@
0fe45e5c62e73dc84674e6ed8c3354ad489b6c6ece540042ff246a0abb3da9e3 packages/business/examples/mosaic-stack.example.json
f5bcb448a5d27c2da425536ad0487bb24c63dd6d8ec169a944fc67d25effa00f packages/business/README.md
0c4d1e87fe76911cb9252320ac5efc98394cb3d5a8403fd4ab2be6d57adf9c56 packages/business/tests/assistant.test.mjs
907074699b67f61ada185123547c65da75ddde591bc6c703cc0dcb14dbb3af7c packages/business/tests/business.test.mjs
e308edbf5e15e6a55fc0ce6eb8b1ccadb3f0c3071146b59a5139bf8ddb231b6f packages/business/tests/chief-of-staff.test.mjs
653ae43cfe894dcf486cad6963bcde0d33c811642d4ebd927d727f95983f2dfa packages/business/tests/cli.test.mjs
2490610d70bf3f03778a3cf2f59a31fa8d9b3fa425dd9b531e4aa2f8ed4b0b8f packages/business/tests/helpers.mjs
9fe2fecc03af3ad31e880bf611dc55e75a25b54f95502baa282eb820c7a90043 packages/business/tests/resolve.test.mjs
028751009b33fe068ceb33603d08f39982e06d870a7e7a6e4a2f2eb2fd6049cd packages/business/tests/role.test.mjs
d95115368e3711ad6a59a7f7570a5b35ac93ff528e513e53f0f039140d9b7b35 packages/cli/tests/launcher.test.mjs
250c3413e51994928c3860da940947aa8f94afe3938cba8236af8c650509bced packages/cli/tests/trackers-boot.test.mjs
3792f9d2b8d41e27d2eb07b1f6b190639a4e75221c6bcf2bda830363f176bc5a packages/harness/tests/bundle.test.mjs
fb229602039dcecfc609e51f7176f264fbbfbe23cd7897ec86a524cab3f58695 packages/harness/tests/mcp-server.test.mjs
cd88d390187fba8dcb7acd0ef05c56306dcb8d60eb7f4f635520f2bad19be83c packages/harness/tests/pi-session.test.mjs
8f92328d54ec790ae2aca7c04b5c768e146120152a46fa01bab3007591106101 packages/harness/tests/tools.test.mjs
2d9f2a3ce4865087fe7b865f48dbc71564fc0efd26e7834aeab05ed796fcc98c packages/webui/tests/reads.test.mjs
35b6465f280999ebac881d941cb6e5cc618e68e6bf55bfccecf27ea1c961c992 roles/assistant.json
f97fe0b24d8873ace1aa65140336085bf012ad794acf03087053fad4a0c7bd02 roles/assistant.md
28e394bba7a4991c5f7edd41d40499ec35cbcd56bd72d412f3c69a45b0ed0284 roles/chief-of-staff.json
f0238afad94190fc0dfbbf7fa97d283df0091dfe17f0be38c5b638d635700744 roles/chief-of-staff.md
a74cbd80054356658cb6517654a016d2111bdbba15a606f982a5aabe1c34dc5d roles/pm.json
7087d972d285f5260859bd40500904665bdd01e1c4d9bf89d405b5ea292faf2c roles/pm.md
@@ -0,0 +1,147 @@
control-chief-of-staff: exit 0; ℹ tests 10 ℹ pass 10 ℹ fail 0
control-assistant: exit 0; ℹ tests 7 ℹ pass 7 ℹ fail 0
control-role: exit 0; ℹ tests 13 ℹ pass 13 ℹ fail 0
control-business: exit 0; ℹ tests 15 ℹ pass 15 ℹ fail 0
control-tools: exit 0; ℹ tests 13 ℹ pass 13 ℹ fail 0
control-bundle: exit 0; ℹ tests 9 ℹ pass 9 ℹ fail 0
control-launcher: exit 0; ℹ tests 17 ℹ pass 17 ℹ fail 0
control-reads: exit 0; ℹ tests 10 ℹ pass 10 ℹ fail 0
m1a-chief-drops-launch: exit 1; ℹ tests 10 ℹ pass 3 ℹ fail 7
failed: ✖ a business grant of a gated action through limits does not widen the role
failed: ✖ a limit that drops role.launch leaves the chief of staff with no launch block
failed: ✖ failing tests:
failed: ✖ the chief of staff holds role.launch and message.send; every other action is gated
failed: ✖ the pm does not hold role.launch
failed: ✖ the resolved record matches the approved list, so a widened file fails
failed: ✖ the role file loads and resolves through loadBusiness
failed: ✖ the shipped file is exactly the approved list
m1b-chief-drops-launch: exit 1; ℹ tests 13 ℹ pass 3 ℹ fail 10
failed: ✖ a coder gets no launch, no resolve_decision, and no task tools without a tracker
failed: ✖ a coder with a tracker gets its own task tools and no task_reassign
failed: ✖ an action outside the instance's authority has no tool
failed: ✖ failing tests:
failed: ✖ launch only when the business's launch block names the instance as launcher
failed: ✖ list_requirements reads the requirement ids, for a task.create holder with a tracker
failed: ✖ list_tasks takes the broker's filters and pages; get_task takes a task_ref
failed: ✖ the assistant gets send_message and the reads, no launch
failed: ✖ the chief of staff gets launch and send_message, and no task verbs
failed: ✖ the PM gets its task verbs and the reads, and no launch
failed: ✖ the review loop: push_working and request_review for the coder, fetch_review and review_verdict for the reviewer, none for the PM
m1c-chief-drops-launch: exit 1; ℹ tests 9 ℹ pass 0 ℹ fail 9
failed: ✖ a bundle is written once: an existing file refuses
failed: ✖ a chief-of-staff bundle lists launch among the bus tools and the manifest's typed tools
failed: ✖ a claude-code bundle adds the wrapped gate hook and the MCP config
failed: ✖ a path with a single quote can't go into the hook command
failed: ✖ a pi bundle: prompt, policy, tools and manifest, 0600 in a 0700 directory
failed: ✖ failing tests:
failed: ✖ sessionModel: agent vars win, then the system's execution settings
failed: ✖ the prompt explains the review loop to a role with push_working, and reviews to one with fetch_review
failed: ✖ the prompt names the project clones in the workspace, and none when there are none
failed: ✖ the prompt's built-in tools line is the harness's own names, as the manifest lists them
m1d-chief-drops-launch: exit 1; ℹ tests 17 ℹ pass 1 ℹ fail 14
failed: ✖ a Claude Code launch with no session credential refuses before it starts
failed: ✖ a host that died hard leaves its sessions to the next host, which finds one already exited (23) and ends their runs so the role can be launched again
failed: ✖ a host that died hard leaves its sessions to the next host, which kills them and ends their runs so the role can be launched again
failed: ✖ a launch client that never closes its side doesn't hold the host's close
failed: ✖ a malformed sessions.json refuses the host start with exit 3 and stays as it was
failed: ✖ a missing or failing bwrap refuses sandbox-unavailable before anything starts; there is no unconfined fallback
failed: ✖ an over-long launch request is refused at once, not at the 10 s idle timeout
failed: ✖ a pi launch needs no session credential: pi logs in through its own ENV_ALLOW variables
failed: ✖ a project clone that can't be made refuses checkout-failed, from --chief and from a session, with no run and no clone left
failed: ✖ a real Claude Code coder's shell can't read a stand-in secret or anything else its sandbox doesn't hold
failed: ✖ a runner killed from the host ends its launch as killed, not failed
failed: ✖ a runner that ignores SIGTERM is killed when the host closes
failed: ✖ a runner that stops at once ends its launch with the runner's reason
failed: ✖ bus start --pm: the launcher instance (the chief of staff) runs under its own PID namespace, registered, with a manifest
failed: ✖ each launch makes sure the workspace has a clone of the project: cloned, fast-forwarded, or fetched and logged
failed: ✖ failing tests:
failed: ✖ the chief of staff launches a coder through its launch tool; the coder answers; refusals name their code
m1e-chief-drops-launch: exit 1; ℹ tests 10 ℹ pass 9 ℹ fail 1
failed: ✖ business: names, vars on the allowlist, authority, credential metadata only
failed: ✖ failing tests:
m1f-chief-drops-launch: exit 1; ℹ tests 15 ℹ pass 6 ℹ fail 9
failed: ✖ a role without Vikunja takes no tracker block
failed: ✖ failing tests:
failed: ✖ launch
failed: ✖ loadBusiness: file checks
failed: ✖ loading writes nothing
failed: ✖ names that are Object.prototype properties don't count as declared
failed: ✖ the fixture business validates and comes back frozen
failed: ✖ the shipped example refuses as written and validates once filled in
failed: ✖ two instances may share a definition
failed: ✖ Vikunja bots
m2a-pm-regains-launch: exit 1; ℹ tests 10 ℹ pass 8 ℹ fail 2
failed: ✖ a launch block whose by is the pm instance is refused
failed: ✖ failing tests:
failed: ✖ the pm does not hold role.launch
m2b-pm-regains-launch: exit 1; ℹ tests 13 ℹ pass 12 ℹ fail 1
failed: ✖ failing tests:
failed: ✖ shipped authority follows the note's table
m2c-pm-regains-launch: exit 0; ℹ tests 13 ℹ pass 13 ℹ fail 0
m2d-pm-regains-launch: exit 0; ℹ tests 9 ℹ pass 9 ℹ fail 0
m2e-pm-regains-launch: exit 0; ℹ tests 10 ℹ pass 10 ℹ fail 0
m3-assistant-launch: exit 1; ℹ tests 7 ℹ pass 4 ℹ fail 3
failed: ✖ a launch block whose by is the assistant is refused
failed: ✖ failing tests:
failed: ✖ the assistant's authority is message.send within role and nothing else
failed: ✖ the shipped file is exactly the approved list
m4-assistant-write: exit 1; ℹ tests 7 ℹ pass 6 ℹ fail 1
failed: ✖ failing tests:
failed: ✖ the shipped file is exactly the approved list
m5-assistant-crossrole: exit 1; ℹ tests 7 ℹ pass 5 ℹ fail 2
failed: ✖ failing tests:
failed: ✖ the assistant's authority is message.send within role and nothing else
failed: ✖ the shipped file is exactly the approved list
m6-assistant-credential: exit 1; ℹ tests 7 ℹ pass 1 ℹ fail 6
failed: ✖ a business grant through limits does not widen the role
failed: ✖ a launch block whose by is the assistant is refused
failed: ✖ failing tests:
failed: ✖ the assistant's authority is message.send within role and nothing else
failed: ✖ the instance takes no credential references and no tracker
failed: ✖ the role file loads and resolves through loadBusiness
failed: ✖ the shipped file is exactly the approved list
m7-chief-write: exit 1; ℹ tests 10 ℹ pass 8 ℹ fail 2
failed: ✖ failing tests:
failed: ✖ the resolved record matches the approved list, so a widened file fails
failed: ✖ the shipped file is exactly the approved list
m8-chief-credential: exit 1; ℹ tests 10 ℹ pass 1 ℹ fail 9
failed: ✖ a business grant of a gated action through limits does not widen the role
failed: ✖ a launch block whose by is the pm instance is refused
failed: ✖ a limit that drops role.launch leaves the chief of staff with no launch block
failed: ✖ failing tests:
failed: ✖ the chief of staff holds role.launch and message.send; every other action is gated
failed: ✖ the instance takes no credential references and no tracker
failed: ✖ the pm does not hold role.launch
failed: ✖ the resolved record matches the approved list, so a widened file fails
failed: ✖ the role file loads and resolves through loadBusiness
failed: ✖ the shipped file is exactly the approved list
m9a-validator-accepts-any-launcher: exit 1; ℹ tests 10 ℹ pass 9 ℹ fail 1
failed: ✖ a launch block whose by is the pm instance is refused
failed: ✖ failing tests:
m9b-validator-accepts-any-launcher: exit 1; ℹ tests 7 ℹ pass 6 ℹ fail 1
failed: ✖ a launch block whose by is the assistant is refused
failed: ✖ failing tests:
m9c-validator-accepts-any-launcher: exit 1; ℹ tests 15 ℹ pass 14 ℹ fail 1
failed: ✖ failing tests:
failed: ✖ launch
m10-pm-contract-launches: exit 1; ℹ tests 10 ℹ pass 9 ℹ fail 1
failed: ✖ failing tests:
failed: ✖ the contract names no duty to talk to the human and the pm contract no launch
m11-chief-contract-talks: exit 1; ℹ tests 10 ℹ pass 9 ℹ fail 1
failed: ✖ failing tests:
failed: ✖ the contract names no duty to talk to the human and the pm contract no launch
m12-assistant-contract-directs: exit 1; ℹ tests 7 ℹ pass 6 ℹ fail 1
failed: ✖ failing tests:
failed: ✖ the contract passes work to the chief of staff and directs no one
m13a-assistant-contract-missing: exit 1; ℹ tests 13 ℹ pass 12 ℹ fail 1
failed: ✖ every role file in roles/ loads through the validator, and a v2 contract exists
failed: ✖ failing tests:
m13b-assistant-contract-missing: exit 1; ℹ tests 7 ℹ pass 1 ℹ fail 6
failed: ✖ a business grant through limits does not widen the role
failed: ✖ a launch block whose by is the assistant is refused
failed: ✖ failing tests:
failed: ✖ the assistant's authority is message.send within role and nothing else
failed: ✖ the instance takes no credential references and no tracker
failed: ✖ the role file loads and resolves through loadBusiness
failed: ✖ the shipped file is exactly the approved list
done
+79
View File
@@ -0,0 +1,79 @@
#!/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
@@ -0,0 +1,15 @@
✔ the role file loads and resolves through loadBusiness (5.380069ms)
✔ the shipped file is exactly the approved list (0.617395ms)
✔ the assistant's authority is message.send within role and nothing else (1.692848ms)
✔ a business grant through limits does not widen the role (1.301892ms)
✔ a launch block whose by is the assistant is refused (1.685068ms)
✔ the instance takes no credential references and no tracker (2.082295ms)
✔ the contract passes work to the chief of staff and directs no one (0.260393ms)
ℹ tests 7
ℹ suites 0
ℹ pass 7
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 75.186139
@@ -0,0 +1 @@
0
@@ -0,0 +1,17 @@
✔ sessionModel: agent vars win, then the system's execution settings (8.173961ms)
✔ a pi bundle: prompt, policy, tools and manifest, 0600 in a 0700 directory (3.869685ms)
✔ a chief-of-staff bundle lists launch among the bus tools and the manifest's typed tools (1.94595ms)
✔ a claude-code bundle adds the wrapped gate hook and the MCP config (2.003966ms)
✔ the prompt's built-in tools line is the harness's own names, as the manifest lists them (6.339124ms)
✔ the prompt names the project clones in the workspace, and none when there are none (4.679244ms)
✔ the prompt explains the review loop to a role with push_working, and reviews to one with fetch_review (5.569899ms)
✔ a bundle is written once: an existing file refuses (1.284826ms)
✔ a path with a single quote can't go into the hook command (1.267172ms)
ℹ tests 9
ℹ suites 0
ℹ pass 9
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 108.140666
@@ -0,0 +1 @@
0
@@ -0,0 +1,23 @@
✔ config directory and file path follow MOSAIC_CONFIG (1.166197ms)
✔ the fixture business validates and comes back frozen (3.596814ms)
✔ two instances may share a definition (1.185427ms)
✔ top-level refusals (3.040992ms)
✔ arbiters and projects (4.657842ms)
✔ role instances (2.599191ms)
✔ Vikunja bots (5.514796ms)
✔ a role without Vikunja takes no tracker block (1.950376ms)
✔ credential references match the definition's services (2.140196ms)
✔ launch (7.052738ms)
✔ loadBusiness: file checks (1.956659ms)
✔ loadBusiness: not a regular file (37.826353ms)
✔ loading writes nothing (1.064115ms)
✔ names that are Object.prototype properties don't count as declared (2.597286ms)
✔ the shipped example refuses as written and validates once filled in (0.596556ms)
ℹ tests 15
ℹ suites 0
ℹ pass 15
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 136.706583
@@ -0,0 +1 @@
0
@@ -0,0 +1,18 @@
✔ the role file loads and resolves through loadBusiness (5.091607ms)
✔ the shipped file is exactly the approved list (0.188753ms)
✔ the resolved record matches the approved list, so a widened file fails (1.373328ms)
✔ the chief of staff holds role.launch and message.send; every other action is gated (1.153466ms)
✔ the pm does not hold role.launch (1.076093ms)
✔ a business grant of a gated action through limits does not widen the role (1.074327ms)
✔ a limit that drops role.launch leaves the chief of staff with no launch block (1.12725ms)
✔ a launch block whose by is the pm instance is refused (1.074131ms)
✔ the contract names no duty to talk to the human and the pm contract no launch (0.266941ms)
✔ the instance takes no credential references and no tracker (1.451867ms)
ℹ tests 10
ℹ suites 0
ℹ pass 10
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 72.564157
@@ -0,0 +1,25 @@
✔ bus start --pm: the launcher instance (the chief of staff) runs under its own PID namespace, registered, with a manifest (1176.763228ms)
✔ the chief of staff launches a coder through its launch tool; the coder answers; refusals name their code (403.445783ms)
✔ each launch makes sure the workspace has a clone of the project: cloned, fast-forwarded, or fetched and logged (3505.567701ms)
✔ a project clone that can't be made refuses checkout-failed, from --chief and from a session, with no run and no clone left (115.683865ms)
✔ a runner that stops at once ends its launch with the runner's reason (189.134417ms)
✔ a Claude Code launch with no session credential refuses before it starts (86.958219ms)
✔ a pi launch needs no session credential: pi logs in through its own ENV_ALLOW variables (173.26982ms)
✔ a runner killed from the host ends its launch as killed, not failed (231.076999ms)
✔ a missing or failing bwrap refuses sandbox-unavailable before anything starts; there is no unconfined fallback (98.960576ms)
✔ a real Claude Code coder's shell can't read a stand-in secret or anything else its sandbox doesn't hold (1045.475968ms)
✔ a host that died hard leaves its sessions to the next host, which kills them and ends their runs so the role can be launched again (603.186416ms)
✔ a host that died hard leaves its sessions to the next host, which finds one already exited (23) and ends their runs so the role can be launched again (3549.875105ms)
✔ a malformed sessions.json refuses the host start with exit 3 and stays as it was (87.166383ms)
✔ an over-long launch request is refused at once, not at the 10 s idle timeout (97.452561ms)
✔ a launch client that never closes its side doesn't hold the host's close (94.048463ms)
✔ a runner that ignores SIGTERM is killed when the host closes (1203.975856ms)
✔ claudeVersion asks MOSAIC_CLAUDE_BIN when set, else claude on PATH (5.024922ms)
ℹ tests 17
ℹ suites 0
ℹ pass 17
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 12757.275816
@@ -0,0 +1 @@
0
@@ -0,0 +1,18 @@
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.165102ms)
✔ credential state from the date alone, as the bus states it (0.135356ms)
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (324.567354ms)
✔ queue: a refused read fails closed with the store's text, redacted (175.047944ms)
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (735.04744ms)
✔ business: names, vars on the allowlist, authority, credential metadata only (8.501691ms)
✔ business: missing or invalid file refuses with the module's text, redacted (7.238663ms)
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (6.790176ms)
✔ settings: release, board, loopback address, notifier binding name only (97.613512ms)
✔ task fields: the committed table with no reads configured, GET only; a bad table refuses with no path (17.893823ms)
ℹ tests 10
ℹ suites 0
ℹ pass 10
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 1463.493381
@@ -0,0 +1 @@
0
@@ -0,0 +1,21 @@
✔ the four shipped version 2 roles load (2.410958ms)
✔ shipped role scopes match addendum B section 2 and the SR runbook (0.722453ms)
✔ shipped authority follows the note's table (0.466127ms)
✔ every role file in roles/ loads through the validator, and a v2 contract exists (0.446442ms)
✔ version 1 files keep loading with no authority (0.804793ms)
✔ the conductor policy isn't a role (0.189712ms)
✔ a missing role file is exit 4, a symbolic link too (0.381166ms)
✔ version 2 refusals (1.001994ms)
✔ authority: closed vocabulary, no gated-only action, no overlap (1.606018ms)
✔ credentials: Gitea scopes (0.667001ms)
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (0.816119ms)
✔ credentials: services (0.388723ms)
✔ contract: a non-empty regular Markdown file beside the role file (0.767818ms)
ℹ tests 13
ℹ suites 0
ℹ pass 13
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 66.911114
@@ -0,0 +1 @@
0
@@ -0,0 +1,21 @@
✔ the PM gets its task verbs and the reads, and no launch (5.785325ms)
✔ the chief of staff gets launch and send_message, and no task verbs (1.69927ms)
✔ the assistant gets send_message and the reads, no launch (1.910748ms)
✔ list_requirements reads the requirement ids, for a task.create holder with a tracker (2.337574ms)
✔ a coder gets no launch, no resolve_decision, and no task tools without a tracker (1.005499ms)
✔ a coder with a tracker gets its own task tools and no task_reassign (0.964471ms)
✔ launch only when the business's launch block names the instance as launcher (1.707095ms)
✔ an action outside the instance's authority has no tool (1.248353ms)
✔ the review loop: push_working and request_review for the coder, fetch_review and review_verdict for the reviewer, none for the PM (3.343682ms)
✔ callTool: one JSON line out, the result back, a refusal rejects (7.281014ms)
✔ list_tasks takes the broker's filters and pages; get_task takes a task_ref (2.090985ms)
✔ callTool sends list_tasks done false unless the session names done; other tools as given (1.99025ms)
✔ replyText: compact JSON up to the cap, an error naming the size and the cap over it (0.696522ms)
ℹ tests 13
ℹ suites 0
ℹ pass 13
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 100.217643
@@ -0,0 +1 @@
0
@@ -0,0 +1,103 @@
✔ the role file loads and resolves through loadBusiness (5.021267ms)
✔ the shipped file is exactly the approved list (0.197538ms)
✔ the resolved record matches the approved list, so a widened file fails (1.283835ms)
✔ the chief of staff holds role.launch and message.send; every other action is gated (1.057029ms)
✔ the pm does not hold role.launch (1.034891ms)
✔ a business grant of a gated action through limits does not widen the role (1.110785ms)
✔ a limit that drops role.launch leaves the chief of staff with no launch block (1.098455ms)
✔ a launch block whose by is the pm instance is refused (1.106585ms)
✖ the contract names no duty to talk to the human and the pm contract no launch (0.901615ms)
✔ the instance takes no credential references and no tracker (1.481531ms)
ℹ tests 10
ℹ suites 0
ℹ pass 9
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 69.339697
✖ failing tests:
test at packages/business/tests/chief-of-staff.test.mjs:132:1
✖ the contract names no duty to talk to the human and the pm contract no launch (0.901615ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /You don't launch sessions/. Input:
'# Role contract: product manager (pm)\n' +
'\n' +
"You hold the `pm` role for one business. You turn the business's approved\n" +
'requirements into tasks, keep the board in order and decide delivery\n' +
"order. You don't write the product's code and you don't review it.\n" +
'\n' +
'## Duties\n' +
'\n' +
'- Break each approved requirement into tasks on the tracker. Every task\n' +
' you create cites the requirement id it serves, such as `REQ-TASK-1`,\n' +
' and the human request that asked for it. A task without both is\n' +
' refused.\n' +
'- Before creating a task, pick its requirement id from\n' +
" `list_requirements`, the approved PRD's list. `task_create` refuses\n" +
' any other id. The request is the `request <id>` in the header of the\n' +
" human message you're answering, not the message's own id.\n" +
"- When no id fits the request, don't create the task, and don't ask in\n" +
' your answer. Raise a decision with action `prd.approve`, `blocking`\n' +
' false and no `task_ref`, naming the request and the nearest ids. It\n' +
" goes to the human's Inbox.\n" +
'- Assign each task to a role instance the business file declares, set\n' +
' its schedule and priority, and close it when its acceptance evidence\n' +
' is in.\n' +
'- `task_close` cites a review verdict id. Find it with `trail` on the\n' +
' task, as a `review.verdict` event. A task with pushed work closes only\n' +
' on an `approve` verdict for its latest push: a missing, `changes` or\n' +
' stale verdict is refused.\n' +
'- You arbitrate delivery order. When two roles disagree about what ships\n' +
' first, the decision comes to you, and you resolve it with a reason.\n' +
'- Take the work the chief of staff passes you. Launch coders and reviewers when a task is ready.\n' +
' When a task needs a coder or reviewer session, say so to the chief of\n' +
' staff by message, naming the role instance and the task.\n' +
'\n' +
'## Authority\n' +
'\n' +
"Your role file lists what you do on your own, what needs another role's\n" +
'arbiter, and what goes to the human. The launch record you received shows\n' +
'the resolved list for this business and project, which may be narrower.\n' +
"Anything the list doesn't name is gated: raise a decision for the human\n" +
'and wait for it. Never route around a refusal.\n' +
'\n' +
'A blocking decision holds a task: it needs `task_ref`, and the task\n' +
'waits for the answer. A decision with no task is non-blocking, and a\n' +
"gated one still reaches the human's Inbox.\n" +
'\n' +
'Only the human stops a run, with `mosaic stop`. If you think a coder or\n' +
'reviewer run should stop, raise a `role.revoke` decision with `blocking`\n' +
'false and the role instance as target, naming the run and saying why.\n' +
'\n' +
"Changing a task's priority or scope against the CTO's technical call is\n" +
'cross-role. Raise it as a decision for the technical arbiter.\n' +
'\n' +
'## Protocol\n' +
'\n' +
'- You act on the tracker and on Gitea only through the typed tools the\n' +
' broker gives you. You never hold a token.\n' +
'- Each turn opens with a "Current state" block, read when the turn\n' +
' started. It is the truth for launch state and task status. A message\n' +
' or a note of yours that disagrees with it is out of date. Before you\n' +
' plan to resume or start work on a task, check the task with\n' +
' `get_task`.\n' +
'- Send messages through the bus, addressed to a role, not a person.\n' +
'- Report a blocker as soon as you find it, with what you tried.\n'
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:139:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: '# Role contract: product manager (pm)\n\nYou hold the `pm` role for one business. You turn the business\'s approved\nrequirements into tasks, keep the board in order and decide delivery\norder. You don\'t write the product\'s code and you don\'t review it.\n\n## Duties\n\n- Break each approved requirement into tasks on the tracker. Every task\n you create cites the requirement id it serves, such as `REQ-TASK-1`,\n and the human request that asked for it. A task without both is\n refused.\n- Before creating a task, pick its requirement id from\n `list_requirements`, the approved PRD\'s list. `task_create` refuses\n any other id. The request is the `request <id>` in the header of the\n human message you\'re answering, not the message\'s own id.\n- When no id fits the request, don\'t create the task, and don\'t ask in\n your answer. Raise a decision with action `prd.approve`, `blocking`\n false and no `task_ref`, naming the request and the nearest ids. It\n goes to the human\'s Inbox.\n- Assign each task to a role instance the business file declares, set\n its schedule and priority, and close it when its acceptance evidence\n is in.\n- `task_close` cites a review verdict id. Find it with `trail` on the\n task, as a `review.verdict` event. A task with pushed work closes only\n on an `approve` verdict for its latest push: a missing, `changes` or\n stale verdict is refused.\n- You arbitrate delivery order. When two roles disagree about what ships\n first, the decision comes to you, and you resolve it with a reason.\n- Take the work the chief of staff passes you. Launch coders and reviewers when a task is ready.\n When a task needs a coder or reviewer session, say so to the chief of\n staff by message, naming the role instance and the task.\n\n## Authority\n\nYour role file lists what you do on your own, what needs another role\'s\narbiter, and what goes to the human. The launch record you received shows\nthe resolved list for this business and project, which may be narrower.\nAnything the list doesn\'t name is gated: raise a decision for the human\nand wait for it. Never route around a refusal.\n\nA blocking decision holds a task: it needs `task_ref`, and the task\nwaits for the answer. A decision with no task is non-blocking, and a\ngated one still reaches the human\'s Inbox.\n\nOnly the human stops a run, with `mosaic stop`. If you think a coder or\nreviewer run should stop, raise a `role.revoke` decision with `blocking`\nfalse and the role instance as target, naming the run and saying why.\n\nChanging a task\'s priority or scope against the CTO\'s technical call is\ncross-role. Raise it as a decision for the technical arbiter.\n\n## Protocol\n\n- You act on the tracker and on Gitea only through the typed tools the\n broker gives you. You never hold a token.\n- Each turn opens with a "Current state" block, read when the turn\n started. It is the truth for launch state and task status. A message\n or a note of yours that disagrees with it is out of date. Before you\n plan to resume or start work on a task, check the task with\n `get_task`.\n- Send messages through the bus, addressed to a role, not a person.\n- Report a blocker as soon as you find it, with what you tried.\n',
expected: /You don't launch sessions/,
operator: 'match',
diff: 'simple'
}
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/pm.md 2026-10-14 19:38:55.168411949 -0500
+++ roles/pm.md 2026-10-14 19:38:55.184649711 -0500
@@ -27,7 +27,7 @@
stale verdict is refused.
- You arbitrate delivery order. When two roles disagree about what ships
first, the decision comes to you, and you resolve it with a reason.
-- Take the work the chief of staff passes you. You don't launch sessions.
+- Take the work the chief of staff passes you. Launch coders and reviewers when a task is ready.
When a task needs a coder or reviewer session, say so to the chief of
staff by message, naming the role instance and the task.
@@ -0,0 +1,95 @@
✔ the role file loads and resolves through loadBusiness (4.941113ms)
✔ the shipped file is exactly the approved list (0.187485ms)
✔ the resolved record matches the approved list, so a widened file fails (1.376016ms)
✔ the chief of staff holds role.launch and message.send; every other action is gated (1.096444ms)
✔ the pm does not hold role.launch (1.101329ms)
✔ a business grant of a gated action through limits does not widen the role (1.211125ms)
✔ a limit that drops role.launch leaves the chief of staff with no launch block (1.112314ms)
✔ a launch block whose by is the pm instance is refused (1.178365ms)
✖ the contract names no duty to talk to the human and the pm contract no launch (0.845836ms)
✔ the instance takes no credential references and no tracker (1.599566ms)
ℹ tests 10
ℹ suites 0
ℹ pass 9
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 70.312162
✖ failing tests:
test at packages/business/tests/chief-of-staff.test.mjs:132:1
✖ the contract names no duty to talk to the human and the pm contract no launch (0.845836ms)
AssertionError [ERR_ASSERTION]: The input was expected to not match the regular expression /main (agent|point of interaction)/i. Input:
'# Role contract: chief of staff (chief-of-staff)\n' +
'\n' +
'You hold the `chief-of-staff` role for one business. You run the\n' +
'organization. You hire and terminate role instances, you own the\n' +
"organization's structure and you are the only role that launches\n" +
'sessions. You take work from the assistant and assign it through the\n' +
"roles that do it. You never talk to the user, but you are the main point of interaction. You don't write the\n" +
"product's code, you don't review it and you don't create tasks yourself.\n" +
'\n' +
'## Duties\n' +
'\n' +
'- Take the work the assistant passes you. Send it to the role that does\n' +
' it, addressed by role, in the words the assistant gave you. The `pm`\n' +
' role turns product work into tasks and decides who holds them and when\n' +
' they run.\n' +
'- Launch role sessions with the `launch` tool, for the instances and\n' +
" within the limits the business file's launch block lists. A launch\n" +
' outside them is refused, not queued. Launch only role instances.\n' +
"- Keep the organization's structure: which role instances exist, who\n" +
' holds them and what they may do. To terminate a role instance, raise a\n' +
' `role.revoke` decision, naming the instance and saying why. To hire one\n' +
' or change the structure, raise a `policy.change` decision until the\n' +
' broker gives you a verb for the organization file.\n' +
'- Report to the assistant what the roles answer, and every refusal with\n' +
' its exact text.\n' +
'\n' +
'## Authority\n' +
'\n' +
'Your role file lists what you do on your own: sending messages and\n' +
"launching role sessions. Anything the list doesn't name is gated. That\n" +
'includes creating or assigning tasks, pushing to a repository, sending\n' +
'messages outside the business, minting credentials, and any change to\n' +
'policy or to a PRD. When an action is gated, raise a decision. It\n' +
'reaches the human through the inbox, not through you. Never route around\n' +
'a refusal.\n' +
'\n' +
'You hold no human authority. Never resolve a decision for the human,\n' +
'never approve a request in their name, and never write words as if they\n' +
'had said them.\n' +
'\n' +
'Only the human stops a run, with `mosaic stop`. If you think a run should\n' +
'stop, raise a `role.revoke` decision with `blocking` false and the role\n' +
'instance as target, naming the run and saying why.\n' +
'\n' +
'## Protocol\n' +
'\n' +
'- You act only through the typed tools the broker gives you. Your tools\n' +
" are read, grep, find and ls, plus the broker's launch and message\n" +
' tools. You have no write tool, and you never hold a token or a\n' +
' credential.\n' +
'- Each turn opens with a "Current state" block, read when the turn\n' +
' started. It is the truth for launch state and task status. A message\n' +
' or a note of yours that disagrees with it is out of date.\n' +
'- Send messages through the bus, addressed to a role, not a person.\n' +
'- Report a blocker as soon as you find it, with what you tried.\n'
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:134:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: '# Role contract: chief of staff (chief-of-staff)\n\nYou hold the `chief-of-staff` role for one business. You run the\norganization. You hire and terminate role instances, you own the\norganization\'s structure and you are the only role that launches\nsessions. You take work from the assistant and assign it through the\nroles that do it. You never talk to the user, but you are the main point of interaction. You don\'t write the\nproduct\'s code, you don\'t review it and you don\'t create tasks yourself.\n\n## Duties\n\n- Take the work the assistant passes you. Send it to the role that does\n it, addressed by role, in the words the assistant gave you. The `pm`\n role turns product work into tasks and decides who holds them and when\n they run.\n- Launch role sessions with the `launch` tool, for the instances and\n within the limits the business file\'s launch block lists. A launch\n outside them is refused, not queued. Launch only role instances.\n- Keep the organization\'s structure: which role instances exist, who\n holds them and what they may do. To terminate a role instance, raise a\n `role.revoke` decision, naming the instance and saying why. To hire one\n or change the structure, raise a `policy.change` decision until the\n broker gives you a verb for the organization file.\n- Report to the assistant what the roles answer, and every refusal with\n its exact text.\n\n## Authority\n\nYour role file lists what you do on your own: sending messages and\nlaunching role sessions. Anything the list doesn\'t name is gated. That\nincludes creating or assigning tasks, pushing to a repository, sending\nmessages outside the business, minting credentials, and any change to\npolicy or to a PRD. When an action is gated, raise a decision. It\nreaches the human through the inbox, not through you. Never route around\na refusal.\n\nYou hold no human authority. Never resolve a decision for the human,\nnever approve a request in their name, and never write words as if they\nhad said them.\n\nOnly the human stops a run, with `mosaic stop`. If you think a run should\nstop, raise a `role.revoke` decision with `blocking` false and the role\ninstance as target, naming the run and saying why.\n\n## Protocol\n\n- You act only through the typed tools the broker gives you. Your tools\n are read, grep, find and ls, plus the broker\'s launch and message\n tools. You have no write tool, and you never hold a token or a\n credential.\n- Each turn opens with a "Current state" block, read when the turn\n started. It is the truth for launch state and task status. A message\n or a note of yours that disagrees with it is out of date.\n- Send messages through the bus, addressed to a role, not a person.\n- Report a blocker as soon as you find it, with what you tried.\n',
expected: /main (agent|point of interaction)/i,
operator: 'doesNotMatch',
diff: 'simple'
}
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/chief-of-staff.md 2026-10-14 19:38:55.299379607 -0500
+++ roles/chief-of-staff.md 2026-10-14 19:38:55.317651008 -0500
@@ -4,7 +4,7 @@
organization. You hire and terminate role instances, you own the
organization's structure and you are the only role that launches
sessions. You take work from the assistant and assign it through the
-roles that do it. You never talk to the user. You don't write the
+roles that do it. You never talk to the user, but you are the main point of interaction. You don't write the
product's code, you don't review it and you don't create tasks yourself.
## Duties
@@ -0,0 +1,81 @@
✔ the role file loads and resolves through loadBusiness (4.432026ms)
✔ the shipped file is exactly the approved list (0.56122ms)
✔ the assistant's authority is message.send within role and nothing else (1.294502ms)
✔ a business grant through limits does not widen the role (1.228737ms)
✔ a launch block whose by is the assistant is refused (1.286177ms)
✔ the instance takes no credential references and no tracker (1.745753ms)
✖ the contract passes work to the chief of staff and directs no one (1.195231ms)
ℹ tests 7
ℹ suites 0
ℹ pass 6
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 67.789806
✖ failing tests:
test at packages/business/tests/assistant.test.mjs:110:1
✖ the contract passes work to the chief of staff and directs no one (1.195231ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /don't direct\s+other agents/. Input:
'# Role contract: assistant (assistant)\n' +
'\n' +
'You hold the `assistant` role for one user in one business. You are the\n' +
"user's main agent. You talk with the user, and you pass the work the\n" +
'user wants done for the business to the chief of staff. You direct\n' +
"other agents, you don't create or assign tasks and you don't launch\n" +
'sessions.\n' +
'\n' +
'## Duties\n' +
'\n' +
"- Talk with the user. Answer what they ask, say plainly what you don't\n" +
' know and keep their requests in their own words.\n' +
'- Send each request for the business to the chief of staff as a message,\n' +
" addressed to the `chief-of-staff` role. Keep the user's words as they\n" +
' gave them, and name the request each message carries.\n' +
'- Relay each ruling the user gives verbatim to the role it concerns.\n' +
" Don't summarize it, paraphrase it or add to it. Say whose ruling it is.\n" +
'- Report to the user what the chief of staff answers, and every refusal\n' +
' with its exact text.\n' +
'\n' +
'## Authority\n' +
'\n' +
'Your role file lists what you do on your own: sending messages. Anything\n' +
"the list doesn't name is gated. That includes creating or assigning\n" +
'tasks, directing another role, launching a role session, pushing to a\n' +
'repository, sending messages outside the business, minting credentials,\n' +
'and any change to policy or to a PRD. When an action is gated, tell the\n' +
'user and wait for their answer. Never route around a refusal.\n' +
'\n' +
'You hold no human authority. Never resolve a decision for the user, never\n' +
'approve a request in their name, and never write words as if they had said\n' +
'them. When a question needs their decision, pass it on as their question\n' +
'and wait for their answer.\n' +
'\n' +
'Only the human stops a run, with `mosaic stop`. If you think a run should\n' +
"stop, say so to the user. Don't stop it yourself.\n" +
'\n' +
'## Protocol\n' +
'\n' +
'- You act only through the typed tools the broker gives you. Your tools\n' +
' are read, grep, find and ls. You have no write tool, and you never hold\n' +
' a token or a credential.\n' +
'- Send messages through the bus, addressed to a role, not a person.\n' +
'- Report a blocker as soon as you find it, with what you tried.\n'
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:113:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: "# Role contract: assistant (assistant)\n\nYou hold the `assistant` role for one user in one business. You are the\nuser's main agent. You talk with the user, and you pass the work the\nuser wants done for the business to the chief of staff. You direct\nother agents, you don't create or assign tasks and you don't launch\nsessions.\n\n## Duties\n\n- Talk with the user. Answer what they ask, say plainly what you don't\n know and keep their requests in their own words.\n- Send each request for the business to the chief of staff as a message,\n addressed to the `chief-of-staff` role. Keep the user's words as they\n gave them, and name the request each message carries.\n- Relay each ruling the user gives verbatim to the role it concerns.\n Don't summarize it, paraphrase it or add to it. Say whose ruling it is.\n- Report to the user what the chief of staff answers, and every refusal\n with its exact text.\n\n## Authority\n\nYour role file lists what you do on your own: sending messages. Anything\nthe list doesn't name is gated. That includes creating or assigning\ntasks, directing another role, launching a role session, pushing to a\nrepository, sending messages outside the business, minting credentials,\nand any change to policy or to a PRD. When an action is gated, tell the\nuser and wait for their answer. Never route around a refusal.\n\nYou hold no human authority. Never resolve a decision for the user, never\napprove a request in their name, and never write words as if they had said\nthem. When a question needs their decision, pass it on as their question\nand wait for their answer.\n\nOnly the human stops a run, with `mosaic stop`. If you think a run should\nstop, say so to the user. Don't stop it yourself.\n\n## Protocol\n\n- You act only through the typed tools the broker gives you. Your tools\n are read, grep, find and ls. You have no write tool, and you never hold\n a token or a credential.\n- Send messages through the bus, addressed to a role, not a person.\n- Report a blocker as soon as you find it, with what you tried.\n",
expected: /don't direct\s+other agents/,
operator: 'match',
diff: 'simple'
}
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/assistant.md 2026-10-14 19:38:55.430709446 -0500
+++ roles/assistant.md 2026-10-14 19:38:55.446652265 -0500
@@ -2,7 +2,7 @@
You hold the `assistant` role for one user in one business. You are the
user's main agent. You talk with the user, and you pass the work the
-user wants done for the business to the chief of staff. You don't direct
+user wants done for the business to the chief of staff. You direct
other agents, you don't create or assign tasks and you don't launch
sessions.
@@ -0,0 +1,39 @@
✔ the four shipped version 2 roles load (2.306789ms)
✔ shipped role scopes match addendum B section 2 and the SR runbook (0.683941ms)
✔ shipped authority follows the note's table (0.426842ms)
✖ every role file in roles/ loads through the validator, and a v2 contract exists (0.315695ms)
✔ version 1 files keep loading with no authority (0.743232ms)
✔ the conductor policy isn't a role (0.138346ms)
✔ a missing role file is exit 4, a symbolic link too (0.29439ms)
✔ version 2 refusals (1.066519ms)
✔ authority: closed vocabulary, no gated-only action, no overlap (1.621213ms)
✔ credentials: Gitea scopes (0.738868ms)
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (0.8268ms)
✔ credentials: services (0.518187ms)
✔ contract: a non-empty regular Markdown file beside the role file (0.523636ms)
ℹ tests 13
ℹ suites 0
ℹ pass 12
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 63.474692
✖ failing tests:
test at packages/business/tests/role.test.mjs:74:1
✖ every role file in roles/ loads through the validator, and a v2 contract exists (0.315695ms)
Error [BusinessError]: role contract not found: /mnt/storage/scratch/researcher/q119/roles/missing.md
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkContract (file:///mnt/storage/scratch/researcher/q119/packages/business/src/role.mjs:98:5)
at validateRoleDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/role.mjs:130:19)
at loadRoleFile (file:///mnt/storage/scratch/researcher/q119/packages/business/src/role.mjs:139:10)
at loadRole (file:///mnt/storage/scratch/researcher/q119/packages/business/src/role.mjs:144:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/role.test.mjs:79:18)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19) {
exitCode: 2
}
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/assistant.json 2026-10-14 19:38:53.952344522 -0500
+++ roles/assistant.json 2026-10-14 19:38:55.569653464 -0500
@@ -2,7 +2,7 @@
"roleVersion": 2,
"name": "assistant",
"title": "Assistant",
- "contract": "assistant.md",
+ "contract": "missing.md",
"tools": ["read", "grep", "find", "ls"],
"network": "api-only",
"authority": {
@@ -0,0 +1,148 @@
✖ the role file loads and resolves through loadBusiness (1.037047ms)
✖ the shipped file is exactly the approved list (1.42952ms)
✖ the assistant's authority is message.send within role and nothing else (0.251584ms)
✖ a business grant through limits does not widen the role (2.606393ms)
✖ a launch block whose by is the assistant is refused (1.499936ms)
✖ the instance takes no credential references and no tracker (1.129709ms)
✔ the contract passes work to the chief of staff and directs no one (0.304019ms)
ℹ tests 7
ℹ suites 0
ℹ pass 1
ℹ fail 6
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 65.793163
✖ failing tests:
test at packages/business/tests/assistant.test.mjs:43:1
✖ the role file loads and resolves through loadBusiness (1.037047ms)
Error [BusinessError]: role contract not found: /mnt/storage/scratch/researcher/q119/roles/missing.md
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkContract (file:///mnt/storage/scratch/researcher/q119/packages/business/src/role.mjs:98:5)
at validateRoleDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/role.mjs:130:19)
at loadRoleFile (file:///mnt/storage/scratch/researcher/q119/packages/business/src/role.mjs:139:10)
at loadRole (file:///mnt/storage/scratch/researcher/q119/packages/business/src/role.mjs:144:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:44:16)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
exitCode: 2
}
test at packages/business/tests/assistant.test.mjs:58:1
✖ the shipped file is exactly the approved list (1.42952ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
{
authority: {
crossRole: [],
withinRole: [
'message.send'
]
},
+ contract: 'missing.md',
- contract: 'assistant.md',
credentials: [],
name: 'assistant',
network: 'api-only',
roleVersion: 2,
tools: [
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:62:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: { roleVersion: 2, name: 'assistant', contract: 'missing.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] },
expected: { roleVersion: 2, name: 'assistant', contract: 'assistant.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] },
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/business/tests/assistant.test.mjs:65:1
✖ the assistant's authority is message.send within role and nothing else (0.251584ms)
Error [BusinessError]: role contract not found: /mnt/storage/scratch/researcher/q119/roles/missing.md
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkContract (file:///mnt/storage/scratch/researcher/q119/packages/business/src/role.mjs:98:5)
at validateRoleDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/role.mjs:130:19)
at loadRoleFile (file:///mnt/storage/scratch/researcher/q119/packages/business/src/role.mjs:139:10)
at loadRole (file:///mnt/storage/scratch/researcher/q119/packages/business/src/role.mjs:144:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:66:16)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19) {
exitCode: 2
}
test at packages/business/tests/assistant.test.mjs:78:1
✖ a business grant through limits does not widen the role (2.606393ms)
Error [BusinessError]: role contract not found: /mnt/storage/scratch/researcher/q119/roles/missing.md
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkContract (file:///mnt/storage/scratch/researcher/q119/packages/business/src/role.mjs:98:5)
at validateRoleDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/role.mjs:130:19)
at loadRoleFile (file:///mnt/storage/scratch/researcher/q119/packages/business/src/role.mjs:139:10)
at loadRole (file:///mnt/storage/scratch/researcher/q119/packages/business/src/role.mjs:144:10)
at checkRoles (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:88:103)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:155:34)
at loadBusiness (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:203:10)
at loadFixture (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:36:28)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:79:27) {
exitCode: 2
}
test at packages/business/tests/assistant.test.mjs:86:1
✖ a launch block whose by is the assistant is refused (1.499936ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /launch\.by names assistant, whose role assistant doesn't hold role\.launch within-role/. Input:
'role contract not found: /mnt/storage/scratch/researcher/q119/roles/missing.md'
at Object.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:25:12)
at expectedException (node:assert:547:17)
at expectsError (node:assert:672:21)
at strict.throws (node:assert:728:3)
at refuses (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:22:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:87:3)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 'role contract not found: /mnt/storage/scratch/researcher/q119/roles/missing.md',
expected: /launch\.by names assistant, whose role assistant doesn't hold role\.launch within-role/,
operator: 'match',
diff: 'simple'
}
test at packages/business/tests/assistant.test.mjs:95:1
✖ the instance takes no credential references and no tracker (1.129709ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /must reference exactly the services its role definition needs \(none; got gitea\)/. Input:
'role contract not found: /mnt/storage/scratch/researcher/q119/roles/missing.md'
at Object.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:25:12)
at expectedException (node:assert:547:17)
at expectsError (node:assert:672:21)
at strict.throws (node:assert:728:3)
at refuses (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:22:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:96:3)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 'role contract not found: /mnt/storage/scratch/researcher/q119/roles/missing.md',
expected: /must reference exactly the services its role definition needs \(none; got gitea\)/,
operator: 'match',
diff: 'simple'
}
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/assistant.json 2026-10-14 19:38:53.952344522 -0500
+++ roles/assistant.json 2026-10-14 19:38:55.684654585 -0500
@@ -2,7 +2,7 @@
"roleVersion": 2,
"name": "assistant",
"title": "Assistant",
- "contract": "assistant.md",
+ "contract": "missing.md",
"tools": ["read", "grep", "find", "ls"],
"network": "api-only",
"authority": {
@@ -0,0 +1,148 @@
✖ the role file loads and resolves through loadBusiness (3.670536ms)
✖ the shipped file is exactly the approved list (1.371759ms)
✖ the resolved record matches the approved list, so a widened file fails (1.059217ms)
✖ the chief of staff holds role.launch and message.send; every other action is gated (0.883108ms)
✖ the pm does not hold role.launch (0.885667ms)
✖ a business grant of a gated action through limits does not widen the role (0.884522ms)
✖ a limit that drops role.launch leaves the chief of staff with no launch block (0.783143ms)
✔ a launch block whose by is the pm instance is refused (1.433136ms)
✔ the contract names no duty to talk to the human and the pm contract no launch (0.267324ms)
✔ the instance takes no credential references and no tracker (1.501525ms)
ℹ tests 10
ℹ suites 0
ℹ pass 3
ℹ fail 7
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 68.041796
✖ failing tests:
test at packages/business/tests/chief-of-staff.test.mjs:49:1
✖ the role file loads and resolves through loadBusiness (3.670536ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-cByCSP/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at loadBusiness (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:203:10)
at loadFixture (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:42:28)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:50:19)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
exitCode: 2
}
test at packages/business/tests/chief-of-staff.test.mjs:64:1
✖ the shipped file is exactly the approved list (1.371759ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
{
authority: {
crossRole: [],
withinRole: [
'message.send',
- 'role.launch'
]
},
contract: 'chief-of-staff.md',
credentials: [],
name: 'chief-of-staff',
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:68:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] },
expected: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] },
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/business/tests/chief-of-staff.test.mjs:76:1
✖ the resolved record matches the approved list, so a widened file fails (1.059217ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-DiPCQn/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at loadBusiness (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:203:10)
at loadFixture (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:42:28)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:77:27)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19) {
exitCode: 2
}
test at packages/business/tests/chief-of-staff.test.mjs:86:1
✖ the chief of staff holds role.launch and message.send; every other action is gated (0.883108ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-zF1eCK/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at loadBusiness (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:203:10)
at loadFixture (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:42:28)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:87:27)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19) {
exitCode: 2
}
test at packages/business/tests/chief-of-staff.test.mjs:94:1
✖ the pm does not hold role.launch (0.885667ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-R6vM7m/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at loadBusiness (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:203:10)
at loadFixture (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:42:28)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:98:27)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19) {
exitCode: 2
}
test at packages/business/tests/chief-of-staff.test.mjs:103:1
✖ a business grant of a gated action through limits does not widen the role (0.884522ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-kTeO8Q/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at loadBusiness (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:203:10)
at loadFixture (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:42:28)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:106:19)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19) {
exitCode: 2
}
test at packages/business/tests/chief-of-staff.test.mjs:115:1
✖ a limit that drops role.launch leaves the chief of staff with no launch block (0.783143ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-fnbpH4/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at loadBusiness (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:203:10)
at loadFixture (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:42:28)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:116:27)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19) {
exitCode: 2
}
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/chief-of-staff.json 2026-10-14 19:37:48.978629348 -0500
+++ roles/chief-of-staff.json 2026-10-14 19:37:48.998010465 -0500
@@ -6,7 +6,7 @@
"tools": ["read", "grep", "find", "ls"],
"network": "api-only",
"authority": {
- "withinRole": ["message.send", "role.launch"],
+ "withinRole": ["message.send"],
"crossRole": []
},
"credentials": []
@@ -0,0 +1,182 @@
✖ the PM gets its task verbs and the reads, and no launch (4.382949ms)
✖ the chief of staff gets launch and send_message, and no task verbs (1.266134ms)
✖ the assistant gets send_message and the reads, no launch (1.053654ms)
✖ list_requirements reads the requirement ids, for a task.create holder with a tracker (1.401073ms)
✖ a coder gets no launch, no resolve_decision, and no task tools without a tracker (0.926067ms)
✖ a coder with a tracker gets its own task tools and no task_reassign (0.806781ms)
✖ launch only when the business's launch block names the instance as launcher (0.898211ms)
✖ an action outside the instance's authority has no tool (0.867373ms)
✖ the review loop: push_working and request_review for the coder, fetch_review and review_verdict for the reviewer, none for the PM (1.140094ms)
✔ callTool: one JSON line out, the result back, a refusal rejects (9.819222ms)
✖ list_tasks takes the broker's filters and pages; get_task takes a task_ref (1.076664ms)
✔ callTool sends list_tasks done false unless the session names done; other tools as given (2.101431ms)
✔ replyText: compact JSON up to the cap, an error naming the size and the cap over it (0.587872ms)
ℹ tests 13
ℹ suites 0
ℹ pass 3
ℹ fail 10
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 96.997532
✖ failing tests:
test at packages/harness/tests/tools.test.mjs:9:1
✖ the PM gets its task verbs and the reads, and no launch (4.382949ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-harness-biz-y9yIFF/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at resolvedFor (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/helpers.mjs:27:20)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/tools.test.mjs:10:14)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
exitCode: 2
}
test at packages/harness/tests/tools.test.mjs:33:1
✖ the chief of staff gets launch and send_message, and no task verbs (1.266134ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-harness-biz-yIZLzB/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at resolvedFor (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/helpers.mjs:27:20)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/tools.test.mjs:34:17)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
test at packages/harness/tests/tools.test.mjs:45:1
✖ the assistant gets send_message and the reads, no launch (1.053654ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-harness-biz-SjmFtj/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at resolvedFor (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/helpers.mjs:27:20)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/tools.test.mjs:46:21)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
test at packages/harness/tests/tools.test.mjs:54:1
✖ list_requirements reads the requirement ids, for a task.create holder with a tracker (1.401073ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-harness-biz-qS5Nju/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at resolvedFor (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/helpers.mjs:27:20)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/tools.test.mjs:55:14)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
test at packages/harness/tests/tools.test.mjs:79:1
✖ a coder gets no launch, no resolve_decision, and no task tools without a tracker (0.926067ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-harness-biz-lsaGWh/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at resolvedFor (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/helpers.mjs:27:20)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/tools.test.mjs:80:17)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
test at packages/harness/tests/tools.test.mjs:88:1
✖ a coder with a tracker gets its own task tools and no task_reassign (0.806781ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-harness-biz-F8wB7E/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at resolvedFor (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/helpers.mjs:27:20)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/tools.test.mjs:89:17)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
test at packages/harness/tests/tools.test.mjs:96:1
✖ launch only when the business's launch block names the instance as launcher (0.898211ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-harness-biz-aMv7wR/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at resolvedFor (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/helpers.mjs:27:20)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/tools.test.mjs:97:42)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
test at packages/harness/tests/tools.test.mjs:103:1
✖ an action outside the instance's authority has no tool (0.867373ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-harness-biz-tvVkh6/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at resolvedFor (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/helpers.mjs:27:20)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/tools.test.mjs:104:17)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
test at packages/harness/tests/tools.test.mjs:109:1
✖ the review loop: push_working and request_review for the coder, fetch_review and review_verdict for the reviewer, none for the PM (1.140094ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-harness-biz-7qZj07/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at resolvedFor (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/helpers.mjs:27:20)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/tools.test.mjs:110:17)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
test at packages/harness/tests/tools.test.mjs:162:1
✖ list_tasks takes the broker's filters and pages; get_task takes a task_ref (1.076664ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-harness-biz-C9Rcjc/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at resolvedFor (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/helpers.mjs:27:20)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/tools.test.mjs:163:40)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/chief-of-staff.json 2026-10-14 19:37:48.978629348 -0500
+++ roles/chief-of-staff.json 2026-10-14 19:37:49.125011680 -0500
@@ -6,7 +6,7 @@
"tools": ["read", "grep", "find", "ls"],
"network": "api-only",
"authority": {
- "withinRole": ["message.send", "role.launch"],
+ "withinRole": ["message.send"],
"crossRole": []
},
"credentials": []
@@ -0,0 +1,177 @@
✖ sessionModel: agent vars win, then the system's execution settings (4.43866ms)
✖ a pi bundle: prompt, policy, tools and manifest, 0600 in a 0700 directory (1.267473ms)
✖ a chief-of-staff bundle lists launch among the bus tools and the manifest's typed tools (1.04923ms)
✖ a claude-code bundle adds the wrapped gate hook and the MCP config (0.978591ms)
✖ the prompt's built-in tools line is the harness's own names, as the manifest lists them (1.576487ms)
✖ the prompt names the project clones in the workspace, and none when there are none (0.947698ms)
✖ the prompt explains the review loop to a role with push_working, and reviews to one with fetch_review (1.138803ms)
✖ a bundle is written once: an existing file refuses (1.901477ms)
✖ a path with a single quote can't go into the hook command (0.966374ms)
ℹ tests 9
ℹ suites 0
ℹ pass 0
ℹ fail 9
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 103.822342
✖ failing tests:
test at packages/harness/tests/bundle.test.mjs:30:1
✖ sessionModel: agent vars win, then the system's execution settings (4.43866ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-harness-biz-PlA1i5/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at resolvedFor (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/helpers.mjs:27:20)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/bundle.test.mjs:31:33)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
exitCode: 2
}
test at packages/harness/tests/bundle.test.mjs:37:1
✖ a pi bundle: prompt, policy, tools and manifest, 0600 in a 0700 directory (1.267473ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-harness-biz-9wIc98/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at resolvedFor (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/helpers.mjs:27:20)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/bundle.test.mjs:38:22)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
test at packages/harness/tests/bundle.test.mjs:74:1
✖ a chief-of-staff bundle lists launch among the bus tools and the manifest's typed tools (1.04923ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-harness-biz-WxVxsV/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at resolvedFor (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/helpers.mjs:27:20)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/bundle.test.mjs:75:13)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
test at packages/harness/tests/bundle.test.mjs:84:1
✖ a claude-code bundle adds the wrapped gate hook and the MCP config (0.978591ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-harness-biz-99QjiB/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at resolvedFor (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/helpers.mjs:27:20)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/bundle.test.mjs:85:13)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
test at packages/harness/tests/bundle.test.mjs:105:1
✖ the prompt's built-in tools line is the harness's own names, as the manifest lists them (1.576487ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-harness-biz-5725J7/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at resolvedFor (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/helpers.mjs:27:20)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/bundle.test.mjs:107:23)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
test at packages/harness/tests/bundle.test.mjs:121:1
✖ the prompt names the project clones in the workspace, and none when there are none (0.947698ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-harness-biz-zFj6Ol/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at resolvedFor (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/helpers.mjs:27:20)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/bundle.test.mjs:123:26)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
test at packages/harness/tests/bundle.test.mjs:142:1
✖ the prompt explains the review loop to a role with push_working, and reviews to one with fetch_review (1.138803ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-harness-biz-B1PbAF/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at resolvedFor (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/helpers.mjs:27:20)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/bundle.test.mjs:146:32)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
test at packages/harness/tests/bundle.test.mjs:158:1
✖ a bundle is written once: an existing file refuses (1.901477ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /EEXIST/. Input:
"BusinessError: /mnt/storage/scratch/tmp/mosaic-harness-biz-5e9zr0/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role"
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/bundle.test.mjs:162:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: BusinessError: /mnt/storage/scratch/tmp/mosaic-harness-biz-5e9zr0/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at resolvedFor (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/helpers.mjs:27:20)
at file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/bundle.test.mjs:164:15
at getActual (node:assert:580:5)
at strict.throws (node:assert:728:24)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/bundle.test.mjs:162:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25),
expected: /EEXIST/,
operator: 'throws',
diff: 'simple'
}
test at packages/harness/tests/bundle.test.mjs:171:1
✖ a path with a single quote can't go into the hook command (0.966374ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-harness-biz-wGkKKD/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at resolvedFor (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/helpers.mjs:27:20)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/harness/tests/bundle.test.mjs:172:13)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/chief-of-staff.json 2026-10-14 19:37:48.978629348 -0500
+++ roles/chief-of-staff.json 2026-10-14 19:37:49.278013144 -0500
@@ -6,7 +6,7 @@
"tools": ["read", "grep", "find", "ls"],
"network": "api-only",
"authority": {
- "withinRole": ["message.send", "role.launch"],
+ "withinRole": ["message.send"],
"crossRole": []
},
"credentials": []
@@ -0,0 +1,295 @@
✖ bus start --pm: the launcher instance (the chief of staff) runs under its own PID namespace, registered, with a manifest (45.309498ms)
✖ the chief of staff launches a coder through its launch tool; the coder answers; refusals name their code (36.210493ms)
✖ each launch makes sure the workspace has a clone of the project: cloned, fast-forwarded, or fetched and logged (36.660573ms)
✖ a project clone that can't be made refuses checkout-failed, from --chief and from a session, with no run and no clone left (38.671542ms)
✖ a runner that stops at once ends its launch with the runner's reason (41.1482ms)
✖ a Claude Code launch with no session credential refuses before it starts (39.618582ms)
✖ a pi launch needs no session credential: pi logs in through its own ENV_ALLOW variables (40.819704ms)
✖ a runner killed from the host ends its launch as killed, not failed (37.559931ms)
✖ a missing or failing bwrap refuses sandbox-unavailable before anything starts; there is no unconfined fallback (36.587442ms)
✖ a real Claude Code coder's shell can't read a stand-in secret or anything else its sandbox doesn't hold (44.044436ms)
file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:44
if (error instanceof BusinessError) throw new CliError(error.message, 3);
^
CliError: /mnt/storage/scratch/tmp/mosaic-launch-oNauXo/config/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at business (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:44:47)
at bootConfig (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:89:10)
at file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/fixtures/launch-host.mjs:12:14
at ModuleJob.run (node:internal/modules/esm/module_job:569:25)
at async node:internal/modules/esm/loader:650:26
at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:101:5) {
exitCode: 3
}
Node.js v26.8.1
✖ a host that died hard leaves its sessions to the next host, which kills them and ends their runs so the role can be launched again (30001.8402ms)
file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:44
if (error instanceof BusinessError) throw new CliError(error.message, 3);
^
CliError: /mnt/storage/scratch/tmp/mosaic-launch-8nenoQ/config/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at business (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:44:47)
at bootConfig (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:89:10)
at file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/fixtures/launch-host.mjs:12:14
at ModuleJob.run (node:internal/modules/esm/module_job:569:25)
at async node:internal/modules/esm/loader:650:26
at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:101:5) {
exitCode: 3
}
Node.js v26.8.1
✖ a host that died hard leaves its sessions to the next host, which finds one already exited (23) and ends their runs so the role can be launched again (30001.592457ms)
✖ a malformed sessions.json refuses the host start with exit 3 and stays as it was (38.394346ms)
✖ an over-long launch request is refused at once, not at the 10 s idle timeout (35.00358ms)
✖ a launch client that never closes its side doesn't hold the host's close (35.966066ms)
✖ a runner that ignores SIGTERM is killed when the host closes (35.502462ms)
✔ claudeVersion asks MOSAIC_CLAUDE_BIN when set, else claude on PATH (4.616447ms)
ℹ tests 17
ℹ suites 0
ℹ pass 1
ℹ fail 14
ℹ cancelled 2
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 60648.387817
✖ failing tests:
test at packages/cli/tests/launcher.test.mjs:132:1
✖ bus start --pm: the launcher instance (the chief of staff) runs under its own PID namespace, registered, with a manifest (45.309498ms)
Error [CliError]: /mnt/storage/scratch/tmp/mosaic-launch-NqiIKY/config/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at business (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:44:47)
at bootConfig (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:89:10)
at setup (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:83:16)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:133:51)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
exitCode: 3
}
test at packages/cli/tests/launcher.test.mjs:205:1
✖ the chief of staff launches a coder through its launch tool; the coder answers; refusals name their code (36.210493ms)
Error [CliError]: /mnt/storage/scratch/tmp/mosaic-launch-9WQTrB/config/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at business (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:44:47)
at bootConfig (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:89:10)
at setup (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:83:16)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:206:46)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
exitCode: 3
}
test at packages/cli/tests/launcher.test.mjs:287:1
✖ each launch makes sure the workspace has a clone of the project: cloned, fast-forwarded, or fetched and logged (36.660573ms)
Error [CliError]: /mnt/storage/scratch/tmp/mosaic-launch-HJIdem/config/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at business (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:44:47)
at bootConfig (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:89:10)
at setup (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:83:16)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:296:23)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
exitCode: 3
}
test at packages/cli/tests/launcher.test.mjs:342:1
✖ a project clone that can't be made refuses checkout-failed, from --chief and from a session, with no run and no clone left (38.671542ms)
Error [CliError]: /mnt/storage/scratch/tmp/mosaic-launch-YbUB4r/config/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at business (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:44:47)
at bootConfig (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:89:10)
at setup (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:83:16)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:345:39)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
exitCode: 3
}
test at packages/cli/tests/launcher.test.mjs:361:1
✖ a runner that stops at once ends its launch with the runner's reason (41.1482ms)
Error [CliError]: /mnt/storage/scratch/tmp/mosaic-launch-8kjKdu/config/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at business (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:44:47)
at bootConfig (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:89:10)
at setup (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:83:16)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:365:39)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
exitCode: 3
}
test at packages/cli/tests/launcher.test.mjs:381:1
✖ a Claude Code launch with no session credential refuses before it starts (39.618582ms)
Error [CliError]: /mnt/storage/scratch/tmp/mosaic-launch-NbLIE2/config/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at business (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:44:47)
at bootConfig (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:89:10)
at setup (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:83:16)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:388:33)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
exitCode: 3
}
test at packages/cli/tests/launcher.test.mjs:396:1
✖ a pi launch needs no session credential: pi logs in through its own ENV_ALLOW variables (40.819704ms)
Error [CliError]: /mnt/storage/scratch/tmp/mosaic-launch-xDB3Jc/config/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at business (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:44:47)
at bootConfig (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:89:10)
at setup (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:83:16)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:400:33)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
exitCode: 3
}
test at packages/cli/tests/launcher.test.mjs:409:1
✖ a runner killed from the host ends its launch as killed, not failed (37.559931ms)
Error [CliError]: /mnt/storage/scratch/tmp/mosaic-launch-lzhFbi/config/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at business (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:44:47)
at bootConfig (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:89:10)
at setup (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:83:16)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:410:33)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
exitCode: 3
}
test at packages/cli/tests/launcher.test.mjs:421:1
✖ a missing or failing bwrap refuses sandbox-unavailable before anything starts; there is no unconfined fallback (36.587442ms)
Error [CliError]: /mnt/storage/scratch/tmp/mosaic-launch-t6rZ5G/config/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at business (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:44:47)
at bootConfig (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:89:10)
at setup (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:83:16)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:425:38)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
exitCode: 3
}
test at packages/cli/tests/launcher.test.mjs:443:1
✖ a real Claude Code coder's shell can't read a stand-in secret or anything else its sandbox doesn't hold (44.044436ms)
Error [CliError]: /mnt/storage/scratch/tmp/mosaic-launch-WRZWBf/config/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at business (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:44:47)
at bootConfig (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:89:10)
at setup (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:83:16)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:460:41)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
exitCode: 3
}
test at packages/cli/tests/launcher.test.mjs:502:3
✖ a host that died hard leaves its sessions to the next host, which kills them and ends their runs so the role can be launched again (30001.8402ms)
'test timed out after 30000ms'
test at packages/cli/tests/launcher.test.mjs:502:3
✖ a host that died hard leaves its sessions to the next host, which finds one already exited (23) and ends their runs so the role can be launched again (30001.592457ms)
'test timed out after 30000ms'
test at packages/cli/tests/launcher.test.mjs:563:1
✖ a malformed sessions.json refuses the host start with exit 3 and stays as it was (38.394346ms)
AssertionError [ERR_ASSERTION]: The validation function is expected to return "true". Received false
Caught error:
CliError: /mnt/storage/scratch/tmp/mosaic-launch-BYt0UB/config/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at async TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:568:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: CliError: /mnt/storage/scratch/tmp/mosaic-launch-BYt0UB/config/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at business (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:44:47)
at bootConfig (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:89:10)
at setup (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:83:16)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:568:24)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7),
operator: 'rejects',
diff: 'simple'
}
test at packages/cli/tests/launcher.test.mjs:575:1
✖ an over-long launch request is refused at once, not at the 10 s idle timeout (35.00358ms)
Error [CliError]: /mnt/storage/scratch/tmp/mosaic-launch-O1uhwT/config/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at business (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:44:47)
at bootConfig (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:89:10)
at setup (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:83:16)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:576:23)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
exitCode: 3
}
test at packages/cli/tests/launcher.test.mjs:586:1
✖ a launch client that never closes its side doesn't hold the host's close (35.966066ms)
Error [CliError]: /mnt/storage/scratch/tmp/mosaic-launch-YIbNMC/config/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at business (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:44:47)
at bootConfig (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:89:10)
at setup (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:83:16)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:587:30)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
exitCode: 3
}
test at packages/cli/tests/launcher.test.mjs:618:1
✖ a runner that ignores SIGTERM is killed when the host closes (35.502462ms)
Error [CliError]: /mnt/storage/scratch/tmp/mosaic-launch-bBlMBO/config/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at business (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:44:47)
at bootConfig (file:///mnt/storage/scratch/researcher/q119/packages/cli/src/config.mjs:89:10)
at setup (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:83:16)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/cli/tests/launcher.test.mjs:619:40)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
exitCode: 3
}
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/chief-of-staff.json 2026-10-14 19:37:48.978629348 -0500
+++ roles/chief-of-staff.json 2026-10-14 19:37:49.442014713 -0500
@@ -6,7 +6,7 @@
"tools": ["read", "grep", "find", "ls"],
"network": "api-only",
"authority": {
- "withinRole": ["message.send", "role.launch"],
+ "withinRole": ["message.send"],
"crossRole": []
},
"credentials": []
@@ -0,0 +1,38 @@
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.169264ms)
✔ credential state from the date alone, as the bus states it (0.132024ms)
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (328.564414ms)
✔ queue: a refused read fails closed with the store's text, redacted (176.45743ms)
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (721.727278ms)
✖ business: names, vars on the allowlist, authority, credential metadata only (6.583273ms)
✔ business: missing or invalid file refuses with the module's text, redacted (7.442982ms)
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (7.671542ms)
✔ settings: release, board, loopback address, notifier binding name only (96.837466ms)
✔ task fields: the committed table with no reads configured, GET only; a bad table refuses with no path (17.052555ms)
ℹ tests 10
ℹ suites 0
ℹ pass 9
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 1453.546523
✖ failing tests:
test at packages/webui/tests/reads.test.mjs:110:1
✖ business: names, vars on the allowlist, authority, credential metadata only (6.583273ms)
AssertionError [ERR_ASSERTION]: {"error":"not-configured","message":"<config>/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role"}
503 !== 200
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/webui/tests/reads.test.mjs:114:10)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 503,
expected: 200,
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/chief-of-staff.json 2026-10-14 19:37:48.978629348 -0500
+++ roles/chief-of-staff.json 2026-10-14 19:38:50.150600677 -0500
@@ -6,7 +6,7 @@
"tools": ["read", "grep", "find", "ls"],
"network": "api-only",
"authority": {
- "withinRole": ["message.send", "role.launch"],
+ "withinRole": ["message.send"],
"crossRole": []
},
"credentials": []
@@ -0,0 +1,185 @@
✔ config directory and file path follow MOSAIC_CONFIG (1.15966ms)
✖ the fixture business validates and comes back frozen (3.021594ms)
✖ two instances may share a definition (0.997641ms)
✔ top-level refusals (2.845587ms)
✔ arbiters and projects (4.416049ms)
✔ role instances (2.236345ms)
✖ Vikunja bots (5.106753ms)
✖ a role without Vikunja takes no tracker block (1.785771ms)
✔ credential references match the definition's services (2.052986ms)
✖ launch (2.433062ms)
✖ loadBusiness: file checks (0.94924ms)
✔ loadBusiness: not a regular file (41.109894ms)
✖ loading writes nothing (1.175058ms)
✖ names that are Object.prototype properties don't count as declared (1.650878ms)
✖ the shipped example refuses as written and validates once filled in (0.615889ms)
ℹ tests 15
ℹ suites 0
ℹ pass 6
ℹ fail 9
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 130.530315
✖ failing tests:
test at packages/business/tests/business.test.mjs:34:1
✖ the fixture business validates and comes back frozen (3.021594ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-SGtMTn/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at check (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/business.test.mjs:24:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/business.test.mjs:35:20)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
test at packages/business/tests/business.test.mjs:47:1
✖ two instances may share a definition (0.997641ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-bFCbwt/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at check (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/business.test.mjs:24:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/business.test.mjs:48:20)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
test at packages/business/tests/business.test.mjs:86:1
✖ Vikunja bots (5.106753ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-C5f9Y3/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at check (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/business.test.mjs:24:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/business.test.mjs:97:20)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
test at packages/business/tests/business.test.mjs:100:1
✖ a role without Vikunja takes no tracker block (1.785771ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-97BKK6/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at check (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/business.test.mjs:24:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/business.test.mjs:108:20)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
test at packages/business/tests/business.test.mjs:120:1
✖ launch (2.433062ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /must not be empty/. Input:
"/mnt/storage/scratch/tmp/mosaic-business-9Oz09c/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role"
at Object.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/business.test.mjs:14:12)
at expectedException (node:assert:547:17)
at expectsError (node:assert:672:21)
at strict.throws (node:assert:728:3)
at refuses (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/business.test.mjs:11:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/business.test.mjs:124:3)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: "/mnt/storage/scratch/tmp/mosaic-business-9Oz09c/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role",
expected: /must not be empty/,
operator: 'match',
diff: 'simple'
}
test at packages/business/tests/business.test.mjs:137:1
✖ loadBusiness: file checks (0.94924ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-AxCrbO/config/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at loadBusiness (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:203:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/business.test.mjs:143:16)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
test at packages/business/tests/business.test.mjs:183:1
✖ loading writes nothing (1.175058ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-gWuGIV/config/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at loadBusiness (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:203:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/business.test.mjs:189:3)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12) {
exitCode: 2
}
test at packages/business/tests/business.test.mjs:195:1
✖ names that are Object.prototype properties don't count as declared (1.650878ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /instances names constructor/. Input:
"/mnt/storage/scratch/tmp/mosaic-business-xvV1Ya/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role"
at Object.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/business.test.mjs:14:12)
at expectedException (node:assert:547:17)
at expectsError (node:assert:672:21)
at strict.throws (node:assert:728:3)
at refuses (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/business.test.mjs:11:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/business.test.mjs:198:3)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: "/mnt/storage/scratch/tmp/mosaic-business-xvV1Ya/businesses/acme.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role",
expected: /instances names constructor/,
operator: 'match',
diff: 'simple'
}
test at packages/business/tests/business.test.mjs:206:1
✖ the shipped example refuses as written and validates once filled in (0.615889ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-xscwzZ/businesses/mosaic-stack.json launch.by names chief, whose role chief-of-staff doesn't hold role.launch within-role
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkLaunch (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:122:5)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:186:57)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/business.test.mjs:216:20)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
exitCode: 2
}
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/chief-of-staff.json 2026-10-14 19:37:48.978629348 -0500
+++ roles/chief-of-staff.json 2026-10-14 19:38:51.658615359 -0500
@@ -6,7 +6,7 @@
"tools": ["read", "grep", "find", "ls"],
"network": "api-only",
"authority": {
- "withinRole": ["message.send", "role.launch"],
+ "withinRole": ["message.send"],
"crossRole": []
},
"credentials": []
@@ -0,0 +1,59 @@
✔ the role file loads and resolves through loadBusiness (4.987588ms)
✔ the shipped file is exactly the approved list (0.188358ms)
✔ the resolved record matches the approved list, so a widened file fails (1.319104ms)
✔ the chief of staff holds role.launch and message.send; every other action is gated (1.018319ms)
✖ the pm does not hold role.launch (3.110803ms)
✔ a business grant of a gated action through limits does not widen the role (1.598655ms)
✔ a limit that drops role.launch leaves the chief of staff with no launch block (1.0075ms)
✖ a launch block whose by is the pm instance is refused (0.940857ms)
✔ the contract names no duty to talk to the human and the pm contract no launch (0.311021ms)
✔ the instance takes no credential references and no tracker (1.903027ms)
ℹ tests 10
ℹ suites 0
ℹ pass 8
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 72.590097
✖ failing tests:
test at packages/business/tests/chief-of-staff.test.mjs:94:1
✖ the pm does not hold role.launch (3.110803ms)
AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
assert.ok(!role.authority.withinRole.includes("role.launch"))
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:96:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: '==',
diff: 'simple'
}
test at packages/business/tests/chief-of-staff.test.mjs:123:1
✖ a launch block whose by is the pm instance is refused (0.940857ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at refuses (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:27:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:124:3)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: undefined,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/pm.json 2026-10-14 19:38:51.832481906 -0500
+++ roles/pm.json 2026-10-14 19:38:51.853617258 -0500
@@ -6,7 +6,7 @@
"tools": ["read", "write", "edit", "grep", "find", "ls"],
"network": "api-only",
"authority": {
- "withinRole": ["task.create", "task.assign", "task.reassign", "task.schedule", "task.update.assigned", "task.close", "message.send"],
+ "withinRole": ["task.create", "task.assign", "task.reassign", "task.schedule", "task.update.assigned", "task.close", "message.send", "role.launch"],
"crossRole": ["task.priority.change", "task.scope.change"]
},
"credentials": [
@@ -0,0 +1,44 @@
✔ the four shipped version 2 roles load (2.346797ms)
✔ shipped role scopes match addendum B section 2 and the SR runbook (0.818105ms)
✖ shipped authority follows the note's table (3.357317ms)
✔ every role file in roles/ loads through the validator, and a v2 contract exists (0.577286ms)
✔ version 1 files keep loading with no authority (0.829688ms)
✔ the conductor policy isn't a role (0.165409ms)
✔ a missing role file is exit 4, a symbolic link too (0.382945ms)
✔ version 2 refusals (0.995054ms)
✔ authority: closed vocabulary, no gated-only action, no overlap (2.081947ms)
✔ credentials: Gitea scopes (0.7104ms)
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (0.73513ms)
✔ credentials: services (0.392153ms)
✔ contract: a non-empty regular Markdown file beside the role file (0.478367ms)
ℹ tests 13
ℹ suites 0
ℹ pass 12
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 73.842102
✖ failing tests:
test at packages/business/tests/role.test.mjs:61:1
✖ shipped authority follows the note's table (3.357317ms)
AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
assert.ok(!auth("pm").withinRole.includes("role.launch"))
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/role.test.mjs:69:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: '==',
diff: 'simple'
}
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/pm.json 2026-10-14 19:38:51.832481906 -0500
+++ roles/pm.json 2026-10-14 19:38:51.982618514 -0500
@@ -6,7 +6,7 @@
"tools": ["read", "write", "edit", "grep", "find", "ls"],
"network": "api-only",
"authority": {
- "withinRole": ["task.create", "task.assign", "task.reassign", "task.schedule", "task.update.assigned", "task.close", "message.send"],
+ "withinRole": ["task.create", "task.assign", "task.reassign", "task.schedule", "task.update.assigned", "task.close", "message.send", "role.launch"],
"crossRole": ["task.priority.change", "task.scope.change"]
},
"credentials": [
@@ -0,0 +1,21 @@
✔ the PM gets its task verbs and the reads, and no launch (5.585509ms)
✔ the chief of staff gets launch and send_message, and no task verbs (1.845498ms)
✔ the assistant gets send_message and the reads, no launch (1.886441ms)
✔ list_requirements reads the requirement ids, for a task.create holder with a tracker (2.590532ms)
✔ a coder gets no launch, no resolve_decision, and no task tools without a tracker (1.161465ms)
✔ a coder with a tracker gets its own task tools and no task_reassign (1.032107ms)
✔ launch only when the business's launch block names the instance as launcher (1.797537ms)
✔ an action outside the instance's authority has no tool (1.284862ms)
✔ the review loop: push_working and request_review for the coder, fetch_review and review_verdict for the reviewer, none for the PM (3.060751ms)
✔ callTool: one JSON line out, the result back, a refusal rejects (7.450533ms)
✔ list_tasks takes the broker's filters and pages; get_task takes a task_ref (2.396108ms)
✔ callTool sends list_tasks done false unless the session names done; other tools as given (2.327551ms)
✔ replyText: compact JSON up to the cap, an error naming the size and the cap over it (0.789313ms)
ℹ tests 13
ℹ suites 0
ℹ pass 13
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 108.249113
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/pm.json 2026-10-14 19:38:51.832481906 -0500
+++ roles/pm.json 2026-10-14 19:38:52.112619781 -0500
@@ -6,7 +6,7 @@
"tools": ["read", "write", "edit", "grep", "find", "ls"],
"network": "api-only",
"authority": {
- "withinRole": ["task.create", "task.assign", "task.reassign", "task.schedule", "task.update.assigned", "task.close", "message.send"],
+ "withinRole": ["task.create", "task.assign", "task.reassign", "task.schedule", "task.update.assigned", "task.close", "message.send", "role.launch"],
"crossRole": ["task.priority.change", "task.scope.change"]
},
"credentials": [
@@ -0,0 +1,17 @@
✔ sessionModel: agent vars win, then the system's execution settings (8.031624ms)
✔ a pi bundle: prompt, policy, tools and manifest, 0600 in a 0700 directory (4.125049ms)
✔ a chief-of-staff bundle lists launch among the bus tools and the manifest's typed tools (1.880694ms)
✔ a claude-code bundle adds the wrapped gate hook and the MCP config (2.024655ms)
✔ the prompt's built-in tools line is the harness's own names, as the manifest lists them (6.023881ms)
✔ the prompt names the project clones in the workspace, and none when there are none (3.810268ms)
✔ the prompt explains the review loop to a role with push_working, and reviews to one with fetch_review (5.309296ms)
✔ a bundle is written once: an existing file refuses (1.284356ms)
✔ a path with a single quote can't go into the hook command (1.360446ms)
ℹ tests 9
ℹ suites 0
ℹ pass 9
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 108.254741
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/pm.json 2026-10-14 19:38:51.832481906 -0500
+++ roles/pm.json 2026-10-14 19:38:52.272621339 -0500
@@ -6,7 +6,7 @@
"tools": ["read", "write", "edit", "grep", "find", "ls"],
"network": "api-only",
"authority": {
- "withinRole": ["task.create", "task.assign", "task.reassign", "task.schedule", "task.update.assigned", "task.close", "message.send"],
+ "withinRole": ["task.create", "task.assign", "task.reassign", "task.schedule", "task.update.assigned", "task.close", "message.send", "role.launch"],
"crossRole": ["task.priority.change", "task.scope.change"]
},
"credentials": [
@@ -0,0 +1,18 @@
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.251887ms)
✔ credential state from the date alone, as the bus states it (0.151212ms)
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (341.049792ms)
✔ queue: a refused read fails closed with the store's text, redacted (177.714544ms)
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (722.511795ms)
✔ business: names, vars on the allowlist, authority, credential metadata only (8.519948ms)
✔ business: missing or invalid file refuses with the module's text, redacted (6.973752ms)
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (8.121819ms)
✔ settings: release, board, loopback address, notifier binding name only (98.140244ms)
✔ task fields: the committed table with no reads configured, GET only; a bad table refuses with no path (18.178798ms)
ℹ tests 10
ℹ suites 0
ℹ pass 10
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 1475.198626
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/pm.json 2026-10-14 19:38:51.832481906 -0500
+++ roles/pm.json 2026-10-14 19:38:52.439622966 -0500
@@ -6,7 +6,7 @@
"tools": ["read", "write", "edit", "grep", "find", "ls"],
"network": "api-only",
"authority": {
- "withinRole": ["task.create", "task.assign", "task.reassign", "task.schedule", "task.update.assigned", "task.close", "message.send"],
+ "withinRole": ["task.create", "task.assign", "task.reassign", "task.schedule", "task.update.assigned", "task.close", "message.send", "role.launch"],
"crossRole": ["task.priority.change", "task.scope.change"]
},
"credentials": [
@@ -0,0 +1,95 @@
✔ the role file loads and resolves through loadBusiness (4.488033ms)
✖ the shipped file is exactly the approved list (1.540344ms)
✖ the assistant's authority is message.send within role and nothing else (0.383942ms)
✔ a business grant through limits does not widen the role (1.332572ms)
✖ a launch block whose by is the assistant is refused (1.206276ms)
✔ the instance takes no credential references and no tracker (1.933985ms)
✔ the contract passes work to the chief of staff and directs no one (0.259623ms)
ℹ tests 7
ℹ suites 0
ℹ pass 4
ℹ fail 3
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 66.867584
✖ failing tests:
test at packages/business/tests/assistant.test.mjs:58:1
✖ the shipped file is exactly the approved list (1.540344ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
{
authority: {
crossRole: [],
withinRole: [
'message.send',
+ 'role.launch'
]
},
contract: 'assistant.md',
credentials: [],
name: 'assistant',
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:62:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: { roleVersion: 2, name: 'assistant', contract: 'assistant.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] },
expected: { roleVersion: 2, name: 'assistant', contract: 'assistant.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] },
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/business/tests/assistant.test.mjs:65:1
✖ the assistant's authority is message.send within role and nothing else (0.383942ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
{
crossRole: [],
withinRole: [
'message.send',
+ 'role.launch'
]
}
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:67:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: { withinRole: [ 'message.send', 'role.launch' ], crossRole: [] },
expected: { withinRole: [ 'message.send' ], crossRole: [] },
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/business/tests/assistant.test.mjs:86:1
✖ a launch block whose by is the assistant is refused (1.206276ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at refuses (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:22:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:87:3)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: undefined,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/assistant.json 2026-10-14 19:38:53.952344522 -0500
+++ roles/assistant.json 2026-10-14 19:38:53.970637880 -0500
@@ -6,7 +6,7 @@
"tools": ["read", "grep", "find", "ls"],
"network": "api-only",
"authority": {
- "withinRole": ["message.send"],
+ "withinRole": ["message.send", "role.launch"],
"crossRole": []
},
"credentials": []
@@ -0,0 +1 @@
1
@@ -0,0 +1,52 @@
✔ the role file loads and resolves through loadBusiness (4.391607ms)
✖ the shipped file is exactly the approved list (1.397263ms)
✔ the assistant's authority is message.send within role and nothing else (1.244289ms)
✔ a business grant through limits does not widen the role (1.087683ms)
✔ a launch block whose by is the assistant is refused (1.178885ms)
✔ the instance takes no credential references and no tracker (1.55519ms)
✔ the contract passes work to the chief of staff and directs no one (0.472435ms)
ℹ tests 7
ℹ suites 0
ℹ pass 6
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 64.964949
✖ failing tests:
test at packages/business/tests/assistant.test.mjs:58:1
✖ the shipped file is exactly the approved list (1.397263ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
... Skipped lines
{
authority: {
crossRole: [],
withinRole: [
'message.send'
...
'read',
+ 'write',
'grep',
'find',
'ls'
]
}
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:62:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: { roleVersion: 2, name: 'assistant', contract: 'assistant.md', tools: [ 'read', 'write', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] },
expected: { roleVersion: 2, name: 'assistant', contract: 'assistant.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] },
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/assistant.json 2026-10-14 19:38:53.952344522 -0500
+++ roles/assistant.json 2026-10-14 19:38:54.088639030 -0500
@@ -3,7 +3,7 @@
"name": "assistant",
"title": "Assistant",
"contract": "assistant.md",
- "tools": ["read", "grep", "find", "ls"],
+ "tools": ["read", "write", "grep", "find", "ls"],
"network": "api-only",
"authority": {
"withinRole": ["message.send"],
@@ -0,0 +1 @@
1
@@ -0,0 +1,79 @@
✔ the role file loads and resolves through loadBusiness (4.742996ms)
✖ the shipped file is exactly the approved list (1.709794ms)
✖ the assistant's authority is message.send within role and nothing else (0.401873ms)
✔ a business grant through limits does not widen the role (1.65882ms)
✔ a launch block whose by is the assistant is refused (1.574074ms)
✔ the instance takes no credential references and no tracker (1.784691ms)
✔ the contract passes work to the chief of staff and directs no one (0.23497ms)
ℹ tests 7
ℹ suites 0
ℹ pass 5
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 69.703441
✖ failing tests:
test at packages/business/tests/assistant.test.mjs:58:1
✖ the shipped file is exactly the approved list (1.709794ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
{
authority: {
+ crossRole: [
+ 'task.priority.change'
+ ],
- crossRole: [],
withinRole: [
'message.send'
]
},
contract: 'assistant.md',
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:62:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: { roleVersion: 2, name: 'assistant', contract: 'assistant.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [Array] }, credentials: [] },
expected: { roleVersion: 2, name: 'assistant', contract: 'assistant.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] },
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/business/tests/assistant.test.mjs:65:1
✖ the assistant's authority is message.send within role and nothing else (0.401873ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
{
+ crossRole: [
+ 'task.priority.change'
+ ],
- crossRole: [],
withinRole: [
'message.send'
]
}
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:67:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: { withinRole: [ 'message.send' ], crossRole: [ 'task.priority.change' ] },
expected: { withinRole: [ 'message.send' ], crossRole: [] },
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/assistant.json 2026-10-14 19:38:53.952344522 -0500
+++ roles/assistant.json 2026-10-14 19:38:54.215640267 -0500
@@ -7,7 +7,7 @@
"network": "api-only",
"authority": {
"withinRole": ["message.send"],
- "crossRole": []
+ "crossRole": ["task.priority.change"]
},
"credentials": []
}
@@ -0,0 +1,164 @@
✖ the role file loads and resolves through loadBusiness (4.048055ms)
✖ the shipped file is exactly the approved list (1.434875ms)
✖ the assistant's authority is message.send within role and nothing else (0.409068ms)
✖ a business grant through limits does not widen the role (1.08749ms)
✖ a launch block whose by is the assistant is refused (1.342017ms)
✖ the instance takes no credential references and no tracker (1.123246ms)
✔ the contract passes work to the chief of staff and directs no one (0.271162ms)
ℹ tests 7
ℹ suites 0
ℹ pass 1
ℹ fail 6
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 64.401603
✖ failing tests:
test at packages/business/tests/assistant.test.mjs:43:1
✖ the role file loads and resolves through loadBusiness (4.048055ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-t1VgIV/businesses/acme.json roles.assistant.credentials must reference exactly the services its role definition needs (gitea; got none)
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkCredentialMap (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:45:5)
at checkRoles (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:108:20)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:155:34)
at loadBusiness (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:203:10)
at loadFixture (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:36:28)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:50:27)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17) {
exitCode: 2
}
test at packages/business/tests/assistant.test.mjs:58:1
✖ the shipped file is exactly the approved list (1.434875ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
... Skipped lines
{
authority: {
crossRole: [],
withinRole: [
'message.send'
...
contract: 'assistant.md',
+ credentials: [
+ {
+ scopes: [
+ 'read:user'
+ ],
+ service: 'gitea'
+ }
+ ],
- credentials: [],
name: 'assistant',
network: 'api-only',
roleVersion: 2,
tools: [
'read',
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:62:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: { roleVersion: 2, name: 'assistant', contract: 'assistant.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [ [Object] ] },
expected: { roleVersion: 2, name: 'assistant', contract: 'assistant.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] },
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/business/tests/assistant.test.mjs:65:1
✖ the assistant's authority is message.send within role and nothing else (0.409068ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ [
+ {
+ scopes: [
+ 'read:user'
+ ],
+ service: 'gitea'
+ }
+ ]
- []
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:68:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [ { service: 'gitea', scopes: [Array] } ],
expected: [],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/business/tests/assistant.test.mjs:78:1
✖ a business grant through limits does not widen the role (1.08749ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-wZ5dqA/businesses/acme.json roles.assistant.credentials must reference exactly the services its role definition needs (gitea; got none)
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkCredentialMap (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:45:5)
at checkRoles (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:108:20)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:155:34)
at loadBusiness (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:203:10)
at loadFixture (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:36:28)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:79:27)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18) {
exitCode: 2
}
test at packages/business/tests/assistant.test.mjs:86:1
✖ a launch block whose by is the assistant is refused (1.342017ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /launch\.by names assistant, whose role assistant doesn't hold role\.launch within-role/. Input:
'/mnt/storage/scratch/tmp/mosaic-business-zHPTYA/businesses/acme.json roles.assistant.credentials must reference exactly the services its role definition needs (gitea; got none)'
at Object.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:25:12)
at expectedException (node:assert:547:17)
at expectsError (node:assert:672:21)
at strict.throws (node:assert:728:3)
at refuses (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:22:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:87:3)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: '/mnt/storage/scratch/tmp/mosaic-business-zHPTYA/businesses/acme.json roles.assistant.credentials must reference exactly the services its role definition needs (gitea; got none)',
expected: /launch\.by names assistant, whose role assistant doesn't hold role\.launch within-role/,
operator: 'match',
diff: 'simple'
}
test at packages/business/tests/assistant.test.mjs:95:1
✖ the instance takes no credential references and no tracker (1.123246ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at refuses (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:22:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:96:3)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: undefined,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,9 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/assistant.json 2026-10-14 19:38:53.952344522 -0500
+++ roles/assistant.json 2026-10-14 19:38:54.343641515 -0500
@@ -9,5 +9,5 @@
"withinRole": ["message.send"],
"crossRole": []
},
- "credentials": []
+ "credentials": [{"service": "gitea", "scopes": ["read:user"]}]
}
@@ -0,0 +1,90 @@
✔ the role file loads and resolves through loadBusiness (4.748247ms)
✖ the shipped file is exactly the approved list (1.110678ms)
✖ the resolved record matches the approved list, so a widened file fails (1.513003ms)
✔ the chief of staff holds role.launch and message.send; every other action is gated (1.128031ms)
✔ the pm does not hold role.launch (1.118588ms)
✔ a business grant of a gated action through limits does not widen the role (1.157359ms)
✔ a limit that drops role.launch leaves the chief of staff with no launch block (1.274752ms)
✔ a launch block whose by is the pm instance is refused (1.16483ms)
✔ the contract names no duty to talk to the human and the pm contract no launch (0.269189ms)
✔ the instance takes no credential references and no tracker (1.53432ms)
ℹ tests 10
ℹ suites 0
ℹ pass 8
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 72.111884
✖ failing tests:
test at packages/business/tests/chief-of-staff.test.mjs:64:1
✖ the shipped file is exactly the approved list (1.110678ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
... Skipped lines
{
authority: {
crossRole: [],
withinRole: [
'message.send',
...
'read',
+ 'write',
'grep',
'find',
'ls'
]
}
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:68:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'write', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] },
expected: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] },
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/business/tests/chief-of-staff.test.mjs:76:1
✖ the resolved record matches the approved list, so a widened file fails (1.513003ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
... Skipped lines
{
authority: {
crossRole: [],
withinRole: [
'message.send',
...
'read',
+ 'write',
'grep',
'find',
'ls'
]
}
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:78:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: { tools: [ 'read', 'write', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] } },
expected: { tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] } },
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/chief-of-staff.json 2026-10-14 19:37:48.978629348 -0500
+++ roles/chief-of-staff.json 2026-10-14 19:38:54.466642714 -0500
@@ -3,7 +3,7 @@
"name": "chief-of-staff",
"title": "Chief of staff",
"contract": "chief-of-staff.md",
- "tools": ["read", "grep", "find", "ls"],
+ "tools": ["read", "write", "grep", "find", "ls"],
"network": "api-only",
"authority": {
"withinRole": ["message.send", "role.launch"],
@@ -0,0 +1 @@
1
@@ -0,0 +1,201 @@
✖ the role file loads and resolves through loadBusiness (3.466125ms)
✖ the shipped file is exactly the approved list (1.522075ms)
✖ the resolved record matches the approved list, so a widened file fails (1.095806ms)
✖ the chief of staff holds role.launch and message.send; every other action is gated (0.883471ms)
✖ the pm does not hold role.launch (0.909578ms)
✖ a business grant of a gated action through limits does not widen the role (0.898507ms)
✖ a limit that drops role.launch leaves the chief of staff with no launch block (0.818502ms)
✖ a launch block whose by is the pm instance is refused (1.176901ms)
✔ the contract names no duty to talk to the human and the pm contract no launch (0.61793ms)
✖ the instance takes no credential references and no tracker (1.176009ms)
ℹ tests 10
ℹ suites 0
ℹ pass 1
ℹ fail 9
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 70.65365
✖ failing tests:
test at packages/business/tests/chief-of-staff.test.mjs:49:1
✖ the role file loads and resolves through loadBusiness (3.466125ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-eFpCma/businesses/acme.json roles.chief.credentials must reference exactly the services its role definition needs (gitea; got none)
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkCredentialMap (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:45:5)
at checkRoles (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:108:20)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:155:34)
at loadBusiness (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:203:10)
at loadFixture (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:42:28)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:50:19)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17) {
exitCode: 2
}
test at packages/business/tests/chief-of-staff.test.mjs:64:1
✖ the shipped file is exactly the approved list (1.522075ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
... Skipped lines
{
authority: {
crossRole: [],
withinRole: [
'message.send',
...
contract: 'chief-of-staff.md',
+ credentials: [
+ {
+ scopes: [
+ 'read:user'
+ ],
+ service: 'gitea'
+ }
+ ],
- credentials: [],
name: 'chief-of-staff',
network: 'api-only',
roleVersion: 2,
tools: [
'read',
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:68:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [ [Object] ] },
expected: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] },
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/business/tests/chief-of-staff.test.mjs:76:1
✖ the resolved record matches the approved list, so a widened file fails (1.095806ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-NK6bo9/businesses/acme.json roles.chief.credentials must reference exactly the services its role definition needs (gitea; got none)
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkCredentialMap (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:45:5)
at checkRoles (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:108:20)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:155:34)
at loadBusiness (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:203:10)
at loadFixture (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:42:28)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:77:27)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18) {
exitCode: 2
}
test at packages/business/tests/chief-of-staff.test.mjs:86:1
✖ the chief of staff holds role.launch and message.send; every other action is gated (0.883471ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-rDyRFH/businesses/acme.json roles.chief.credentials must reference exactly the services its role definition needs (gitea; got none)
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkCredentialMap (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:45:5)
at checkRoles (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:108:20)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:155:34)
at loadBusiness (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:203:10)
at loadFixture (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:42:28)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:87:27)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18) {
exitCode: 2
}
test at packages/business/tests/chief-of-staff.test.mjs:94:1
✖ the pm does not hold role.launch (0.909578ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-WOEz7M/businesses/acme.json roles.chief.credentials must reference exactly the services its role definition needs (gitea; got none)
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkCredentialMap (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:45:5)
at checkRoles (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:108:20)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:155:34)
at loadBusiness (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:203:10)
at loadFixture (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:42:28)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:98:27)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18) {
exitCode: 2
}
test at packages/business/tests/chief-of-staff.test.mjs:103:1
✖ a business grant of a gated action through limits does not widen the role (0.898507ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-ysvukt/businesses/acme.json roles.chief.credentials must reference exactly the services its role definition needs (gitea; got none)
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkCredentialMap (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:45:5)
at checkRoles (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:108:20)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:155:34)
at loadBusiness (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:203:10)
at loadFixture (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:42:28)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:106:19)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18) {
exitCode: 2
}
test at packages/business/tests/chief-of-staff.test.mjs:115:1
✖ a limit that drops role.launch leaves the chief of staff with no launch block (0.818502ms)
Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-pPfUhl/businesses/acme.json roles.chief.credentials must reference exactly the services its role definition needs (gitea; got none)
at refuse (file:///mnt/storage/scratch/researcher/q119/packages/business/src/errors.mjs:12:9)
at checkCredentialMap (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:45:5)
at checkRoles (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:108:20)
at validateBusinessDocument (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:155:34)
at loadBusiness (file:///mnt/storage/scratch/researcher/q119/packages/business/src/business.mjs:203:10)
at loadFixture (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:42:28)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:116:27)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18) {
exitCode: 2
}
test at packages/business/tests/chief-of-staff.test.mjs:123:1
✖ a launch block whose by is the pm instance is refused (1.176901ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /launch\.by names pm, whose role pm doesn't hold role\.launch within-role/. Input:
'/mnt/storage/scratch/tmp/mosaic-business-X4Xmc7/businesses/acme.json roles.chief.credentials must reference exactly the services its role definition needs (gitea; got none)'
at Object.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:30:12)
at expectedException (node:assert:547:17)
at expectsError (node:assert:672:21)
at strict.throws (node:assert:728:3)
at refuses (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:27:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:124:3)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: '/mnt/storage/scratch/tmp/mosaic-business-X4Xmc7/businesses/acme.json roles.chief.credentials must reference exactly the services its role definition needs (gitea; got none)',
expected: /launch\.by names pm, whose role pm doesn't hold role\.launch within-role/,
operator: 'match',
diff: 'simple'
}
test at packages/business/tests/chief-of-staff.test.mjs:142:1
✖ the instance takes no credential references and no tracker (1.176009ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at refuses (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:27:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:143:3)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: undefined,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,9 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/roles/chief-of-staff.json 2026-10-14 19:37:48.978629348 -0500
+++ roles/chief-of-staff.json 2026-10-14 19:38:54.596643981 -0500
@@ -9,5 +9,5 @@
"withinRole": ["message.send", "role.launch"],
"crossRole": []
},
- "credentials": []
+ "credentials": [{"service": "gitea", "scopes": ["read:user"]}]
}
@@ -0,0 +1 @@
1
@@ -0,0 +1,38 @@
✔ the role file loads and resolves through loadBusiness (5.227261ms)
✔ the shipped file is exactly the approved list (0.216671ms)
✔ the resolved record matches the approved list, so a widened file fails (1.360346ms)
✔ the chief of staff holds role.launch and message.send; every other action is gated (1.204196ms)
✔ the pm does not hold role.launch (1.327856ms)
✔ a business grant of a gated action through limits does not widen the role (1.310635ms)
✔ a limit that drops role.launch leaves the chief of staff with no launch block (1.263068ms)
✖ a launch block whose by is the pm instance is refused (1.462439ms)
✔ the contract names no duty to talk to the human and the pm contract no launch (0.37384ms)
✔ the instance takes no credential references and no tracker (5.221906ms)
ℹ tests 10
ℹ suites 0
ℹ pass 9
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 73.503014
✖ failing tests:
test at packages/business/tests/chief-of-staff.test.mjs:123:1
✖ a launch block whose by is the pm instance is refused (1.462439ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at refuses (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:27:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/chief-of-staff.test.mjs:124:3)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: undefined,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/packages/business/src/business.mjs 2026-10-14 19:38:54.712662424 -0500
+++ packages/business/src/business.mjs 2026-10-14 19:38:54.730645286 -0500
@@ -118,7 +118,7 @@
requireId(launch.by, `${where}.by`);
if (!Object.hasOwn(roles, launch.by)) refuse(`${where}.by names ${launch.by}, which the business file doesn't declare`);
const launcher = definitions[roles[launch.by].definition];
- if (!launcher.authority.withinRole.includes("role.launch")) {
+ if (false) {
refuse(`${where}.by names ${launch.by}, whose role ${launcher.name} doesn't hold role.launch within-role`);
}
const instances = requireDistinctList(launch.instances, `${where}.instances`, (name) => {
@@ -0,0 +1,35 @@
✔ the role file loads and resolves through loadBusiness (6.442661ms)
✔ the shipped file is exactly the approved list (0.726312ms)
✔ the assistant's authority is message.send within role and nothing else (1.702801ms)
✔ a business grant through limits does not widen the role (1.357181ms)
✖ a launch block whose by is the assistant is refused (1.889341ms)
✔ the instance takes no credential references and no tracker (2.328818ms)
✔ the contract passes work to the chief of staff and directs no one (0.658413ms)
ℹ tests 7
ℹ suites 0
ℹ pass 6
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 84.029268
✖ failing tests:
test at packages/business/tests/assistant.test.mjs:86:1
✖ a launch block whose by is the assistant is refused (1.889341ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at refuses (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:22:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/assistant.test.mjs:87:3)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: undefined,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/packages/business/src/business.mjs 2026-10-14 19:38:54.712662424 -0500
+++ packages/business/src/business.mjs 2026-10-14 19:38:54.861646563 -0500
@@ -118,7 +118,7 @@
requireId(launch.by, `${where}.by`);
if (!Object.hasOwn(roles, launch.by)) refuse(`${where}.by names ${launch.by}, which the business file doesn't declare`);
const launcher = definitions[roles[launch.by].definition];
- if (!launcher.authority.withinRole.includes("role.launch")) {
+ if (false) {
refuse(`${where}.by names ${launch.by}, whose role ${launcher.name} doesn't hold role.launch within-role`);
}
const instances = requireDistinctList(launch.instances, `${where}.instances`, (name) => {
@@ -0,0 +1,43 @@
✔ config directory and file path follow MOSAIC_CONFIG (1.114323ms)
✔ the fixture business validates and comes back frozen (3.611743ms)
✔ two instances may share a definition (1.07717ms)
✔ top-level refusals (3.083891ms)
✔ arbiters and projects (4.886909ms)
✔ role instances (2.195591ms)
✔ Vikunja bots (4.907487ms)
✔ a role without Vikunja takes no tracker block (2.080745ms)
✔ credential references match the definition's services (2.54856ms)
✖ launch (2.102669ms)
✔ loadBusiness: file checks (1.911454ms)
✔ loadBusiness: not a regular file (38.535041ms)
✔ loading writes nothing (1.127878ms)
✔ names that are Object.prototype properties don't count as declared (2.647859ms)
✔ the shipped example refuses as written and validates once filled in (0.678868ms)
ℹ tests 15
ℹ suites 0
ℹ pass 14
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 129.534082
✖ failing tests:
test at packages/business/tests/business.test.mjs:120:1
✖ launch (2.102669ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at refuses (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/business.test.mjs:11:10)
at TestContext.<anonymous> (file:///mnt/storage/scratch/researcher/q119/packages/business/tests/business.test.mjs:123:3)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: undefined,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,11 @@
--- /mnt/storage/src/mosaic-stack/agents/researcher/work/queue-119/mutants/.bak.Wznjl6/packages/business/src/business.mjs 2026-10-14 19:38:54.712662424 -0500
+++ packages/business/src/business.mjs 2026-10-14 19:38:55.001647927 -0500
@@ -118,7 +118,7 @@
requireId(launch.by, `${where}.by`);
if (!Object.hasOwn(roles, launch.by)) refuse(`${where}.by names ${launch.by}, which the business file doesn't declare`);
const launcher = definitions[roles[launch.by].definition];
- if (!launcher.authority.withinRole.includes("role.launch")) {
+ if (false) {
refuse(`${where}.by names ${launch.by}, whose role ${launcher.name} doesn't hold role.launch within-role`);
}
const instances = requireDistinctList(launch.instances, `${where}.instances`, (name) => {
@@ -0,0 +1,105 @@
32a67dc1d5d2ea3f9f7e3ed01dee56bb77163067b7bb8f7123f76b6b77e2a2ff build.md
8ebb56b55d274c36f8e6e4d617c37e260bbc2a11fa3b05de6299df34e54ac388 candidate-manifest.sha256
20214fa69ceb5da7f6a64efe5812edce9b6e2a0836a06bb06d347f7475f34cae mutants/control-assistant.out
9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa mutants/control-assistant.rc
f8e268ac9f2bb5dae8ec881fc322d8e0e455bbc8558f1d0d30822622a6db4c9d mutants/control-bundle.out
9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa mutants/control-bundle.rc
b68f641d5084f6907609b29cc19a1a6703ef490b7c46ca0f00c2fe4e337be8e6 mutants/control-business.out
9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa mutants/control-business.rc
24a6dfd37399b95048f2f151a202af46a9a29e152fe28dab5381cab89be1b888 mutants/control-chief-of-staff.out
9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa mutants/control-chief-of-staff.rc
07dfbc80e16e7171afa85a7bb4dcfae7fbe429242773f092363f00284c7a2a6a mutants/control-launcher.out
9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa mutants/control-launcher.rc
7a3b7c7398f25d0bd1f4744378ef464f5b7591e28cb1f8a362acb9ed8de3e056 mutants/control-reads.out
9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa mutants/control-reads.rc
798db167bf5baf94ce1fa5ab850845af34f40c101a7b37d4a9468b922978e0f1 mutants/control-role.out
9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa mutants/control-role.rc
8922f1c1ac50f57422532802b0e5414951b3e1d1df589dfd808896348a98c9a6 mutants/control-tools.out
9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa mutants/control-tools.rc
bcfb3e7171b96ca394f7865ff08ebacee41db9cfc012530f17db0507eab3fd12 mutants/m10-pm-contract-launches.out
4be29ec28026325258b03a183c05d959302b3294578f1b458abb0d0142b8919d mutants/m10-pm-contract-launches.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m10-pm-contract-launches.rc
ae9c55b6df0bf0588a46817aa4f3a2925fc401a33e39f6c3b1d4a1355840278c mutants/m11-chief-contract-talks.out
bacc257d903848dd85a2b75ed801c2c8b012ffc47618845ebd34a7d5bea6bb96 mutants/m11-chief-contract-talks.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m11-chief-contract-talks.rc
c4125e2cb9c36833b1193d42afd6d1c0b1dccc124588c24123c605d436773da1 mutants/m12-assistant-contract-directs.out
413d8444826b41b9667f32e02fd03dfb28dd57a8954159b97705454dc23a02e4 mutants/m12-assistant-contract-directs.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m12-assistant-contract-directs.rc
6121f69efcde862860940475d911314a931431ebfd8674b219bdf78e3437b05c mutants/m13a-assistant-contract-missing.out
9ffbbade4871ff3d3eb79756b917fa81acf71b161b61e12af3d0c977934d97c1 mutants/m13a-assistant-contract-missing.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m13a-assistant-contract-missing.rc
06b937ad30a9cf428151d8e242537bd2fb736e2648ba5b368280f684cdc9ffc2 mutants/m13b-assistant-contract-missing.out
6806bcb2e15206d065b0f69505713c17a810c1213cd2ab9d855f772a013d2f7d mutants/m13b-assistant-contract-missing.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m13b-assistant-contract-missing.rc
de90cdf3aa267b6e71ebee6b41058bb55442259dad38d2188ccb68f709988c94 mutants/m1a-chief-drops-launch.out
f6d9c9e9f12584ccc0a867cff5b25e950775568609a4d61b080b820f424109e8 mutants/m1a-chief-drops-launch.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m1a-chief-drops-launch.rc
889fc0eb03d32242f833b4de63587d41b2fceae8d7f612699f3f979db5d2820e mutants/m1b-chief-drops-launch.out
b77ca00a70bdfb2ac510f681e1877f9c1b9cc8c50973679fb53159531d45585c mutants/m1b-chief-drops-launch.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m1b-chief-drops-launch.rc
895b5f924986fc0d59c7b8f6854d7d28409c8e8a04dfd73677dc2175c6f59ed9 mutants/m1c-chief-drops-launch.out
805255dcaaa1390f31d71b100faa1d79e14d3f5f59331a928c06933727a098be mutants/m1c-chief-drops-launch.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m1c-chief-drops-launch.rc
850c7ba67a799d167f520a57997584ebe4a246bb420ea89ca27f028b5f82f0de mutants/m1d-chief-drops-launch.out
42b2ccd96a6061d195360ab1586df27752356aace629473b33f82be96929aa60 mutants/m1d-chief-drops-launch.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m1d-chief-drops-launch.rc
127397ac44076eed1c6bd9409db821f65f64d5c5954e450eefad55b91c6620bd mutants/m1e-chief-drops-launch.out
1306159d0ff8b397d2bbde757d09bcbeed6c186fbf5193790fe3ef2e041fd758 mutants/m1e-chief-drops-launch.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m1e-chief-drops-launch.rc
6d78802b57cf1c534b59b57714c290768bb2e4992d3bd57b7386eb3d6ebdaa40 mutants/m1f-chief-drops-launch.out
c0d762b8182bf2e9fac6c15336b3ac98eeb432eb1efcb63a51bd929093f5aa91 mutants/m1f-chief-drops-launch.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m1f-chief-drops-launch.rc
aec994f9f0da740606b870591a5efab6a98ab7775bb67a9e0e3fdb9a09009fb1 mutants/m2a-pm-regains-launch.out
55f26e21a9daa64078f1cd43ef3e264243d1d41009fc8e35a9c111c248eca612 mutants/m2a-pm-regains-launch.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m2a-pm-regains-launch.rc
831c5598caca95ecfc04a4d0df32263c4665601f6216649bf4ed7d13b8cc8bd1 mutants/m2b-pm-regains-launch.out
15235db7d4104f8fa0ee2fad84f2ff977bfb7d64144a13564f1fb1b8a9937419 mutants/m2b-pm-regains-launch.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m2b-pm-regains-launch.rc
10052acab24fd04f8649300b306a4d8a617df7dc0fc4105403ccb4b1864cfcc9 mutants/m2c-pm-regains-launch.out
1ac98a45f06dfa59bbba4148ebc7c253b747b887519eecd8efc95a90ce3d871d mutants/m2c-pm-regains-launch.patch
9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa mutants/m2c-pm-regains-launch.rc
2cd187390b6e1bb4d0c567f3cb0b5cecf60cdf3a3b75fe0f18d19bead33fce6c mutants/m2d-pm-regains-launch.out
04b3b1064866b2837d1012f4e2c3b5a1df919bbed83ec6661f507f9cad8f32a7 mutants/m2d-pm-regains-launch.patch
9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa mutants/m2d-pm-regains-launch.rc
685dc67e0b95547ec3cf714cee1927437ec67a93fd63dde4fa7d73528436843c mutants/m2e-pm-regains-launch.out
1f084985e0e7d32d82b440c5f7654cf6ec3fa872d4dae27865c3cf892e247b70 mutants/m2e-pm-regains-launch.patch
9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa mutants/m2e-pm-regains-launch.rc
8dfc2ff1cc99a3640e209bf34d0f231c85365c3e4449f519a7c730a39dd78472 mutants/m3-assistant-launch.out
f48575f3e24b22de8a446fea339b0390adc31406fedac3233b31979d59b38657 mutants/m3-assistant-launch.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m3-assistant-launch.rc
122ef39d3bb0c4bb11d977e6b2e9430651d18f225357d71b0e3eb63afe9ec2ab mutants/m4-assistant-write.out
2a17a78bf0e65d3bf6c00e9982a35e09a8cf81ec73e5f0d431bf88596f304e02 mutants/m4-assistant-write.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m4-assistant-write.rc
4edba2357c7d6670fb33451b34c309d67636bc8e7dbc4ea51514b5d2055ebbce mutants/m5-assistant-crossrole.out
ec42a7c564c2e6b19f2902126509d97d4bdc5919f3db5b16fe56a3a60177fc3a mutants/m5-assistant-crossrole.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m5-assistant-crossrole.rc
f7e441ceb419eb0f9d659ba3abe67357a33b58ba22bd6e2b0615bc1fa3293859 mutants/m6-assistant-credential.out
d8e9e105d81f211d5d6a1aba0270aed96926397db014c67b2d43e19cd5bbed66 mutants/m6-assistant-credential.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m6-assistant-credential.rc
826966f0045f5b17c1bd30836c6c90489ac0002f5278719fad8802b4954a0568 mutants/m7-chief-write.out
1d321d947f75f82a0b77bd0859ea53697a4649245521b02158f90d3c25f0a90d mutants/m7-chief-write.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m7-chief-write.rc
73ee25746dc618a697687e90bbfe2fa9b676a23207a09798e427ed5f2d88382b mutants/m8-chief-credential.out
5c2c528c7485f8453037cbf9b1cb0eef8841c76eb08c218cf4634e2775996a9f mutants/m8-chief-credential.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m8-chief-credential.rc
cf6edf4723f7e6560eff68d5a5996222c66f8f920cdb7efdcbe5820e00de22b1 mutants/m9a-validator-accepts-any-launcher.out
8d49a707c3b8a0b6b9966cc775f9928d3bde0888d5fb3281f7edebc859a09ba7 mutants/m9a-validator-accepts-any-launcher.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m9a-validator-accepts-any-launcher.rc
12cb2044aad1c81d6ffbbd4a81395e5adbd64c1609aa61cbe7566c0ce2d961d4 mutants/m9b-validator-accepts-any-launcher.out
ec968e1ca36ccf21548290c58fb540509ca5b4ab44f5e54a32785cbbc5d99a8e mutants/m9b-validator-accepts-any-launcher.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m9b-validator-accepts-any-launcher.rc
875f4be90457fdc89f7f92709e15000bd86738517e2fd3c6787b2ffa4273335e mutants/m9c-validator-accepts-any-launcher.out
33f22744c31971cbe614c3edc109e09860206ad7173a85e079b6076f21b40b37 mutants/m9c-validator-accepts-any-launcher.patch
4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865 mutants/m9c-validator-accepts-any-launcher.rc
8a8524db550f433863b4a5f12647f0866033bd5d185281dbb49481e869d2bec9 mutants-run.txt
83c3b1712a75b4e3114b879fde51b2752ecc3622b1c37ea1cc4218a601bf352c mutants.sh
b73a0976771b5226d90bb368fe93d90ef384a55eca95273874ec9ea0dd3ffce7 row119.patch
ddacea45cc85d6125e016a6aa4abd18d17554f492ce27359cc0eed8ad51b82fc suites/business.txt
2b1e99a388cc96da2555a514a37007f9d6e6113928f4f2f02539a30600762c59 suites/bus.txt
0d6cea57f7f2e75989801d3700990f488286c18ab968884b015c11966376934e suites/cli.txt
045179f68e5f83457251e17b3ea0376a36323e93e82e74b3779b70fbf6ca093d suites/conversation.txt
a2f2020f2a4747bd1c47dab32a9ab70e9c0bd62c31de78c9f871f26f026693d5 suites/harness.txt
39e022f7a0fdeada20a6054f2543c3b97c454fcae6df6f36d55963aa2d30ff06 suites/seat.txt
460f6c61ee69a2dca490d4a10788c83508a2c731f0fe15d0bac1e86658ca25a0 suites/tasks.txt
ccea37f4ffde1f160dcd1a1864988083a645d974e892065c956b338d0f8c853a suites/test-task.txt
a5709103bff634e76b1ee460d8ca97c228526151ad95e528c774752ee9e8ee55 suites/webui.txt
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,166 @@
npm notice run test
npm notice run node --test tests/*.test.mjs
✔ agents: a held role with nothing on the bus is idle; a role session gets the claim rows unchanged (49.384223ms)
✔ agents: working counts what the holder received and has not read; routed alone is not working (33.623199ms)
✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (51.383272ms)
✔ agents: working wins over waiting, and Input needed still shows (21.866653ms)
✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (31.096925ms)
✔ agents: state reads only its own business, even for a run name both use (32.782999ms)
✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (32.946874ms)
✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (34.223111ms)
✔ agents: stranded is oldest first and capped at 50 for a role (149.387792ms)
✔ agents: stranded is per role and per business (42.034853ms)
✔ agents: a message routed to a held role and not yet received is not stranded (15.427707ms)
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (43.250308ms)
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (39.695895ms)
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (34.466992ms)
✔ launch events require a human CLI capability; generic emit cannot forge authority events (24.029094ms)
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (26.240864ms)
✔ a recommendation that is not exactly one option key refuses invalid-recommendation after the options (50.425174ms)
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (22.753948ms)
✔ task action subjects and linked decision trail are complete and ordered (45.780962ms)
✔ launch binding is durable and reconnecting requires the identical trusted record (18.483094ms)
✔ business isolation includes inherited object names and cross-business message references (30.428515ms)
✔ authority never transfers between action, run, target, unresolved or replaced role holder (45.908424ms)
✔ task projection uses schema current view, skipping earlier and equal-start polls (22.346528ms)
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (29.957541ms)
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (17.577378ms)
✔ both arbiters require human resolution when their cross-role route is themselves (28.088945ms)
✔ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (22.396626ms)
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (4.044745ms)
✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (37.917892ms)
✔ credentials refuses a role session and any argument (28.927412ms)
✔ credentials reads the status on each request, and a broker without one lists none (32.081434ms)
✔ the broker process answers credentials without a token, path or variable in its output, log or store (98.288158ms)
✔ only validated broker references load; returned data and exceptions cannot expose a known token (10.928291ms)
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (5.810698ms)
✔ expiry refuses use and env references never become client data (1.357074ms)
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (2.434756ms)
✔ opaque tokens shorter than 16 characters refuse before use (3.930869ms)
✔ a human resolve sends the raising role one message from the human, and its request is the answer (64.455611ms)
✔ a cross-role decision resolved by its arbiter comes from that role and run, with no request (33.051813ms)
✔ a human withdraw sends one message with the note (30.855078ms)
✔ a human seen before the close leaves the request on the close's answer, not the seen (44.655889ms)
✔ the raising run's withdraw, a supersede, a within-role raise and seen send nothing (50.005946ms)
✔ refused closes send nothing: invalid-choice, decision-closed, not-resolver (53.867305ms)
✔ a raising role nobody holds at the close gets the message on the next claim (41.036719ms)
✔ a human DECISION send keeps its own instruction as the request, not a close's answer (42.207659ms)
✔ roles lists open decisions routed to a role, and never the human inbox (60.871261ms)
✔ recent lists closed decisions of every route, newest close first, with how each closed (41.058616ms)
✔ recent gives the closing event as closed when the decision was seen first (40.916607ms)
✔ recent stops at RECENT rows (152.660087ms)
✔ every row is what trail already gives the same session for that id (58.821182ms)
✔ a role session can't list decisions, so it can't read another role's open decisions (66.201023ms)
✔ decisions is a read: one business, strict arguments, nothing written (48.67061ms)
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (36.6956ms)
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (19.754792ms)
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (19.780958ms)
✔ endLaunch leaves a claim another run took alone (26.78267ms)
✔ refuse records action.refused against the caller with the code only (18.474217ms)
✔ launches off refuses role.launch with launch-revoked until launches on (27.182253ms)
✔ broker process: launch ops authorize role.launch, record refusals and end runs (134.541434ms)
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (4.931939ms)
✔ process reader gets own kernel identity without exposing environment values (1.223458ms)
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (1.342824ms)
✔ real pid 1 remains inspectable when its environment is protected (0.451218ms)
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (39.070827ms)
✔ missing and foreign-business citations refuse and roll back message and grant (25.24062ms)
✔ cross-role sends still need a matching resolved decision and consume it once (30.550102ms)
✔ messages pages all human messages newest first without writes; read state is history (493.201465ms)
✔ messages refuses role callers, invalid paging and cursors outside this human feed (21.622385ms)
✔ views messages passes only its paging arguments through (0.341173ms)
✔ messages isolates businesses even with a valid foreign cursor (15.370686ms)
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (104.969217ms)
✔ startup token refusal returns safe code without value or partial listening broker (60.732531ms)
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (106.822131ms)
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (139.487944ms)
✔ trusted host registers later launches; socket clients never have a registration verb (76.24671ms)
✔ runtime excludes declared project roots even when host supplies no repoRoots (50.336838ms)
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (72.469026ms)
✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (56.511176ms)
✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (24.515072ms)
✔ a message with no reply gives an empty list (23.150006ms)
✔ replies come oldest first and stop at REPLIES (129.566555ms)
✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (53.716416ms)
✔ strict arguments; a role session gets human-required (55.564335ms)
✔ views passes replies through as one call with the id (0.799411ms)
✔ requirements returns the committed list to a holding role session, and no one else (36.879039ms)
✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (19.993024ms)
✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (26.212816ms)
✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (42.770341ms)
✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (33.756739ms)
✔ review.request says which run holds the reviewer role, or null, and a null request waits for a claim (37.815061ms)
✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (62.13919ms)
✔ reviewFetch answers only the addressed reviewer, for the request it cites (37.388693ms)
✔ review.verdict answers one request once, to the role that asked (45.979383ms)
✔ task.close on a pushed task needs an approve verdict for the latest push (62.974417ms)
✔ a refusal the host decided is filed on the task it names, and only a task ref counts (37.949579ms)
✔ v3b prototype refusals, views and append-only mutations (120.173143ms)
✔ gated approval authorizes once, survives store reopen, and fresh approval works (41.746307ms)
✔ another run cannot consume an approval; a failed check leaves it usable (29.632712ms)
✔ two scheduled callers have exactly one grant and one consumed refusal (28.587763ms)
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (56.460779ms)
✔ class drift gated to cross-role refuses before consumption (39.620882ms)
✔ class drift cross-role to gated refuses before consumption (37.381219ms)
✔ class drift gated to within-role refuses before consumption (40.242234ms)
✔ class drift cross-role to within-role refuses before consumption (37.749804ms)
✔ class drift within-role to gated refuses before consumption (28.56679ms)
✔ class drift within-role to cross-role refuses before consumption (25.402299ms)
✔ message.send consumes approval and prevents a later send or authorize (28.252153ms)
✔ role.revoke consumes approval and prevents a later revoke or authorize (26.619285ms)
✔ a business that never switched launches reads allowed with no time and no event (24.552263ms)
✔ launches revoked and then restored read allowed, with the restore time and event id (23.473762ms)
✔ a task closed on the tracker leaves the open list, and total counts the open ones (24.445026ms)
✔ a tombstone is not open (23.677469ms)
✔ the open list stops at the cap and says so; total still counts every open task (31.047913ms)
✔ a role and a reader session read state; arguments, and an unbound cap, refuse (19.994669ms)
✔ a human session reads the same state, with the same cap (25.227503ms)
✔ reading state writes nothing: every table keeps its row count (21.847483ms)
✔ creates private WAL store and excludes a second writer until explicit close (24.570004ms)
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (27.246511ms)
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (23.96838ms)
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (84.963565ms)
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (22.897338ms)
✔ async transactions refuse before invoking their function (14.498062ms)
✔ the session default fits under the reply cap and pages to the end with next (228.496566ms)
✔ the short form: no description, assignees by role, every listed key (105.390943ms)
✔ each filter, alone and together, and total counts every match (187.665406ms)
✔ task_ref reads one task in full, as the whole board has it (103.515549ms)
✔ no arguments is the read the Console and mosaic make, unchanged (80.300203ms)
✔ an unknown argument or a bad value refuses invalid-request (362.574786ms)
✔ tasks rows carry priority and requirement for every session that reads tasks (59.690105ms)
✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (68.616345ms)
✔ an empty business and one with snapshots but no task.created read cleanly (14.497092ms)
✔ recordTask keeps sync reads and a role write apart (34.058062ms)
✔ read_at must be one canonical UTC format, so the projection compares strings safely (18.500069ms)
✔ a bad entry refuses the whole record (19.250747ms)
✔ taskView reads the projection for one business (24.478487ms)
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (31.474439ms)
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (278.546351ms)
✔ without an adapter the server refuses every task verb (20.059427ms)
✔ the runtime refuses an invalid adapter and closes a valid one (20.730262ms)
✔ the process loads the S3 adapter from plain-data trackers (118.433808ms)
✔ the tester binds only when the business sets bus.tester to exactly true (106.852593ms)
✔ a business may not name a role tester, so the tester address is never a role (24.328038ms)
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (45.416599ms)
✔ a tester message is from tester, starts [mos test], and writes no human.input or authority (18.920147ms)
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (20.482593ms)
✔ the tester reads the human views a reader reads (24.085721ms)
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.364101ms)
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.42516ms)
✔ testerNonce reads only the tester's variable and gives null on any read error (0.269691ms)
✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (22.206638ms)
✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (710.743158ms)
✔ socket capability stamps launch identity; shared views use wire, no SQL client (36.107291ms)
✔ two wire claims serialize; a lost reply never automatically retries (27.349126ms)
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (15.169522ms)
✔ client preserves UTF-8 when a response divides a multibyte character (12.264245ms)
✔ committed mutation followed by dropped reply reports unknown and is never retried (17.755748ms)
ℹ tests 156
ℹ suites 0
ℹ pass 156
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 1450.14557
@@ -0,0 +1,88 @@
npm notice run @mosaic/[email protected] test
npm notice run node --test tests/
✔ the role file loads and resolves through loadBusiness (8.757295ms)
✔ the shipped file is exactly the approved list (1.17548ms)
✔ the assistant's authority is message.send within role and nothing else (2.293263ms)
✔ a business grant through limits does not widen the role (2.390957ms)
✔ a launch block whose by is the assistant is refused (2.081935ms)
✔ the instance takes no credential references and no tracker (3.446612ms)
✔ the contract passes work to the chief of staff and directs no one (0.339161ms)
✔ config directory and file path follow MOSAIC_CONFIG (2.282523ms)
✔ the fixture business validates and comes back frozen (6.702787ms)
✔ two instances may share a definition (2.461208ms)
✔ top-level refusals (6.70917ms)
✔ arbiters and projects (8.534383ms)
✔ role instances (4.605411ms)
✔ Vikunja bots (6.355468ms)
✔ a role without Vikunja takes no tracker block (2.239476ms)
✔ credential references match the definition's services (2.514846ms)
✔ launch (8.006632ms)
✔ loadBusiness: file checks (2.248071ms)
✔ loadBusiness: not a regular file (42.616881ms)
✔ loading writes nothing (1.39767ms)
✔ names that are Object.prototype properties don't count as declared (2.81657ms)
✔ the shipped example refuses as written and validates once filled in (0.631327ms)
✔ the role file loads and resolves through loadBusiness (8.439724ms)
✔ the shipped file is exactly the approved list (0.827515ms)
✔ the resolved record matches the approved list, so a widened file fails (2.684616ms)
✔ the chief of staff holds role.launch and message.send; every other action is gated (1.981672ms)
✔ the pm does not hold role.launch (2.1383ms)
✔ a business grant of a gated action through limits does not widen the role (2.654861ms)
✔ a limit that drops role.launch leaves the chief of staff with no launch block (2.213253ms)
✔ a launch block whose by is the pm instance is refused (1.827647ms)
✔ the contract names no duty to talk to the human and the pm contract no launch (0.379178ms)
✔ the instance takes no credential references and no tracker (2.101069ms)
✔ usage errors exit 4 (299.552943ms)
✔ validate: a good business exits 0 and prints instance digests (68.003738ms)
✔ validate: project files (327.672493ms)
✔ validate: missing files and a broken system config (254.070024ms)
✔ validate: credential reference problems exit 2 and name each one (71.290439ms)
✔ validate: a token file inside the repository is refused (68.143184ms)
✔ validate: role definitions come from MOSAIC_ROLES_DIR (202.173732ms)
✔ resolve: prints one instance's record (267.89148ms)
✔ resolve: refusals (400.549345ms)
✔ parse: exactly one of file or env, plus the service's date (4.093698ms)
✔ check: a good file has no problems (1.300717ms)
✔ check never opens the file: a write-only token passes (0.554356ms)
✔ check: file problems (1.392279ms)
✔ check: token files can't live in the repository or dataRoot, even through a linked directory (1.351493ms)
✔ check: dates and environment references (0.614926ms)
✔ path and load (3.205446ms)
✔ refusals (1.894874ms)
✔ systemVars flattens the validated config (3.049647ms)
✔ precedence: system, business, project, project role, agent (6.841932ms)
✔ limits narrow the definition and never widen it (3.008136ms)
✔ role.launch stays within-role only for the instance the launch block names (7.418399ms)
✔ limits.authority without role.launch leaves the launcher with no launch block (2.962364ms)
✔ limits.authority narrows cross-role actions too (4.208876ms)
✔ classify (1.824688ms)
✔ the record carries what the broker and launcher need (1.64436ms)
✔ digest: key order doesn't matter, any value change does (5.962938ms)
✔ refusals (2.345609ms)
✔ the four shipped version 2 roles load (4.1598ms)
✔ shipped role scopes match addendum B section 2 and the SR runbook (1.276827ms)
✔ shipped authority follows the note's table (0.788543ms)
✔ every role file in roles/ loads through the validator, and a v2 contract exists (0.865688ms)
✔ version 1 files keep loading with no authority (1.107582ms)
✔ the conductor policy isn't a role (0.242917ms)
✔ a missing role file is exit 4, a symbolic link too (0.556779ms)
✔ version 2 refusals (1.957412ms)
✔ authority: closed vocabulary, no gated-only action, no overlap (3.549793ms)
✔ credentials: Gitea scopes (1.62296ms)
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.671689ms)
✔ credentials: services (0.821372ms)
✔ contract: a non-empty regular Markdown file beside the role file (1.541676ms)
✔ every key names known layers and a merge rule (1.188872ms)
✔ unknown keys and wrong layers refuse (0.928826ms)
✔ types (2.445085ms)
✔ merge: defaults, then the most specific layer wins (0.422748ms)
✔ merge: limits only narrow, and provenance lists each source (0.582765ms)
✔ merge doesn't change its inputs (0.228965ms)
ℹ tests 78
ℹ suites 0
ℹ pass 78
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 2059.880607
@@ -0,0 +1,199 @@
npm notice run @mosaic/[email protected] test
npm notice run node --test tests/*.test.mjs
✔ every screen and overlay fits 120x36, 80x24 and the narrow layout, row for row (1755.202058ms)
✔ width counts terminal cells: wide CJK and emoji 2, U+FE0F after a one-cell base 1, marks 0 (85.06996ms)
✔ broker text is shown, never interpreted: no raw control character or bidi override reaches the terminal (204.722714ms)
✔ NO_COLOR: no escape codes, except reverse video on the selected row (351.642792ms)
✔ 1-9 shows the decide flow's own lines and question; only y resolves (60.776791ms)
✔ the app shows the same decide text as mosaic decide (25.334185ms)
✔ any key but y or Y at the question answers No; nothing is sent (225.55458ms)
✔ below 40x10 the question is hidden, so every key answers it No, y included (Filbert R3) (78.698708ms)
✔ under 40 columns a tall terminal shows the too-small screen even where the decide box would fit; y sends nothing (Filbert N10) (321.92585ms)
✔ a hidden decide box names the way out, and the status line under it does not promise y (Filbert N12) (43.934631ms)
✔ a throw while answering the question goes to crash, sends nothing and leaves no job pending (27.818442ms)
✔ type-ahead never answers the question: a paste, keys typed before it is drawn, keys inside askMs (435.140456ms)
✔ quitting at the question answers No and sends nothing, armed or not (52.395722ms)
✔ a timer tick reads nothing under an overlay, never answers the question, and keeps a resolved status (46.544282ms)
✔ a long question is cut inside the question; the action, target, choice and prompt stay (Dewey B1) (57.941874ms)
✔ a decide box too big for the terminal says so, and every key answers it No, y included (Dewey B1, round 3) (410.656819ms)
✔ at every size the decide box shows each line y depends on, or says it is hidden (9567.651319ms)
✔ outcome unknown: the CLI's sentence, no retry, then the trail or a re-read (65.837924ms)
✔ refused and failed reads keep the last list, marked stale; a refused decide says refused (31.822798ms)
✔ help or a detail box cut to fit keeps its last line, Esc included (Dewey) (15.841057ms)
✔ keys: views, moving, trail of a selection, the command line, help and quit (79.096174ms)
✔ one option: the status line says Press 1, not 1-1 (16.823581ms)
✔ no business: the app says so and :business picks one (9.94374ms)
✔ mosaic with no argument: exit 3 in an agent run, exit 4 with the usage off a terminal, the app on one (2.347613ms)
✔ scripts/mosaic with no argument reaches the app entry; the seat usage it printed before has not drifted (186.383747ms)
✔ strings to keep exactly live once, in the decide flow and the transport; the app does not restate them (0.472519ms)
✔ decide with --note still asks; a No sends nothing (11.377688ms)
✔ the app's business comes from a live bus host only (1.374452ms)
✔ runApp leaves the terminal as it found it on q, end of input, SIGTERM, SIGINT, SIGHUP and an error, the first frame's included (7.771707ms)
✔ draw is pure: same state, same frame (0.945961ms)
✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (35.738873ms)
✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (31.200288ms)
✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (23.088521ms)
✔ decide prints a declining choice as declining (22.152697ms)
✔ an unknown outcome is reported once and never resent (17.664246ms)
✔ a decision closed before the answer arrives exits 2 and points at its trail (19.12664ms)
✔ a prefix that matches two open decisions exits 2 and resolves neither (20.045416ms)
✔ without --business a command uses the live host's business, and a stale host.json is not a host (19.969845ms)
✔ every human command refuses inside an agent run before it touches the bus (18.777842ms)
✔ usage errors exit 4; no business and no host is a usage error (20.997088ms)
✔ agents and tasks print through the broker (18.901526ms)
✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (2.636662ms)
✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (76.567871ms)
✔ trackers come from the tracker.* variables of the one project that names a tracker project (119.573988ms)
✔ with two projects, the one that sets tracker.project supplies the tracker and the other is no conflict (59.154242ms)
✔ two projects that each name a tracker project refuse, since the boot shape holds one (55.842043ms)
✔ a business without tracker.baseUrl gets no trackers entry (61.63999ms)
✔ an unknown business and a broken system config refuse with exit 3 (91.97515ms)
✔ follow prints an unsolicited message after plain talk returned (48.59522ms)
✔ talk --follow continues after reply and prints a later unsolicited message (46.078265ms)
✔ inbox lists all unread human messages under decisions, does not deliver or read them (388.936882ms)
✔ follow refuses agent context and bad arguments (10.874554ms)
✔ follow Ctrl-C wakes the poll sleep and removes its handler; transport errors also clean up (12.49567ms)
✔ plain talk leaves the process SIGINT handlers unchanged (11.741403ms)
✔ empty views say so (1.084984ms)
✔ the trail keeps the broker's order and names a decision's task without its rows (1.449098ms)
✔ tasks print the tracker fields the snapshot carries (0.324599ms)
✔ the review loop: the coder pushes and asks, the reviewer fetches and approves, and the task's trail holds each step (599.489395ms)
✔ refusals: no action, non-fast-forward, an unknown commit or request, and a push for another task moves only that task's ref (397.704181ms)
✔ the size cap refuses a push before any byte (180.338435ms)
✔ a fetch over the cap refuses before any byte reaches the reviewer (409.779723ms)
✔ the socket refuses a request with no capability, an unknown one, and a bad request line (189.404458ms)
✔ a rigged clone runs nothing: hooks, fsmonitor, a filter, a gitlink and a symlinked .git/config leave no marker (341.200626ms)
✔ an identity with no role, or of another business, refuses not-a-role and moves no ref (305.832007ms)
✔ a review of a project the business no longer has refuses unknown-project (226.339594ms)
✔ past maxActive connections the socket refuses git-busy, and a hung-up connection frees its slot (150.335156ms)
✔ close() answers host-closing to a connection still sending its request line (151.033922ms)
✔ a bundle that trickles in refuses body-timeout at its deadline and frees its slot (573.293435ms)
✔ a reviewer gone before its bundle was sent is filed fetch-aborted, not as the host's fault (528.643759ms)
✔ a reviewer that hangs up the moment it has the whole bundle was served, not aborted (695.033182ms)
✔ send: a close after every byte left is served; one that fails a queued write is fetch-aborted (42.073341ms)
✔ a bundle with no bytes for bodyIdle refuses body-timeout long before its deadline (470.55894ms)
✔ send: a close once the loop is past its last chunk is served, and a failed write there is fetch-aborted (45.364464ms)
✔ letGo waits for an answer to leave for at most its cap, then destroys the socket (302.399856ms)
✔ a record that fails and can't be undone refuses rollback-failed, which the host logs (406.296601ms)
✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (981.345635ms)
✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (193.361313ms)
✔ a second host for the same data root refuses with exit 3 while the first runs (92.94006ms)
✔ two bus starts racing for one data root leave one host running; the other refuses that a host already runs (537.266114ms)
✔ a host takes over a host.lock its dead host left, and puts back a live claim that replaced it meanwhile (1.848804ms)
✔ a host.lock that goes between the check and the read is tried again; one that can't be parsed still refuses as unreadable (1.167912ms)
✔ a live host's lock stays, however old the file is (0.751121ms)
✔ a host.lock left by a host killed with SIGKILL doesn't stop the next bus start (835.71707ms)
✔ while a host boots, a reader sees no host: the human commands still ask for --business, stop and status find none (243.306814ms)
✔ a host started before host.lock existed, holding only host.json, still refuses a second start, which leaves no lock (37.827168ms)
✔ a broker fork that throws leaves no host.lock and no host.json (38.184922ms)
✔ a broker send that throws leaves no host.lock and no host.json (37.128017ms)
✔ a broker that misses its boot wait leaves no host.lock and no host.json (40.998687ms)
✔ a notifier fork that throws stops the broker and leaves no host.lock and no host.json (99.718839ms)
✔ a notifier send that throws stops the broker and leaves no host.lock and no host.json (107.631642ms)
✔ a boot reply with no reader for the business refuses with exit 3 naming it, before the notifier starts, and leaves no host.lock and no live broker (100.668983ms)
✔ a boot reply with no reader for the business refuses with exit 3 naming it, with no notifier, and leaves no host.lock and no live broker (93.394ms)
✔ a host.json that can't be written stops both children and leaves no host.lock (218.450091ms)
✔ an error event on the broker before it replies fails the start at once, and leaves no host.lock and no live child (43.887179ms)
✔ an error event on the notifier before it replies fails the start at once, and leaves no host.lock and no live child (100.136878ms)
✔ an error event on the broker while the notifier starts fails the start once the notifier replies (249.692882ms)
✔ an error event on the broker once the host is up stops the host with exit 1, and leaves no host.lock and no host.json (249.532964ms)
✔ an error event on the notifier once the host is up stops the host with exit 1, and leaves no host.lock and no host.json (264.191528ms)
✔ a broker reply that misses the wait breaks the channel: the late reply answers nothing, later requests refuse, the host exits 1 (1103.778306ms)
✔ a broker reply with another request's id, or none, breaks the channel and the host exits 1 (194.21146ms)
✔ a broker reply with no request waiting breaks the channel and the host exits 1 (94.166327ms)
✔ a notifier that refuses stops the broker and the host refuses with exit 3 (169.803145ms)
✔ a notifier that refuses after the broker died still refuses with exit 3, without a send to the dead broker (167.096847ms)
✔ a notifier that dies before it replies, after the broker died, still refuses, without a send to the dead broker (93.964289ms)
✔ a close send that fails with EPIPE after the notifier refuses still gives the notifier's refusal, exit 3 (151.504923ms)
✔ a close send that fails with EPIPE after the notifier dies unanswered still gives the notifier's error (87.015545ms)
✔ close() whose stop and close sends fail with EPIPE still finishes, with exit 1 (188.799999ms)
✔ watchChildren reports a child that died before it was called, and one that dies later (28.269286ms)
✔ bus stop refuses to signal a live pid that is not a bus host (203.002829ms)
✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (322.940126ms)
✔ bus start refuses with exit 3 without a notifier config (134.010318ms)
✔ bus start runs until bus stop; status reports it while it runs (744.256824ms)
✔ bus-service.sh renders the unit and installs it into a given directory (66.48246ms)
✔ bus-service.sh pins the claude binary, refuses one the unit can't carry, and renders without one (87.274248ms)
✔ bus-service.sh refuses a directory pin and keeps a symlink default unresolved (20.814288ms)
✔ bus-service.sh refuses a default claude without the execute bit, or in a PATH dir with a space (13.368898ms)
✔ bus start --pm: the launcher instance (the chief of staff) runs under its own PID namespace, registered, with a manifest (1346.4959ms)
✔ the chief of staff launches a coder through its launch tool; the coder answers; refusals name their code (508.605067ms)
✔ each launch makes sure the workspace has a clone of the project: cloned, fast-forwarded, or fetched and logged (3672.4744ms)
✔ a project clone that can't be made refuses checkout-failed, from --chief and from a session, with no run and no clone left (143.597981ms)
✔ a runner that stops at once ends its launch with the runner's reason (221.551812ms)
✔ a Claude Code launch with no session credential refuses before it starts (115.463598ms)
✔ a pi launch needs no session credential: pi logs in through its own ENV_ALLOW variables (222.486237ms)
✔ a runner killed from the host ends its launch as killed, not failed (256.497589ms)
✔ a missing or failing bwrap refuses sandbox-unavailable before anything starts; there is no unconfined fallback (114.122135ms)
✔ a real Claude Code coder's shell can't read a stand-in secret or anything else its sandbox doesn't hold (1332.984708ms)
✔ a host that died hard leaves its sessions to the next host, which kills them and ends their runs so the role can be launched again (636.743917ms)
✔ a host that died hard leaves its sessions to the next host, which finds one already exited (23) and ends their runs so the role can be launched again (3592.303557ms)
✔ a malformed sessions.json refuses the host start with exit 3 and stays as it was (93.151042ms)
✔ an over-long launch request is refused at once, not at the 10 s idle timeout (94.826346ms)
✔ a launch client that never closes its side doesn't hold the host's close (93.829958ms)
✔ a runner that ignores SIGTERM is killed when the host closes (1216.10315ms)
✔ claudeVersion asks MOSAIC_CLAUDE_BIN when set, else claude on PATH (4.930876ms)
✔ zoned uses the IANA zone across DST (30.505883ms)
✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (32.165556ms)
✔ two blocking decisions get two DMs with different nonces (21.183486ms)
✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.449784ms)
✔ a failed DM is journaled, backs off, and is retried until it lands (27.335718ms)
✔ five definite refusals stop a DM: one gave-up line, one log line, and a restart keeps the count (29.781901ms)
✔ 429s, 5xx-style unknowns and refusals without a status never count toward the limit (29.763338ms)
✔ a crash between the fifth refusal and its gave-up line: the next poll appends it and sends nothing (20.295208ms)
✔ polled every POLL_MS against a permanent 403, a DM is sent at 0, 30, 60, 90 and 120 min and gives up only then (153.433737ms)
✔ a restart after the second refusal does not send before that refusal's 30 min are up (72.01007ms)
✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (21.646608ms)
✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (12.690625ms)
✔ an inbox read failure is logged and the next poll retries (1.218455ms)
✔ no Discord id reaches the journal or the log (13.707228ms)
✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (3.327947ms)
✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (3.29716ms)
✔ the journal: a whole file that is one torn line truncates to empty (1.787397ms)
✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (0.592322ms)
✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (0.671195ms)
✔ the journal: a line with a wrong type refuses with exit 3 and names the field (2.897815ms)
✔ the journal: a symlinked directory refuses and says it is a link (0.446215ms)
✔ the journal: a dangling directory link, a parent that is a file and a journal that is a directory each refuse with exit 3 (0.611818ms)
✔ the journal: an append after the file was swapped for a symlink refuses and writes nothing through it (0.862738ms)
✔ the journal: a directory it cannot write or create refuses with exit 3 and names the path (0.733768ms)
✔ digest content stays within Discord's 2000 characters (0.41042ms)
✔ runLoop never overlaps ticks and stops after the one in flight (111.120641ms)
✔ shared copy: npm ci, pushed reachability, readonly/existing copy, webui delegation (688.486048ms)
✔ wrapper preserves Q81 lease and marker, rejects foreign/symlink; check uses effective roots (536.085067ms)
✔ bus unit copy/pm pins, install refuses all busy states and failed reads, drop-in refusal (528.478209ms)
✔ missing package manifest refuses before npm; npm failure removes the unpublished temp copy (352.959961ms)
✔ real pinned install loads bus and Console; same-copy mosaic talk passes proof, other trees refuse (4098.605397ms)
✔ bus status reads effective copy/pm/drop-ins without writes; check refuses an overridden ExecStart (575.976938ms)
✔ drop-ins in either override field and malformed metadata refuse; uninstall also checks --no-reload (197.548476ms)
✔ install never shows a template name on systemd 261; template drop-ins still refuse (187.50017ms)
✔ unlisted instance drop-in is found in another unit lookup directory; path discovery errors refuse (186.052321ms)
✔ inherited mosaic-.service.d override refuses without changing the unit (204.842172ms)
✔ inherited service.d override refuses without changing the unit (210.536185ms)
✔ inherited drop-in unreadable directory or file refuses before writing (169.111231ms)
✔ talk --tester sends a [mos test] message as tester from inside an agent and prints the role's reply (40.0176ms)
✔ talk --tester refuses text without the [mos test] prefix before any call (19.256361ms)
✔ the read verbs take --tester inside an agent, and the trail shows the tester as the sender (29.776371ms)
✔ decide, stop and launches take no --tester (20.084843ms)
✔ the tester transport runs tester-cli.mjs, and the tester's refusals exit 3 (1.780937ms)
✔ with no stand-in transport, --tester runs tester-cli.mjs on the data root's socket (1.377433ms)
task.close {} answered: invalid-request; fake saw 18 requests, first GET /info 200, GET /projects/1 200, GET /projects/1/views 200
task.close on a missing task answered: task-not-found; it made GET /tasks/999 404
✔ bootConfig trackers reach the S3 adapter in the real broker child, which goes ready against a fake Vikunja (378.766035ms)
✔ the transport writes {business, verb, args} to the child and reads its JSON (56.140433ms)
✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2264.185677ms)
✔ busExit and refuseInsideAgent (0.610863ms)
✔ talk sends a REQUEST, prints and reads everything that arrives, and stops at the reply (66.06505ms)
✔ talk --wait 0 only sends; no reply within --wait exits 1 and says where it will show (1028.065757ms)
✔ a late reply the Console has listed still reaches the next talk (2033.842939ms)
✔ talk, stop and launches off/on refuse inside an agent run; bad arguments are usage errors (17.350045ms)
✔ launches off and on go to the broker and change the business's launch state (14.66282ms)
✔ launches list reads sessions.json and marks a stale entry; stop reports it and refuses a non-runner (14.528604ms)
ℹ tests 187
ℹ suites 0
ℹ pass 187
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 14369.811614

Some files were not shown because too many files have changed in this diff Show More