Skip to content

fix(packets): URL and modal leftovers from #167 (#180) - #204

Merged
dborup merged 6 commits into
masterfrom
codex/issue-180-packets-url-modal
Oct 4, 2026
Merged

dborup merged 6 commits into
masterfrom
codex/issue-180-packets-url-modal

Conversation

@dborup-agent

Copy link
Copy Markdown
Collaborator

Relates to #180

Fixes the open items 2–6 of #180 (URL and modal leftovers found in the #167 review). Item 1 was fixed by #167 and is not touched here.

Plan

One commit per item, each with its test and fix, plus one test-only commit:

  1. Item 2: Clear Filters on a detail URL closes the detail and writes #/packets?….
  2. Item 3: the SlideOver stacks above the top nav, and its × is 48×48.
  3. Item 2 E2E: compare Clear with the reload instead of counting rows (test-only).
  4. Item 4: the View Path modal leaves Escape to a layer opened over it.
  5. Item 5: the View Path modal closes on route change, and Back/Forward does not reopen a closed modal.
  6. Item 6: Escape closes the ≤640 px detail sheet.

There are no new config values, so nothing is needed in the customizer.

Decision for item 2

Clear Filters also removes the detail subpath. The detail closes, ?obs= and ?viewPath=1 go with it, and the URL becomes the list URL #/packets?… with no hash filter.

Items

Item 2: Clear Filters on a detail URL

Fix The Clear handler resets selectedObservationId, calls closeDetailPanel() and then updatePacketsUrl({ subpath: '', obs: null }). closeDetailPanel() now re-renders the rows only if a row was selected, so a Clear with no detail open still renders nothing extra (#121 test).
Test test-issue-147-packets-url-detail-params.js: the Clear cases now expect #/packets (3 cases are red on master). test-issue-180-packets-detail-close.js: no render when nothing is selected. E2E at 1400 px: detail URL → Clear → #/packets, Clear hidden, detail closed → reload shows the same count, Clear hidden, empty hash input. The #147 E2E Clear step is updated to the decision.
Mutants Clear calls updatePacketsUrl() without a detail argument (keeps the subpath): 3 unit cases and the E2E fail. Clear does not close the detail: the unit tests (#147, #121/#132 harness) and the E2E fail.

Item 3: SlideOver × under the top nav at 641–1023 px

Fix .slide-over-backdrop and .slide-over-panel use the existing --z-modal-backdrop and --z-modal tokens instead of 1000/1001. That puts them above the sticky .top-nav (1100), as the z-index scale's migration policy asks. The SlideOver is already role=dialog, aria-modal and focus-trapped. .slide-over-close is 48×48 (it was 44).
Test E2E at 641, 800 and 1023 px: × is the topmost element at its centre (elementFromPoint), is at least 48×48, and a real page.click on it closes the SlideOver and writes the list URL. On master, the nav (top-nav / theme-toggle-track) is at the × centre.
Mutants Panel back to z-index: 1001: all 3 widths fail (the backdrop is now on top of ×). min-width/height: 44px: all 3 widths fail on the size check.

Behaviour change: while the SlideOver is open, its backdrop now covers the top nav and the ≤768 px bottom nav. A click there closes the SlideOver instead of navigating.

Item 4: Escape is swallowed by the View Path modal

Fix The modal's capture-phase handler does nothing when the focused element is in a floating layer drawn over the modal. That means all of these hold: the element is outside the modal; it is inside a position: fixed ancestor; that ancestor is not one of the packets detail surfaces the modal opens over (SlideOver, mobile sheet); and the element is topmost at its own centre. Examples are the global search (Ctrl+K), the nav More menu, a hamburger menu, the More sheet and a filter popover. If focus is on the page, on a control under the modal's backdrop, or in the sticky top nav, Escape still closes the modal and stops the event (#167).
Test test-packet-path-map.js: 6 cases (search, fixed nav menu → left alone; covered control, SlideOver, mobile sheet, sticky top nav → modal closes). The 2 "left alone" cases are red on master. E2E at 1400 px: Ctrl+K → Escape closes the search while the modal stays with viewPath=1, and the next Escape closes the modal. The same flow is tested with the nav More menu.
Mutants The handler always stops Escape: 2 unit cases and both E2E steps fail. The SlideOver/sheet exclusion is removed: 2 unit cases fail, and so does the #147 tablet step ("Escape closes only the View Path modal; the SlideOver stays"). The hit test is removed: the covered-control unit case fails.

Item 5: Back/forward reopens a closed View Path modal

Fix packet-path-map.js: while open, the modal listens for hashchange and closes when the hash path changes. A change to the query only keeps it open. open() marks the #/packets/<hash> history entry with an id in history.state; this changes no URL. If the modal is closed while another route is shown, the id goes on a list of closed entries, capped at 50. The new PacketPathMap.restore(hash) (used by packets.js init() for ?viewPath=1) does not reopen a modal whose entry is on that list. The cold-load updatePacketsUrl() then drops ?viewPath=1, because no modal is open. updatePacketsUrl() keeps history.state instead of writing null. A new link to the same URL (no state) and a reload of an entry whose modal was open still open the modal.
Test test-packet-path-map.js: 9 cases. They cover: the mark in state without a URL write; no mark off the packets route; route change closes the modal without a URL write; a query-only change keeps it; Forward after a route-change close; Escape-close while on another route; a new link still opens; a reload still opens; the 50-entry bound. test-issue-147-…: updatePacketsUrl() keeps history.state. E2E at 800 and 1400 px: from #/nodes → packet → View Path → Back closes the modal → Forward does not reopen it and viewPath leaves the URL.
Mutants No hashchange listener: 4 unit cases and both E2E steps fail ("stayed open over #/nodes"). restore() ignores the closed list: 3 unit cases and both E2E steps fail. updatePacketsUrl() writes null state again: the #147 unit case and both E2E steps fail. init() calls open() instead of restore(): both E2E steps fail.

Item 6: Escape never closes the mobile detail sheet

Fix closeDetailPanel() (called by the packets Escape handler) also removes .open from #mobileDetailSheet, as its × does.
Test test-issue-180-packets-detail-close.js: the sheet closes and the selection clears (red on master). E2E at 390 px (mobile): Escape closes the sheet. With View Path open over the sheet, the first Escape closes only the modal and the second closes the sheet.
Mutant The sheet is not closed: the unit case and both E2E steps fail.

Tests run locally

Performance

  • The Escape hit test runs only for an Escape keydown while the View Path modal is open: one ancestor walk and one elementFromPoint.
  • The hashchange listener exists only while the modal is open.
  • The closed-entry list is capped at 50.
  • closeDetailPanel() now skips renderTableRows() when no row is selected. Before this change, every Escape on the packets page re-rendered the visible rows.
  • No API calls were added, and there are no full DOM rebuilds.

Known leftovers (not in this PR)

  • At 641–1023 px, the View Path modal (.modal-overlay, z-index 200) is drawn under the SlideOver. This was already the case at 1001, before this PR. Raising it above the SlideOver also needs the global search overlay (300) raised, so it needs its own stacking decision.
  • The packets Escape handler (pktEsc) listens on document. So Escape in the global search also closes the desktop detail pane under it. On master this happens even without the modal. With this PR, the modal itself stays.
  • Closing the desktop detail pane or the mobile sheet (× or Escape) still does not write the URL (out of scope, noted in the fix(packets): updatePacketsUrl drops ?obs= and ?viewPath= on load and on filter changes #147 E2E).

🤖 Generated with Claude Code

dborup and others added 6 commits October 4, 2026 05:32
)

A #/packets/<hash> subpath sets filters.hash on load, so a Clear Filters
that kept the subpath was undone by a reload: the list went back to one
row and the Clear button came back. Clear now closes the detail and
writes the list URL #/packets?… (decision for #180 item 2). The rest of
the #121/#132 Clear behaviour and buildPacketsQuery() are unchanged.

closeDetailPanel() re-renders the rows only when a row was selected, so
Clear with no detail open still renders nothing extra (#121 test).

Tests: the #147 unit and E2E Clear steps now expect #/packets; new
test-issue-180-packets-detail-close.js and
test-issue-180-packets-url-modal-e2e.js (Clear → reload at 1400 px).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tton (#180)

At 641–1023 px the SlideOver panel (z-index 1001) sat under the sticky
.top-nav (1100), so its header and × were covered and a real click on ×
hit the nav. The SlideOver is a modal dialog (role=dialog, aria-modal,
focus trap), so its backdrop and panel now use the existing
--z-modal-backdrop / --z-modal tokens, per the z-index scale's migration
policy. The close button's tap target is 48×48 (Kpa-clawbot#2052 house rule).

E2E at 641, 800 and 1023 px: × is the topmost element at its centre, at
least 48×48, and a real click closes the SlideOver.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ow count

Clear restores the default 15 min window, and the fixture's packets can
age out of it during a long E2E run, so "more than one row after Clear"
is not stable. The step now waits for the /api/packets response and
checks that the reload shows the same count, URL and Clear state as
Clear did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hat layer's (#180)

The modal's capture-phase Escape handler stopped every Escape, so with
the global search (Ctrl+K) or the nav More menu open over the modal, the
first Escape closed the modal underneath and the search/menu stayed.

The handler now leaves Escape alone when focus is in a floating layer
drawn over the modal: outside it, inside a position:fixed ancestor that
is not a packets detail surface the modal opens over (SlideOver, mobile
sheet), and topmost at its own centre. Focus on the page, a control
under the modal's backdrop, or the sticky top nav still closes the
modal, and the #167 "Escape closes only the top layer" behaviour over
the detail pane and the SlideOver is unchanged.

Tests: unit cases for each layer kind in test-packet-path-map.js; E2E at
1400 px for the search and the More menu over the modal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d does not reopen it (#180)

The modal stayed open over the next page after Back, and the packets
history entry kept ?viewPath=1 (#167), so Forward onto it reopened a
modal the user had already closed.

- The modal closes on a hashchange to another path (Back/Forward, a nav
  link). A query-only change keeps it.
- open() marks the #/packets/<hash> entry it opened on in history.state
  (no URL write). When the modal is closed while another route is
  shown, that mark goes on a bounded list (50), and restore() -- now
  used by packets.js init() for ?viewPath=1 -- does not reopen it. The
  cold-load updatePacketsUrl() then drops ?viewPath=1, so the URL says
  what is shown. A new link to the same URL (no state) and a reload of
  an entry whose modal was open still open it.
- updatePacketsUrl() keeps history.state instead of wiping it.

Tests: unit cases in test-packet-path-map.js and the #147 unit test;
E2E at 800 and 1400 px: Back closes the modal, Forward does not reopen
it and drops viewPath.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Escape handler calls closeDetailPanel(), which only reset the
desktop pane (#pktRight), so the ≤640 px bottom sheet never closed on
Escape. It now closes the sheet too, the same way its × does. With the
View Path modal open over the sheet, the modal's capture-phase handler
still takes the first Escape.

Tests: unit cases in test-issue-180-packets-detail-close.js; E2E at
390 px: Escape closes the sheet, and with View Path over it the first
Escape closes only the modal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dborup-agent

Copy link
Copy Markdown
Collaborator Author

Rapport — CS-pve-agent3 PR#204 #180 — head 4c7c35e

Status: Items 2–6 of #180 are fixed and tested on this draft PR. Every new test fails against master and passes here, and at least one mutant per item is killed. CI is green on 4c7c35e1. The PR stays a draft and is not merged.

Evidence tags: [T] = tested (a command was run and its output read), [A] = analysis only (code reading, not executed), [K] = known leftover or limitation.

Commits (base 6f7a5e7c)

Commit Item
fac293fd 2: Clear Filters on a detail URL also leaves the detail
2a5bf642 3: the SlideOver stacks above the top nav; × is 48×48
6071dbab test-only: the item-2 E2E compares Clear with the reload instead of counting rows
c3bf7190 4: Escape in a layer opened over the View Path modal is that layer's
696fab84 5: the modal closes on route change; Back/Forward does not reopen it
4c7c35e1 6: Escape closes the ≤640 px detail sheet

Item 2: Clear Filters on a detail URL

Decision Clear also removes the detail subpath. The detail closes and the URL becomes #/packets?…, with no hash filter and no ?obs=/?viewPath=. I found no strong reason for the other option: letting a cold-loaded subpath select the packet without setting filters.hash would change what every existing #/packets/<hash> link shows. buildPacketsQuery() is unchanged; the #147 golden test passes [T].
Fix The Clear handler calls closeDetailPanel() and updatePacketsUrl({ subpath: '', obs: null }). closeDetailPanel() re-renders only if a row was selected, so the #121 test "Clear renders nothing extra" still holds [T].
Tests Unit, #147 file: 3 Clear cases now expect #/packets. They are red on master (3 fail) and green here [T]. Unit, new test-issue-180-packets-detail-close.js: no render when nothing is selected; red on master [T]. E2E at 1400 px: detail URL → Clear → #/packets, detail closed, Clear hidden → reload gives the same count, Clear hidden, empty hash input [T]. The #147 E2E Clear step is updated to the decision [T].
Mutants M2a: Clear calls updatePacketsUrl() without a detail argument. Killed: 3 unit cases and the E2E fail [T]. M2b: Clear does not close the detail. Killed: the #147 unit, the #121/#132 harness and the E2E fail [T].

Item 3: SlideOver × under the top nav (641–1023 px)

Fix .slide-over-backdrop and .slide-over-panel now use the existing var(--z-modal-backdrop) and var(--z-modal) tokens instead of 1000/1001, so they sit above .top-nav (1100). .slide-over-close is 48×48 (it was 44). No colours changed.
Tests E2E at 641, 800 and 1023 px. elementFromPoint at the × centre returns ×, the button is at least 48×48, and a real page.click closes the SlideOver. On master the element at the × centre is top-nav or theme-toggle-track, so all 3 fail [T]. A screenshot at 800 px shows the panel header and × above the nav [T].
Mutants M3a: panel back to z-index: 1001. Killed at all 3 widths; the backdrop is now on top of × [T]. M3b: 44 px. Killed at all 3 widths [T].
Behaviour change While the SlideOver is open, its backdrop covers the top nav and the ≤768 px bottom nav. A click there closes the SlideOver instead of navigating [A]. test-slideover-1056/1168, test-bottom-nav-1061 and the gesture E2Es are green [T].

Item 4: Escape swallowed by the View Path modal

Fix The capture-phase handler leaves Escape alone when the focused element is in a floating layer drawn over the modal. That means: it is outside the modal; it has a position: fixed ancestor; that ancestor is not the SlideOver or the mobile sheet; and the element is topmost at its own centre. Otherwise the handler still stops the event and closes the modal (#167).
Tests Unit, test-packet-path-map.js: 6 cases. The search and fixed-menu cases are left alone; the covered-control, SlideOver, mobile-sheet and sticky-top-nav cases close the modal. The 2 "left alone" cases are red on master [T]. E2E at 1400 px: Ctrl+K → Escape closes the search while the modal stays with viewPath=1, and the next Escape closes the modal. The same flow works with the nav More menu. Both are red on master [T]. The #147 tablet Escape step still passes [T].
Mutants M4a: always swallow Escape. Killed: 2 unit cases and 2 E2E steps fail [T]. M4b: no SlideOver/sheet exclusion. Killed: 2 unit cases and the #147 tablet step fail [T]. M4c: no hit test. Killed: the covered-control unit case fails [T].

Item 5: Back/forward reopens a closed View Path modal

Fix The modal closes on a hashchange to another path; a change to the query only keeps it. open() marks the #/packets/<hash> history entry with an id in history.state, without writing a URL. If the modal is closed while another route is shown, the id goes on a list capped at 50. PacketPathMap.restore(), now used by init() for ?viewPath=1, does not reopen a modal whose entry is on that list. updatePacketsUrl() now keeps history.state instead of writing null, and drops viewPath when no modal is open. A new link to the same URL and a reload of an entry whose modal was open still open the modal [T, unit].
Tests Unit: 9 cases in test-packet-path-map.js and 1 in the #147 file. The relevant ones are red on master [T]. E2E at 800 and 1400 px: #/nodes → packet → View Path → Back closes the modal → Forward does not reopen it and drops viewPath. Red on master: "the View Path modal stayed open over #/nodes after Back" [T].
Mutants M5a: no hashchange listener. Killed: 4 unit cases and both E2E steps fail [T]. M5b: restore() ignores the closed list. Killed: 3 unit cases and both E2E steps fail [T]. M5c: updatePacketsUrl() writes null state. Killed: the #147 unit case and both E2E steps fail [T]. M5d: init() calls open() instead of restore(). Killed: both E2E steps fail [T].

Item 6: Escape never closes the mobile detail sheet

Fix closeDetailPanel(), which the Escape handler calls, also removes .open from #mobileDetailSheet, the same way its × does.
Tests Unit, test-issue-180-packets-detail-close.js: the sheet closes and the selection clears; red on master [T]. E2E at 390 px (mobile): Escape closes the sheet. With View Path open over the sheet, the first Escape closes only the modal and the next closes the sheet. Both are red on master [T].
Mutant M6: the sheet is not closed. Killed: the unit case and both E2E steps fail [T].

Tests, browser and E2E

  • [T] sh test-all.sh: 202 files passed, 0 failed (locally and in CI). node test-frontend-helpers.js: 707 passed, 0 failed.
  • [T] The new E2E test-issue-180-packets-url-modal-e2e.js gives 10/10 on this branch. Against a second server that serves master's public/ with the same fixture, it gives 0/10.
  • [T] 31 E2E files from deploy.yml (packets, path map, SlideOver, filters, gestures, nav, a11y axe, test-e2e-playwright.js) all exit 0. They ran against a local Go server on this worktree with e2e-fixture.db, prepared as in CI: freshen, grouped-row seed SQL, corescope-migrate, [feat] node detail, separate flood-adv from zero-hop adv Kpa-clawbot/CoreScope#2073 seed. I confirmed the server's cwd and -public path, and that the served packet-path-map.js contains restore. I stopped servers by the pid that lsof found on the port.
  • [T] scripts/check-xss-sinks.sh --diff origin/master exits 0 with no findings.
  • [T] Registration: the unit file is in test-all.sh, and the E2E is in the deploy.yml Playwright step after the fix(packets): updatePacketsUrl drops ?obs= and ?viewPath= on load and on filter changes #147 E2E. The fork-guard count is still 9.
  • [T] Screenshots: at 800 px, the SlideOver header and × are above the nav. At 1400 px, after Ctrl+K and Escape, the search is closed and the modal is still open.

CI (run for head 4c7c35e1)

Job Result
✅ Go Build & Test success (test-all.sh 202/0, packet-path-map.js 64/0)
🎭 Playwright E2E Tests success (#180 E2E 10/10, #147 E2E green)
🏗️ Build & Publish Docker Image success
📦 Release Artifacts skipped (fork guard)
🚀 Deploy Staging skipped (fork guard)
📝 Publish Badges & Summary skipped (fork guard)

Known leftovers

  • [K][T] At 641–1023 px, the View Path modal (.modal-overlay, z-index 200) is drawn under the SlideOver. This was already the case on master at z-index 1001. Fixing it means raising the modal above the SlideOver, and then the global search overlay (300) would also have to go above it. That is a separate stacking decision.
  • [K][T] Escape in the global search also closes the desktop detail pane under it. The cause is that the packets Escape handler listens on document. Master does the same, even without the modal. With this PR, the modal itself stays open.
  • [K][A] With the modal open, if focus is on a tab of the fixed bottom nav (≤768 px), Escape is left to that layer. The bottom nav has no Escape handler, so the modal stays open until focus moves.
  • [K][A] Closing the desktop detail pane or the mobile sheet (× or Escape) still does not write the URL. This was out of scope and is noted in the fix(packets): updatePacketsUrl drops ?obs= and ?viewPath= on load and on filter changes #147 E2E.
  • [K][A] Item 1 has a unit test for the route guard but still no E2E (not part of this task). Since item 3, the SlideOver backdrop covers the nav, so the item-1 scenario can no longer be reached by clicking the nav.
  • [K][T] test-packets.js fails 13 cases on master too. It is not in test-all.sh and was not changed here.

@dborup-agent

Copy link
Copy Markdown
Collaborator Author

Review — CS-pve-agent1 PR#204 packets-url-modal — head 4c7c35e

Dom: APPROVE with nits

Independent read-only review of head 4c7c35e182776e17ac3363161e835d7c81dcd0c8. The head was the same before and after the review (git ls-remote). I tested two trees: the head itself, and the merge with the current origin/master (33b0dfe5). The merge has no conflicts (git merge-tree → 45d167ff), and master has no changes under public/ since the PR's base 6f7a5e7c.

Evidence: [T] = test or CI run by me, [A] = analysis (code reading), [K] = taken from the author's report, not re-run.

Findings

Prio Where Description
P3 public/packet-path-map.js:135, restore() at :190 Item 5 residual: a reload between Back and Forward reopens the closed modal. closedEntries lives only in memory, but the packetPathModal id survives in history.state. Repro on the merged tree at 1400 px [T]: #/nodes → packet → View Path → Back (the modal closes) → reload on #/nodes → Forward. The modal is open again and the URL has ?viewPath=1. Without the reload, Forward correctly does not reopen it [T]. Suggestion: keep the list in sessionStorage, which has the same per-tab lifetime as the history entries and stays bounded at 50, wrapped in try/catch. Or document it as a known limit. This does not block.
P3 public/packet-path-map.js:100 (test gap) No test covers Escape with focus inside the View Path modal. Mutant M4 removes overlay.contains(el). All unit tests and both the #180 and #147 E2Es stay green [T], yet the mutant breaks real behaviour. A Playwright probe shows that with focus on #packetPathClose or #packetPathCopyLink, Escape closes the modal on the PR [T] but not on M4 (the modal stays and viewPath=1 stays) [T]. Suggest one unit case (focused element inside the overlay → closes, stops propagation) or an E2E step that focuses the modal's × before Escape.
P3 public/packets.js:3725–3730 Boy Scout cleanup: the "Show on map" handler calls closeDetailPanel() and then closes #mobileDetailSheet by hand. Since item 6, closeDetailPanel() already does that (:944), so the second block is redundant [A].
Info public/style.css:1363/1371 This behaviour change is documented and accepted: at 641–1023 px, the SlideOver's backdrop (now 9000) covers the top nav and the bottom nav, so a click there closes the SlideOver. That fits a role=dialog/aria-modal, focus-trapped panel [A]. The documented leftover remains: the View Path modal (.modal-overlay, 200) is drawn under the SlideOver at tablet widths. It was already under it at 1001 on master [A]. It deserves its own follow-up issue, together with the global search (300) stacking.

No P0–P2 findings.

Verification items

1. Items 2–6 of #180, each fixed and tested

Item Fix (analysis) Test that fails without the fix
2 – Clear removes the detail subpath (user decision) The Clear handler resets selectedObservationId, calls closeDetailPanel() and then updatePacketsUrl({ subpath: '', obs: null }). closeDetailPanel() re-renders only if a row was selected [A]. E2E at 1400 px [T]: Clear → #/packets, detail closed, Clear hidden → the reload matches. Unit #147: 3 Clear cases [T], killed by my mutant M2b. New unit "no extra render" [T], killed by M2a. Red on master: [K].
3 – SlideOver × clickable and 48 px at 641–1023 Backdrop and panel use var(--z-modal-backdrop) and var(--z-modal) (9000/9100), above .top-nav (1100) and .bottom-nav (1200). .slide-over-close is 48×48 [A]. E2E at 641, 800 and 1023 px [T]: elementFromPoint returns ×, size ≥ 48×48, and a real click closes. Killed by M3 (47 px) and M3b (panel 1150). Red on master: [K].
4 – Escape not swallowed by a layer over the modal focusInLayerAbove() checks four things: focus is outside the modal; it has a fixed ancestor; that ancestor is not the SlideOver or the sheet; and the element is topmost at its centre [A]. E2E Ctrl+K search and nav More menu [T], and the #147 tablet step [T]. Unit: killed by M4b (no fixed/hit test). The "focus inside" branch is untested; see the findings.
5 – back/forward does not reopen a closed modal hashchange closes the modal on a path change. The entry is tagged in history.state and goes on closedEntries (max 50), which restore() respects. updatePacketsUrl() keeps history.state [A]. E2E at 800 and 1400 px [T]. Unit: killed by M5 (a query-only change closes), M5b (no bound) and M5c (existing state lost) [T]. The reload case remains; see the findings.
6 – Escape closes the ≤640 px sheet closeDetailPanel() removes .open from #mobileDetailSheet [A]. E2E at 390 px, two steps [T]. Unit: killed by M6 (wrong class removed) [T].

2. No regression in Clear Filters (#121/#132), and buildPacketsQuery() is unchanged. test-issue-121-clear-filters-selection.js, test-clear-filters.js and test-filter-ux-e2e.js / test-issue-1122-packets-filter-ux-e2e.js are green [T]. The PR does not touch buildPacketsQuery() [A]. The skipHash path in updatePacketsUrl() is unchanged, and the #147 golden-value test is green [T]. Callers without a detail argument keep the subpath and ?obs= as before. Only Clear passes { subpath: '' } now [A].

3. Conflict with #201 (head 0111b7a2). git merge-tree has no conflicts in any order: master + #201 + #204 and master + #204 + #201 both give the same tree 63b67be6 [T]. The two PRs touch different rules in style.css (.nav-btn/.ch-icon-btn vs .slide-over-close/z-index). On the combined tree, #201's test-touch-targets.js, test-issue-2052-touch-target-css.js, test-channel-ux-followup.js and test-test-all.js pass, and so do #204's unit tests [T]. Order: it does not matter technically. Merging #204 first is slightly cleaner, because its .slide-over-close 48 px is already in line with #201's 48 px house rule, and #201 then needs no rebase [A].

4. Tests

  • sh test-all.sh on the merged tree: 212 passed, 0 failed [T]. node test-frontend-helpers.js: 707 passed, 0 failed [T].
  • E2E against a local Go server built from the merged tree, with -public set to the merged public/. The e2e-fixture.db copy was prepared as in CI: freshen, grouped-row seed SQL from deploy.yml, corescope-migrate, then the [feat] node detail, separate flood-adv from zero-hop adv Kpa-clawbot/CoreScope#2073 seed [T]. Results:
    • test-issue-180-packets-url-modal-e2e.js: 10/10.
    • test-issue-147-packets-url-obs-e2e.js: 12/12.
    • test-slideover-1056, test-slideover-1168-munger, test-bottom-nav-1061, test-filter-ux, test-gesture-hints-1065, test-gestures-1062, test-gestures-1185, test-issue-1122, test-issue-1128-packets-layout, test-issue-1128-multi-viewport, test-issue-1146, test-issue-1486, test-issue-1567, test-issue-1692, test-issue-1705-subpath-contrast, test-a11y-1705, test-issue-1758, test-packet-trace-alignment, test-path-inspector, test-touch-gestures-coverage, test-nav-more-floor-1139 and test-nav-drawer-1064: all exit 0.
  • test-e2e-playwright.js fails locally at step 7 ("Version info lives on Perf dashboard": #navStats timeout), so the fail-fast stops it. It fails the same way against a second server that serves master's public/ with the same fixture [T], so it is environmental and not caused by this PR. In CI it is green on this head [K]/CI.
  • Servers were stopped by the pid of the listening port (lsof -iTCP:<port> -sTCP:LISTEN -t) [T].
  • CI on 4c7c35e1: Go Build & Test ✅, Playwright E2E ✅, Docker ✅; Release/Deploy/Badges are skipped (fork guard) [T]/CI.

Own mutants (applied to a copy of the merged tree; unit tests, plus E2E against a separate mutant server for CSS/UI):

Mutant Item Result
M2a: closeDetailPanel() always re-renders 2 Killed: test-issue-180-packets-detail-close.js
M2b: Clear calls updatePacketsUrl() without a detail 2 Killed: #147 unit, 3 cases
M3: .slide-over-close min-height: 47px 3 Killed: #180 E2E at 641, 800 and 1023 px
M3b: panel z-index: 1150 3 Killed: #180 E2E (backdrop above ×, 4 steps) and the #147 tablet step
M4: no overlay.contains(el) 4 Survived all unit and E2E tests. A probe shows a real regression (see findings)
M4b: Escape left alone for all focus outside the modal 4 Killed: test-packet-path-map.js
M5: hashchange compares the full hash (a query-only change closes) 5 Killed: test-packet-path-map.js
M5b: closedEntries unbounded 5 Killed: test-packet-path-map.js
M5c: tagEntry() drops existing history.state keys 5 Killed: test-packet-path-map.js
M6: the sheet has the wrong class removed 6 Killed: test-issue-180-packets-detail-close.js

5. Rules

  • scripts/check-xss-sinks.sh --diff origin/master exits 0 with no findings [T].
  • No new colours: the only #… hits in the diff are #180 in comments. The z-index values use the existing --z-modal* tokens [T].
  • Fork guards: 9 on both head and master. The only deploy.yml change is the one E2E line, placed after fix(packets): updatePacketsUrl drops ?obs= and ?viewPath= on load and on filter changes #147 [T].
  • No closing keywords in the PR body or the commit messages [T].
  • The new unit test is registered in test-all.sh [T].

Not verified

  • The author's "red on master" runs for the new tests (the 0/10 E2E against master public/) were not re-run. I covered the "fails without the fix" requirement with my own mutants instead [K].
  • The remaining steps of test-e2e-playwright.js after step 7 locally, because of the environmental failure. They rely on CI [K].
  • Real touch devices and Safari/Firefox (Chromium only). No screenshots were taken. The 48 px check and the topmost-element check were verified by E2E only.
  • Item 1 (fixed by fix(packets): keep ?obs=/?viewPath= on packet detail URLs (#147) #167) is outside this PR and was not re-tested.
  • The axe/a11y E2E suites outside packets/nav were not run locally (CI is green).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants