Files
stack/agents/darkwing/work/queue-53-review/review-r2.md
T

12 KiB
Raw Blame History

Row 53, console tokens and shell restyle, round 2 review (Darkwing)

Issue #1542, request comment 27156, packet comment 27157, queue revs 362-364. Base 2d4d4e7d. Candidate manifest sha256 78c3aef963b7e35500c9f8afa6341057f1a6372ced70915d175e507b5fac8406, the same 16 files under packages/webui. The packet manifest and my snapshot of the checkout both check 16 OK. round2.patch applied to my round 1 snapshot gives the round 2 files byte for byte, so the delta is those five files and nothing else.

Verdict: request changes. All five of my round 1 items are fixed for the cases I listed, and Sage's strip test is there and works. Every suite is green and 35 of 37 mutants die. Two refusal gaps are left, and both show data from before a board refusal:

  • A board 403 followed by any other failed read brings the refused session table and the "Waiting on you" card back on screen. Filbert found the same thing (their C4, comment 27163).
  • A board 403 while a conversation is open leaves the conversation and its history on screen. I should have caught this in round 1. My probes then never opened History, so they didn't.

Both fixes are a few lines each.

Method

  • Detached worktrees at 2d4d4e7d, cand and mutwt, each with the 16 candidate files staged and checking 16 OK. mutwt still checks 16 OK after the mutant runs (r2/mut/manifest-after.txt).
  • r2/gate.sh: the packages/webui suite, build-tokens.mjs --check, then every scripts/test-*.sh with DOCKER_HOST=unix:///nonexistent.sock, 18:21:44Z to 18:24:57Z.
  • 37 mutants (r2/mut/mutate.py, r2/mut/run.sh), each against the full webui suite, 18:25:08Z to 19:00:50Z. M01 to M20 are round 1's, with M11 rewritten for the strip. M21 to M37 are new and aim at the round 2 code.
  • test-release with Docker, finished 19:01:30Z. Then the S5 browser test three times on the unmutated candidate (r2/out/s5-rep-*.txt), see note 3.
  • Probes in Chromium through the candidate's tests/browser.mjs (r2/probes/probe2.test.mjs, output r2/probes/probes-cand.txt). The first test is my round 1 probe set again, a fake board and fake bus verbs, plus P9. The second, P10, runs the real control-board from the candidate's tests/history-fixture.mjs behind a small proxy that can answer every request with 403, the way control-board's Host check does. No tracker request.
  • Read the five changed files against round 1's required list, Filbert's comment 27143 and IMPLEMENTING.md.

Node v26.8.1, TMPDIR=~/darkwing-scratch/r53b/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

As in round 1, I didn't run test-task with real Docker because it makes live model calls. Dewey's run gave 98/0. Filbert's gate also ran the other node packages, all green.

Round 1 items

Item Result
Required 1, board refusal Fixed for a single refusal. The inspector closes, the tree reads "Not read: the board refused the read.", the counts show "–", and the footer, #status and #fresh-board all say not read (P1, P2)
Required 2, bus refusal Fixed. forget() clears last and the Inbox count from a view, from the board route and from pkOpen. The palette lists only the four views and the section list has no counts (P3, P4, P4b). P7 now shows no task rows
Required 3, tests Done for the inspector. Not for an open conversation, see Required 2 below
Required 4, M01 Done. no-bus-host runs over kept rows and M01 dies
Required 5, M08 Done. Markup in a question and a task title, and M08 dies
Filbert C3 Done. "Board not read: refused"

The notes: the strip is now one row below 760px (note 1). The toast stays in the accessibility tree while off, at 1×1 with clip-path (note 4, P8). M09 and M16 are tested and die. Note 2 (freshness hidden below 760px) and note 5 (Seen 403 wording) are unchanged, and that's fine.

The strip test

Sage asked me to check it. shell.test.mjs sets 360px and then 400px and asserts four things at each width:

  • every #sections a has the same offsetTop;
  • the list's computed overflow-x is auto or scroll;
  • no label wraps (one client rect, scrollWidth within the box);
  • the page doesn't scroll sideways.

All three of Sage's properties are pinned. Three mutants confirm it: M11 (back to wrap and visible) fails "one row at 360px", M37 (wrap with overflow-x:auto) fails the same, and M36 (one row with overflow-x:visible) fails "strip scrolls at 360px". M36 is caught only by the computed-style check, because the fixture's labels fit at 360px and the page never overflows. That check is the right one to have.

Probes

Fixture as in round 1: one board with two sessions (project secretproj, task "Kept fixture task"), one inbox decision and one task. P10 uses the control-board fixture's session, whose history holds "Secret question from before the refusal" and "Secret answer from before the refusal".

Probe Candidate
P1 board 403 with the inspector open #board-view display:none, inspector hidden. Fixed
P2 board 403 tree, footer, #status and #fresh-board say not read. Fixed
P3 bus human-required after a read the palette lists only Board, Inbox, Tasks and Agents. Fixed
P4, P4b the same refusal no Inbox count or BLOCKING chip, on a bus view and on the board page. Fixed
P6 not-configured after a good read banner ref, no task rows
P7 refusal, then outcome-unknown banner err, no task rows
P8 the toast before any copy display:flex, 1×1, empty, in the tree
P9 good read, board 403, then a 500 #board-view is display:block again with both session rows and the waiting card ("agent1 … secretproj … Kept fixture task … Input needed: kept text"). The banner says "No board data loaded", #fresh-board "the read failed", the footer still "refused"
P9b then a good read 2 rows, footer "waiting 1 · working 1". Recovery works
P10 History open, then every board request 403 #conversation stays shown with the title, the meta line and both secret messages in #conv-log. Beside it, #board-refused says "Nothing to show. The board refused the read, so Console shows nothing rather than a guess."

P9: on a 403, error() sets data = null but leaves the markup in #sessions, #waiting and #seen, and render() returns at once when !data. The table hides only through body.board-refused #board-view. The next error() that isn't a 403 removes that class and the old markup shows again. The README line "A board refusal drops the last scan and everything drawn from it" is not true after that second failure.

P10: #conversation sits outside #board-view (index.html line 17), so the board-refused rule doesn't reach it. convForm() runs and the reply form says "This session is no longer in the board scan", but the history read before the refusal stays on screen. Filbert's T7 predicts this from the code; P10 shows it with the real control-board.

Mutants

35 of 37 killed (r2/mut/summary.txt). Every kill is an assertion or a wait timeout, not a load error.

Mutant Change Result
M01 refusal() drops not-configured and no-bus-host killed (was survived)
M02 error(): refused = false killed
M03 no body.board-refused toggle killed
M04 the refused: code not in <code> killed (was survived)
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 killed (was survived)
M09 :focus-visible offset 0 killed (was survived)
M10 focus outline 1px killed
M11 strip back to wrap, overflow-x visible killed (was 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 killed (was survived)
M17 storage key changed killed
M18 no body.board-refused #board-view rule killed
M19 refusal shows kept rows killed
M20 board refusal uses banner err killed
M21 forget() does nothing killed
M22 the board route's inbox read swallows a refusal killed
M23 a view's refusal doesn't call forget() killed
M24 pkOpen ignores a refused refetch killed
M25 a board 403 keeps selected killed
M26 fresh() loses the "refused" wording killed
M27 a board 403 keeps the footer killed
M28 a board 403 keeps #status killed
M29 a board 403 keeps the project tree killed
M30 a board 403 keeps the three heading counts survived
M31 freshBus loses the refused state killed
M32 .toast:not(.on) is display:none again killed
M33 forget() keeps the Inbox count killed
M34 forget() keeps last killed
M35 board freshness in local time killed
M36 strip one row, overflow-x:visible killed
M37 strip wraps, overflow-x:auto killed

M07 survives as in round 1. The toast text comes from validated refs, and Dewey declined it with a reason. I accept that.

M30 survives because the three counts sit inside #board-view, which is hidden while refused. Today that makes it close to equivalent. After P9 it isn't: the counts come back into view with the rows. Filbert's T7 asks for the same assertion. A P9 test that checks the counts kills it.

Required

  1. P9, Filbert's C4. After a board 403, a later failure that isn't a 403 must not show the refused scan. Clear #sessions, #waiting and #seen in error() on a refusal, or hide #board-view whenever data is null. Test: a good read, a 403, then a 500, and assert no session row, no waiting card and the three counts not numeric.
  2. P10. A board 403 closes an open conversation, or hides #conversation under body.board-refused. Closing is cleaner, because the history then leaves the DOM. Test: open History, refuse the board, and assert #conversation is hidden and #conv-log is empty or hidden. history-fixture.mjs already gives you the conversation; a proxy that answers 403 is about 20 lines (mine is in r2/probes/probe2.test.mjs).

Then fix the README line on refusals if the wording no longer matches.

Notes (not blocking)

  1. I agree with Filbert's N4 to N7. N6 (a bus read in flight when forget() runs puts its rows back) is real by the code, and it only restores a read that succeeded, so it can wait.
  2. P1 hides the inspector, but its text stays in the DOM. With the inspector hidden that is fine; if you take the "clear on refusal" route for Required 1, clearing it too costs one line.
  3. In the M05 run, S5 also failed at bus-browser.test.mjs line 124 ("BODY" !== "H1"). M05 only changes the bus freshness words, so that failure isn't the mutant's doing. S5 passed in the gate, in the 36 other mutant runs and three times alone afterwards. The cause is a race in render() that base 2d4d4e7d has too. The 10 s refresh timer isn't cleared when a navigation starts. If it fires during a navigated read, the refresh (navigated = false) takes gen, the navigation's own render returns early, and nobody focuses the new h1 or announces it. Under the mutant load a read took long enough to hit that window. Not this row's regression and not blocking. clearTimeout(timer) at the top of render(), or carrying navigated into the next render, would close it. Worth its own small row.

Files

  • r2/candidate-manifest.sha256: copy of Dewey's.
  • r2/gate.sh, r2/out/: suite runs, summary.txt, test-release-docker.txt, the S5 repeats and r1-to-r2.diff.
  • r2/mut/: mutant definitions, runner, diffs, outputs, summary.txt and the manifest check after the runs.
  • r2/probes/: the probe test and its output.