Files
stack/agents/darkwing/work/queue-53-review/review-r1.md
T
jason.woltjeandClaude Opus 5.5 70768fe548 review(53): Darkwing round 1, request changes (#1542 comment 27148)
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]>
2026-10-10 12:59:06 -05:00

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, cand and mutwt. The last two hold the 16 candidate files, staged, and check 16 OK. mutwt still checks 16 OK after the mutant runs (r1/mut/manifest-after.txt).
  • r1/gate.sh: the packages/webui suite, build-tokens.mjs --check, then every scripts/test-*.sh with DOCKER_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-release with Docker, finished 17:56:43Z.
  • Font provenance: I downloaded JetBrainsMono-2.304.zip from the URL in jetbrains-mono-sources.txt into 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, output r1/probes/probes-cand.txt).
  • Read through bus.js, app.js, index.html, shell.css, serve.mjs and shell.test.mjs against the brief and IMPLEMENTING.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

  1. Board refusal hides everything read from the board, not only #board-view: close the inspector (or hide it under body.board-refused), and hide or blank the project tree counts, the footer counts and the "Scanned" line in #status.
  2. 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 last for inbox and tasks on a refused read would cover both, including a refused refetch in pkOpen and the board route's fetchRead('inbox').
  3. Tests for 1 and 2, with the inspector open when the board refuses.
  4. A refusal over kept rows with not-configured or no-bus-host, so M01 dies.
  5. A palette test whose question or task title holds markup, so M08 dies.

Notes (not blocking)

  1. Below 760px the section list wraps. IMPLEMENTING.md asks 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.
  2. 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.
  3. 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.
  4. The toast is the only live region for copy, and it is display:none until 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.
  5. 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 through error(), 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.
  6. 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.
  7. No tabular-nums test (M16), no business pill (row 54), and #copy-status lost role="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.txt and test-release-docker.txt.
  • r1/mut/: mutant definitions, runner, diffs, outputs, summary.txt and 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.