# Row 142 (#1636): mosaic talk prints only the reply to its own message Seat: Researcher. Reviewer: Filbert. Gate owner: Sage. Brief: `docs/plans/2026-10-15_q167-north-star-rows.md`, section "mosaic talk prints only the reply to its own message". **Base: 8e2c5c2d0** (detached worktree `/mnt/storage/scratch/researcher/q142`). Canonical HEAD was 10d4b526a at packet time; the five commits after 8e2c5c2d0 are docs and queue only (`git diff --name-only 8e2c5c2d0 HEAD -- packages scripts docs/TOOLS.md package.json` is empty). I first built on e37d6e583; HEAD had moved and `packages/cli/src/cli.mjs` changed (row 144), so I rebased my diff onto 8e2c5c2d0 before any mutant or gate run. It applied without conflict. `row142.patch` is a diff of my changes only: 4 files, +43 −24. It applies with `git apply --cached --check` on 8e2c5c2d0 and round-trips to the candidate tree `91503e62b8e0ce7b1895ffb7f3b1bbe534b52bcb`. `candidate-manifest.sha256` lists the 4 files. Nothing is pushed, and no live store, unit, credential or `~/.mosaic` file was touched. I hold no Gitea credential; the in-review move and the review request are Sage's. ## What the row does `packages/cli/src/cli.mjs`, in the `talk` receive loop (one added line): ```js if (!follow && m.in_reply_to !== sent.id) continue; ``` - Without `--follow`, `talk` prints and marks read only the message whose `in_reply_to` is the one it sent. Every other message is skipped before the print and before `message.read`. - `--follow` and `mosaic follow` (`receiveOnly`, which sets `follow`) keep today's behaviour: they print and read everything until Ctrl-C. - The timeout error now ends "…it may still come, and mosaic inbox shows it". It used to say "the next mosaic talk shows it", which is false now: a later `talk` skips a late reply to an earlier message. ### Consequence to know about (not a defect, the brief asks for it) `message.receive` delivers each message once (`packages/bus/src/broker.mjs:1258`, it selects messages with no `delivered` or `read` row). A message `talk` skips is therefore **delivered but unread**: `mosaic inbox` lists it (its unread count is "no read row", broker.mjs:1509), but `mosaic follow` will not print it, because `follow` only receives undelivered messages. So the place a skipped message shows up is `mosaic inbox`, which is what the brief says ("leaves the rest unread for `mosaic inbox`"), and that is why the timeout message names `inbox` only and not `follow`. A late reply that arrives after `talk` has exited and before any other receive is still undelivered, so `follow` would print it too. I did not write a test that locks in the `follow` limitation. No bus change was made or needed. ## Files | File | Change | |---|---| | `packages/cli/src/cli.mjs` | the filter line, the timeout message, the comment above `talk` | | `packages/cli/tests/verbs.test.mjs` | three tests rewritten (below) | | `docs/TOOLS.md` | the `talk` sentence in the host-commands paragraph (listed in the brief) | | `packages/cli/README.md` | the `talk` bullet. **Outside the brief's listed files**; added because it described the old behaviour ("prints every message that arrives and marks it read", "the next `mosaic talk` prints it") and would now be wrong. Sage ruled it stays in (sage-0388). | ### Tests (`packages/cli/tests/verbs.test.mjs`) 1. "talk sends a REQUEST, prints and reads only the reply to it, and leaves other messages unread" (was "prints and reads everything…"). During the wait a cto INFO note and an unsolicited coder INFO note arrive before the coder's reply. Asserts: neither note is printed, the reply is printed, exactly one `message.read` call, and `mosaic inbox` lists both notes as "messages: 2 unread". It runs with `--wait 5` so a reply that is never printed fails an assertion instead of waiting out the default 900 s. 2. The timeout test asserts the new message text. 3. "a late reply the Console has listed stays unread for mosaic inbox, and the next talk skips it" (was "…still reaches the next talk"). The next `talk` does not print the late reply, makes no `message.read` call, and `mosaic inbox` lists the reply as unread. The Console reply-view assertion (`s.read("replies", {id})`) is kept. `follow.test.mjs` is unchanged; its two cases ("talk --follow continues after reply…", "follow prints an unsolicited message after plain talk returned") guard `--follow` and `follow`. `tester.test.mjs` is unchanged and passes. ## Mutants `mutants.sh `, started 2026-10-15T22:39:03Z, results in `mutants-run.txt`, one `.out`, `.rc` and `.patch` per mutant in `mutants/`. Three controls (verbs, follow and tester tests on the unmutated candidate) pass 6/6 each. **All 6 mutants fail their tests**, each by a named assertion. The worktree was restored after the run (`git status` shows the same 4 files). | Mutant | What it breaks | Killed by | |---|---|---| | m1 | the filter line removed, so talk prints and reads everything again | verbs: the other-messages test, the late-reply test | | m2 | the filter also applies under `--follow` and `follow` | follow.test.mjs: both cases | | m3 | the filter inverted, so only the other messages print | verbs: the other-messages test, the late-reply test | | m4 | only the reply is printed but every message is marked read | verbs: the other-messages test, the late-reply test | | m5 | the timeout message goes back to "the next mosaic talk shows it" | verbs: the timeout test | | m6 | any reply (any non-empty `in_reply_to`) is printed, so a late reply to an earlier message shows | verbs: the late-reply test | **Correction to my first mutant run.** The first run (22:31:46Z) killed m1, m2, m4, m5 and m6 by assertion, but m3 ended with exit 124: the 400 s timeout. The inverted filter never prints the reply, and the first test waited out `talk`'s default 900 s, so the test hung and did not fail an assertion. A timeout is weak evidence of a kill, so I gave test 1 `--wait 5`, confirmed the cli verbs test still passes (6/6), deleted that run's output, and reran all 9 runs. m3 now fails two tests by assertion. The table is from the second run. ## Gate `suites.sh `, 2026-10-15T22:39:44Z to 22:43:18Z (`gate-started.txt`, `gate-finished.txt`, `suites-run.txt`, output per suite in `suites/`). All rc=0. It runs the cli suite and **every** `scripts/test-*.sh` the brief names (10 of them), plus the other node suites: | Suite | Result | |---|---| | cli | 206/206 | | business | 101/101 | | bus | 214/214 | | plugins | 21/21 | | tasks | 97/97 | | webui | 113/113 | | harness | 90/90 | | seat | 135/135 | | scripts/test-auth.sh | 15 passed, 0 failed | | scripts/test-conductor.sh | 17 passed, 0 failed | | scripts/test-config.sh | 24 passed, 0 failed | | scripts/test-discord.sh | 78 passed, 0 failed | | scripts/test-extension-package.sh | 18 passed, 0 failed | | scripts/test-foundation.sh | 44 passed, 0 failed | | scripts/test-land-work.sh | 84 passed, 0 failed | | scripts/test-queue.sh | 28 passed, 0 failed | | scripts/test-release.sh | 14 passed, 0 failed | | scripts/test-task.sh | 98 passed, 0 failed | No flake (#258, #261, #262, #263) recurred (no match in any suite output). The slot was Sage's "slot clear" (sage-0392, 22:31:24Z); a busy check by exit status, `/proc` cwd links only, read CLEAR at 22:39:03Z and again before the gate. Docker: the gate left one network, `q142_default`, which is mine; I removed it with `docker network rm q142_default` and the network list now equals the before-list. No container of mine is left. ## Follow-up, not in this row: stale "the next mosaic talk prints it" text These say a reply still reaches the next `mosaic talk`. After this row that is false. They are outside the row's owned files; I did not touch them. To list on #1636: - `packages/webui/src/public/bus.js:261` (a comment) and `:280` (the Console's send-section text: "…never marks one delivered or read, so the next mosaic talk in a terminal still prints each one."). `bus.js` is under `public/`, so it needs decision 123 later. - `packages/webui/tests/send-browser.test.mjs:105` asserts that exact send-section text. - `packages/bus/src/broker.mjs:1519`, a comment about the `replies` read. - `docs/plans/2026-10-11_console-reply-view.md:28`. ## Not covered - A live `mosaic talk` against a running bus. The evidence is the in-process broker tests; the row goes live in the re-pin after the 3e window. - Row 128's named threads (out of scope per the brief) and the bus (unchanged). - Carried, unrelated to this row: the two row 132 non-blocking notes (`change_organization` "replaces" wording versus per-instance merge; a revision-only call refused by the writer, not the schema), for the next relevant row.