Skip to content

Live event point layer: /event-layers endpoint + map circles (Phases 0–1) - #2

Merged
mortonanalytics merged 6 commits into
mainfrom
feature/event-point-layer-design
May 28, 2026
Merged

Live event point layer: /event-layers endpoint + map circles (Phases 0–1)#2
mortonanalytics merged 6 commits into
mainfrom
feature/event-point-layer-design

Conversation

@mortonanalytics

Copy link
Copy Markdown
Owner

Summary

  • Adds a live event point layer so ingested stream_events render as MapLibre circles in the UX shell — the P0 visualization gap that blocks every data app (and the Epicenter seismic demo). Supports outcome P7.
  • Declarative + feed-agnostic: a per-stream view_config (JSON Pointer geometry/style mapping) is the only place feed-specific knowledge lives; zero feed-specific code in the render/projection path.
  • Scope: Phases 0–1 of md/plans/event-point-layer-plan.md (DB scaffold + connector write path; endpoint walking skeleton + map UI). Phases 2 (config validation + partial-failure UI) and 3 (legend, accessible event list, popup, empty states) are not in this PR.

What changed

  • Database: migration 0030_streams_view_config.sql adds nullable streams.view_config JSONB + a partial index. NULL = not a point layer; no backfill.
  • API: GET /api/v1/workspaces/:id/event-layers — two org-scoped queries (catalog of geo-mapped streams so zero-event streams still emit a layer; events with LIMIT+1 for unambiguous truncation), pure projection (src/services/event_layers.rs) with a field-leakage guard (only declared property_fields + injected _event_id/_observed_at reach the wire), window/limit validation. view_config threaded through StreamDescriptorupsert_named and all connector impls (real configs for FIRMS/IRWIN; None elsewhere; OpenAPI reads from connector config).
  • UI: static/app.js updateMapLayers now fetches /map-layers and /event-layers in parallel, renders circles above rasters with an "Events" badge, handles event-only workspaces (renders when there are no raster layers), and fits bounds to point coords. CSS for the event row + badge.

Entities introduced

Entity Key fields
streams.view_config (JSONB) lon_pointer, lat_pointer, property_fields[], attribution, style{size_*, color_*, label_field}
EventLayersResponse (wire) layers[], streamsOk[], streamsFailed[], truncated, queriedAt

Test plan

  • Lib unit tests pass (cargo test) — connector pointer-resolution + projection logic
  • cargo clippy --all-targets -- -D warnings clean
  • Integration tests (cargo test --test phase_event_layers -- --ignored --test-threads=1) — AC-1/4/5/6/7: happy-path projection + no leakage, cross-org 404 isolation, LIMIT+1 truncation keeping newest, >30d window 400
  • E2E (tests/e2e/event-layers.spec.ts) — AC-8 coexistence/z-order/badge, event-row visibility toggle, event-only workspace render
  • No regression: full tests/e2e/map-panel.spec.ts suite green (stubbed parallel /event-layers to stay hermetic), contract_api_routes/phase13_connectors/phase12_peer green

Requirements decision

No md/requirements/ source-of-truth exists in this repo; the design (md/design/event-point-layer.md) and plan (md/plans/event-point-layer-plan.md) are the contract of record and are included in this branch. No requirements propagation applies.

Notes for reviewers

  • Pipeline docs (md/design/event-point-layer.md, md/plans/event-point-layer-plan.md) are intentionally not archived — Phases 2–3 are still pending.
  • Two commits carry [wip] prefixes; left intact to avoid a force-push on the already-pushed branch.

🤖 Generated with Claude Code

mortonanalytics and others added 6 commits May 27, 2026 22:53
P7 substrate work. Adds the design doc for rendering IONe-ingested
stream_events as GeoJSON point layers on the existing MapLibre shell —
declarative per-stream view_config (RFC 6901 JSON Pointers) so IONe
stays format-agnostic. Forcing function is the Epicenter seismic-monitor
demo; the feature is generic for any geo connector.

Premise verified against connector code: FIRMS + IRWIN already write
geo-bearing payloads JSON Pointer can reach; NWS writes only the
observation properties (lat/lon lives on the connector config) and
needs a follow-up. Public feeds have no peer MCP app, so they cannot
ride the existing tile_url passthrough — this is what earns the
feature its place vs the simpler peer-vector alternative.

- md/design/event-point-layer.md: new design doc (db + api + ui).
  API contract table, wiring graph, 12 mechanically-verifiable
  acceptance criteria, devil's-advocate pass with code-grounded
  verification.
- md/design/app-integration-playbook.md: new §4b documenting the
  IONe-ingested event-layer surface and noting it adds no peer-side
  contract obligation.
- md/plans/infrastructure-backlog.md: scaffolded the P0-P5
  infrastructure backlog framed around Epicenter as the current
  demand signal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds md/plans/event-point-layer-plan.md (medium, 4 phases) and folds
Codex's plan review back into both the plan and the design doc.

Resolutions (all six findings):

- High-1 (AC-11 broken by event-only SQL): split into two queries — a
  catalog of geo-mapped streams + an events fetch. Zero-event geo
  streams now surface as EventLayer entries with empty collections.

- High-2 (UI bails on empty rasters; ignores map.on('load')): rewrite
  updateMapLayers state machine. Render when rasters OR events are
  non-empty; both layer types added inside one map.on('load') callback;
  fitMapBounds extended to compute viewport from GeoJSON points when no
  raster bounds exist.

- High-3 (no production view_config write path): Phase 0 now plumbs
  view_config through StreamDescriptor → StreamRepo::upsert_named →
  all 8 connector impls (real values for FIRMS/IRWIN/OpenAPI; None
  for NWS/fs_s3/mcp_client/slack/smtp). Real FIRMS and IRWIN streams
  become renderable in production, not just seeded test rows.

- Medium-1 (truncated ambiguous at len==limit): LIMIT $N + 1 in the
  events query; service trims and sets the flag unambiguously. Design
  language tightened; per-stream chatty-stream cap deferred to a new
  Open Question 6.

- Medium-2 (validation thinner than contract): Phase 2 spells out a
  six-step validator (pointer syntax mirroring openapi.rs:503; required
  pointers; name shape + uniqueness + reserved-key collision; style
  triple all-or-nothing; range/domain shape + length parity; style
  field-name reference into property_fields).

- Medium-3 (axe-core not installed; server not running): Phase 3 adds
  @axe-core/playwright to package.json devDeps; every Playwright gate
  now states the server precondition explicitly (matches the existing
  playwright.config.ts:4-10 pattern).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ite path

Adds nullable streams.view_config (migration 0030), threads view_config
through StreamDescriptor + upsert_named, and gives FIRMS/IRWIN real geo+style
configs (lowercase vs Pascal-case pointers) while OpenAPI reads it from
connector config. Supports the P0 live event point layer (P7).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GET /api/v1/workspaces/:id/event-layers projects geo-mapped stream_events into
GeoJSON point layers (catalog + LIMIT+1 events queries, org-scoped, pure
projection with field-leakage guard). Static map UI fetches it in parallel with
/map-layers, renders circles above rasters with an "Events" badge, and handles
event-only workspaces. Covers AC-1/4/5/6/7 (integration) + AC-8 (e2e). Supports
the P0 live event point layer (P7).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…space)

Adds two Phase 1 e2e specs flagged by preflight: event-row visibility toggle
hides the circle layer, and an event-only workspace (empty /map-layers) still
renders circles instead of the empty/destroy branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mortonanalytics
mortonanalytics merged commit 391e330 into main May 28, 2026
3 checks passed
@mortonanalytics
mortonanalytics deleted the feature/event-point-layer-design branch May 28, 2026 19:38
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