diff --git a/agents/morgana/work/queue-225/BUILD.md b/agents/morgana/work/queue-225/BUILD.md new file mode 100644 index 000000000..7b8e27eb9 --- /dev/null +++ b/agents/morgana/work/queue-225/BUILD.md @@ -0,0 +1,122 @@ +# Row 225 (C2-10b) Console chat: replies, citations, write notices, status line + +Owner: Morgana, from sage-3582 (18:53Z 10-23). Base: origin/refactor 45dca69f6. Branch: morgana/row225. +Brief: docs/plans/2026-10-20_case2-rows.md, C2-10b. Design: agents/gosalyn/work/case2/DESIGN.md 7.4, +5.2 (the reply schema and the check result) and the write notice and `library.revert` (about line 286). + +## Ruling on the dependency gap (sage-3591) + +225 builds against the design contracts. `renderReply` stays pure, and the caller supplies the answers +it can't know. The revert route proxies to the bus verb and shows the refusal code until the verb +exists. Landing order: 225 lands after 217 (C2-06), not after 288 (C2-02). Sage adds `after 217` to +row 225. Before 288 lands, the Revert button gets the bus's refusal and shows its code, as the brief +allows. + +## A1. The check result I assume (to 217, Launchpad) + +The broker stores one check result with each agent-to-human message the guard passes. The Console reads +it on the message row that `/api/chat/threads//messages` returns, under the key `check`: + +```json +{ + "check": { + "citations": [ + { "scope": "canon", "agent": null }, + { "scope": "supporting", "agent": "acme/scribe" }, + { "scope": "web", "agent": null } + ] + } +} +``` + +- `check` is `null` or absent on a message the guard didn't check: a human's message, a free-text reply, + a notice, or a message from before 217. +- `check.citations` has one entry per entry of the body's `citations`, in the same order. +- `scope` is one of `canon`, `playground`, `supporting` and `web`. A url cite is `web`. +- `agent` is the `Mosaic-Agent` trailer value, `/`, from + `git log -1 --format=%(trailers:key=Mosaic-Agent,valueonly) -- `, or `null` when the + file's last commit at `commit` carries none. A url cite has `agent: null`. The Console shows the + agent-written label when `agent` is a nonempty string, and names the agent. + +The Console trusts nothing in the body for scope or label. A cite with no matching check entry renders +with no badge and no label. It doesn't fall back to a scope it worked out itself. + +If 217 uses other names, I change mine to theirs before either of us pushes (sage-3591 item 1). + +## A2. The revert verb I assume (to 288, Honker) + +- Verb: `library.revert`. A human verb (design M4): from an agent session it refuses `human-required`, + like `library.status` (`broker.mjs:5133`). +- Args: `{ "project": , "commit": <40 lowercase hex> }`. Both are required, and no other key + is allowed (`invalid-request`), as the other library verbs check with `keys(args, ...)`. +- Result: `{ "project", "path", "reverted": , "commit": }`. +- Refusal codes the Console shows by name: + - `library-revert-not-agent`: `commit` has no `Mosaic-Agent` trailer for this business, or isn't + reachable from the served commit. + - `stale-base`: a later commit changed that path. + - `human-required`, `invalid-request`, `project-not-configured`. + - Before 288 lands, the bus refuses the verb (expected `unknown-verb`, `broker.mjs:6578`), and the Console + shows that code. +- HTTP status in the Console (`packages/webui/src/bus.mjs` `busStatus`): `stale-base` and + `library-revert-not-agent` map to 409. Every other code keeps the existing mapping. + +## A3. The status line + +`GET /api/library/status?project=` proxies `library.status {project}` through the existing +`libraryStatus` option. `cli.mjs:110` already passes that option, but `startServer` doesn't read it. +The status line shows: + +- the served commit (7 characters) and its age; +- `library-diverged` when the sync state names it; +- the fetch error code; +- the reply limits in force, and the keys that fell back; +- the count of R-problems, linking to the list. + +It reads the fields `readSyncState` returns at the base (`packages/library/src/mirror.mjs:232`). It shows +any field 217 adds (the limits) only when present. + +## A4. "Changed since" (ruled sage-3596) + +Sage ruled that 217 supplies `last`. On a `thread.messages` row that carries `check`, each path cite's +entry gets `last`, the file's last commit at the served tip, read when the message is read. When `last` +is absent the Console marks nothing changed, and a test pins that. + +One more field is needed, raised in morgana-0200. The cited `commit` is a library commit, not the +file's own last commit, so a cite made at a commit that didn't touch the file would differ from `last` +while the file is unchanged. I asked 217 to also store `file`, the file's last commit at the cited +commit. That's `%H` from the same `git log -1 -- ` call that reads the trailer, so it +costs no extra git call. The Console marks a cite changed when both `file` and `last` are 40-hex and +differ. The cited commit is reachable from the served one (5.2), so `file` is an ancestor of `last`, +and differing means newer. A missing or malformed `file` or `last` marks nothing. + +The entry the Console reads, in full: `{ scope, agent, file?, last? }`. + +## A5. `no-cited-answer` retires + +`renderItems` and its `NOTICES` (`no-cited-answer`, `no-answer`, `turn-failed`) go. 217 sends the +runner's fixed texts as plain bodies (design 5.2, "Runner texts"), and renderReply shows those as free +text. Design 7.4 names no other notice than `library-write`, so nothing is lost (sage-3596 item 3). + +## Files + +Both deviations below were accepted in sage-3596 and are named in the manifest for Dewey. The brief owns these: + +- `packages/library/src/render.mjs`: `renderReply` replaces `renderItems`. +- `packages/library/src/index.mjs`: the export. +- `packages/library/README.md`: the section. +- `packages/library/tests/render.test.mjs`. + +It also owns `bus.js`, its stylesheet, `serve.mjs` (only the two routes) and new webui tests. Two +deviations, both recorded here: + +- The chat view's message list is `packages/webui/src/public/chat.js:85` (`window.MosaicChat`), not + `bus.js`. The reply, notice and Revert rendering goes in `chat.js` and `chat.css`. The status line + goes in the chat thread view, so it is also in `chat.js`. +- `packages/webui/src/console-bus.mjs` and `cli.mjs` each get one `libraryRevert` line, beside the + existing `libraryStatus` wiring. + +## Overlap with row 220 (C2-10a, Researcher, 3ef6da90d) + +220 changes one line each in the webui tests discord.test.mjs, fixture.mjs, history-fixture.mjs, +relaunch.test.mjs and return-flow.test.mjs. Its other paths are in control-board, conversation, discord +and business. 225 doesn't touch those five files or those packages. Its webui tests are new files.