Files
jason.woltje 4ff4f44c98 lead: commit researcher's untracked evidence (1406 files: queue-101 queue-105 queue-128 queue-134 queue-143 queue-148 queue-183 queue-228)
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.
2026-11-02 15:05:14 -06:00

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's d2 (p.repo=? OR 1) makes it match. mutants.sh has t7-any-project for that edit. Dewey's d1 (t.instance=? OR 1) is equivalent (thread.create allows one threaded instance), noted in mutants.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 with send_message in_reply_to the 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.mjs gains a third test pinning it (mutants r8 to r10). 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.md is gone.
  • Round 2 results: see "Round 2 results" at the end of this file.

What it does

  1. A human save of spec/north-star.md through row 148's project.file.save makes the bus store one notice per pm and cto instance. Sender _bus, run bus, class RESULT, in no thread. The body names the project, the new commit and the editor's base commit, says to read the change with read_spec, and says where the answer goes (the chief of staff, in_reply_to the notice id).
  2. pm and cto answer once: send_message class BLOCK naming each conflict, or class INFO beginning "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.
  3. 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.
  4. Only that verb sends a notice. Docs saves, refused or stale saves, an agent's record.write commit and a git commit outside the Console send none.

Design choices and their cost

  • Hook placement. The notice is written in the project.file.save branch of the broker, after the commit, in its own transaction. If it cannot be stored, the save answers outcome-unknown and the commit stands (the Console reloads to the new commit, as after record.write). No other code path calls #specNotice (broker.mjs:2335; hook at 1252-1255).
  • No new event kind. The events.kind CHECK list in schema.sql would need a migration. The notice and the answers are messages rows. 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_match refuses 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. #threadFor walks 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; hook 301-305). Trigger delivery_same_thread then 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 shipped messaging lists (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 what thread.create means.
  • BLOCK checks (#checkBlock, 2368): sender must be a pm or cto instance (block-sender), recipient the chief-of-staff instance (block-recipient), and in_reply_to must name a _bus spec 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.send already 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-unknown with 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 _bus is 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 in gate-r1/ and gate-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-auth 15, test-conductor 17, test-config 24, test-discord 78, test-extension-package 18, test-foundation 44, test-land-work 86, test-queue 28, test-release 14, test-task 98: all rc 0. The Docker network q143r1_default was 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 left b5 (notice not routed) and b7 (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-thread survives and is equivalent. t3 removes the body test (m.body.startsWith(SPEC_NOTICE)) and keeps the from_role test: the only other _bus body, row 146's launch notice (#notice), starts "Message from", so the regex in #specThread cannot match it at that offset and the lookup returns null. The from_role test stays as defense in depth (Dewey F2: I had first described this the wrong way round).
  • Not run: scripts/test-*.sh on 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-review 12/12, spec-review-contracts 3/3, tools 20/20, chief-of-staff 10/10, assistant 11/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-auth 15, test-conductor 17, test-config 24, test-discord 78, test-extension-package 18, test-foundation 44, test-land-work 86, test-queue 28, test-release 14, test-task 98: all rc 0. Network list before and after identical (q143r1_default removed); no q143r1 container 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, r10 each fail the chief-of-staff contract test. The one survivor is still t3-any-bus-notice-opens-thread, equivalent for the reason given above. Dewey's d1 is equivalent (noted in mutants.sh). Sources were restored by the script's trap; worktree status shows only the 11 candidate paths.
  • Base: origin/refactor was 8fcd0887e when 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.mjs untouched: no new business vocabulary was needed.

Process notes

  • Listed /mnt/storage/scratch/sage-gate-template once (names only, no file read) while looking for the gate template before I understood it is Sage-only. Contents unread; I wrote my own suites.sh from row 148's.
  • No credentials held or exported; no commit, no push; SESSIONS.md line appended, uncommitted.