Live event point layer: /event-layers endpoint + map circles (Phases 0–1) - #2
Merged
Merged
Conversation
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>
8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
stream_eventsrender as MapLibre circles in the UX shell — the P0 visualization gap that blocks every data app (and the Epicenter seismic demo). Supports outcome P7.view_config(JSON Pointer geometry/style mapping) is the only place feed-specific knowledge lives; zero feed-specific code in the render/projection path.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
0030_streams_view_config.sqladds nullablestreams.view_config JSONB+ a partial index. NULL = not a point layer; no backfill.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 withLIMIT+1for unambiguous truncation), pure projection (src/services/event_layers.rs) with a field-leakage guard (only declaredproperty_fields+ injected_event_id/_observed_atreach the wire), window/limit validation.view_configthreaded throughStreamDescriptor→upsert_namedand all connector impls (real configs for FIRMS/IRWIN;Noneelsewhere; OpenAPI reads from connector config).static/app.jsupdateMapLayersnow fetches/map-layersand/event-layersin 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
streams.view_config(JSONB)lon_pointer,lat_pointer,property_fields[],attribution,style{size_*, color_*, label_field}EventLayersResponse(wire)layers[],streamsOk[],streamsFailed[],truncated,queriedAtTest plan
cargo test) — connector pointer-resolution + projection logiccargo clippy --all-targets -- -D warningscleancargo 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 400tests/e2e/event-layers.spec.ts) — AC-8 coexistence/z-order/badge, event-row visibility toggle, event-only workspace rendertests/e2e/map-panel.spec.tssuite green (stubbed parallel/event-layersto stay hermetic),contract_api_routes/phase13_connectors/phase12_peergreenRequirements 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
md/design/event-point-layer.md,md/plans/event-point-layer-plan.md) are intentionally not archived — Phases 2–3 are still pending.[wip]prefixes; left intact to avoid a force-push on the already-pushed branch.🤖 Generated with Claude Code