Files

6.0 KiB

Filbert review, row 129 round 1: changes

Candidate manifest cfbadbd49902f5f3740392fec9218279adc2a661cfb28341fba02790f327232b (15 paths), base 70504d23a. I reviewed and ran on 70504d23a plus the 15 manifest paths; all 15 hashes match, and git log 70504d23a..806ab89df on the 15 paths is empty. The packages/webui/src/public/ diff (bus.js, chat.css, chat.js, index.html) needs Mos's decision 123 go on this candidate before landing.

Brief: docs/plans/2026-10-14_prd-1.2-build.md:552-567, "The WebUI chat area".

(a) Edits to existing WebUI tests (sage-0747)

Each count increase also names the chat item:

  • packages/webui/tests/browser.test.mjs:21-22: scripts 4 to 5, and script[src="/chat.js"] is counted exactly once.
  • packages/webui/tests/shell.test.mjs:266-267: section SVGs 9 to 10, and the #/chat section link reads Chat with icon /icons.svg#i-inbox.
  • packages/webui/tests/shell.test.mjs:463: the exact palette views list now includes #/chat; the deepEqual checks and waits use that list.
  • packages/webui/tests/views.test.mjs:59: the shared page() wait needs 10 section links and the #/chat link reading Chat. Two tests use this helper, which is why BUILD lists it twice.

No other assertion was weakened. Chat reuses the inbox icon, as BUILD says.

(b) Mutant 16, read-array

mutants-first.txt:16 records 16-read-array SURVIVED. The cause is the one BUILD gives. The malformed-response case read only messages, where the page metadata value.at(-1) throws on a non-array, so the catch still answered 502 invalid-response without the guard. packages/webui/tests/chat.test.mjs:146-150 now sends { rows: [] } to /api/chat/projects and /api/chat/threads and expects 502 invalid-response. Without the guard at chat-http.mjs:98 both would return 200 with that object as rows, so the mutant is killed for the right reason. The source is unchanged; only the test was added.

Brief acceptance

  • List, create, send and the per-thread view: chat.test.mjs has one verified-write test, one write-fence test and one refusal or lost-receipt test for each of the four POSTs, plus fixed-verb and paging reads. chat-browser.test.mjs covers list and create, per-thread drafts and history, send and receipts.
  • A thread the user doesn't own: the real-broker test (chat.test.mjs:153-226) inserts a same-business thread owned by someone-else and a foreign-business thread. GET messages, POST messages and POST open give 403 thread-not-owned and 404 thread-not-found, with no private names in the response, and the messages, thread_opens and action.allowed counts are unchanged.
  • Mutants for each: Rocko's 64 (BUILD, final table) include mutants for each of these.

Checked and correct

  • chat-http.mjs repeats the send, named, single identity header, Origin and Fetch Metadata fences on every POST, ahead of the JSON type, the 16384-byte cap and strict keys. Methods other than GET and POST get 405. Unknown transport codes become invalid-response, and only fixed messages are returned.
  • chat.mjs maps to fixed verbs only; no browser-chosen verb, owner, business or class crosses it.
  • chat.js renders every label and body through esc(inert(...)), links only UUID ids, aborts stale reads with a generation check, and forgets private state on sign-out and on forget() from bus.js.
  • The bus.js diff only adds the section, the palette entry, the MosaicChat.forget() and leave() hooks, the route delegation and the send-config refresh for /chat.

Changes requested (tests only; the source is correct)

My own mutants left four shipped guards that no test pins. The brief asks for tests and mutants for create and send, so each of these is a change. Each fix is a test addition; no source change is needed.

  1. chat.js send target. Changing the send POST in packages/webui/src/public/chat.js:232 from threads/${id}/messages to threads/${id}/open survives chat-browser.test.mjs. The fetch stub records path for each write, but the tests assert only body. Assert the path of each write too, for create project, create thread, send and Resume.
  2. createThread receipt instance check. Replacing the instance check at packages/webui/src/chat-http.mjs:105 with false survives chat.test.mjs. The malformed-receipt cases use only {}, which fails the id check first. Add createThread receipts { id } with no instance and one with an instance that fails KEY. Both should give 503 outcome-unknown with sent: 'unknown'.
  3. Receipt fields. Replacing chat-http.mjs:111 with const receipt = { ...value }; survives. The comment above that line says only the operation's receipt fields are exposed, but each test answer is exactly the receipt. Give one answer an extra field (for example owner or capability) and assert the response doesn't carry it.
  4. Bidi controls. Dropping \u202a-\u202e\u2066-\u2069 from the inert class at chat.js:6 survives chat-browser.test.mjs. HOSTILE already contains U+202E, but the test only checks the ESC escape. Assert that the rendered project label and message body contain no raw U+202E.

Ran by me

Slot from sage-0761, on 70504d23a plus the 15 manifest paths. Busy check CLEAR before and after, manifest checked before and after, TMPDIR in my own scratch, DOCKER_HOST a dead socket.

  • Singles, 16:42:28Z to 16:43:03Z: chat.test 20/20, chat-browser.test 11/11, browser.test 1/1, shell.test 5/5, views.test 3/3. Total 40/40, 0 skipped.
  • Mutants, 16:43:03Z to 16:44:02Z: 6 of my own, none of them in Rocko's 64. The controls for chat.test and chat-browser.test pass first. Killed: the repo KEY check (chat.test, "chat validates names, body, project and strict empty open payload") and the GET/POST method guard (chat.test, "chat assets, GET purity..."). The four that survived are listed above.

Not mine

Rocko's receipts (agents/rocko/work/queue-129/r1/): final gate 16:24:07Z, WebUI 177, bus threads 14, CLI thread parity 11, total 202; 64 of 64 mutants killed after the read-array test change; chat-final-shape.log 20/20; named-browser, named-shell and named-views logs 1/1, 5/5 and 3/3.