Files

9.5 KiB

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):

"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).

Round 2 (Filbert round 1, comment 28522, rev 1233)

F1 (blocking): roles/assistant.md duty 3 told the assistant to relay rulings "to the role it concerns", which sends to roles other than the chief of staff and contradicts REQ-ASSIST-1 and REQ-ASSIST-2. The line was copied from chief-of-staff.md. Fixed on the same base (c6570f142): the assistant relays each ruling verbatim to the chief of staff, naming whose ruling it is and which role it concerns, and doesn't send it to that role itself.

Pin: packages/business/tests/assistant.test.mjs, "the contract passes work to the chief of staff and directs no one", now asserts the new duty wording, "Don't send it to that role yourself", and doesNotMatch /to the role it concerns/.

Changed since round 1: roles/assistant.md and packages/business/tests/assistant.test.mjs only. The other 20 candidate files are byte-identical. row119.patch and candidate-manifest.sha256 are rebuilt; the patch applies to c6570f142 by git apply --cached --check, and no commit since touches a candidate path.

Run: assistant 7/7, role 13/13, chief-of-staff 10/10 (suites/round2-business-files.txt, single files, no slot). The round 1 suite outputs and mutants-run.txt still describe the other 20 files. I ran no new mutant for the new pin; the round 1 mutant m12 (assistant contract directs agents) still fails on its own check. A mutant for the new pin needs the slot and Sage's go.