NODEDC_MISSION_CORE/docs/adr/0009-device-plugin-observat...

99 lines
4.3 KiB
Markdown

# ADR 0009: device-plugin observation runtime and K1 extraction
- Status: accepted
- Date: 2026-07-17
- Extends: ADR 0003, ADR 0004 and ADR 0008
## Context
The manifest/action boundary isolated the browser shell, but the host observation
service still imported K1 archive discovery, `.k1mqtt` preparation, MQTT timing
metadata and the K1 normalizer. A second sensor could not provide durable replay
without adding vendor branches to `SessionStore`, `SessionRecordingMaterializer`
and `app.py`.
## Decision
Every installed device plugin may contribute an optional observation runtime:
- one or more `ObservationArchiveSource` values with a plugin ID, archive ID,
confined root, discovery callback and recovery callback;
- exactly one recording exporter for that plugin ID;
- generic `ObservationSessionCandidate`, `SessionSource` and
`ObservationArtifactCandidate` values returned to the host.
The host owns SQLite lifecycle, path confinement, preparation scheduling,
cross-process cache locking, atomic publication, RRD delivery, recorded-media
admission and browser APIs. A plugin owns native evidence discovery, transport
format validation, recovery semantics, timeline-origin extraction and conversion
of its primary artifact into the canonical recorded Rerun artifact.
`ReplayCommand` is now vendor-neutral. It contains an owning `plugin_id`, an
opaque primary artifact ID, an ordered tuple of confined artifacts and a ready
timeline origin. It contains no filename suffix, MQTT topic or sidecar name.
The materializer selects the exporter by `plugin_id` and stages every validated
artifact prefix while preserving only its plugin-owned basename.
Derived RRD cache v7 binds the published RRD to:
- plugin ID and primary artifact ID;
- ordered artifact IDs and media types;
- full file identity and replay boundary for every artifact;
- per-artifact prefix digest;
- derived recording digest and zero-based timeline bounds.
Cache v6 and older sidecars are rebuilt once. The native evidence is not
rewritten.
The concrete implementation is physically located below:
```text
src/k1link/device_plugins/xgrids_k1/
ble/ mqtt/ protocol/ analyze/ net/ usb/
viewer/
facade.py camera.py archive.py observation.py rrd_export.py cli.py
```
The manifest entrypoint is
`k1link.device_plugins.xgrids_k1.facade:build_xgrids_k1_plugin`. Generic
`sessions`, `web/app.py`, `web/session_api.py` and the Rerun consumer do not
import this package or contain K1 formats, topics or identifiers.
Plugin SDK v0alpha2 additionally defines immutable
`RuntimeActionInvocation` and `RuntimeActionResult`. The host dispatcher creates
and validates them for every plugin action, including legacy compatibility
routes, before returning the path-free output document.
## Acceptance gate
The automated gate builds an unrelated synthetic spatial-sensor plugin which:
1. discovers its own native evidence extension;
2. reconciles into the unchanged host SQLite catalog;
3. produces a generic `ReplayCommand`;
4. materializes and reuses an RRD through its plugin-keyed exporter.
A dependency test also fails if generic observation hot-path files regain K1,
XGRIDS, topic, native-format or concrete-plugin imports. Adding a second device
therefore requires a manifest/runtime contribution and a plugin-owned reviewed
UI contribution, not edits to the session API, preparation queue, Rerun consumer
or main application shell. ADR 0010 defines that frontend boundary.
## Consequences
The semantic and physical vendor boundary is complete for the current
single-process architecture. Existing K1 raw evidence, camera archives and
operator workflows remain compatible. The `k1link` distribution name and CLI
command remain transitional compatibility names; their implementation is now
plugin-owned.
ADR 0011 subsequently places the action control plane behind a versioned
descriptor/handshake/health transport seam. Observation discovery and export
remain host-called plugin contributions and are not yet portable process
transport contracts.
This ADR does not claim process isolation, crash containment, a durable operation
journal, signed plugin bundles, multi-device concurrency, portable SDK stream
envelopes or SDK `EvidenceStore` persistence. Those require a supervisor and a
new physical regression gate; they must not be inferred from module extraction.