Repository navigation
fix(region-filter): honor URL regions across views and clean up page listeners - #549
Conversation
…ed listeners (#531) Unit tests for RegionFilter.syncFromUrl (parsing, precedence over the saved selection, unknown codes, one notification) and the router order, plus a Playwright suite: fresh and in-app ?region= links on Packets, Nodes, Live, Channels and Analytics, listener counts over ten navigations, the stale Packets listener and back/forward. Against master: unit 1/26 and E2E 1/18 pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…listeners (#531) RegionFilter.syncFromUrl() owns the hash-query region parameter: codes are trimmed, empty entries and repeats dropped, unknown codes dropped once regions are known (none left means all regions), and a link with codes overrides the saved selection and is saved like a click. No parameter or an empty one leaves the saved selection as before. The router calls it after the old page's destroy() and before the new page's init(), so every view's first region-dependent request, Live's early node fetch included, carries the link's region, with no reload. Listeners hear a genuine change once and a no-op notifies nobody. Packets drops its own ?region= handling, which applied the link after its first fetch. Packets, Nodes and Live now remove their RegionFilter and AreaFilter listeners in destroy(), so a region change elsewhere no longer reloads packets or rewrites the URL to #/packets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rapport — CS-pve-agent1 PR#549 #531 — head 496fba6Status: All requirements implemented and tested; 21/21 mutants killed; CI green on the first attempt (no reruns); draft, not marked ready. Evidence: [T] test run locally against a Go server on the CI-prepared Red/green counts: unit Requirements
Invariants
Local runs [T]
CI per job [K]
Known flaky #271 did not occur. No job was re-run. Remaining items
|
Review — CS-pve-agent3 PR#549 — head 496fba6Dom: APPROVE with nits Independent, read-only review. Evidence tags: [T] run by me locally · [A] code reading / analysis · [K] CI on this head. Scope reviewed: Findings
No blocking defects found. Answers to the review points
Invariants
Tests run [T] (merged tree
|
| Mutant | Killed by |
|---|---|
U1: knownCodes matches case-sensitively |
unit: "codes match case-insensitively…" |
U2: sameSelection becomes order-sensitive |
unit: "an unchanged selection (any order) notifies nobody" |
U3: drop the all-unknown notify() after fetchRegions() |
unit: "fresh load: link with only unknown codes … notifies once" |
U4: dedupe case-sensitively in parseUrlRegions |
unit: "fresh load: repeats differing only in case…" |
U5: router calls syncFromUrl before destroy() |
unit: router order test |
| E1: same as U5, in the browser | E2E 5/18 red: the outgoing page's listener reloads (extra /api/packets or /api/nodes) on every SPA link |
E2: Live's destroy() keeps its area-outline listener |
E2E: "10 navigations…": area listeners on packets grew 1 → 2 |
7/7 killed. E1 also shows why the destroy→sync→init order matters.
CI per job [K] (run 40076266623, attempt 1, no reruns)
Go Server Tests 1/3, 2/3, 3/3 · Go Ingestor Tests · Go Libraries & CLI · Verify Go Test Assignments · JavaScript Unit Tests & Lint · Script & Container Unit Checks · Playwright E2E 1/3, 2/3 (new suite 18 passed, 0 failed), 3/3 · Verify E2E Shards & Coverage · Validate Docker Image · Build & Publish Docker Image · Go Build & Test: all pass. Publish Badges, Release Artifacts and Deploy Staging were skipped (fork). Known flaky tests #256 and #267 did not show up.
Not verified
- I did not re-run the 14 other existing E2E suites the author lists. I relied on CI shards 1–3 passing on head [K], and the merge with fix(route-view): exclude payload-derived endpoints from displayed hop count #546 touches no file in this PR's frontend path except the shard manifest and
test-all.shregistration. - I did not exercise Observers or Map with
?region=in a browser (only [A]). - I did not test races where
/api/config/regionsis slow or fails during an SPA navigation; I only read that code path [A]. - No staging or production checks, by design.
…oice and ?region=All loading twice (#531) - A change in the region control leaves an existing ?region= in the hash, so Back or a reload brings the link's region back over the user's choice. Unit and E2E cases for Packets, Nodes, Live, Channels, Analytics and Observers; other hash parameters and the route must be kept. - ?region=All (any case) must mean all regions with one unfiltered request. - The E2E seeds the saved selection once per tab, so a reload keeps what the page saved. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd treat All as all regions (#531) - RegionFilter rewrites a region parameter already in the hash after a change in the control (URLState.updateHashParams + replaceState, other parameters and the route kept, removed for all regions), before listeners are told. One helper covers every view with a control; a hash without the parameter is left alone, and Packets still writes its own. - "All" in any case in ?region= is the server's Kpa-clawbot#770 catch-all: alone it selects all regions without a request carrying it, next to real codes it is dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rapport — CS-pve-agent3 PR#549 runde-2 — head 928642aReview feedback addressed (commit Evidence: [T] run locally against a Go server on the CI-prepared
Tests (red before → green after)
New unit cases (10). The 3 marked guard also pass on round 1:
New E2E cases (10). Each one before the fix:
The E2E harness now seeds the saved selection once per tab, using a Mutants (8 unit, 4 of them also in E2E) — all killed [T]
Local runs [T] (this head)
CI per job [K] (run 40083574500, attempt 1, head
|
| Job | Result |
|---|---|
| Go Server Tests 1/3, 2/3, 3/3 | pass |
| Go Ingestor Tests | pass |
| Go Libraries & CLI | pass |
| Verify Go Test Assignments | pass |
| JavaScript Unit Tests & Lint | pass |
| Script & Container Unit Checks | pass |
| Playwright E2E Tests 1/3, 2/3, 3/3 | pass (new suite 28 passed, 0 failed in 2/3, about 3 min) |
| Verify E2E Shards & Coverage | pass |
| Validate Docker Image | pass |
| Build & Publish Docker Image | pass |
| Go Build & Test | pass |
| Publish Badges & Summary, Release Artifacts, Deploy Staging | skipped |
Known flaky tests #271 and #301 did not occur. No job was re-run.
Invariants
- No Go,
cmd/or.github/changes; no newmap[string]interface{}; no CSS or colour changes [A] - Commits: author and committer
dborup <kontakt@meshview.dk>; no rebase, amend or force-push (the push was a fast-forward from496fba6d) [T] - PR description updated (Plan, Tests, Residuals); the PR stays a draft [T]
- Perf: one
URLStateparse and rebuild per click in the control, only when the hash hasregion; no new requests, and one fewer on a fresh?region=All[A][T]
Not verified
- No staging or production checks, by design.
- Safari's
replaceStatethrottling path (caught and ignored) was not exercised in a browser.
Review — CS-Minimax PR#549 — head 928642aDom: APPROVE with nits Independent, read-only re-review of the whole PR (rounds 1 and 2), not only the round-2 delta. Evidence tags: [T] run by me locally · [A] code reading / analysis · [K] CI on this head. Scope: Findings
No blocking defects found. Answers to the review points
Invariants
Tests run [T] (merged tree
|
| Mutant | Result |
|---|---|
MA: knownCodes keeps the link's spelling instead of the configured one |
killed (unit: "take the configured spelling") |
MC: the last region parameter wins instead of the first |
killed (unit: "the first region parameter wins") |
ME (unit): syncFromUrl does not saveToStorage() |
killed (unit: 4 red) |
MF: only exactly All is the catch-all |
killed (unit: "in any case … ALL") |
MD: syncUrlRegion passes null instead of history.state |
survived, see finding 3 |
MB: drop _urlBeforeLoad = false in fetchRegions |
survived, equivalent on the success path (finding 5) |
EA (E2E): syncFromUrl does not save |
killed (E2E 4 red: in-app SJC,XXX, Packets shared handling, both ?region=All cases) |
EB (E2E): drop the all-unknown notify() after fetchRegions() |
killed (E2E: "fresh link with only an unknown code": last /api/nodes region XXX, want null) |
6 of 8 killed. One survivor is equivalent; the other is a test gap (finding 3).
CI per job [K] (run 40083574500, attempt 1, head 928642a6, base e08072be)
Go Server Tests 1/3, 2/3, 3/3 · Go Ingestor Tests · Go Libraries & CLI · Verify Go Test Assignments · JavaScript Unit Tests & Lint · Script & Container Unit Checks · Playwright E2E 1/3, 2/3, 3/3 · Verify E2E Shards & Coverage · Validate Docker Image · Build & Publish Docker Image · Go Build & Test: all pass. Publish Badges & Summary, Release Artifacts and Deploy Staging were skipped. No reruns. Known flaky tests #256 and #267 did not show up. CI ran on base e08072be; the later master commits (#543, #547, #550) touch no file in this PR, and I ran the local suites on the merged tree.
Not verified
- I did not re-run the tests on the newest master
a4126c63. The merge is clean, and neighbors: follow-ups from #541 (graph-age clamp, cache/publish tests, atomic.Pointer) #550 changes only Go and config. - I did not run
test-channels-client-state-152-decrypt-e2e.js,test-region-scope-approval-e2e.jsortest-issue-406-packets-live-channel-e2e.js. They start their own server or ingestor; I relied on CI [K]. - I did not test races where
/api/config/regionsis slow or fails during an SPA navigation, and I did not test Safari'sreplaceStatethrottling (code reading only) [A]. - No staging or production checks, by design.
Relates to #531
Problem
?region=from the hash, and it applied the link after its first/api/packetsrequest, without telling anyone. A fresh#/packets?region=SJCover a savedSFOfetched SFO and labelled it SJC.replaceState'd the URL to#/packets?.... Nodes (area) and Live (two area listeners) also leaked one listener per visit.Plan and changes
public/region-filter.js:RegionFilter.syncFromUrl(hash)owns the hash-queryregionparameter:region,region=,region=,,,region=%20region= SJC ,OAK,SJCSJC,OAK: codes trimmed, empty entries and repeats (case-insensitive) dropped, order keptregion=SJC%2COAKregion=OAK®ion=SJCURLSearchParams.getsemantics)region=SJC,XXXSJC: unknown codes are droppedregion=XXXregion=sjcSJC: codes match case-insensitively, as the server does, and take the configured spellingfetchRegions()cleanup drops unknown onesregion=All(any case, the server's Kpa-clawbot#770 catch-all)All; next to real codes (SJC,All) it is dropped/api/config/regionslists every observer IATA (handleConfigRegions), so a dropped code matches no observer and the request already returned the same data.public/region-filter.js: hash write-back after a control change. When the user changes the region control, aregionparameter already in the hash follows the selection: it is rewritten, or removed for all regions, withURLState.updateHashParams+history.replaceState, keeping the route and the other parameters (Analytics'tab, Live'snode, ...), before listeners are told. One helper intoggleRegion()covers every view with a control (Packets, Nodes, Live, Channels, Analytics, Observers), so a stale link value cannot override the choice on Back or on reload. A hash withoutregionis left alone; Packets still writes its own full URL. Map has no region control.public/app.js:navigate()callsRegionFilter.syncFromUrl(location.hash)after the old page'sdestroy()and before the new page'sinit(). This covers the fresh load, SPA links and back/forward. Every view's first region-dependent request carries the link's region, including Live's node fetch, which runs before Live callsRegionFilter.init(). No second load is needed. Calling it afterdestroy()means the outgoing page's listeners are gone and cannot react.public/packets.js: drops_pendingUrlRegion, its own URL parsing and its ownsetSelected()call. It keeps handles for its region and area listeners and removes them indestroy().public/nodes.js,public/live.js: keep and remove their AreaFilter listener handles indestroy(). The other views already did this.Compared with upstream Kpa-clawbot#2256, this applies the link in the router rather than in
init()plus a notification afterfetchRegions(). That removes the double load the upstream review raised: Live reloaded after its early fetch, and Nodes and Analytics loaded twice on in-app links.setSelected()stays non-notifying, which leaves the #416 notification question separate.No config, schema, workflow or Go change. No customizer values.
Tests
test-issue-531-region-url.js(unit, registered intest-all.sh), 36 cases. Covers parsing and precedence, unknown codes and case before and after regions load, one notification versus a no-op, fresh-load cleanup notifications, the router orderdestroy → syncFromUrl → init,?region=All, and the hash write-back after a control change (route, subpath and other parameters kept, removed for All,replaceStateonly, before listeners are told, a hash withoutregionuntouched). Against master: 1/26 of the first-round cases pass (the click-notifies refactor guard); against round 1 (496fba6d): 29/36.test-issue-531-region-url-e2e.js(Playwright, shard 2), 18 cases on the CI fixture (regions MRY/OAK/SFO/SJC, saved selection SFO):#/<view>?region=SJCon Packets, Nodes, Live, Channels and Analytics: the first region-dependent request carriesSJC, the control shows SJC, SJC is saved, and the per-path API request counts equal a load with SJC already savedSJC,XXX, Packets writing backSJC, and a no-op link that loads once/api/packetsrequest and the URL stays#/nodes?region=SJC, OAK in the control on Packets, Nodes, Live, Channels, Analytics and Observers: the hash says OAK, other parameters kept, and a reload keeps OAK#/nodes?region=SJC, OAK in the control, Channels, Back: OAK, not SJC; picking All removesregionand Back and reload keep All?region=All/?region=aLLon Nodes and Live: one unfiltered request496fba6d): 19/28.Perf
syncFromUrlis O(codes + regions) and runs once per navigation. The E2E compares the per-path counts of every/api/request (minus the/api/statsand/api/healthzpollers) against a baseline for each view, both fresh and after an SPA link./api/packetson region or area changes elsewhere, and Nodes and Live no longer reload once per earlier visit on an area change.Local validation
go test ./...incmd/serverandcmd/ingestor: oksh test-all.sh: 264/264 files, includingtest-frontend-helpers.jse2e-fixture.db(freshened, seeded, migrated as indeploy.yml): the new suite plus 14 existing suites (coretest-e2e-playwright.js, the bug(live): region filter wipes all packets and lines from view (regression on master since #1080) Kpa-clawbot/CoreScope#1136 live region, Page modules leak delegated handlers on #app/document (compare, channels, live) — remainder of #405 #428 live/channels and observers.js leaks delegated click/keydown handlers on the shared #app container #405 observers listener-leak, fix(pages): drop late responses that belong to an earlier page visit (#2218, #2230, #2200) Kpa-clawbot/CoreScope#2248 lifecycle, test: 'home My Mesh card: direct names, relay count, dialog lists both' E2E times out intermittently (recurring) #521, fix(packets): updatePacketsUrl drops ?obs= and ?viewPath= on load and on filter changes #147, fix(packets): leaving with the SlideOver open rewrites the new page's URL; Clear Filters on a detail URL re-filters on reload #180, Evaluate upstream packet-table sizing, Full Names and path popup improvements (2090) #217, Feature request: Add an optional “Hide CONTROL packets” checkbox to the packet monitor #96, fix(channels): keep client-only state (PSK selection, My Channels, unread) across a channel-list refresh #152, filter-ux and bug: /packets init() serializes loadObservers→loadPackets — 25-40s time-to-first-row on slow links (root cause of #1662 flakes) Kpa-clawbot/CoreScope#1692; in round 2 also Page modules leak delegated handlers on #app/document (compare, channels, live) — remainder of #405 #428 compare, fix(channels): keep client-only state (PSK selection, My Channels, unread) across a channel-list refresh #152 decrypt, bug(packets): filter UX disaster — help panel overlaps table, toolbar chaotic, path chips spill rows Kpa-clawbot/CoreScope#1122, fix(packets): enforce the selected channel filter on WebSocket updates #406 and security(ui): escape untrusted node, observer and route values in HTML rendering #333 pages) all passscripts/check-xss-sinks.sh --diff origin/master: cleannode scripts/run-e2e-shard.js --validate: validResiduals
regionto a URL that has none; only an existing parameter is kept in step. Older history entries keep the region they were opened with (the link wins on Back to them, by design).🤖 Generated with Claude Code