Skip to content

port(upstream#1821): packets observer filter keeps multi-observer rows in grouped mode - #27

Merged
dborup merged 3 commits into
masterfrom
codex/port-upstream-1821-grouped-observer-filter
Sep 20, 2026
Merged

dborup merged 3 commits into
masterfrom
codex/port-upstream-1821-grouped-observer-filter

Conversation

@adminopenclaw8-sketch

@adminopenclaw8-sketch adminopenclaw8-sketch commented Sep 13, 2026 •

Copy link
Copy Markdown
Collaborator

Split out of #25 (commit a3f983a2 there). This branch holds exactly one upstream change so it can be reviewed, tested and reverted on its own.

Upstream

Problem

In grouped mode the Packets table re-filtered the already server-filtered rows against each row's observer_id. That id is only the representative observation (longest path), and _children are not loaded yet at that point. So an observer filter hid every multi-observer transmission whose representative was a different observer.

Change

The client-side filter is extracted into applyObserverFilter(displayPackets, filters, groupByHash, hashOnly). In grouped mode it returns the rows unchanged, because the server's EXISTS filter over all observations is authoritative. Flat mode keeps the defensive client-side filter. The function is exported for tests, and test-frontend-helpers.js gains cases that exercise the real function.

Adaptation to this fork

None. The cherry-pick applied without conflicts and the changed lines are identical to upstream.

Dependencies and merge order

Verification

Local run of the same commands as CI's “Go Build & Test” job (server tests with -race), on this branch and on master fda24ca5 under the same conditions (same machine, run one after another):

Check master fda24ca5 this branch verdict
xss-gate-diff PASS PASS
channel-lib-test PASS PASS
decrypt-cli-build-test PASS PASS
dockerfile-copy-invariants FAIL FAIL not runnable locally: script needs bash ≥4 (declare -A), macOS has 3.2; identical on master
staging-disk-monitor PASS PASS
css-vars-lint PASS PASS
JS unit tests from CI list + AGENTS.md set (67 files) see below 63 PASS, 4 FAIL
test-issue-1375-scope-stats-fetch.js FAIL FAIL Exactly one api('/scope-stats' call exists (the fixed loader) — found 2 baseline failure, unchanged
test-issue-1648-m4-emoji-scan.js FAIL FAIL map.js has 1 emoji/misc-icon hit(s): baseline failure, unchanged
test-a11y-axe-routes-coverage.js FAIL FAIL axe ROUTES missing analytics tabs (issue #1706): areas, foreign-traffic, wardriving baseline failure, unchanged
test-frontend-helpers.js FAIL FAIL favStar returns empty star for non-favorite: The expression evaluated to a falsy value:, favStar returns filled star for favorite: The expression evaluated to a falsy value: baseline failure, unchanged

Baseline failures (fail identically on master; not introduced or changed here): see rows marked baseline failure, unchanged.

Browser validation (local, fixture DB, no staging/production): Local Go server (built from master) on the committed E2E fixture DB (freshened, migrated and seeded exactly like CI), serving this branch's public/, compared side by side with the same server serving master's public/. Seeded one transmission (hash fae0c9e6d357a814) with 3 observations from observers Kennedy (representative), GY889 and DUBLIN — the same seed CI uses for Kpa-clawbot#1486. Opened #/packets?observer=<GY889>&timeWindow=262800 in grouped mode. /api/packets?groupByHash=true&observer=<GY889> returns 11 rows including the seeded one. master rendered "Latest Packets (10)" — the seeded multi-observer row hidden by the client-side re-filter (the bug). this branch rendered "Latest Packets (11)" with the seeded "Kpa-clawbot#1486 fixture" row visible.

Not run:

  • Playwright E2E suites (no local Playwright install); CI's E2E job will also be skipped, see below.
  • eslint (not installed locally; CI installs it on the fly).
  • Go -race/tests for modules this PR does not touch (unchanged code, identical to master).
  • Browser tests against staging/production (deliberately none).

Expected GitHub CI: “Go Build & Test” is expected to fail on TestPruneOldNeighborMetrics, which already fails on master (see #25's run). Downstream jobs (Playwright, image build) are therefore skipped. “Deploy Staging” and all GHCR publish steps only run on push to master and cannot run for this PR.
Two further ingestor tests have failed intermittently in this split's CI on branches whose cmd/ingestor tree is byte-identical to master (#27, #28), so they can also appear here without being caused by this change:

GitHub CI result: run 34749195195 on 72f64e93. Go Build & Test: failure; all downstream jobs incl. Deploy Staging skipped. Failed tests:

🤖 Generated with Claude Code

…de (Kpa-clawbot#1748) (Kpa-clawbot#1821)

Closes Kpa-clawbot#1748.

## Root cause

`renderTableRows()` in `public/packets.js` re-filtered the already
server-filtered `/api/packets` (grouped) results client-side, comparing
`filters.observer` against each row's `observer_id`. But that field is
only the **representative** observer chosen for display —
`QueryGroupedPackets` (`cmd/server/db.go:554-606`) picks the observation
with the longest observed path via a `LEFT JOIN ... ORDER BY
length(path_json) DESC LIMIT 1`, purely for display purposes.

The server-side filter (`buildTransmissionWhere`,
`cmd/server/db.go:725-790`) is already correct: it uses an `EXISTS`
subquery over **all** observations of a transmission, so any row it
returns was genuinely seen by at least one selected observer.

The client then discarded rows *again*, checking only the
representative's `observer_id`, with a fallback to `p._children` — which
is `undefined` on initial page load (only fetched lazily on row-expand
or when the observer-sort dropdown changes, see the `obsSortSel` change
handler). Net effect: a multi-observer transmission stayed visible under
an observer filter only when the filtered observer happened to also be
the representative (longest-path) observer — which matches the reported
"works only for whichever observer logged it first" behavior in dense
meshes, where longest-path and earliest-seen correlate.

## Fix

Skip the client-side observer re-filter entirely when `groupByHash` is
active — the server's `EXISTS` filter is authoritative for grouped rows
and needs no client-side correction. The flat/expanded-mode path
(single-observation rows, each with its own exact `observer_id` from
`buildPacketWhere`) keeps the existing children-aware filter unchanged,
which already had test coverage under Kpa-clawbot#537 for the case where
`_children` is already populated.

## Tests

Added 6 cases in `test-frontend-helpers.js` covering the specific gap
Kpa-clawbot#537's tests didn't reach — grouped mode with `_children` still
`undefined` (the actual initial-load state that triggers this bug). New
tests confirm:
- A multi-observer row whose representative doesn't match the filter is
kept in grouped mode (the core bug).
- Grouped mode never re-filters client-side (trusts the server).
- Flat mode behavior is unchanged (matches by own `observer_id`, falls
back to already-loaded `_children`).

Full run: `test-frontend-helpers.js` 631 passed / 2 failed (same 2
pre-existing `favStar` failures reproduce identically on unmodified
`master` — unrelated). `test-packet-filter.js` 92/92. `test-aging.js`
18/18.

Operator context: running CoreScope for SaarMesh (SaarLorLux, DE/FR/LU,
800+ nodes, 14 observers) — this was hiding a large share of traffic
whenever filtering by a non-primary observer in our dense mesh.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
(cherry picked from commit 02c2338)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Sep 13, 2026
Dennis Jakobsen and others added 2 commits September 20, 2026 11:29
Brings the branch up to master 27470cb (post #54) so CI runs on current master.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Independent review found that skipping the client-side re-filter in grouped
mode made the observer filter a complete no-op in the default view, which is
worse than the bug being fixed: the dropdown handler was the only server-side
filter handler that did not refetch, so it relied entirely on that
client-side pass to narrow an already-loaded page. Reproduced by driving the
real renderTableRows(): rows whose sole observer was someone else stayed
visible under an observer filter until the next page load.

The observer filter is a server-side filter (buildPacketsParams sends
'observer'), so changing it now calls loadPackets(), like hash, time window,
group toggle, myNodes, region and channel already do. That makes the grouped
early return correct rather than merely harmless, and also fixes the
pre-existing converse: unticking back to All Observers now restores the wider
set instead of leaving the page narrowed.

Also corrects two comments that cited code paths which do not run
(buildPacketWhere has no non-test callers; the usual grouped path is
store.QueryGroupedPackets, with buildTransmissionWhere only as the
older-than-oldestLoaded fallback — both scan all observations, so the
conclusion stands), and closes three test gaps the reviewer found by mutation
testing: the hashOnly case was asserted in grouped mode where the early
return made it vacuous, the children fixture used only string ids so the
String() coercion could be dropped unnoticed, and nothing pinned the refetch
contract. All three mutants now fail.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dborup
dborup merged commit dbdf1c0 into master Sep 20, 2026
6 checks passed
dborup pushed a commit that referenced this pull request Sep 20, 2026
Brings the branch up to master dbdf1c0 (post #54, #27) so CI runs on current master.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
dborup pushed a commit that referenced this pull request Sep 20, 2026
Brings the branch up to master f63148a (post #27, which also touched
public/packets.js in a different region) so CI runs on current master.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

3 participants