99 lines
4.3 KiB
Markdown
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.
|