Repository navigation
fix(ui): Affinity Debug toggle and mobile group-row aria-expanded (#254) - #255
Conversation
…xpanded (#254) test-issue-254-affinity-debug-toggle.js renders the node page's Affinity Debug card from the real nodes.js and checks for a disclosure button (aria-expanded, aria-controls on the hidden body, caret-right collapsed, caret-down expanded) with no inline handler, and that the toggle and the delegated click handler open and close it. Red on master: the card is inline in loadFullNode, with a broken inline onclick. test-packets.js: under the 600 px mobile breakpoint of mobile-page-actions.js a multi-observation group row is select-hash without aria-expanded; above it, it stays the #189 toggle. 3 cases red on master. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…254) Node page with meshcore-affinity-debug=true and /api/debug/affinity answered by page.route (no request reaches the server, no API key): the heading button opens and closes the body by click, Enter and Space, with caret and aria-expanded following. Packets: the seeded Kpa-clawbot#1486 group is the #189 toggle at 1400 px, select-hash without aria-expanded at 390 px (tap and Enter open the detail sheet, the group does not expand), and a 1400 -> 390 -> 1400 resize re-renders it. 9 of 12 steps red on master. Registered in deploy.yml after the #189 caret E2E. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…without aria-expanded (#254) nodes.js: the Affinity Debug heading's inline onclick held the Phosphor sprite markup with unescaped double quotes, so the attribute was cut short and the handler was a syntax error; the body never opened. The card now comes from renderAffinityDebugCard(): the heading holds a <button aria-expanded aria-controls> for the body (hidden attribute), with caret-right collapsed and caret-down expanded (the #189 convention). One delegated click listener on #nodeFullBody, wired once in init() because loadFullNode re-renders on theme-refresh. style.css keeps the h4 look for the button with inherited font and colour; no new colours. packets.js: under the mobile breakpoint mobile-page-actions.js (Kpa-clawbot#1461 #7) turns a group-row click into select-hash, but the row still said aria-expanded, and Enter/Space still expanded it. buildGroupRowHtml now derives data-action and aria-expanded from one flag: under the breakpoint the row is select-hash with no aria-expanded, so tap, Enter and Space all select. mobile-page-actions.js exports isMobile() as window.MobilePageActions so the breakpoint stays in one place; its redirect is unchanged. A debounced resize listener re-renders the visible rows only when the breakpoint is crossed. Desktop rows are unchanged from #248. Also fixes the caret helper in test-issue-254-affinity-debug-toggle.js, which kept the "ph-" prefix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rapport — CS-pve-agent3 PR#255 #254 — head 6f09368Status: Both #254 items are fixed on a draft PR. Every requirement has a unit and/or E2E test, all 8 mutants are killed, local suites are green and CI is green on attempt 1. The PR stays a draft. Evidence tags: [T] test or run output, [A] assessment or inference, [K] checked in code, diff, git or CI. Branch
The plan is posted on #254. Part 2 uses the "render Requirements
Red on master [T]
Mutants [T]Each mutant ran against both unit files, the new E2E and the #189 E2E, with a server restart per mutant. Each was then restored (
All 8 are killed by at least one layer. ¹ The log line was truncated, so the count is the ✗ markers visible. Suites (local Go server on a copy of
|
| Suite | Result |
|---|---|
sh test-all.sh |
219/219 files |
node test-frontend-helpers.js |
707 passed, 0 failed |
node test-packets.js |
139 passed, 0 failed |
| New #254 E2E | 12/12 |
| Packets E2Es: #189, Kpa-clawbot#1486, Kpa-clawbot#1122 Details clamp + filter UX, Kpa-clawbot#1128 layout + multi-viewport, #96, #147, #180, Kpa-clawbot#1692, slide-over Kpa-clawbot#1056 / Kpa-clawbot#1168, filter UX, Kpa-clawbot#1758, touch gestures, packet-trace alignment, mobile VCR Kpa-clawbot#1267 | all exit 0 |
| Nodes E2Es: Kpa-clawbot#2073, Kpa-clawbot#1147, #199, nodes export, favourite re-render, node reach, Kpa-clawbot#2027, node liveness | all exit 0 |
Touch targets (test-touch-targets.js, Kpa-clawbot#2052) |
exit 0, 11/11 |
test-a11y-axe-1668.js |
0 violations, 120 cells |
test-e2e-playwright.js |
132/135, 3 skipped |
Screenshots (local, Chromium, SCREENSHOT_DIR; not attached, because the CLI cannot upload images) [T]
- Light, 1400 px, collapsed: the card heading "AFFINITY DEBUG" keeps the muted uppercase
h4look, with a single right caret before the magnifying glass and no body below. - Light and dark, expanded: one down caret. The body shows the mocked data ("No affinity edges for this node", Graph Stats, "Cache age: E2E-254 mock"). The dark theme uses the card's normal tokens, and no colour is added.
- 390 px (touch): the seeded group row has no expand column. A tap opens the bottom detail sheet ("Observations (3)"), and the row stays collapsed.
CI [K]
Run on head 6f09368f, attempt 1, conclusion success. No reruns.
- ✅ Go Build & Test:
test-all.sh219/219, includingtest-issue-254-affinity-debug-toggle.js; the PR-only XSS--diffpreflight ran. - ✅ Playwright E2E Tests: the Follow-up to #248: Affinity Debug toggle broken + reversed carets; group-row aria-expanded on mobile #254 E2E 12/12 and the test: 13 red orphan unit tests + 4 orphan E2E files left after #187; collapsed packet groups show an up-caret #189 E2E 3/3; the bug(packets): filter UX disaster — help panel overlaps table, toolbar chaotic, path chips spill rows Kpa-clawbot/CoreScope#1122 Details clamp (test: Details-clamp E2E 'advert links in Details stay visible and clickable' is time-dependent and flaky #244) passed 18/18.
- ✅ Build & Publish Docker Image.
- 📦 Release Artifacts, 🚀 Deploy Staging, 📝 Publish Badges: skipped (fork-guarded).
Neither known flake fired (#244 Details clamp, #250 TestStatsFileHasNoCredentials_118).
Remainder
- A group expanded on desktop keeps its child rows after shrinking to mobile, and has no collapse path there. This dead-end existed before (Tufte v2: mobile packets view — kill chrome, kill duplication (follow-up to #1458 / #1459) Kpa-clawbot/CoreScope#1461 API: harden configured-scope contract for Local Mesh consumers #7). The row now announces no state. [A]
- The Tufte v2: mobile packets view — kill chrome, kill duplication (follow-up to #1458 / #1459) Kpa-clawbot/CoreScope#1461 API: harden configured-scope contract for Local Mesh consumers #7 click redirect now only matters for rows rendered before a breakpoint crossing (inside the 150 ms debounce). It is deliberately left unchanged; a later cleanup could drop it. [A]
- Open/closed state of the Affinity Debug card resets when
loadFullNodere-renders (theme-refresh). This happened before too. [K] - The card still fetches
/api/debug/affinityon page load, even while collapsed. Unchanged. [K] aria-expandedonrole="row"in a plain table: screen-reader output has not been checked (carried over from fix(packets): collapsed groups show a right caret, expanded a down caret (#189) #248). [A]- Not verified: Firefox, Safari, a real screen reader, staging or production (by design). No customizer values were added.
Review — CS-Macmini PR#255 affinity-toggle — head 6f09368Dom: APPROVE med nits Independent, read-only review. I made no changes to the PR. Local runs used Evidence tags: [T] test or run output, [A] assessment or inference, [K] checked in code, diff, git or CI. Findings
No blocking findings. 1. Affinity Debug
2. Mobile:
|
| Test | master d05b0db5 |
merged dcec26c5 |
|---|---|---|
test-issue-254-affinity-debug-toggle.js |
1 passed, 8 failed | 9/9 |
test-packets.js |
136 passed, 3 failed (390 / 390-expanded / 600 px) | 139/139 |
test-issue-254-affinity-toggle-mobile-aria-e2e.js |
3 passed, 9 failed | 12/12 |
- The E2E mocks
/api/debug/affinitywithctx.routeand never sends a key. I confirmed that every affinity request in my own runs carried an emptyX-API-Key(≥ 1 request). [T][K] - The unit tests run the real code.
test-packets.jsloadspublic/packets.jsandpublic/mobile-page-actions.jsinto a vm sandbox and variesinnerWidth. The Follow-up to #248: Affinity Debug toggle broken + reversed carets; group-row aria-expanded on mobile #254 unit test loads the realpublic/nodes.js. [K]
Mutants
Each mutant is a copy of the merged tree's public/. For each one I ran the #254 unit test, test-packets.js, the #254 E2E and the #189 E2E against a fresh server.
| Mutant | #254 unit | test-packets.js |
#254 E2E | #189 E2E | Reviewer checks | Verdict |
|---|---|---|---|---|---|---|
M1: master's inline onclick card put back into the loadFullNode template (renderer exported but unused) |
9/9 green | green | 5 failed | green | no button, script aborts | killed by the E2E only (F1) |
M1b: renderAffinityDebugCard() returns master's inline markup |
7 failed | green | 5 failed | green | aborts | killed |
M2: aria-expanded back on mobile group rows |
green | 3 failed | 3 failed | green | 4 failed | killed |
M3: delegated listener added in loadFullNode (every render) instead of init |
green | green | 12/12 | green | 1 failed (call count only) | equivalent, see below |
M4: own innerWidth < 600 check instead of MobilePageActions.isMobile() |
green | 2 failed | 12/12 | green | 1 failed (600 px touch) | killed by the unit test |
M5: resize listener not added in init |
green | green | 1 failed | green | 2 failed | killed |
| M6: carets swapped | 3 failed | green | 5 failed | green | aborts | killed |
M7: toggle flips aria-expanded and the caret but leaves the body hidden |
2 failed | green | 3 failed | green | 1 failed | killed |
M8: resize re-render without resetting _lastVisibleStart / _lastVisibleEnd |
green | green | 1 failed | green | 2 failed | killed |
M3 is behaviourally equivalent. addEventListener with the same function reference, type and capture flag on the same element is de-duplicated by the DOM. So re-adding the named onFullBodyClick on every loadFullNode registers it once. My call-counting check saw 4 calls on the live #nodeFullBody, but one click still toggled exactly once. The code comment "would stack listeners" is therefore stricter than necessary for a named handler, though it would apply to an inline closure. Wiring it in init() is still the cleaner choice. [T][A]
All non-equivalent mutants are killed by the PR's own tests. M1 is killed only by the E2E (F1). [T]
5. Rules
- No hardcoded colours. The added lines contain no hex,
rgb()orhsl()values. The CSS usesinheritandnone. [K] - XSS gate:
scripts/check-xss-sinks.sh --diff origin/masteron the head exits 0. I ran it in a temporary detached worktree, which I removed afterwards.git diff --checkis clean. [T] - Fork guards:
github.repository == 'Kpa-clawbot/CoreScope'occurs 9 times indeploy.ymland once inrelease-fast-path.yml, the same as master. [K] deploy.yml: exactly one added line, the Follow-up to #248: Affinity Debug toggle broken + reversed carets; group-row aria-expanded on mobile #254 E2E right after the test: 13 red orphan unit tests + 4 orphan E2E files left after #187; collapsed packet groups show an up-caret #189 caret E2E. [K]- Closing keywords: none in the title, body or commit messages. The PR says "Relates to Follow-up to #248: Affinity Debug toggle broken + reversed carets; group-row aria-expanded on mobile #254",
closingIssuesReferencesis empty, and Follow-up to #248: Affinity Debug toggle broken + reversed carets; group-row aria-expanded on mobile #254 is still open. [K] - Commit author: all three commits have author and committer
dborup <kontakt@meshview.dk>. [K] - No new
map[string]interface{}: the PR changes no Go code. [K]
Suites (merged tree, against origin/master d05b0db5)
| Suite | Result |
|---|---|
sh test-all.sh |
219/219 files [T] |
node test-frontend-helpers.js |
707 passed, 0 failed [T] |
node test-packets.js |
139 passed, 0 failed [T] |
#254 E2E / #189 E2E / test-issue-1461-mobile-page-actions.js |
12/12, 3/3, 6/6 [T] |
| Packets E2Es: Kpa-clawbot#1486, Kpa-clawbot#1122 clamp (18/18) + filter UX, Kpa-clawbot#1128 layout + multi-viewport, #96, #147, #180, Kpa-clawbot#1692, slide-over Kpa-clawbot#1056 / Kpa-clawbot#1168, filter UX, Kpa-clawbot#1758, touch-gestures coverage, packet-trace alignment | all exit 0 [T] |
| Nodes E2Es: Kpa-clawbot#2073, Kpa-clawbot#1147, #199, nodes export, favourite re-render, node reach, Kpa-clawbot#2027, node liveness; touch targets, Kpa-clawbot#2052 | all exit 0 [T] |
test-e2e-1267-mobile-vcr.js |
not run: it needs /usr/bin/chromium, which this macOS host lacks [T] |
test-a11y-axe-1668.js |
local net=1 (mobile light /observers colour contrast on "connected") and identical on master. CI on this head reports net=0. Local and time-dependent, not this PR. [T][K] |
test-e2e-playwright.js |
local fail-fast at "Version info lives on Perf dashboard" (the #navStats wait times out), identical on master. It passes in CI on this head. [T][K] |
The local Go server ran on a copy of e2e-fixture.db, prepared as in CI: freshen, the seed SQL from deploy.yml, corescope-migrate, then seeds 2073 and 199. I stopped it by port.
CI: run on head 6f09368f, attempt 1, conclusion success. The log shows the #254 E2E at 12/12, axe at net=0, and Version info ✅. [K]
Screenshots (local Chromium; described, not attached)
- Light, 1400 px, expanded: "AFFINITY DEBUG" keeps the muted uppercase
h4look, with a single down caret before the magnifying glass. The body shows the mocked "No affinity edges for this node" and Graph Stats. [T] - Dark, 1400 px, collapsed: a single right caret, no body, and the card uses the normal dark card tokens. [T]
- 390 px, touch, light and dark, expanded after a tap: the heading button spans the card at 48 px height with one down caret. The body text wraps cleanly, and nothing overflows horizontally. [T]
- 390 px packets, light and dark: the seeded group row shows Time / Type / Details with no expand column. A tap opens the bottom sheet, and the row stays collapsed. [T]
Not verified
- Firefox, Safari / WebKit, a real phone or tablet, and a real screen reader. In particular, I have not heard how
aria-expandedon arole="row"in a plain table is announced; this is carried over from fix(packets): collapsed groups show a right caret, expanded a down caret (#189) #248. - A real WebSocket stream: the live update was injected through the page's WS listeners.
test-e2e-1267-mobile-vcr.jslocally (no/usr/bin/chromium).- The full
test-e2e-playwright.jslocally (fail-fast on an env-only failure that also fails on master); I relied on CI for it. - Staging and production, by design.
Relates to #254
Follow-up to #248 (#189), findings F1 and F2 from its review. Plan: comment on #254.
1. Node page "Affinity Debug" toggle
Bug: the heading's inline
onclickheld the Phosphor sprite markup with unescaped double quotes. The browser cut the attribute short, the handler was a syntax error, and the body never opened. The carets were also reversed against the #189 convention (caret-downcollapsed,caret-upopen).Fix (
public/nodes.js,public/style.css):renderAffinityDebugCard(). The heading holds a<button type="button" class="affinity-debug-toggle" aria-expanded aria-controls="affinityDebugBody">. The body uses thehiddenattribute.caret-rightcollapsed,caret-downexpanded, from one helper, so the render and the toggle cannot disagree.toggleAffinityDebug(btn)flipsaria-expanded, the body and the caret. One delegated click listener on#nodeFullBodyis wired once ininit(). It is not wired inloadFullNode, which re-runs ontheme-refreshand would stack listeners. As a<button>, it handles Enter and Space natively.h4look (font/color/letter-spacing/text-transform: inherit) and gets a 48 px min-height on coarse pointers. No new colours./api/debug/affinityfetch and its rendering are unchanged.2. Mobile group rows:
select-hash, noaria-expandedBug: under the 600 px breakpoint,
mobile-page-actions.js(Kpa-clawbot#1461 #7) rewrites a group-row click toselect-hash, and the expand column is hidden. The row still announcedaria-expanded="false". Also, Enter/Space on a focused row still went throughtoggle-selectand expanded the group (the redirect only catchesclick). The E2E confirmed this on master.Choice: render the row as
select-hashunder the breakpoint, rather than only dropping the attribute.buildGroupRowHtmlderivesdata-actionandaria-expandedfrom one flag:aria-expandedis present if and only if the row toggles;Details:
mobile-page-actions.jsexportswindow.MobilePageActions = { isMobile }, so the 600 px value stays in one place. Without that module nothing redirects, so the row toggles.resizelistener, added in the pageinitand removed indestroy, re-renders the visible rows only when the breakpoint is actually crossed (e.g. a phone rotating 390 ↔ 844 px).toggle-select, caret andaria-expanded.Perf:
groupRowSelectsOnActivate()is oneinnerWidthcomparison per rendered row, and only the visible slice is rendered (virtual scroll).Tests
Tests come first: two red commits, then the fix.
test-issue-254-affinity-debug-toggle.js(new, intest-all.sh) runs the realnodes.jsin a vm sandbox. It checks:aria-expanded="false",aria-controlson the hidden body, onecaret-right, noon*=attribute);test-packets.jsgets 7 new cases:select-hashwithoutaria-expanded, also when expanded;mobile-page-actions.js, a row toggles.test-issue-254-affinity-toggle-mobile-aria-e2e.js(new, indeploy.ymlafter the test: 13 red orphan unit tests + 4 orphan E2E files left after #187; collapsed packet groups show an up-caret #189 caret E2E):meshcore-affinity-debug=true, and/api/debug/affinityanswered bypage.route, so no request reaches the server and no API key is sent;SCREENSHOT_DIRsaves screenshots.Gates
scripts/check-xss-sinks.sh --diff origin/masteris clean.git diff --checkis clean.deploy.yml, 1 inrelease-fast-path.yml.🤖 Generated with Claude Code