# ADR 0006: Vendor-neutral observation sources and live-only timeline Status: accepted for the experimental Mission Core runtime, 2026-07-16. ## Context The spatial scene and camera workspace must accept one device, a custom rig or a future multi-sensor vehicle without adding vendor-specific branches to the host UI. The currently verified K1 profile exposes one spatial visualization path and two independent H.264 camera preview producers. The browser does not yet have a camera delivery adapter, a shared point-cloud/video clock or confirmed camera intrinsics/extrinsics. Hard-coded camera slots, direct RTSP URLs in React components and an apparently seekable timeline would therefore create three false contracts: a fixed sensor count, browser ownership of device transport and replay that does not exist. ## Decision 1. A device plugin normalizes its capabilities into `ObservationSourceDescriptor[]`. Mission Core consumes only these generic descriptors. 2. `id` identifies a runtime source instance; `sourceId` identifies the producer inside the plugin model; `semanticChannelId` describes the canonical channel. Transport addresses are not identities. 3. Provider metadata and device/session/acquisition binding are separate. A new acquisition may change layout context without changing the logical producer. 4. The host owns visibility, z-order, position, resize and fullscreen. A plugin cannot inject global layout behavior. 5. Direct device RTSP URLs do not cross into the generic UI. A future read-only camera adapter must publish a browser-decodable local delivery URL. 6. Until a bounded buffer and clock mapping exist, the shared timeline is `live-only`, `seekable=false`, `sessionRecording=false` and explicitly marked `host-arrival-best-effort`. 7. Directional 3D camera frustums are not rendered until calibration and extrinsics are available and verified. An unresolved camera may be listed as a source, but its orientation must not be invented. ## Current K1 mapping | Producer | Canonical channel | Host representation | | --- | --- | --- | | `sensor.lidar.primary` | `spatial.point-cloud.live` | primary Rerun viewport | | `sensor.camera.left` | `camera.preview.live` | auxiliary media window / camera card | | `sensor.camera.right` | `camera.preview.live` | auxiliary media window / camera card | Both camera producers share the proven canonical channel and remain distinct by `sourceId`. Their current state is `declared`: the compatibility profile proves the channels, but no browser preview URL is advertised yet. ## UI consequences - The spatial scene source picker renders any number of plugin-published sources. - Auxiliary media uses the design-system `WorkspaceWindow`, constrained to the scene bounds, with controlled drag, resize, close and maximize state. - The Cameras workspace filters media modalities and renders 0, 1, 2 or N cards; only its grid scrolls when the source count exceeds available space. - Point-cloud focus and camera focus stay inside the application panel rather than taking over the browser viewport. - Empty and declared channels remain honest placeholders; no demo frames or synthetic sensor values are substituted. ## Follow-up gate The next implementation step is a plugin-owned, read-only H.264 adapter that terminates RTSP/RTP locally and exposes a browser-supported stream plus measured per-source metrics. Synchronized rewind becomes eligible only after the adapter provides bounded buffering and an evidenced mapping between the RTP 90 kHz clock and the spatial stream clock. ## Code anchors - `apps/control-station/src/core/runtime/contracts.ts` - `apps/control-station/src/core/observation/useObservationLayout.ts` - `apps/control-station/src/components/ObservationSources.tsx` - `apps/control-station/src/components/ObservationTimeline.tsx` - `apps/control-station/src/workspaces/Workspaces.tsx` - `apps/control-station/src/device-plugins/xgrids-k1/observationSources.ts`