Round 1 packet in agents/researcher/work/queue-132/: row132.patch (8 files, +162 -19) on b6a588cd2,
candidate manifest, build.md, 28 mutants killed, full gate green. No candidate change applied here.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
8.7 KiB
Row 132 (#1628): harness tools for ending a run and changing the organization file
Seat: Researcher. Reviewer: Filbert (broker authority, no sandbox change). Gate owner: Sage.
Requirements: REQ-ROLE-2, REQ-COS-1, REQ-LIMIT-1.
Brief: docs/plans/2026-10-14_prd-1.2-build.md, section "Harness tools for ending a run and changing the organization file".
Base: b6a588cd2 (detached worktree). Rows 119, 120, 122 and 123 are in it. No path under packages/harness, packages/bus or
packages/business changed between b6a588cd2 and canonical HEAD 4a34fb0d6 (checked with git diff --name-only; later commits are docs and queue only).
row132.patch is a diff of my changes only: 8 files, +162 −19. It applies with git apply --cached --check on b6a588cd2 and
round-trips to the candidate tree 85056b71653ac02634d1742d2e72e4f3588bcbbd.
candidate-manifest.sha256 lists the 8 files (packages/bus/src/broker.mjs among them), file sha256 ba644f8d97cfa88175fe0157d9f77523cee7e5821ddbd676b29aae46fa73cf07.
Nothing is committed in the worktree, nothing is pushed, and no live store, unit, credential or ~/.mosaic file was touched.
For the 3d re-pin window: the chief's bundle manifest
The chief of staff's bundle manifest lists both tools in typed and untyped is [], for both harnesses.
Test: packages/harness/tests/bundle.test.mjs:85, "a chief-of-staff bundle lists end_run and change_organization as typed and nothing untyped, for either harness".
It builds a bundle for the chief instance (definition chief-of-staff) with buildBundle and asserts, per harness:
| Harness | manifest.typed |
manifest.untyped |
|---|---|---|
pi |
send_message, launch, end_run, change_organization, raise_decision, list_agents, inbox, trail, list_tasks, get_task |
[] |
claude-code |
the same ten, each as mcp__mosaic__<name> (so mcp__mosaic__end_run, mcp__mosaic__change_organization) |
[] |
The same test checks the prompt's Bus tools: line and that the "Actions you hold that have no bus tool yet" line is absent.
tools.test.mjs:33 asserts untypedActions(chief, tools) is [] (it was ["role.end", "organization.change"] after row 123).
Controls and mutants m3b and m4b (below) fail this test if either tool goes missing from typed.
What the row does
end_run(packages/harness/src/tools.mjs:141): a session gets it when its resolvedlimits.authorityholdsrole.end. Verbrole.revoke, parametersroleandreason(1 to 500 characters), nodecision. The broker already refuses the caller's own run and the assistant's withrole-end-refused; the runner passes that code through (refused: role-end-refused).change_organization(tools.mjs:147): a session gets it when it holdsorganization.change. Verborganization.set, parametersrevision(64 hex characters, required),limits,models,instances. A stale revision fails withrevision-conflict.- Key list in one place, with a failing test.
ORGANIZATION_KEYSis exported fromtools.mjsfor the harness; the broker's own list atpackages/bus/src/broker.mjs:99is now exported too, andtools.test.mjs:64fails if the two differ (mutants m15, m16). - Bundle text (
bundle.mjs:92-96): the "only the human stops a run" bullet is now chosen by whether the session hasend_run. Arole.endholder is told to useend_runwith a reason and what is still the human's (mosaic stop, and arole.revokedecision for anything the broker refuses). Every other role keeps today's text byte for byte (testbundle.test.mjs:98). - README text in
packages/harness/README.mdandpackages/business/README.md.
Bus change: the export is the only one
packages/bus/src/broker.mjs changes by one word, const to export const, on the line that declares ORGANIZATION_KEYS (line 99; +1 −1).
No bus behaviour changes. No verb, check, schema, migration or message text in packages/bus differs. The bus suite is
191/191 on this candidate.
Out of scope: the chief cannot read the revision (row 134)
change_organization needs the file's current revision. The only read, the broker's limits verb, is human-only
(broker.mjs:729: if (!role && !s.human) fail("human-required")), and organization.set takes the keys at line 732 with revision required.
So today the tool works only when the chief already has the current revision from somewhere else. A stale or unknown
revision fails with revision-conflict (packages/business/src/organization.mjs:185), so change_organization fails closed.
This does not block the 3d window, which checks the manifest only.
The gap is row 134, queued at rev 1327, brief docs/plans/2026-10-15_organization-revision-read.md (commit 26f4f9701). Row 132 does not grow to cover it.
Other items I did not touch: P19 task 299 (outcome-unknown after the writer) is not part of this row; the reason control-character
decision from row 123 round 2 stands.
Tests
| File | Change |
|---|---|
packages/harness/tests/tools.test.mjs |
chief test renamed and its tool list updated, untypedActions back to [] (line 33); shape test (46); parity with the broker's list (64); non-grant roles and per-grant (69) |
packages/harness/tests/bundle.test.mjs |
chief Bus tools regex (74); typed/untyped for both harnesses (85); stop-line variants (98) |
packages/harness/tests/runner.test.mjs |
fixture gains chief and assistant, setup() takes as/organization; test 377 runs both tools through a real broker with a fake organization writer: {"revoked":true}, refused: role-end-refused for the self and assistant cases, the writer called with ["demo","limits.set",args,"chief-of-staff.chief"], refused: invalid-request for a key outside the list, refused: revision-conflict for a stale revision |
Mutants
mutants.sh <worktree> <outdir>, started 2026-10-15T18:00:05Z (mutants-started.txt), results in mutants-run.txt, one .out, .rc and .patch per mutant in mutants/.
Three controls (the tools, bundle and runner tests on the unmutated candidate) pass: 16/16, 11/11, 23/23.
All 28 mutants fail their tests. The worktree was restored after the run (git status shows the same 8 files).
| Mutant | What it breaks | Killed by |
|---|---|---|
| m1, m2 | end_run / change_organization for every role |
tools test (7 failures each) |
| m3, m3b / m4, m4b | either tool never offered (tools, bundle) | tools 3, bundle 3 / tools 3, bundle 2 |
| m5, m5b | end_run verb wrong (tools; runner) |
shape test; runner test 377 |
| m6 | end_run carries no action, so no grant is checked |
tools tests |
| m7, m7b | change_organization verb wrong (tools; runner) |
shape test; runner test 377 |
| m8 | change_organization carries no action, so no grant is checked |
tools tests |
| m9 | reason optional |
shape test |
| m10 | end_run takes a decision |
shape test |
| m11 | reason unbounded |
shape test |
| m12 | revision optional |
shape test |
| m13 | revision pattern dropped |
shape test |
| m14 | end_run description loses the own-run, assistant and role-end-refused sentence |
shape test |
| m15 | a key dropped from the harness list | shape test, parity test |
| m16 | a key added to the broker list | parity test |
| m17 | the bundle keeps the old stop line for the chief | stop-line test |
| m18 | the new stop line for every role | stop-line test, pi bundle test |
| m19, m20, m21 | the new text loses the reason requirement, the own-run and assistant sentence, or the "anything the broker refuses" wording | stop-line test |
| m22 | the runner drops the tool's arguments | runner test 377, capability test |
| m23 | the runner sends the tool name as the verb | runner test 377, capability test |
| m24 | the runner hides the broker's refusal code | runner test 377, capability test |
Gate
suites.sh <worktree> <outdir>, 2026-10-15T18:01:54Z to 18:03:33Z (gate-started.txt, gate-finished.txt, suites-run.txt, output per suite in suites/).
All rc=0; the full set, including the harness, bus and business suites the brief asks for:
| Suite | Result |
|---|---|
| business | 92/92 |
| bus | 191/191 |
| plugins | 21/21 |
| tasks | 97/97 |
| cli | 195/195 |
| webui | 112/112 |
| harness | 90/90 |
| seat | 132/132 |
| scripts/test-land-work.sh | 84 passed, 0 failed |
No flake (#258, #261, #262, #263) recurred. The gate left no Docker network (before and after docker network ls identical). A busy check (by exit status, /proc cwd links only) was clear at 2026-10-15T17:59:14Z before the run.
Not covered
- The live chief session: no session was launched. The evidence is the bundle test and the runner test against a real broker.
- Whether a model uses
end_runwell. The text tells it what to do; nothing tests the model.