12 KiB
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,candandmutwt, each with the 16 candidate files staged and checking 16 OK.mutwtstill checks 16 OK after the mutant runs (r2/mut/manifest-after.txt). r2/gate.sh: thepackages/webuisuite,build-tokens.mjs --check, then everyscripts/test-*.shwithDOCKER_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-releasewith 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, outputr2/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'stests/history-fixture.mjsbehind 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 ahas the sameoffsetTop; - the list's computed
overflow-xisautoorscroll; - no label wraps (one client rect,
scrollWidthwithin 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
- P9, Filbert's C4. After a board 403, a later failure that isn't a
403 must not show the refused scan. Clear
#sessions,#waitingand#seeninerror()on a refusal, or hide#board-viewwheneverdatais null. Test: a good read, a 403, then a 500, and assert no session row, no waiting card and the three counts not numeric. - P10. A board 403 closes an open conversation, or hides
#conversationunderbody.board-refused. Closing is cleaner, because the history then leaves the DOM. Test: open History, refuse the board, and assert#conversationis hidden and#conv-logis empty or hidden.history-fixture.mjsalready gives you the conversation; a proxy that answers 403 is about 20 lines (mine is inr2/probes/probe2.test.mjs).
Then fix the README line on refusals if the wording no longer matches.
Notes (not blocking)
- 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. - 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.
- In the M05 run, S5 also failed at
bus-browser.test.mjsline 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 inrender()that base2d4d4e7dhas too. The 10 s refresh timer isn't cleared when a navigation starts. If it fires during a navigated read, the refresh (navigated = false) takesgen, the navigation's own render returns early, and nobody focuses the newh1or 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 ofrender(), or carryingnavigatedinto 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 andr1-to-r2.diff.r2/mut/: mutant definitions, runner, diffs, outputs,summary.txtand the manifest check after the runs.r2/probes/: the probe test and its output.