Candidate on8e2c5c2d0: 4 files, tree91503e62b8. 6 of 6 mutants killed, 3 controls pass, cli suite and every scripts/test-*.sh green. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
105 lines
8.5 KiB
Markdown
105 lines
8.5 KiB
Markdown
# 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 <worktree> <outdir>`, 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 <worktree> <outdir>`, 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.
|