Repository navigation
fix(packets): filter excluded types before pagination (#242) - #348
Conversation
dborup
left a comment
There was a problem hiding this comment.
Independent review of commit 3f12bea4f3d808bae7c2a7291f83d39cfa0b124e against 6444294d.
No actionable findings identified in the reviewed changes.
Checked exclusion parsing and bounded canonical masks, parameterized SQL and NULL handling, raw/grouped filtering before pagination and total counting, SQL fallback, bypass prevention in indexed memory paths, grouped-cache identity, frontend complement construction, pinned-hash exceptions, filter refetches, and asynchronous response guards.
Independently executed from an archive of the fetched PR head:
go test ./... -run 'TestPacketExclusion' -count=1incmd/server: passed.node test-packets.js: 161 passed.node test-issue-96-hide-control.js: 28 passed.- New packet-exclusion Chromium suite against the local test server: all 8 scenarios passed, including capped pages, live updates, type selection, stale responses, and view remounts.
- Existing Hide CONTROL Chromium suite: 10 passed.
Limitations: this review did not independently rerun the full Go/frontend suites, race tests, or performance benchmarks. The new browser suite uses deterministic intercepted packet responses; backend exclusion semantics were independently exercised by the Go regressions. No production or staging deployment was tested. This is a comment review, not a merge approval or CI-completion claim.
|
Taking this PR over from here. Next: an independent review (and, where CI is red, a fix round first). The issue link was changed from a closing keyword to "Relates to" to match the fork's convention; the issue is closed manually after merge. |
…tches The Playwright job went red on Kpa-clawbot#1122's Details row clamp E2E at 900px and 375px once the e2e fixture was older than 15 minutes (CI: 13.7 min after freshen-fixture.sh). Both viewports rendered an empty or single-row list. Cause: that test widens the list to "All time" by assigning #fTimeWindow.value = '0'. Below 1025px packets.js deliberately omits the "All time" option, so the assignment selects nothing and leaves the select blank. Its change handler then clamps savedTimeWindowMin to 15, and the blank-value fallback added here resolves the refetch to those 15 minutes instead of the all-time window the test assumed -- which is empty once the fixture has aged past it. Desktop kept passing because the option exists above 1024px. The fallback itself is the behaviour we want: a shared ?timeWindow=240 URL also leaves the select blank, and reading that as Number('') === 0 would silently widen every new refetch to All time. Reading the DOM cannot tell the two blanks apart, so the test stops forcing an option the page does not offer: it selects "All time" only where that option exists and otherwise keeps the URL window it already pins, which spans the whole fixture. - test-issue-1122-details-row-clamp-e2e.js: select "All time" only when the option is present. 18/18 at all three viewports against a 20-minute-old fixture, on this branch and on master; 8 failures before the change. - test-issue-242-type-exclusions-e2e.js: new scenario pinning the contract this PR introduced -- a ?timeWindow=240 URL whose window has no dropdown option must survive the Hide CONTROL refetch instead of widening to All time. Reverting the guard to Number(selectedWindowValue) fails it with "the refetch dropped the time window and asked for all time". Relates to #242. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Rapport — CS-Minimax PR#348 ci-fix — head d7f264eReview feedback addressed (commit Evidence tags: [T] = executed test/command, [A] = code analysis/argument, [K] = known/pre-existing behaviour outside this PR. 1. Playwright job red: Kpa-clawbot#1122 Details row clamp E2E failed at 900px and 375px — BLOCKING, fixedThe independent review on Symptom [T] — run 37605035332, job "Playwright E2E Tests": Root cause [A] — that test widens the list to "All time" by assigning Deterministic A/B [T] — fixture prepared exactly as
The repaired test also still passes on Fix — the production fallback stays; it is the behaviour we want. A shared New regression test, red before / green after [T] — added a 9th scenario to this Restored, 9/9 green. Observation (no change made)[A] Moving the CONTROL filter server-side means the client no longer sees the excluded Merge[T] TestsAll of the following executed locally [T] on the merged tree (
E2E (Chromium,
The broad suite's 3 skips are the pre-existing fixture/flaky skips (two "Packets The 242/96/1122/1122-ux/258 suites were additionally run against a deliberately MutantsThree mutants, each reverted afterwards and re-verified green [T]:
CI per job — run 37617607635, head
|
| Job | Result |
|---|---|
| ✅ Go Build & Test | pass (24m8s) |
| 🎭 Playwright E2E Tests | pass (22m0s) |
| 🏗️ Build & Publish Docker Image | pass (46s) |
| 📦 Release Artifacts | skipping |
| 🚀 Deploy Staging | skipping |
| 📝 Publish Badges & Summary | skipping |
No job was re-run; neither known-unstable test (#271, #301) appeared [K], so the
"re-run once" path was not needed. #1122 Details row clamp E2E reports === Results: passed 18 failed 0 ===
and the new scenario reports PASS: a URL time window absent from the dropdown survives an exclusion refetch.
One caveat, stated plainly [A]: in this run #1122 executed 11.3 minutes after
freshen-fixture.sh (12:22:27 → 12:33:47), i.e. still inside the 15-minute window,
whereas the failing run hit it at 13.7 minutes. So this green run is not by itself
a discriminating test of the fix — the proof is the deterministic 20-minute-aged A/B
above (master 18/18, PR head 8 failures, PR head + fix 18/18). The fix removes the
dependency on fixture age entirely at those viewports.
Scope
No production behaviour was changed in this round; the blank-select fallback this PR
introduced is kept as-is because it is correct. The round touches only the two E2E
files. No ingestor, schema, config, dependency or deployment changes. Not merged, not
marked ready, no issue closed; no staging or production system was contacted.
Review — CS-pve-agent3 PR#348 — head d7f264eDom: APPROVE med nits Independent, read-only review of Evidence tags: [T] = I ran it, [A] = code reading/argument, [K] = known or pre-existing behaviour outside this PR. Findings
No blocking findings. 1. Correctness: full pages, total/count, offset stability
Both issue acceptance criteria 1 and 2 hold end to end. There were no page errors. I inspected the screenshot: the list is filled with RESPONSE/ADVERT/REQUEST/CHANNEL MSG and the checkbox is checked. 2. PerformanceQuery plan [T] (synthetic 1,000,533-row DB, SQL path timings [T] (
The SQL fallback is only entered when In-memory path [T], synthetic 300K-tx store,
Exclusion is a predicate in the existing single pass ( 3. Frontend: type filter, Hide CONTROL (#211), deep links, localStorage
4. The CI fix: real or flaky?Real and deterministic, and the report's explanation is correct. [T] I aged a CI-prepared fixture copy by 20 minutes and ran both servers:
This reproduces the author's A/B exactly. The old test assigned CI per job, run 37617607635 on 5. Tests, red before / green after, mutantsMerged tree
Red before / green after, per acceptance criterion [T]:
Note: the #242 E2E mocks My own mutants [T]; each one was run in a scratch copy, so nothing touched the PR:
Invariants [T]: Not verified
|
Conflict in public/packets.js (loadPackets background hop job): master replaced the flat hop collection with resolveHopsForPackets() (#165); this branch snapshots the loaded list and drops the job when a newer load has started (#242). Kept both: resolve per observer from the snapshot, behind the generation check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The held Hide CONTROL response and the newer type response rendered the same rows after client filters, so dropping the generation check after api() still passed this scenario. The server now ages one matching packet out between the two requests, so a late earlier response is visible. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
) With no exclusion (the default page) excludes() still dereferenced every tx's separately allocated PayloadType before looking at the mask. Testing mask != 0 first restores master parity on the in-memory path. Interleaved A/B, 300K tx, limit=1000, mask 0, cold grouped cache, n=18 (6 rounds x 3, -benchtime 2s), median: raw: 15.20 ms -> 13.08 ms (master 13.16 ms) grouped: 17.89 ms -> 15.73 ms (master 15.94 ms) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…242) With CONTROL excluded server-side and dropped from the live feed, the "(CONTROL packets are hidden)" note can only appear between the checkbox change and the refetch. Say so at the call site, and pin that window in the #242 browser test (held refetch over an all-CONTROL page) so the machinery stays covered rather than silently dead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rapport — CS-pve-agent1 PR#348 master-sync — head 5899d46Review feedback addressed (commit Commits in this round, on top of
Evidence tags: [T] = executed, [A] = code reading/argument, [K] = known or pre-existing behaviour outside this PR. 0. Master sync[T]
[T] After the merge, 1. #211 "CONTROL packets are hidden" hint is near-unreachable (Low): addressed by documenting it and pinning it in a testI kept the machinery and did not delete it. [T] It is not dead: Mutant [T]: Side observation, no change made [K]/[A]: the hint row is in the DOM but invisible at 1024 px. The packets column-hiding pass ( 2. Mask-0 path ~10% slower in memory (Low, perf): changed
Proof [T], interleaved A/B, 300K synthetic tx,
A second interleaved run, master vs after, gave raw 13.16 / 13.06 ms and grouped 15.94 / 15.88 ms. That is parity. The 300K benchmark was a scratch file and is not committed; the committed Red/green [A]: a pure evaluation-order change has no functional effect that a deterministic unit test can observe. The only way would be dereferencing a forged invalid pointer through 3. Stale-response E2E scenario could not catch a missing guard on its own (Nit, test strength): changedThe held Hide-CONTROL response and the newer type response rendered the same rows after the client filters. Now the test server ages packet 1003 out between answering the held request and the newer request. The late response still carries 1003, so if it were applied it would be visible. Expected rows are now Mutant M3 [T], removing
Restored: 9/9 scenarios green. 4. NULL
|
| Suite | Result |
|---|---|
test-issue-242-type-exclusions-e2e.js |
9/9 scenarios (with the new steps) |
test-issue-96-hide-control-e2e.js (Hide CONTROL) |
10 passed, 0 failed |
test-issue-1122-packets-filter-ux-e2e.js |
6 passed, 0 failed |
test-filter-ux-e2e.js |
12 passed, 0 failed |
test-issue-1122-details-row-clamp-e2e.js |
18 passed, 0 failed |
test-issue-165-grouped-hop-warn-e2e.js (conflict area) |
3 passed, 0 failed |
test-issue-1692-packets-init-parallel-e2e.js |
1 passed, 0 failed |
test-issue-147-packets-url-obs-e2e.js |
12 passed, 0 failed |
test-issue-180-packets-url-modal-e2e.js |
12 passed, 0 failed |
test-e2e-playwright.js (broad) |
132/135, 3 skipped, 0 failed |
The 3 broad-suite skips are the pre-existing ones: two "clicking row shows detail pane" flaky skips and the GO_BASE_URL perf test [K].
Mutants (one per changed finding), all restored and re-verified green [T]
controlHidingEmptiedList→return false: the new feat(packets): server-side exclusion of packet types in /api/packets for the type filter and Hide CONTROL #242 E2E step fails, andtest-issue-96-hide-control.jsgives 26/2.excludes()with the old order (typ before mask): +16% raw / +14% grouped at 300K (benchmark, see 2).- M3, drop the post-
api()generation check: the strengthened scenario 5 fails on its own.
CI per job — run 37660906198, head 5899d461, attempt 1 [T]
| Job | Result |
|---|---|
| ✅ Go Build & Test | pass (24m15s) |
| 🎭 Playwright E2E Tests | pass (25m4s): broad 132/135 with 3 skipped; all 9 #242 scenarios PASS; Hide CONTROL 10/0; Kpa-clawbot#1122 details 18/0 |
| 🏗️ Build & Publish Docker Image | pass (56s) |
| 📦 Release Artifacts | skipped |
| 🚀 Deploy Staging | skipped |
| 📝 Publish Badges & Summary | skipped |
No re-run was needed, and neither #271 nor #301 appeared [K].
Scope
The merge commit only resolves the one conflict. The review-fix commits touch cmd/server/packet_exclusions.go (one line + comment), a comment in public/packets.js and test-issue-242-type-exclusions-e2e.js; the PR description gained a "User-visible changes" section. No ingestor, schema, config, dependency or workflow changes. The PR was not merged, no issue was closed, and no staging, production or upstream system was contacted. Note: the PR was already non-draft when I took it over, and I left its draft state unchanged.
Review — CS-pve-agent3 PR#348 — head 5899d46Dom: APPROVE This is a short, read-only delta re-review. It covers only what changed after my previous review on
I did not re-review master's own changes. All runs used Evidence tags: [T] = I ran it, [A] = code reading/argument, [K] = known or pre-existing behaviour outside this PR. Findings
1. Conflict resolution in
|
| Step | Header count | Rows rendered (virtual) | Request | Hop links |
|---|---|---|---|---|
| initial | (1000) | all CONTROL | no excludeTypes |
0 |
| Hide CONTROL on | (495) | RESPONSE/ADVERT/REQUEST/TXT/CHANNEL MSG, 0 CONTROL | excludeTypes=11 |
358 named hops |
| reload | (495), hideControl=1 in URL |
same | excludeTypes=11 |
358 |
| Hide CONTROL off | (1000) | all CONTROL | none | 0 |
| type = Channel Msg | (166) | only type 5 | excludeTypes=0,1,2,3,4,6,…,15 |
491 |
There were no page errors. I inspected the screenshot: the list is filled with non-CONTROL rows, the checkbox is ticked, and the paths show resolved node names. So filtering before pagination and hop resolution work together after the refetch. The numbers match my run on d7f264ee (495/166, against master's 19/4).
2. Server: mask != 0 && early exit
[A] The change is correct. parsePacketTypeExclusions returns 0 for an absent or empty excludeTypes. The previous expression could only return true when mask&(1<<typ) != 0, so with mask 0 it was already always false. The reorder changes no result for any input. It only skips the *typ dereference. The SQL side already had if mask == 0 { return }, so the two paths are now symmetric.
Is there a test? Yes, semantically. "Mask 0 = no exclusion" is asserted by check("", 5, …), before and after the exclusion queries in TestPacketExclusionsBeforePagination. It runs in memory, database and fallback mode, raw and grouped, and it counts the NULL row. My Go mutants [T]:
mask == 0 || …→grouped=false total=0 want 5. Killed.mask != 0 || …→&excludeTypes=11 … total=0 want 2andTestPacketExclusionMaskAndIndexesfails. Killed.- Dropping
mask != 0 &&(the old ordering) → passes. This is an equivalent mutant by construction, and no functional test can tell it apart. I agree with the author that the benchmark is the only detector. [T] An interleaved A/B of the committedBenchmarkPacketExclusions30K, mask 0,-benchtime 2s, n=8 per cell, 4 rounds alternating between two test binaries that differ only in this line:
| old ordering | mask != 0 && first |
|
|---|---|---|
| raw | 773 µs (732–818) | 626 µs (611–649) |
| grouped | 927 µs (905–962) | 766 µs (750–882) |
That is about −19%/−17% on the default no-exclusion request. It agrees with my 300K measurement on d7f264ee and with the author's n=18 run.
3. Test and comment changes: do they fix my nits?
| My previous nit | Change | Verdict |
|---|---|---|
| 1. The #211 hint is near-unreachable | Kept the machinery. The call-site comment explains that the hint fires only between the checkbox change and the refetch. Scenario 1 now holds that refetch and asserts the hint. The PR description has a new "User-visible changes" section. | Fixed. [T] My mutant: dropping the immediate renderTableRows() in setHideControl() (refetch only) → the new step fails (did not match /CONTROL packets are hidden/). [T] Against master's frontend the new step fails (Expected held packet request, no refetch). So the step pins real #242 behaviour. |
| 2. Mask-0 path ~10% slower | mask != 0 && first |
Fixed, see 2. |
| 3. Stale-response scenario cannot stand alone | Packet 1003 is aged out of the mocked server after the held request was answered, so the late response is visibly different. Expected rows are [1001] before and after the release, and the fixture is restored afterwards. |
Fixed. [A] selected is computed before the await held, so the held payload really does carry 1003. [T] My M3 again (remove if (generation !== packetLoadGeneration) return; after api()): scenario 5 now fails on its own ("a late earlier response replaced the newest results"). On d7f264ee the same mutant passed scenario 5. |
4. NULL payload_type can short a typed page |
No change; author disagrees | Accepted. [A] cmd/ingestor/db.go:2032 PayloadType int is non-nullable, so NULL only appears in legacy or hand-written rows. Keeping NULL rows matches the documented contract and the in-memory predicate. |
| 5. Flake issue numbers | Acknowledged | Fine. |
Tests
Merged tree e54b272d (linux/amd64, Go 1.27.1, Node 22) [T]:
cmd/server:go build ./...andgo vet ./...clean;go test ./... -count=1→ ok 392.6s, 0--- FAILcmd/ingestor: not rerun. Neither the PR nor the delta touchescmd/ingestor/orinternal/[A]. CI's Go job ran it green on head [T].sh test-all.sh→ 233 passed, 0 failed (233 files)node test-frontend-helpers.js→ 709 passed, 0 failed.test-packets.js161/0,test-issue-96-hide-control.js28/0.
E2E (Chromium) [T]: local merged server, e2e-fixture.db prepared as in deploy.yml (freshen → Kpa-clawbot#1486/Kpa-clawbot#1791 seed SQL → corescope-migrate → seeds 2073/199/245). All servers were stopped by port.
| Suite | Result |
|---|---|
test-issue-242-type-exclusions-e2e.js |
9/9 scenarios, including the new hint step and the strengthened scenario 5 |
test-issue-96-hide-control-e2e.js |
10/0 |
test-issue-165-grouped-hop-warn-e2e.js (conflict area) |
3/0 |
test-issue-1122-details-row-clamp-e2e.js |
18/0 |
test-issue-1122-packets-filter-ux-e2e.js |
6/0 |
test-issue-1692-packets-init-parallel-e2e.js |
1/0 |
test-issue-147-packets-url-obs-e2e.js |
12/0 |
test-filter-ux-e2e.js |
12/0 |
My mutants, each in a scratch copy of public/ or cmd/server, with nothing touching the PR [T]:
| # | Mutant | Result |
|---|---|---|
| MF1 | hop job → flat observer-less resolveHops (pre-#185) |
killed by #165 E2E |
| MF2 | drop the post-api() generation check |
killed by #242 scenario 5 alone |
| MF3 | setHideControl() without the immediate re-render |
killed by #242 scenario 1 (hint step) |
| G1 | mask == 0 || … |
killed |
| G2 | mask != 0 || … |
killed |
| G3 | drop mask != 0 && |
equivalent (perf only), survives as expected |
Red before / green after: unchanged from my previous review for AC1–AC4 (Go TestPacketExclusionsBeforePagination / …Validation, test-packets.js, test-issue-96-hide-control.js, #242 E2E). In this round [T]: the #242 E2E against master's frontend fails at the first step, and passes 9/9 on the merged tree.
CI per job, run 37660906198 on 5899d461, attempt 1 [T]:
| Job | Result |
|---|---|
| Go Build & Test | pass (24m15s; cmd/server ok 1072.9 s, cmd/ingestor ok) |
| Playwright E2E Tests | pass (25m4s): broad 132/135 with 3 pre-existing skips [K]; #242 9/9 PASS; #96 10/0; #165 3/0; Kpa-clawbot#1122 details 18/0 |
| Build & Publish Docker Image | pass |
| Release / Deploy Staging / Badges | skipped (fork guards) |
The CI log has no failures. The known flakes #256 (Hash Stats sort) and #267 (backfill write-hold) did not occur, and nothing was re-run.
Invariants [T]:
cmd/serverstays read-only: the delta adds no write SQL, and the exclusion is still a read-sideWHERE.- 0 new
map[string]interface{}outside tests. - No new hard-coded colours.
scripts/check-xss-sinks.sh --diff origin/masterexits 0 in a scratch clone at head, andgit diff --checkis clean.- Fork guards: 9 in
deploy.ymland 1 inrelease-fast-path.yml, identical to master. - No closing keywords in the title, body or commits ("Relates to").
- All 7 PR commits are authored and committed by
dborup <kontakt@meshview.dk>. - The delta outside the merge touches only
packet_exclusions.go(+3/−1), one comment inpackets.jsand the feat(packets): server-side exclusion of packet types in /api/packets for the type filter and Hide CONTROL #242 E2E.
Not verified
- This round's benchmark used only the committed 30K store; my 300K measurement is from the previous round.
- I did not run
cmd/ingestorlocally (no diff), and I did not run the broadtest-e2e-playwright.jslocally (CI: 132/135, 3 skipped). - Only Chromium was used; I did not test WebKit or Firefox.
- I did not use production-scale data. The browser check used a 1,633-tx fixture copy.
- I did not contact any staging or production system.
Summary
Relates to #242.
excludeTypesfiltering before pagination and totals for raw/grouped packet queries, including SQLite retention fallback, memory index paths and grouped cache identity.nodesmulti-node path are explicitly rejected; singlenodeis supported.No ingestor, schema, config/customizer, dependency or deployment changes. No packet deletion.
Performance
Exclusion membership is a bounded 16-bit mask checked in the existing scan; no extra store traversal or per-packet API request. Filtering is O(n), followed by the existing O(m log m) sort of matching rows. SQL uses at most 16 bound values. UI actions refetch one list request; existing expanded-row requests are unchanged.
Independent local benchmark, Apple M5, 30,000 synthetic transmissions, 50-row page, cold grouped cache:
These are current-path comparisons, not before/after speed claims or production latency estimates.
Validation
3f12bea4.Tests use disposable localhost fixtures; no staging or production systems were contacted. An independent reviewer will inspect the published commit and post an English review. CI remains a separate gate.
User-visible changes
?timeWindow=240) is now used for refetches and for the cold load. Before, a blank select made the cold load ask for all time.