{"body":"## Filbert review: row 177 round 1 (#1675), changes\n\nRequest 29343. Candidate manifest `9aaabae85984feb287381e2f4e548d1345b7069280c486d2a95725c735845971` on base `c49de08e8` (candidate commit 1971683e14a6). Design 99ce5780...2660f0, approved in 29333.\n\n**Verdict: changes.** One finding (F1) blocks. F2 and F3 are code and test text, so decision 137 doesn't treat them as doc-only; they're small and can ride the same round. N1 and N2 are notes.\n\n### What I ran\n\n- Built `~/filbert-scratch/r177-r1/wt`: `c49de08e8`, then `git apply --index build.patch`.\n - `sha256sum -c` on the manifest: 16/16 OK, and the manifest file hashes to `9aaabae8...845971`.\n - The cached diff's patch-id `7e903ddf9fc5` equals that of `git diff c49de08e8 1971683e14a6`.\n- Base drift: `git log c49de08e8..refactor` and `..origin/refactor` on the 16 paths are both empty.\n- Hash pins and path readers over packages/*/tests and scripts/test-*.sh: no 64-hex pin names a manifest path (channel-proof pins human-cli.mjs, human.mjs, transport.mjs and webui bus.mjs, none of them in the manifest). The README readers are bus-browser (a webui README phrase, still present) and pinned-copy (existence only). That agrees with Dewey's grep.\n- Tests, one file at a time, TMPDIR `~/fs177`, slot grant sage-1158 after my /proc CLEAR: 9 files, 66/66 pass, 0 fail, 0 skip, every rc 0 (02:45:29Z-02:45:44Z). Counts are awk sums over the spec logs.\n - bus: tester-threads 9, tester-writes 8, tester 14, messaging 13, channel-proof 2.\n - cli: tester-reads 6.\n - webui: tester-listener 10, tester-cli 2, tester-browser 2.\n- One probe test outside the tree, for F1 (below). No mutants.\n- Dewey's M1-M14 (M13 on the fixed test), bus 445/445, webui 257/258 (1 skip), conversation 182/182, cli 396/396 on the rerun and the 10 test-*.sh runs are their receipts, not mine.\n\n### F1 (blocks): a tester-thread run can still put a decision in the human's inbox\n\nBrief point 3 says replies on a tester thread never reach the human's inbox, and the guard at broker.mjs:1849-1852 stops the run's own `message.send` to `human`. `decision.raise` isn't covered.\n\n- broker.mjs:589-601: an action outside `withinRole` and `crossRole` classifies `gated`.\n- broker.mjs:1666: a gated, uncited raise routes `'human'`.\n- broker.mjs:1671: the only check is `#checkMessaging(route, s.role)`, human to assistant, and the shipped roles/assistant.json lists `receiveFrom: [\"human\", ...]`, so it passes.\n- DESIGN.md:82-84 says decisions \"need `crossRole` authority\" and are \"out of reach\". That's wrong: `crossRole: []` makes every other action gated, and gated raises go to the human.\n- roles/assistant.md:30-31 tells the assistant to \"raise a decision ... for anything outside\" its grants, so this is the path the contract points it to mid-test.\n\nProbe, same business shape as tester-threads.test.mjs (shipped assistant messaging, authority `withinRole ['message.send']`, `crossRole []`): the tester creates a project and a thread, sends `probe`, an assistant run launches on that thread and claims, then calls `decision.raise` with `action: 'task.create'`, two options, `blocking: false`. Output:\n\n```\nROUTE human gated\nINBOX-BEFORE []\nINBOX-AFTER [{\"seq\":1,...,\"raised_by_role\":\"assistant\",\"raised_by_run\":\"a-run\",\"class\":\"gated\",\"action\":\"task.create\",\"route_to\":\"human\",...}]\n```\n\nThe human's `inbox` gets a test decision with nothing marking it as tester traffic, and resolving it messages the tester-thread run as the human. The bus README row 177 paragraph (:614) says replies \"never reach the human's inbox\" and lists the chief of staff as the only other reach, so it's incomplete too.\n\nFix I'd suggest: in `decision.raise`, refuse a run of a tester-owned thread when `route === 'human'` (the same `tester-thread-human` code, or a decision-specific one), using the `testerThread` test from message.send. Add a T4-style case to tester-threads.test.mjs that a gated raise from the tester-thread run refuses while the human-thread run's raise still routes to the human. Then fix DESIGN.md:82-84 in the BUILD.md deviations and the README sentence. An arbitrated raise that routes to the chief stays N1's chief reach, so the README can say that.\n\n### F2: the dispatch comment doesn't name row 177's writes\n\nbroker.mjs:1294-1296 says the tester view \"also carries the writes name.set and project.create\" and that \"neither branch checks `reader`\". Row 177 adds `thread.create`, `thread.open`, `thread.send` and `assistant.stop` under the same `reader` view, plus own-thread `thread.messages`; each passes a tester by `if (!s.tester) this.#human(s)`. The comment should name them. It's a .mjs file, so it isn't doc-only.\n\n### F3: tester-writes.test.mjs still counts seven reads\n\n:60 is titled \"each of the seven reads\", and READS now has six entries since `thread.list` moved out; :2 says \"makes seven writes\", which also doesn't cover the five new verbs the pin adds. Test text, not doc-only.\n\n### Checked against the brief and 29333\n\n- Points 1-2: owner `tester` via `#owner(s)` in `#thread`, create, list and the open `by`; `from_role 'tester'`, `from_run 'tester-cli'`, `request: null`, no `human.input` (T1, T2, my run).\n- Point 3: replies route to `tester` with the lists read as the human only inside a tester thread (broker.mjs:1849-1852, T3, T4). F1 is the gap.\n- Point 4: `assistant.stop` scopes `#threadHolders` to the owner and records `by 'tester'` and \"stopped by the tester\"; the human's case keeps its old `by` (T5).\n- Point 6: TESTER_CHAT_WRITE admits only the four chat write routes; the tester branch checks the listener's own Origin, then Sec-Fetch-Site, and JSON; 7332 still refuses `x-mosaic-tester` (W1-W4).\n- Call (a): the boot check refuses `human: 'tester'` (T6). Call (b): cli.mjs:359-360 and thread.mjs:110-111 are comments only. Call (c): the name leak is in the README and T1:87.\n- 29333 N1 is in the README; N2 is in the webui README tester section.\n\n### Notes\n\n- **N1** (pre-existing, not on changed lines): `#threadFor` (broker.mjs:322-347) follows an `in_reply_to` chain to a message's thread with no owner check. A tester `message.send` to a threaded instance, in reply to a message in a human thread, would land in that thread. With the shipped lists `#checkMessaging` refuses the tester first (message-sender-refused, T3), so only a definition without lists reaches it. Follow-up.\n- **N2**: Dewey's deviation 4 (chat.mjs's \"human transport\" strings) stays a follow-up.\n"}