Repository navigation
Description
Follow-ups from the review of #255 (#254, merged as 3bb2cb89). Items 1 and 2 are pre-existing behaviour that #255 did not cause. Items 3 and 4 are test gaps in #255's own tests. Line references are to master 3bb2cb89.
1. An expanded packet group has no collapse path after crossing to mobile width
At 1400 px, expand a multi-observation group in the grouped packets view, then resize to ≤ 600 px. The row re-renders as select-hash without aria-expanded, as #255 intends. It keeps class="expanded", though, and its child rows stay visible. The expand column is hidden at that width, so nothing can collapse the group.
Before #255 the same row announced a stale aria-expanded="true", so this is not a regression. It is the dead end that upstream #1461 #7 describes.
Expected: a group expanded on desktop does not leave visible children on mobile without a way back. For example:
- clear the hash from
expandedHasheswhen the layout mode flips to mobile; or - keep the state but omit the child rows from the visible slice while in mobile mode, so they come back on desktop.
Test: an E2E that expands a group at 1400 px, resizes to 390 px and asserts that no child rows are visible, or that the group can be collapsed. Add a mutant without the fix.
2. nodes.js adds a document keydown listener (nodesEsc) on every node-page init
init() adds the listener on each visit, and it is only removed when Escape is pressed. Navigating between nodes A → B → C stacks three listeners, and Escape then sets the same hash three times. That is harmless today, but the listeners leak.
Expected: at most one listener. Remove it in the page's destroy(), or register it once. Add a test that counts the listeners, or the hash writes, after repeated navigation.
3. Test gap: the unit test does not prove that the node page uses the new renderer
test-issue-254-affinity-debug-toggle.js exercises renderAffinityDebugCard() in isolation. A mutant that puts the old inline onclick card back into the loadFullNode template, and leaves the renderer exported but unused, passes the unit test 9/9; only the E2E catches it. The "no inline event handler anywhere in the card" case is also green on master, because it scans an empty string there.
Expected: a fast-layer assertion that the loadFullNode template uses renderAffinityDebugCard(), or that nodes.js has no onclick= in the #node-affinity-debug card.
4. Test gap: Space on a mobile group row
The #254 E2E presses Enter on a 390 px group row but not Space. The handler treats both keys the same, and the review checked Space by hand: the detail sheet opens and the group does not expand. Add Space to the E2E.
Evidence
- From the fix(ui): Affinity Debug toggle and mobile group-row aria-expanded (#254) #255 review comment (findings F1–F4): items 1 and 3 were reproduced there with a test or mutant; item 4 was checked by hand there.
- Item 2 is based on reading the code.
- Nothing here has been re-run on
3bb2cb89.
Activity
dborup commentedon Oct 5, 2026
Plan — CS-Minimax #259
Branch
codex/issue-259-followups-255offorigin/master3bb2cb89, in a dedicated worktree. Tests first (red on master), then the fix. No rebase, amend or force-push; explicitgit addonly. Draft PR,Relates to #259, no closing keywords.1. Expanded group has no collapse path on mobile
Choice: keep
expandedHashes, drop the children from the rendered slice while in mobile mode (the second option in the issue), rather than clearing the set on the flip.Why:
#/packets/<hash>/<obs>adds the hash toexpandedHashesbefore the firstrenderTableRows()(packets.jsaround thedirectPacketHashbranch). Clearing on the mode flip alone would not catch that; a render-time rule catches both.aria-expandedandexpandedHasheskeep behaving the same across live updates, sorting and filtering.Implementation sketch (
public/packets.js):groupIsExpandedInView(hash)=expandedHashes.has(hash) && !groupRowSelectsOnActivate();buildGroupRowHtmluses it for theexpandedclass, the caret and the child rows, so a mobile row renders exactly like a collapsed one (noexpanded,caret-right, notr.group-child);_getRowCountuses it too, so the virtual-scroll row counts and the rendered rows cannot diverge (fix(ops): serialise SQLite backups per container (#403) #424);_onGroupRowModeResize) also calls_invalidateRowCounts(), because the per-entry row count changes when the mode flips.Tests:
test-packets.js: at 390 px an expanded group renders noexpandedclass, acaret-rightand no child rows, and_getRowCountis 1; at 1400 px the same group still rendersexpanded,caret-down, its children and_getRowCount= 1 + children; the hash stays inexpandedHashesacross 1400 -> 390 -> 1400.test-issue-259-mobile-expanded-group-e2e.js: expand the seeded 3-observation group at 1400 px, resize to 390 px, assert no child rows are visible (and that the row claims no expanded state), then resize back to 1400 px and assert the children andaria-expanded="true"are back. Registered indeploy.ymlnext to the Follow-up to #248: Affinity Debug toggle broken + reversed carets; group-row aria-expanded on mobile #254 line.2.
nodesEsclistener leakinit()adds an anonymous-per-callnodesEscon every node-page init and only removes it when Escape fires. Fix: lift it to one module-level named handler, register it in the direct-node branch ofinit(), and remove it indestroy()(which also runs on the in-modulenavigateToNodepath and from the router). A stable reference also makes a repeataddEventListenera DOM no-op.Test: new
test-issue-259-nodes-esc-listener.js— a vm sandbox with a countingdocument.addEventListener/removeEventListener, run init/destroy for node A -> B -> C, assert the net number of documentkeydownlisteners is at most 1, then fire Escape and assert exactly onelocation.hashwrite.3. Test gap F1 — the fast layer does not prove the template uses the renderer
Add to
test-issue-254-affinity-debug-toggle.jssource-level assertions onpublic/nodes.js:loadFullNodetemplate interpolates${renderAffinityDebugCard()};id="node-affinity-debug"occurs exactly once in the file (i.e. only inside the renderer);onclick=/on*=attribute anywhere near anode-affinity-debugoccurrence.That kills Macmini's M1 ("old inline card back in the template, renderer exported but unused", which passes 9/9 today) in the fast layer, and stops the "no inline handler" case from being vacuously green on an empty string.
4. Test gap F3 — Space on a mobile group row
Add a Space step to the 390 px block of
test-issue-254-affinity-toggle-mobile-aria-e2e.js: focus the group row, press Space, assert the detail sheet opens, the group does not expand and the row is stillselect-hashwithoutaria-expanded.Mutants (at least one per item)
buildGroupRowHtml/_getRowCountback to the plainexpandedHashes.has()check (the master behaviour) — must failtest-packets.jsand the new E2E.destroy()no longer removes the listener and the handler is re-created per init — must fail the new nodes-esc test.onclickcard put back into theloadFullNodetemplate with the renderer left exported but unused — must now failtest-issue-254-affinity-debug-toggle.js, not only the E2E.packets.js(drop' 'from the keydown branch) — must fail the new Space step.Each mutant is reverted with
git checkout --and the tree verified clean afterwards.Suites and gates
sh test-all.sh,node test-frontend-helpers.js,node test-packets.js,node test-issue-254-affinity-debug-toggle.js, plus the #254, #189 and Kpa-clawbot#1461 E2Es and the new ones against a local Go server on a copy ofe2e-fixture.db, prepared as in CI (freshen, the seed SQL,corescope-migrate, seeds 2073 and 199), on a free local port, stopped by port.Gates: no hardcoded colours, no per-item API calls,
scripts/check-xss-sinks.sh --diff origin/masterclean, fork guards unchanged (9 indeploy.yml, 1 inrelease-fast-path.yml), the new E2E registered beside the #254 line.Screenshots at 390 px and 1400 px, light and dark, described in the report comment on the PR.
test(packets): make the mobile "does not expand" assertions bite (#259)
test(nodes): cover the list view's Escape listener too (#259)
fix(nodes): one list-view Escape listener, not one per render (#259)
Merge branch 'master' into codex/issue-259-followups-255
Merge pull request #260 from dborup/codex/issue-259-followups-255
dborup commentedon Oct 6, 2026
Fixed by #260 (merged as
edec4253). Remaining review nits are tracked in #282.