feat(k1): complete primary acquisition lifecycle

This commit is contained in:
DCCONSTRUCTIONS
2026-07-17 23:03:59 +03:00
parent 9d51080d2e
commit aa3680948f
66 changed files with 6093 additions and 544 deletions
+124 -41
View File
@@ -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.