Refusal leaves board data in the inspector, project tree, footer and scan line, and bus data in the Inbox count and the Ctrl+K palette. Gate green, 13 of 20 mutants killed, fonts match the upstream archive. Co-Authored-By: Claude Opus 5.5 <[email protected]>
10 KiB
Row 53, console tokens and shell restyle, round 1 review (Darkwing)
Issue #1542, request comment 27138, packet comment 27139, queue revs
358-359. Base 8a7871ff. Candidate manifest sha256
75569953c1a2cd7b7266208d6e6607290335f1bfe0cf94d96ed224e480a1dda8, 16
files under packages/webui. The packet manifest and the snapshot I took
from the checkout both check 16 OK.
Verdict: request changes. The restyle itself is good. Tokens and the sprite are byte copies, the fonts match the upstream archive, every suite is green, and 13 of 20 mutants die on assertions. The problem is the refusal state. The packet says a board 403 "fails closed (kept rows hidden)" and a bus refusal never shows kept rows. The main table and the bus view do that. Five other places still show data read before the refusal: the inspector, the project tree, the footer and the scan line on the board, and the Inbox count and the Ctrl+K palette on the bus. The fix is small and listed under "Required".
Method
- Detached worktrees at
8a7871ff:base,candandmutwt. The last two hold the 16 candidate files, staged, and check 16 OK.mutwtstill checks 16 OK after the mutant runs (r1/mut/manifest-after.txt). r1/gate.sh: thepackages/webuisuite,build-tokens.mjs --check, then everyscripts/test-*.shwithDOCKER_HOST=unix:///nonexistent.sock, 17:31:49Z to 17:34:30Z.- 20 mutants (
r1/mut/mutate.py,r1/mut/run.sh), each against the full webui suite, 17:37:26Z to 17:56:15Z. test-releasewith Docker, finished 17:56:43Z.- Font provenance: I downloaded
JetBrainsMono-2.304.zipfrom the URL injetbrains-mono-sources.txtinto an empty directory and compared hashes (r1/fonts/check.txt). - Probes in Chromium through the candidate's own
tests/browser.mjs, with a fake board and fake bus verbs, no tracker request (r1/probes/probe.test.mjs, outputr1/probes/probes-cand.txt). - Read through
bus.js,app.js,index.html,shell.css,serve.mjsandshell.test.mjsagainst the brief andIMPLEMENTING.md.
Node v26.8.1, TMPDIR=~/darkwing-scratch/r53a/tmp.
Suites
| Suite | Result |
|---|---|
| packages/webui (node) | 27/0 |
| build-tokens --check | rc 0, "tokens.css is current" |
| test-auth | 15/0 |
| test-conductor | 17/0 |
| test-config | 24/0 |
| test-discord | 66/0 |
| test-extension-package | 18/0 |
| test-foundation | 44/0 |
| test-queue | 27/0 |
| test-release | 4/0 without Docker, 14/0 with it |
| test-task, Docker unreachable | 26/0 |
I didn't run test-task with real Docker, because it makes live model
calls. The candidate changes nothing outside packages/webui, and
Dewey's run gave 98/0.
Copies and fonts
tokens.css and icons.svg are byte-equal to docs/design. The archive
hashes to 6f6376c6…7bbf, the value in the sources file. The Regular,
Medium and Bold woff2 files and OFL.txt from the archive hash the same
as the shipped files (a9cb1cd8…, 086c48df…, c503cc5e…, 30f0c136…).
Probes
Fixture: one board with two sessions (project secretproj, task
"Kept fixture task"), one inbox decision ("Kept question?") and one task
("Kept task title"). Each probe reads once, then makes the read refuse.
| Probe | Candidate |
|---|---|
| P1 board 403 with the inspector open | #board-view is display:none, but the inspector stays open with agent1's state, project secretproj and task "Kept fixture task" |
| P2 board 403 | the project tree shows "All projects 2 / secretproj 2"; the footer shows "waiting 1 · working 1"; #status shows "Scanned |
P3 bus human-required on #/inbox, after #/tasks was read |
the palette lists "Kept question?" and "#7 Kept task title" |
| P4 the same refusal | the section list shows Inbox "1" and "1 BLOCKING" |
| P4b the same on the board page | same as P4 |
P6 not-configured after a good read |
banner ref, no task rows. Correct |
P7 refusal, then outcome-unknown |
the rows from before the refusal come back under the stale banner, "1 s ago". Fine by the brief, see note 6 |
| P8 the toast before any copy | display:none |
P1 and P2 come from body.board-refused #board-view{display:none}: it
hides the table and nothing else. The inspector, the tree, the footer and
#status are outside #board-view. P3 and P4 come from the kept cache.
pkItems() builds from last.get('inbox') and last.get('tasks'), and
pkOpen refetches with Promise.allSettled, so a refused refetch leaves
the old rows in last. The Inbox count keeps its last value, because on
the board route a failed fetchRead('inbox') lands in catch {}.
Base does none of this either. There, every failure showed kept data
under a stale banner, so this isn't a regression. It does contradict the
row's own claim and IMPLEMENTING.md line 122 ("refusal (fail closed: the
refusal code in mono and the file or rule that is missing)"). The project
name and the task title in P1 are exactly what a refusal is supposed to
stop showing.
Mutants
13 of 20 killed (r1/mut/summary.txt). Every kill is an assertion or a
wait timeout in the new tests, not a load error.
| Mutant | Change | Result |
|---|---|---|
| M01 | refusal() drops not-configured and no-bus-host |
survived |
| M02 | app.js error(): refused = false |
killed |
| M03 | no body.board-refused toggle |
killed |
| M04 | the refused: code not in <code> |
survived, equivalent: why() still puts the same code in <code> |
| M05 | no stale words in the bus freshness line | killed |
| M06 | System sets data-mode |
killed |
| M07 | toast text not escaped | survived |
| M08 | palette label not escaped | survived |
| M09 | :focus-visible offset 0 |
survived |
| M10 | focus outline 1px | killed |
| M11 | the section list below 760px loses flex-wrap and overflow-x |
survived |
| M12 | no section icons | killed |
| M13 | accept() doesn't clear board-refused |
killed |
| M14 | task-link ↑/↓ reversed | killed |
| M15 | /icons.svg served as text/plain |
killed |
| M16 | no tabular-nums |
survived |
| M17 | storage key changed | killed |
| M18 | no body.board-refused #board-view rule |
killed |
| M19 | refusal shows kept rows (Dewey's "refusal") | killed |
| M20 | board refusal uses banner err |
killed |
M01 matters most. Both browser tests refuse with human-required only,
so nothing checks that not-configured or no-bus-host fail closed over
kept rows. My P6 shows the candidate does it, but no test holds it.
M08 is the palette label, which is bus text (d.question, the task
title). The code escapes it; no test feeds it markup. M07 is the toast,
whose text is Copied: <command>; the commands come from validated refs,
so M07 is lower risk.
Required
- Board refusal hides everything read from the board, not only
#board-view: close the inspector (or hide it underbody.board-refused), and hide or blank the project tree counts, the footer counts and the "Scanned" line in#status. - Bus refusal drops the kept bus data outside the view: the Inbox count
and BLOCKING chip in the section list, and the palette's decisions and
tasks. Clearing
lastforinboxandtaskson a refused read would cover both, including a refused refetch inpkOpenand the board route'sfetchRead('inbox'). - Tests for 1 and 2, with the inspector open when the board refuses.
- A refusal over kept rows with
not-configuredorno-bus-host, so M01 dies. - A palette test whose question or task title holds markup, so M08 dies.
Notes (not blocking)
- Below 760px the section list wraps.
IMPLEMENTING.mdasks for "a horizontal strip that scrolls inside itself". The packet lists it as a deviation; Sage should rule on it, and M11 shows no test pins either form. - Below 760px the freshness line is
display:none, so a phone shows no read time in the command bar. The stale banner still says it in the view. - No test checks the focus offset (M09), and table rows use
outline-offset:-2px. Inset rings on rows are reasonable; the brief says 2px offset. - The toast is the only live region for copy, and it is
display:noneuntil the first copy. A live region that is out of the accessibility tree when its text arrives may not be announced the first time. I couldn't confirm it with a screen reader, so treat it as a risk. The usual fix is to keep the region rendered and empty, and hide it with the visually-hidden pattern. - The board refusal sets the server's message in
<code>("refused:board-refused" in the test, "non-local Host refused" from control-board). That is prose in mono. A Seen POST that fails also goes througherror(), so a 403 on Seen would say "The board refused the read". That 403 only comes from control-board's Host/Origin check, which refuses the read too, so the wording is wrong only in name. - After a refusal, the next non-refusal failure shows the rows from
before the refusal as stale (P7). They carry their read time, which is
what the brief asks of the error state. Clearing
last(Required 2) would remove them too; either is fine. - No
tabular-numstest (M16), no business pill (row 54), and#copy-statuslostrole="status"(the packet explains why).
Filbert's review
Filbert posted request changes in comment 27143 at 17:52Z, while my
mutants ran. I read it after writing the above and before posting. Their
C1 and C2 are my Required 1 and 2, found separately with their own probe,
and their T1 is my Required 4 (my M01, their refusal-403-only). I agree
with their C3: the board freshness line should say the board wasn't read,
not "stale", which fits under Required 1. I didn't check their N1
(board time local, bus time UTC). It is cheap to settle in the same
round. My Required 5 (M08, the palette escape) isn't in their list.
Files
r1/candidate-manifest.sha256: copy of Dewey's.r1/gate.sh,r1/out/: suite runs,summary.txtandtest-release-docker.txt.r1/mut/: mutant definitions, runner, diffs, outputs,summary.txtand the manifest check after the runs.r1/probes/: the probe test and its output.r1/fonts/check.txt: the archive and file hashes, and the two copy checks.