# Row 128 brief, refreshed 2026-10-18 This replaces the short brief in `docs/plans/2026-10-14_prd-1.2-build.md` ("mosaic talks to the assistant in a named thread"). Rows 121, 142 and 143 have landed since that brief was written, so the bus side is done and the CLI side can be named exactly. Line numbers are at ac55e84fe. ## mosaic talks to the assistant in a named thread ### Problem The bus has every project and thread op since row 121 (#1615): `project.create` and `project.list` (`packages/bus/src/broker.mjs:2050`, `:2068`), `thread.create`, `thread.list`, `thread.open`, `thread.send` and `thread.messages` (`:2074`, `:2098`, `:2112`, `:2119`, `:2146`). No code in `packages/cli` or `packages/webui` calls any of them. The assistant is the business's threaded instance, and the broker refuses a human's plain `message.send` to it with `thread-required-use-thread-send` (`broker.mjs:288`). So today Jason can't create a project or a thread, and `mosaic talk assistant ...` can only fail. Acceptance run 314 (`docs/acceptance/314.md`) needs both: step 1 creates a bus project, an assistant thread in it and opens it, and step 3 talks in that thread and expects only the assistant's reply. Requirements: REQ-CLI-1, REQ-CLI-2, REQ-THREAD-2, REQ-USER-2 (`docs/prd/mosaic-stack.md:475`, `:668`, `:673`). Gitea #1622. ### Owner and reviewer - Owner: researcher. - Reviewer: filbert (CLI only, no broker or sandbox change). ### Files owned - `packages/cli/src/cli.mjs` (dispatch, `USAGE`, `talk` and `follow`) - `packages/cli/src/project.mjs` (the `create` and `list` subcommands) - `packages/cli/src/thread.mjs` (new: thread verbs and the thread resolver) - `packages/cli/tests/thread-cli.test.mjs` (new) - `packages/cli/tests/project.test.mjs` - `packages/cli/tests/follow.test.mjs`, only if the follow loop's existing tests need a thread case - `packages/cli/README.md`, `docs/TOOLS.md` - `docs/acceptance/314.md`, steps 1 and 3 only: replace "row 128's commands" with the real command lines - the packet `agents/researcher/work/queue-128/` No change to `packages/bus`. If a verb below can't be built on the existing ops, stop and tell Sage; that is a brief change, not a fix in scope. ### What ships Verbs. Every one goes through `callFor` (`cli.mjs:172`), so the human transport, `--business` and the refusal inside an agent work as they do for `talk` today. Each list verb takes `--json`. 1. `mosaic project create [--repo ]` calls `project.create`. `mosaic project list` calls `project.list` and prints name, repo and a short id. They join `init` and `check` under the `project` verb. `init` and `check` keep their exact arguments and refusals; `create` and `list` work on the bus record, `init` and `check` on the repository on disk, and `docs/TOOLS.md` says that in one sentence. 2. `mosaic threads [--project ]` calls `thread.list` and prints project name, thread name, instance and a short id. 3. `mosaic thread create ` calls `thread.create` and prints the id and the instance. `mosaic thread open ` calls `thread.open`. `mosaic thread read ` pages `thread.messages` and prints the history oldest first; it marks nothing read. 4. `mosaic talk --thread [--wait | --follow]` sends with `thread.send`, class REQUEST, and takes one positional. Without `--follow` it prints only the message whose `in_reply_to` is the sent id, as row 142 does for roles (`cli.mjs:348`), and leaves the rest unread. With `--follow` it prints every message in that thread after the sent one and marks read those addressed to the human; messages of other threads and outside threads stay unread for `mosaic inbox`. `mosaic follow --thread ` is the same loop without the send. 5. Plain `mosaic talk ` to a threaded instance asks for a thread; there is no default thread. On `thread-required-use-thread-send` the CLI exits 2 with a message that names `--thread` and `mosaic threads`. The bus never picks between instances, and a CLI default would pick between Jason's threads for him. The packet records this choice. Thread and project references. A project is its name or its id. A thread is its id, an id prefix of at least 8 characters, or `/`. The resolver reads `project.list` and `thread.list` and refuses an unknown or ambiguous reference before any send, the way `findDecision` does (`cli.mjs:111`). Refusals. `thread-not-owned`, `thread-not-found`, `project-not-found`, `project-exists`, `thread-exists`, `unknown-repo`, `thread-instance-missing` and `thread-instance-ambiguous` each print one plain line and exit non-zero. The CLI adds no check the broker already makes. Tests, in `packages/cli/tests/thread-cli.test.mjs` against a real broker the way `thread-launch.test.mjs` builds one: - project create and list, including `project-exists` and `unknown-repo`; `init` and `check` arity refusals unchanged; - thread create, list (with and without `--project`), open and read; - the resolver: id, prefix, `/`, too-short prefix, unknown and ambiguous; - `talk --thread` prints only the reply to the sent id and leaves an unrelated message unread; `--wait` timeout; `--follow` prints only that thread's messages and leaves another thread's message unread; - plain `talk` to the assistant gets the `--thread` message, exit 2; - REQ-USER-2: a thread owned by another human. `talk --thread`, `thread open` and `thread read` on it are refused with `thread-not-owned`, and no message, open or delivery row is written. Mutants: one per behavior above, recorded in the packet with the killing test, as row 142's packet does. Suites: `node --test packages/cli/tests/*.test.mjs`, then the full gate. Acceptance 314: after this row, step 1's bus half is `mosaic project create stack --repo stack`, `mosaic thread create stack assistant`, `mosaic thread open stack/assistant` and `mosaic threads --project stack`; step 3 is `mosaic talk --thread stack/assistant ""`. The builder writes the real lines into `docs/acceptance/314.md`. ### Out of scope - Renaming a project or a thread: no broker op exists. P19 follow-up. - The Console chat area: row 129 (#1623). - Closing, deleting or archiving threads. - Any broker, schema or launcher change. - A live smoke. Steps 1 and 3 run in Jason's acceptance run, after the re-pin and with Mos's go. ### Gate Filbert approves on #1622, naming the candidate manifest digest. Sage reruns the full gate at the landing target, and it must be green (`scripts/test-*.sh` plus the package suites, 25 of 25 as on 6e7fdf7ff). The REQ-USER-2 test above passes and its mutant is killed.