Skip to content

feat(regions): timeline playback and period comparison - #502

Merged
dborup merged 2 commits into
masterfrom
codex/regions-timeline-comparison
Oct 17, 2026
Merged

dborup merged 2 commits into
masterfrom
codex/regions-timeline-comparison

Conversation

@dborup

@dborup dborup commented Oct 16, 2026 •

Copy link
Copy Markdown
Owner

Summary

Second Regions milestone: timeline playback and adjacent-period comparison. Stacked on #501; review this PR against codex/regions-time-window-coverage until that first milestone is merged.

  • Keep the existing rolling Latest view as the default. Add a Timeline view with hourly steps through the last seven days, 1h/24h/7d intervals, keyboard controls and local playback.
  • Compare the selected interval with the immediately preceding equal-duration interval. Show relays newly observed, observed in both, and not observed in the selected interval, plus relay-appearance changes.
  • Make mode, interval, scope, timestamp and comparison shareable in the URL. Preserve the map viewport during playback; provide an explicit Fit map action.
  • Add a bounded /api/regions/history snapshot with 504 completed UTC-hour buckets. Reuse the existing background relay computation; no per-frame API requests or packet-store scans in the handler.

Evidence and limitations

  • Intervals are half-open and exclude the unfinished current UTC hour. The maximum 21-day horizon supports adjacent seven-day comparisons at the oldest selectable frame, but does not extend actual retained history.
  • Changes and membership classifications are withheld if either interval has incomplete, unknown or loading history, or if any safety cap was reached.
  • Counts are distinct transmissions per relay and scope. Totals are relay appearances, not globally unique packets. “Not observed” does not mean offline; “newly observed” does not mean newly created.
  • Both intervals use the current identity catalogue and latest known self-reported positions, not historical movement. Archived metadata is used only when there is no current node row. Existing identity, alias, blacklist and geographic visibility rules apply.
  • Radio coverage, region-health scoring and inter-region route changes are not inferred. Those remain later milestones.
  • No schema, operator configuration, dependencies, deployment or upstream-repository changes. Future playback/default settings are tracked for the customizer in the design document.

Performance

History publication is deterministic and capped at 5,000 identities, 32 scopes per identity and 250,000 populated bucket cells. Sparse prefix sums are indexed once in the browser; frames read indexed counts rather than rescanning buckets. Markers are keyed and reused.

Local Apple M5 measurements (not runner guarantees):

  • 30,000 transmissions / 500 relays: shared relay+history computation 17.09 ms, 28.51 MB allocated.
  • Warm bulk endpoint: 2.53 ms, approximately 1.95 MB JSON.
  • Representative maximum-cell DTO: 18.82 MB uncompressed JSON, 16.59 ms encoding. Compression remains controlled by the existing optional gzip setting; this is an explicit payload-size trade-off.
  • Browser helper at 5,000 nodes / 250,000 cells: 113 ms initial indexing and 0.256 ms mean frame selection in Node 26. Leaflet rendering is additional work.

Verification

  • Full Go server and ingestor suites passed; focused Regions/relay race suite passed.
  • All 257 JavaScript test files passed.
  • Core browser suite: 154 passed, 3 fixture-dependent skips, no failures.
  • Focused Regions browser suite passed against normal and instrumented local servers. Desktop and narrow-mobile screenshots inspected.
  • Regression coverage includes boundaries, adjacent periods, deduplication, visibility, archived metadata, paired retention coverage, actual bucket caps, URL/history navigation, keyboard controls, local-only playback, teardown, error recovery and map bounds excluding unrelated historical outliers.
  • Independent review completed; the map-fit finding was fixed and covered before commit.
  • Browser coverage capture is included in the shard report, including non-core shards, with collision-safe filenames.

CI readiness follow-up

The first CI run exposed an existing race in the two #382 node-table corner fixtures: initial theme refresh can replace the table between locator resolution and style evaluation. A controlled local reproduction confirmed isConnected=false and an empty computed overflow, while the replacement table correctly computed visible. These fixtures now use the same Home/theme-ready setup as the adjacent tooltip fixtures. All corner/overflow assertions are retained; no production node or theme behavior changed.

GitHub CI is dispatched explicitly on the feature branch because the repository's automatic PR workflow only targets master, while this PR is stacked. Fork guards prevent publishing or deployment.

Final validation: full CI run 38893099259 passed on 29cd1a05 (all three server shards, all three browser shards, ingestor, libraries, JavaScript/lint, image validation, and both aggregate gates). Final remote-head review has no open findings. Not merged or deployed.

@dborup

dborup commented Oct 16, 2026

Copy link
Copy Markdown
Owner Author

CI/review follow-up addressed (commit 29cd1a05):

  1. The two existing Explain transmission vs observation counts in node details (upstream #2131) #382 corner fixtures now wait for initial theme readiness before opening node details, matching the adjacent tooltip fixtures. The first CI run exposed a detached-table race; a controlled local reproduction returned isConnected=false and empty computed styles, while the replacement table correctly computed overflow: visible.
  2. All corner/overflow assertions remain unchanged. No production node or theme behavior was changed.
  3. Local verification: the 19 affected browser cases passed; the full instrumented browser suite passed (154 tests, 3 fixture-dependent skips). Independent review found no open issues with the fix.

A fresh full CI run is validating the current head: 38893099259. The superseded run was cancelled after its failure was diagnosed, not retried without investigation.

@dborup
dborup changed the base branch from codex/regions-time-window-coverage to master October 17, 2026 10:50
@dborup

dborup commented Oct 17, 2026

Copy link
Copy Markdown
Owner Author

Review — CS-Coordinator PR#502 — head 29cd1a0

Verdict: APPROVE with one note.

Reviewed against master 68f243d8 (#501 is merged, and this PR is retargeted to master).

Checked

  • Merged tree (master + this head, built locally). go vet is clean, the full cmd/server suite passes, and -race on Region|Relay|412|470 passes.
  • Lock and work placement. The history buckets are built inside the existing computeRepeaterRelaySnapshot pass with a stable key order, and are published with the immutable relay snapshot. The handler only serializes the snapshot, so there is no packet-store scan per request. This stays consistent with the perf(store): move runtime SQL outside packet-store locks #495 rule.
  • Staging (real data, 312 relays):
    • /api/regions/history returns 200 in 0.52–0.65 s.
    • The payload is 1.1 MB raw and 141 KB with gzip, which the instance already serves. That is well below the 18.8 MB worst case in the PR text.
    • It has 504 hourly buckets and no truncation flags.
    • There are 0 panic, fatal, concurrent map or SQLITE_BUSY entries.
  • UI on staging. The Latest/Timeline view select, previous/next/play hour controls and Fit map work. 362 relays are drawn, and the state is deep-linked (#/regions?window=24h&view=timeline&at=…).

Note (expected behaviour, worth documenting for operators)

The history endpoint covers 21 days (504 buckets), but the instances we run keep 48 h (staging, meshview.dk) or 168 h (meshview-pve) in memory. On 48 h instances most of the timeline is outside retained history. Period comparison is withheld when the preceding interval is not fully covered, so a 24 h comparison is borderline and a 7 d comparison is never available. The behaviour is correct and conservative. A short line in the UI or docs ("timeline depth depends on retentionHours") would avoid confusion.

@dborup
dborup merged commit 5ff889d into master Oct 17, 2026
18 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.

1 participant