feat(k1): complete primary acquisition lifecycle
This commit is contained in:
+124
-41
@@ -1,15 +1,19 @@
|
||||
# Observation sessions, playback and workspace layout
|
||||
|
||||
Status: implemented for native K1 point/pose evidence and host-side camera
|
||||
archival. Recorded spatial playback is exposed in the Mission Core observation
|
||||
workspace. The camera archive/player contract is implemented and covered by
|
||||
tests, but all retained real K1 sessions predate canonical camera archival.
|
||||
Physical archived-camera playback therefore remains an open acceptance gate;
|
||||
historical sessions contain no recoverable video.
|
||||
archival. Valid K1 `ModelingReport` scan time, distance and speed are also
|
||||
materialized as recorded Rerun time series. Recorded spatial playback is exposed
|
||||
in the Mission Core observation workspace. The camera archive/player contract
|
||||
is implemented and covered by tests, but all retained real K1 sessions predate
|
||||
canonical camera archival. Physical shared-timeline playback of one selected
|
||||
camera plus point cloud therefore remains an open acceptance gate; historical
|
||||
sessions contain no recoverable video.
|
||||
|
||||
## Operator path
|
||||
|
||||
1. Start a normal acquisition from **Парк → Локальное устройство**.
|
||||
1. Enter the required project name and start a normal acquisition from **Парк →
|
||||
Локальное устройство**. The NFKC-normalized/trimmed value is local display
|
||||
metadata, not a filesystem path, and becomes the saved-session catalog name.
|
||||
2. Open **Наблюдение → Пространственная сцена**. Live point cloud, trajectory
|
||||
and the selected camera remain live-only while acquisition is running.
|
||||
3. Stop acquisition normally, or allow the local service to recover an
|
||||
@@ -35,6 +39,15 @@ historical sessions contain no recoverable video.
|
||||
opening restores display settings, tool windows and dynamic source-window
|
||||
positions. This action saves no sensor evidence.
|
||||
|
||||
Mission Core does not switch an active acquisition to another spatial source.
|
||||
Every nonterminal or unknown acquisition state fail-closes saved-session replay,
|
||||
persisted replay reattach and manual RRD/Rerun input/apply/reset with
|
||||
**Завершите текущий приём перед сменой источника.** A plugin-owned automatic
|
||||
source action clears the older source and opens the scene only after its start
|
||||
returns success; failure leaves the current scene mounted. This internal
|
||||
success-result transition is not an operator switch. The guard does not stop the
|
||||
scanner or local receiver automatically.
|
||||
|
||||
Display controls have no separate Apply/Reset transaction. Boolean controls
|
||||
commit immediately; sliders, colors and selects commit on release/blur or after
|
||||
a short quiet period, and concurrent commits collapse to the newest value. A
|
||||
@@ -75,12 +88,12 @@ A queued job may legitimately wait behind another export and therefore uses the
|
||||
overall preparation deadline rather than the active-export heartbeat timeout.
|
||||
|
||||
The first preparation of a long capture can take time because every decodable
|
||||
point/pose message is projected into RRD. Later openings reuse a verified,
|
||||
digest-bound cache. Derived RRD cache v7 is intentionally incompatible with v6
|
||||
and older generations. It retains v6's real `session_time = 0` payload anchor
|
||||
and additionally binds the cache to the owning plugin ID, primary artifact and
|
||||
ordered set of confined source artifacts. Older generations are rebuilt once in
|
||||
the background.
|
||||
point/pose message and every valid K1 `ModelingReport` is projected into RRD.
|
||||
Later openings reuse a verified, digest-bound cache. Derived RRD cache v9 is
|
||||
intentionally incompatible with v8 and older generations. In addition to plugin
|
||||
and ordered-artifact identity, it binds the durable clock origin and active
|
||||
envelope and materializes real `session_time = 0` origin and sealed completion
|
||||
rows. Older generations are rebuilt once in the background.
|
||||
The browser may use the strict `source_url` with `If-Match`, while the embedded
|
||||
Rerun loader uses the canonical `viewer_source_url` whose lowercase SHA-256
|
||||
`generation` query is bound to the same launch descriptor. A missing or stale
|
||||
@@ -106,7 +119,15 @@ checkout:
|
||||
│ └── sessions/
|
||||
│ ├── .current_session # present only while a writer owns the root
|
||||
│ └── <UTC>_viewer_live/
|
||||
│ ├── captures/ # native MQTT source evidence
|
||||
│ ├── manifest.redacted.json # normalized local project display metadata
|
||||
│ ├── captures/
|
||||
│ │ └── mqtt_live/
|
||||
│ │ ├── mqtt.raw.k1mqtt
|
||||
│ │ ├── mqtt.metadata.jsonl
|
||||
│ │ ├── mqtt.timeline.origin.json
|
||||
│ │ ├── mqtt.timeline.json # provisional transport envelope
|
||||
│ │ ├── mqtt.timeline.session-<sha256>.json # sealed generation
|
||||
│ │ └── mqtt.summary.json # atomic active-envelope pointer
|
||||
│ └── media/ # canonical camera archives
|
||||
└── recordings/
|
||||
├── .export.lock # cross-process conversion/eviction lock
|
||||
@@ -156,6 +177,9 @@ For the current K1 profile:
|
||||
```text
|
||||
MQTT callback
|
||||
├─ durable native .k1mqtt + aligned metadata (source of record)
|
||||
├─ durable clock origin (before camera production)
|
||||
├─ provisional + content-addressed envelopes (transport/session bounds)
|
||||
├─ ModelingReport -> live product metrics (before visual preview queue)
|
||||
└─ bounded latest-wins Rerun live preview (disposable)
|
||||
|
||||
selected RTSP producer
|
||||
@@ -166,14 +190,21 @@ selected RTSP producer
|
||||
The live preview is intentionally allowed to drop frames under load. Native
|
||||
point/pose evidence and camera archive writes do not traverse that queue.
|
||||
|
||||
Native `.k1mqtt` bytes with aligned metadata and canonical camera fMP4 archives
|
||||
are the evidence source of truth. The derived RRD contains every decodable point
|
||||
and pose frame from that source, but remains a rebuildable view rather than an
|
||||
evidence master. A cache entry is rebuilt only when native source
|
||||
Native `.k1mqtt` bytes with aligned metadata, the capture-clock artifacts and
|
||||
canonical camera fMP4 archives are the evidence source of truth. The derived RRD
|
||||
contains every decodable point and pose frame plus every valid `ModelingReport`
|
||||
distance/speed/scan-time sample from that source, but remains a rebuildable view
|
||||
rather than an evidence master. A cache entry is rebuilt only when native source
|
||||
identity/digests change or an incompatible derived-data export revision is
|
||||
introduced. A UI blueprint or workspace-layout revision never invalidates or
|
||||
rewrites the data RRD. Cache v4/v5 payloads are not reusable as v6 because they
|
||||
do not guarantee the real zero-time anchor.
|
||||
rewrites the data RRD. Cache v8 and older payloads are not reusable as v9 because
|
||||
they do not bind both real capture-envelope endpoints.
|
||||
|
||||
`ModelingReport` time is the device's `ScanTime` counter at two ticks per second;
|
||||
distance and speed are the reported `MoveDistance`/`MoveSpeed` values. Live state
|
||||
keeps the current device scan generation and does not accumulate an earlier
|
||||
distance after the device resets scan time and route distance. These values are
|
||||
not reconstructed from pose integration or a browser timer.
|
||||
|
||||
Expensive cache misses run through the bounded preparation worker and one global
|
||||
cross-process export gate to cap peak RAM, CPU and temporary-disk use. Crash
|
||||
@@ -183,6 +214,52 @@ active response leases do not wait behind that gate. The derived cache has an
|
||||
8 GiB default quota, preserves a 2 GiB default filesystem reserve and evicts
|
||||
least-recently-used RRDs only; it never deletes native evidence.
|
||||
|
||||
## Capture-clock envelope
|
||||
|
||||
New K1 captures first publish bounded schema-1
|
||||
`captures/mqtt_live/mqtt.timeline.origin.json` with start epoch and monotonic
|
||||
nanoseconds. The writer creates and synchronizes this immutable artifact after
|
||||
its raw/metadata files are open and before camera production is armed. A crash
|
||||
therefore cannot leave retained camera evidence whose intended session zero
|
||||
existed only in memory.
|
||||
|
||||
MQTT shutdown then publishes schema-1 `mqtt.timeline.json` with the same start
|
||||
plus transport completion. Capture-summary schema 2 initially points to it with
|
||||
`capture_clock_scope: transport`, and binds both origin/envelope names and
|
||||
SHA-256 values. That provisional file is not rewritten.
|
||||
|
||||
After the camera archive and MQTT/runtime stop, the acquisition owner creates
|
||||
`mqtt.timeline.session-<sha256>.json` with the extended session completion and
|
||||
atomically switches `mqtt.summary.json` to that content-addressed name, digest
|
||||
and `capture_clock_scope: session` while still holding the active-session lease.
|
||||
Publication order is sealed file first, summary pointer second, so a crash leaves
|
||||
the previous pointer valid and the idempotent seal can converge on retry. The
|
||||
same summary update records `session_elapsed_seconds`. Files and containing
|
||||
directories are synchronized at their publication boundaries.
|
||||
|
||||
Discovery validates the summary-selected filename, both digests, the shared
|
||||
origin and the envelope bounds, then catalogs that exact artifact. The
|
||||
materializer passes its exact staged path to the exporter; it never chooses a
|
||||
sealed generation by glob or “latest file” ordering. Direct/legacy export keeps
|
||||
the fixed provisional-name fallback only for evidence outside the cataloged
|
||||
session contract, and orphan sealed candidates not selected by the validated
|
||||
summary are ignored.
|
||||
|
||||
A provisional `transport` scope is never advertised as combined camera replay.
|
||||
An interrupted origin-only point/pose prefix may use the durable origin and its
|
||||
last validated message as a compatibility end, but it cannot advertise camera
|
||||
media. If the origin, active envelope, summary pointer or digest relationship is
|
||||
missing/corrupt, new-schema discovery fails closed instead of inventing a
|
||||
session boundary.
|
||||
|
||||
For a sealed generation, `session_time = 0` is the envelope start and the RRD
|
||||
end is the envelope completion. Cache v9 logs real rows at
|
||||
`/__mission_core/session_origin` and `/__mission_core/session_end`, so a camera
|
||||
fragment accepted before the first MQTT message or after the last point/pose
|
||||
message can remain inside the declared RRD interval. Valid legacy sessions
|
||||
without this artifact keep their first/last-message compatibility fallback and
|
||||
cannot gain historical camera coverage.
|
||||
|
||||
## Camera archive contract
|
||||
|
||||
New acquisitions archive each selected source and codec epoch below the same
|
||||
@@ -275,6 +352,11 @@ camera acceptance run retained point/pose evidence only.
|
||||
process or power failure may discard the final uncommitted group. Raw bytes
|
||||
from an interrupted commit window may survive beyond the durable metadata;
|
||||
replay uses only the last validated metadata-aligned raw boundary.
|
||||
- New capture-summary schema 2 binds the durable origin and its active envelope
|
||||
by name and SHA-256. A normal combined replay requires the owner-sealed,
|
||||
content-addressed `session` scope. A crash can leave an origin-only or
|
||||
provisional `transport` generation, but discovery will not use either to
|
||||
advertise camera coverage. A mismatched origin/envelope/summary fails closed.
|
||||
- A native capture with a missing final summary is accepted only when the raw
|
||||
and metadata prefix is bounded, aligned and structurally valid. It is cataloged
|
||||
as `interrupted`, never silently promoted to `ready`.
|
||||
@@ -385,15 +467,17 @@ API boundary.
|
||||
|
||||
## Current limit
|
||||
|
||||
The spatial RRD, trajectory and archived fMP4 cameras use the same operator
|
||||
scrubber now. Camera epochs are aligned to zero-based `session_time` from the
|
||||
shared host-arrival monotonic clock and rendered through MSE. The client selects
|
||||
an epoch only inside its declared inclusive interval and verifies that the
|
||||
The spatial RRD, trajectory, archived device-metric series and archived fMP4
|
||||
cameras use the same operator scrubber now. Camera epochs are aligned to
|
||||
zero-based `session_time` from the shared host-arrival monotonic clock and
|
||||
rendered through MSE. The metric tab carries device-reported route distance,
|
||||
speed and scan time at their `ModelingReport` receive times. The client selects
|
||||
a camera epoch only inside its declared inclusive interval and verifies that the
|
||||
decoded MSE seekable duration covers that interval. This remains best-effort
|
||||
correlation: codec PTS and K1 sensor exposure time are not proven to share the
|
||||
LiDAR clock. A codec epoch whose init segment does not expose a browser-supported
|
||||
codec or whose timing cannot be proven remains retained evidence and fails
|
||||
closed in the UI/background preparation.
|
||||
correlation: codec PTS, K1 sensor exposure time and LiDAR firing time are not
|
||||
proven to share a device clock. A codec epoch whose init segment does not expose
|
||||
a browser-supported codec or whose timing cannot be proven remains retained
|
||||
evidence and fails closed in the UI/background preparation.
|
||||
Historical sessions with no canonical camera archive honestly show no recorded
|
||||
video. Recorded blueprints can change accumulation, grid visibility, point and
|
||||
trajectory visibility, point radius and a uniform custom point color without
|
||||
@@ -403,21 +487,20 @@ declared RRD has been received and decoded. Height, intensity, distance and RGB
|
||||
palettes remain baked into current RRD rows; fully dynamic recoloring requires
|
||||
exporting the corresponding scalar components in a future recording schema.
|
||||
|
||||
## Verification checkpoint — 2026-07-17
|
||||
## Verification boundary — 2026-07-17
|
||||
|
||||
The repository state described above passed the complete local pre-push gate:
|
||||
|
||||
- `uv sync --frozen --group dev` completed against the locked Python graph;
|
||||
- `uv run pytest`: **284 passed**;
|
||||
- `uv run ruff check .`: clean;
|
||||
- project mypy and strict plugin-SDK mypy: clean across 51 and 12 source files;
|
||||
- the XGRIDS K1 profile loader and emitted v0alpha2 JSON schema validated;
|
||||
- clean `npm ci` ran the required Rerun 0.34.1 postinstall patch;
|
||||
- frontend unit suite: **111 passed, 0 failed**;
|
||||
- TypeScript project build and Vite production build completed;
|
||||
- the generated `dist/index.html` and non-empty Rerun WASM artifact were verified.
|
||||
The current automated gate covers project-name validation, source-switch
|
||||
fail-closure, optional plugin scene controls, bounded `ModelingReport` decode,
|
||||
inert start/stop encoding and correlation, capture-clock publication/validation,
|
||||
cache-v9 origin/end materialization and archived metric series. The standard
|
||||
repository gate remains Python tests/Ruff/mypy plus frontend unit tests,
|
||||
TypeScript checking and the Vite production build; exact pass counts belong to
|
||||
the commit's CI/pre-push result rather than this durable architecture contract.
|
||||
|
||||
Vite still reports its expected large-chunk warning for the embedded Rerun
|
||||
viewer/WASM payload. That is a packaging optimization item, not a failed gate.
|
||||
No retained physical K1 session contains the new canonical camera archive yet,
|
||||
so real recorded-camera playback remains an explicit hardware acceptance test.
|
||||
No retained physical K1 session contains the new canonical camera archive, so a
|
||||
real point-cloud plus one-camera recorded playback remains an explicit hardware
|
||||
acceptance test. Automated protocol tests also do not authorize K1 modeling
|
||||
publishing: legitimate OpenAPI key provisioning and durable save completion
|
||||
remain separate physical/security gates.
|
||||
|
||||
Reference in New Issue
Block a user