Unclaimed files left in the canonical checkout. Inventory: canonical-cleanup-inventory.md (commit-as-evidence groups). detect-secrets scan clean on 2026-11-02: hits are sha256 pins, git object ids, test sentinels already in tracked tests and unit-file lines.
14 KiB
Row 143 (#1637): agents review Jason's spec commits, and the assistant summarizes
Owner: Researcher. Reviewer: Dewey (approves on #1637 naming the candidate manifest). Gate owner: Sage.
Built 2026-10-18 in worktree /mnt/storage/scratch/researcher/r143-wt, detached at base 8517e5f45
(the brief is pinned at blob 8a3f0acca2 since rev 1633, commit c0af7f248, docs/plans/2026-10-15_q167-rows-r3.md,
section "Agents review Jason's spec commits, and the assistant summarizes"; I built against 3fec535fd2. The
heading-to-next-heading text of that section hashes the same in both blobs, and c0af7f248 changed only a bullet in the row 149 section, per sage-0652). Files: changed-paths.txt (10 in round 1, 11 in round 2), hashes in
candidate-manifest.sha256, full diff with the two new test files in row143.patch
(round 2: 377 insertions, 4 deletions across the 11 files, new test files included).
Round 2 (Dewey's changes, comment 28853, rev 1642; sage-0661)
Base 8fcd0887e (queue c550efa27 plus the brief re-pin). Brief blob 606f7e09052f (commit 8fcd0887e, rev 1645).
git diff 8517e5f45 8fcd0887e on the manifest paths is empty, so nothing drifted. The candidate is now 11 files:
roles/chief-of-staff.md is added (changed-paths.txt, candidate-manifest.sha256, row143.patch regenerated).
- N1 (blocking). New test "a more recently opened thread in another project does not take the relay": a second
bus project, the thread the user opened last, and the relay still lands in the stack project's thread. The
fixture's broker clones the business at construction, so the second project has no repo;
p.repo=?does not match it, and Dewey'sd2(p.repo=? OR 1)makes it match.mutants.shhast7-any-projectfor that edit. Dewey'sd1(t.instance=? OR 1)is equivalent (thread.create allows one threaded instance), noted inmutants.sh. The earlier "most recently opened" test is renamed "within the notice's project ...": it is within one project. - F5. One bullet in
roles/chief-of-staff.md, after the "Report to the assistant" bullet, in its own item so row 149's edits to the file merge on either side. It tells the chief to relay a BLOCK or an accept withsend_messagein_reply_tothe message that carried it, says the bus refuses a relay without that link, and that a BLOCK doesn't revert the commit.spec-review-contracts.test.mjsgains a third test pinning it (mutantsr8tor10). Whichever of 143 and 149 lands second rebases and reruns every touched test file. - F2, F3 are corrected in this file (below). F7: the blank line before the new bullet in
roles/pm.mdis gone. - Round 2 results: see "Round 2 results" at the end of this file.
What it does
- A human save of
spec/north-star.mdthrough row 148'sproject.file.savemakes the bus store one notice per pm and cto instance. Sender_bus, runbus, class RESULT, in no thread. The body names the project, the new commit and the editor's base commit, says to read the change withread_spec, and says where the answer goes (the chief of staff,in_reply_tothe notice id). - pm and cto answer once:
send_messageclassBLOCKnaming each conflict, or classINFObeginning "Accept". Their contracts (roles/pm.md,roles/cto.md) say to check against settled decisions and work in flight. A BLOCK does not revert the commit. - The chief of staff relays the result to the assistant; the assistant tells Jason in its own words
(
roles/assistant.md), and says the change stands. - Only that verb sends a notice. Docs saves, refused or stale saves, an agent's
record.writecommit and a git commit outside the Console send none.
Design choices and their cost
- Hook placement. The notice is written in the
project.file.savebranch of the broker, after the commit, in its own transaction. If it cannot be stored, the save answersoutcome-unknownand the commit stands (the Console reloads to the new commit, as afterrecord.write). No other code path calls#specNotice(broker.mjs:2335; hook at1252-1255). - No new event kind. The
events.kindCHECK list inschema.sqlwould need a migration. The notice and the answers aremessagesrows. Cost: see "Observability" below. - The BLOCK is unthreaded; the relay is threaded through the notice. My first design filed the BLOCK in
the assistant's thread. The schema trigger
message_threads_matchrefuses that: a message may join a thread only if the thread's instance is the message's sender or recipient, and a pm-to-chief message is neither. So a BLOCK reaches the chief of staff in no thread. The chief's relay to the assistant replies to the BLOCK.#threadForwalks the reply chain, and when it reaches a bus message whose body starts "The human saved spec/north-star.md", it files the relay in the user's most recently opened thread of that notice's project whose instance is the assistant (#specThread,2377-2389; hook301-305). Triggerdelivery_same_threadthen routes it to that thread's holder. - Limitation, stated plainly. If Jason has no thread in that project with the assistant instance, the
relay is refused
thread-required-reply-to-thread-message. The BLOCK still reaches the chief and the commit stands, but the assistant cannot hear of it. Tested ("with no thread ..."). I chose this over letting a message reach the assistant outside any thread, which REQ-ASSIST-2 and the assistant's shippedmessaginglists (human and chief-of-staff only) do not allow. If Jason wants the notice to open a thread, that is a follow-up row; it changes whatthread.createmeans. - BLOCK checks (
#checkBlock,2368): sender must be a pm or cto instance (block-sender), recipient the chief-of-staff instance (block-recipient), andin_reply_tomust name a_busspec notice addressed to that sender (block-reply-required). A role cannot forge a notice by pasting its text: the sender is checked as_bus.thread.sendalready refused BLOCK.
Precondition 6 (does landed code launch a pm or cto that is not running?)
No. The notice to a role no run holds waits and is delivered at the next claim (the same path as any
message to an unrun role). The broker launches nothing and does not tell launch.by: #specNotice does not
call #waiting or #notice (row 146's launch notice, sent only from message.send). A pm or cto starts only when the
chief of staff launches it. Test: "a notice to a role no run holds waits for a claim, and launches nobody
and tells nobody". If you want launch.by told, that is a small follow-up and a policy call, not part of this
brief; I did not add it.
The tools.mjs line (sage-0648)
packages/harness/src/tools.mjs:78 gains "BLOCK" at the end of MESSAGE_CLASSES. That is outside the
brief's owned list. Why it is needed: the send_message tool's class enum is what a pm or cto run can
offer a model. Without BLOCK in it, the model has no way to send the class the broker now accepts, and the
contract text that tells it to answer with a BLOCK would name a value the tool schema rejects. One token on
one line. Covered by packages/harness/tests/tools.test.mjs:33 ("send_message offers the BLOCK class,
beside the classes it already had"); mutant h1 removes it and that test fails.
Rows 149 (Dewey, raise_decision domain parameter) and 155 (end_run text) also touch tools.mjs, in
different hunks (MESSAGE_CLASSES here, raise_decision there). Row 149's harness test is the new
raise-domain.test.mjs, not tools.test.mjs (Dewey F3; I had first written that 149 edits tools.test.mjs).
Whichever of 143 and 149 lands second rebases onto the other and reruns every touched test file; my two hunks
are one line in tools.mjs and one appended test in tools.test.mjs.
Overlap with row 149 on role contracts
Row 149 edits roles/pm.md and roles/chief-of-staff.md and adds a proactive line to every roles/*.md. My additions are one new bullet
in each of roles/assistant.md, roles/cto.md, roles/pm.md and (round 2) roles/chief-of-staff.md, each its own list item; no existing
line is changed. A rebase should be a clean add on both sides. Whichever lands second reruns
packages/business/tests/spec-review-contracts.test.mjs and the existing contract tests.
Observability (for the live smoke, and the gap)
No new events, so the tester sees: pm's and cto's answers as action.allowed events (trail <chief key>);
the assistant's summary to the human through messages; and agents showing pm and cto working. The notice
itself is not visible to a tester (messages lists only rows to the human; trail needs a subject id).
Counting notices needs a read-only store query that Mos would have to allow. All of this is in
live-smoke-plan.md, with the TBD event names filled in. Nothing live was run (no live steps without Mos's go on
#259).
Tests
packages/bus/tests/spec-review.test.mjs(new, 12 after round 2; the round 1 list below, plus the other-project test): one notice per pm and cto per commit, each commit its own; none for a docs save, an agent record commit, a git commit outside the Console, a stale, invalid or agent save; waiting notice for an unrun role, delivered at claim, nobody launched or told; none for a business without pm or cto;outcome-unknownwith the commit standing when the notice cannot be stored; a BLOCK reaches the chief, the relay lands in the user's thread, the commit stands, an accept is an ordinary INFO; no thread: BLOCK reaches the chief, relay refused; relay picks the most recently opened thread; every BLOCK refusal (sender, recipient, reply required, another role's notice, another bus notice) leaves the message count unchanged; a forged copy of the notice text opens neither a BLOCK nor a thread; a reply to_busis refused.packages/business/tests/spec-review-contracts.test.mjs(new, 3 after round 2): the contract text of pm and cto, of the chief of staff (round 2) and of the assistant (the brief's text test).packages/harness/tests/tools.test.mjs(+1): the BLOCK class.
Results
Run in r143-wt at base 8517e5f45 with the candidate applied, during my slot (sage-0648):
- Gate (
suites.sh, output ingate-r1/andgate-r1.log): project 37, bus 343, cli 230, webui 143, business 115, mosaic 69, plugins 28, tasks 97, harness 109, seat 150, all pass.test-auth15,test-conductor17,test-config24,test-discord78,test-extension-package18,test-foundation44,test-land-work86,test-queue28,test-release14,test-task98: all rc 0. The Docker networkq143r1_defaultwas removed; networks before and after are identical; no containers left. - Caveat: the gate ran before I added three assertions to
spec-review.test.mjs(the forged-notice test, the delivery row and the base in the notice body, found by mutants). The final file passes 11/11 singly, in the worktree and in the canonical tree. I did not rerun the whole gate for a test-only change that nothing else reads. - Mutants (
mutants.sh,mutants-r1.log,mutants-r1b.log): controls pass (11, 20, 2). 29 mutants: 28 caught. First pass leftb5(notice not routed) andb7(base dropped from the body) alive; I added a delivery-row assertion and a base assertion and both now fail.t3-any-bus-notice-opens-threadsurvives and is equivalent.t3removes the body test (m.body.startsWith(SPEC_NOTICE)) and keeps thefrom_roletest: the only other_busbody, row 146's launch notice (#notice), starts "Message from", so the regex in#specThreadcannot match it at that offset and the lookup returns null. Thefrom_roletest stays as defense in depth (Dewey F2: I had first described this the wrong way round). - Not run:
scripts/test-*.shon the final test file only (shell scripts do not read it), and Sage's integration gate at landing.
Round 2 results
Run in r143-wt at base 8fcd0887e with the 11-file candidate, in the slot Sage cleared (sage-0663), 2026-10-18.
cmp shows the worktree files identical to the canonical ones, and the manifest hashes the canonical files.
- Singles (
r2-singles/):spec-review12/12,spec-review-contracts3/3,tools20/20,chief-of-staff10/10,assistant11/11. - Gate (
suites.sh,gate-r2/,gate-r2.log; 13:33:18Z to 13:37:06Z): project 37, bus 345, cli 230, webui 143, business 116, mosaic 69, plugins 28, tasks 97, harness 109, seat 150, all pass.test-auth15,test-conductor17,test-config24,test-discord78,test-extension-package18,test-foundation44,test-land-work86,test-queue28,test-release14,test-task98: all rc 0. Network list before and after identical (q143r1_defaultremoved); noq143r1container left. - Mutants (
mutants.sh,mutants-r2/,mutants-r2.log): controls pass (12, 20, 3). 36 mutants, 35 caught. New this round:t7-any-project(Dewey's d2) fails "a more recently opened thread in another project does not take the relay";r8,r9,r10each fail the chief-of-staff contract test. The one survivor is stillt3-any-bus-notice-opens-thread, equivalent for the reason given above. Dewey's d1 is equivalent (noted inmutants.sh). Sources were restored by the script's trap; worktree status shows only the 11 candidate paths. - Base: origin/refactor was
8fcd0887ewhen run, the base I built on, so no rebase.
Assumptions
- pm and cto instances start only through the chief of staff's launch; nothing here changes that.
- "The user's most recently opened thread" means the last
thread.open, else the newest created. - The pm/cto answer is one message each; the chief decides what the assistant hears.
vocabulary.mjsuntouched: no new business vocabulary was needed.
Process notes
- Listed
/mnt/storage/scratch/sage-gate-templateonce (names only, no file read) while looking for the gate template before I understood it is Sage-only. Contents unread; I wrote my ownsuites.shfrom row 148's. - No credentials held or exported; no commit, no push; SESSIONS.md line appended, uncommitted.