Skip to content

port(upstream#1957): carry observer_id into replayed packets - #41

Merged
dborup merged 4 commits into
masterfrom
codex/port-upstream-1957-replay-observer-id
Sep 20, 2026
Merged

dborup merged 4 commits into
masterfrom
codex/port-upstream-1957-replay-observer-id

Conversation

@adminopenclaw8-sketch

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

Copy link
Copy Markdown
Collaborator

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

Upstream

Problem

With a region filter active, VCR replay on the Live map rendered nothing, and the Replay button on packet detail silently did nothing. The region filter (packetMatchesRegion) matches on observer_id, which replayed packet objects never carried.

Change

Replay packet objects built in live.js and in the packet-detail Replay handler now carry observer_id and observer_iata.

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/. Real UI flow with the Live region filter set to SFO (meshcore-region-filter): opened packet 76c71994d1680a57 (31 hops, observer in SFO) on the Packets page and clicked Replay; the object written to sessionStorage['replay-packet'] was captured. master: observer_id is undefined. this branch: observer_id = the SFO observer (C0FFEEC7…) and observer_iata = SFO — the field packetMatchesRegion needs. Same result with advert 4953f0d6c2c602d2 (B8714384…, SFO). Partial: the visible replay itself could not be observed in this environment — a master control run without any region filter also showed "0 pkts" and no route animation after Replay (animations draw on canvas; screenshots 2 s after the click show nothing in all three runs). So the browser check proves the data fix on the real UI path, not the rendered outcome; the region-matching path for replayed packets is covered by the extended test-live-region-filter.js (in CI's JS unit list): 9 cases on master, 13 on this branch, all passing locally.

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 34751057436 on 61dddb7e. Go Build & Test: failure; all downstream jobs incl. Deploy Staging skipped. Failed tests:

  • TestPruneOldNeighborMetrics: fails on master, documented baseline

🤖 Generated with Claude Code

efiten and others added 4 commits September 13, 2026 10:42
…yed packets (Kpa-clawbot#1957)

Closes Kpa-clawbot#1898. Closes Kpa-clawbot#1900.

Two issues, one omission.

With a region filter active, VCR replay on the Live map rendered
**nothing at all** (Kpa-clawbot#1898), and the Replay button on packet detail
**silently did nothing** (Kpa-clawbot#1900).

## Cause

`packetMatchesRegion` (`public/live.js:80-92`) matches a packet group by
looking up `packets[i].observer_id` in the observer roster map. A packet
whose `observer_id` is null is skipped, and when none match it returns
`false` and the caller drops the whole group (`live.js:3374`).

`dbPacketToLive()` returned `observer` (the resolved name) but never
`observer_id`. So every replayed packet was skipped, and every group was
dropped. The Replay button had the same gap: both branches passed
`obsName(o.observer_id)` and threw the id itself away.

**The value was there the whole time.** The VCR builds its entries with
`Object.assign({}, p, obs, ...)`, so the observation's `observer_id` is
on the input, and the server has returned `observer_id`, `observer_name`
and `observer_iata` per packet since `cmd/server/db.go:345-347`. Only
the object literal dropped it.

## Fix

Carry `observer_id`, and `observer_iata` alongside it so
`obsIataBadgeHtml` (`live.js:102-108`) can use the direct field for
replayed packets instead of falling back to the roster map.

Three lines of behaviour, in two files.

## Verification

Four regression tests in `test-live-region-filter.js`. **Three fail
without the fix**, checked by reverting `live.js` and re-running:

```
❌ Kpa-clawbot#1898: dbPacketToLive carries observer_id through
❌ Kpa-clawbot#1898: a replayed packet survives an active region filter
✅ Kpa-clawbot#1898: dropping observer_id is what broke it (guards the regression)
❌ Kpa-clawbot#1898: observer_iata is carried so the badge needs no roster lookup
```

The one that passes either way does so on purpose: it asserts a packet
carrying **no** `observer_id` is still dropped, pinning the mechanism so
a future change cannot make the filter match everything.

That test's sandbox needed `getParsedDecoded` and `getParsedPath`.
`live.js:14` captures those from `packet-helpers.js` at load time and
the sandbox does not load it, so they are stubbed in the sandbox
definition rather than assigned afterwards. Assigning later is too late
for that capture, which cost me two attempts.

Other suites unaffected: `test-live.js` 95 passed,
`test-packet-filter.js` 99, `test-frontend-helpers.js` 656.
`test-1110-live-filter.js` fails identically on unmodified master with
`ERR_CONNECTION_REFUSED`; it is an E2E test needing a server on port
13581.

## Note

Both issues were filed separately and neither names the other. They are
the same root cause in sibling code paths, which is why they are fixed
together rather than in two PRs.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 9488e9c)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
Mutation testing showed half this PR was unverified: removing observer_id
from live.js's dbPacketToLive fails 2 of the 13 region-filter tests, but
removing it from packets.js's replay mapping passed the entire suite. The
only coverage lived in test-live-region-filter.js, which reaches live.js
alone — so the Kpa-clawbot#1900 half could have regressed silently.

The mapping sat inline in the replay button's anonymous click handler, so
nothing could call it. Extracted it as buildReplayPackets(pkt, data, ctx)
and exported it through _packetsTestAPI, following the precedent
applyObserverFilter set in Kpa-clawbot#1748: test the real production mapping, not a
re-implementation of it that cannot fail when the original does. The
extraction is behaviour-neutral — all existing suites are unchanged.

Two tests now pin both branches: a single-observation packet carries
observer_id and observer_iata alongside the resolved name, and a
multi-observer packet gives each observation its OWN observer_id rather
than the transmission's representative. Removing observer_id from either
branch now fails.

One test detail worth naming: the assertions join the ids into a string
instead of using deepStrictEqual. The array comes from the vm sandbox's
realm, so its prototype differs from the test realm's and a strict deep
compare fails on identical contents.

Verified: live-region-filter 13/13, frontend-helpers 705 passed (up from
703; the 2 remaining failures are the pre-existing favStar ones that also
fail on master), XSS gate 34/34, eslint 0 errors.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Independent review proved the buildReplayPackets extraction is
behaviour-neutral — a differential harness ran master's verbatim inline
block against the extracted function over 5000 randomized inputs (0-3
observations, nulls, garbage timestamps, malformed path_json/decoded_json)
with 0 mismatches, comparing key order and the parse-cache side effects as
well as values. Two findings fixed here.

The port's own test named "Kpa-clawbot#1898: dropping observer_id is what broke it
(guards the regression)" never called dbPacketToLive. It hand-built
{ observer: 'Obs One' } and asserted on packetMatchesRegion alone, so it
duplicated the existing "does not match when packet has no observer_id"
test and survived the very mutation its name claims to guard. It now drives
the real mapping and is named for what it actually checks: a source row
without an observer_id yields a packet that the filter skips. The genuine
regression guard is the sibling test, which does kill that mutation.

Switched my multi-observer assertions from joined strings to assert.deepEqual.
The loose form compares correctly across the vm sandbox's realm — which is
why deepStrictEqual fails there on identical contents — and asserts more than
a join. Also dropped a stale "line ~85" reference from a comment.

Filed as follow-up, not fixed here: the "Multibyte only" gate six lines above
the region gate has the identical defect. groupIsMultibyte reads raw_hex and
route_type, and neither replay mapping emits either (they emit `raw`, and no
route_type at all), so with that option enabled Replay still renders nothing.
Pre-existing, opt-in, and untested because the existing E2E hand-builds
packets with a literal raw_hex and bypasses both mappings.

Verified: region-filter 13/13 and still kills the live.js mutation (11/2);
frontend-helpers 705 passed and still kills both packets.js branches.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dborup
dborup merged commit 834c8da into master Sep 20, 2026
6 checks passed
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