feat(fleet-ui): organize apparatus equipment beneath its computer

This commit is contained in:
DCCONSTRUCTIONS
2026-09-25 21:38:25 +03:00
parent d2ec9e95b1
commit e2b0d839eb
13 changed files with 225 additions and 102 deletions
@@ -0,0 +1,69 @@
# 0050 — Apparatus composition and its onboard device branch
## Operator job and decision
Owner-approved 25.09.2026: configure the rover as a whole while seeing which
computer owns each device. Keep the apparatus identity above its computer.
The apparatus has a compact name/class summary, apparatus settings, and an
Equipment section. The current single computer is an expandable resource row;
its devices appear beneath it. Host facts and unpairing belong to its settings.
The previously proposed flat peer list of computer and cameras was rejected:
it hides the actual USB/runtime dependency. The present implementation supports
one onboard computer; multiple computers and flight-controller transports are
not claimed. A future controller's placement must follow its actual connection,
not an invented independent link. Actuation role is separate from topology.
## Actions and persistence
The outer plus is owned by the vehicle list and creates an apparatus. Inside a
vehicle it is absent. Equipment has an attach-computer action and inventory
refresh; the computer row has wireless-device enrollment, settings and monitor.
USB devices are discovered by Node. Wireless enrollment reuses plugin-owned
forms, including K1, and cannot run while the owning computer is unavailable.
ResourceRow, Inspector, IconButton, SettingsCard and Window are existing DG
primitives. No new visual primitive or product root is introduced. The stored
v1 board-layout ids remain compatible: settings/devices control the two outer
sections; computer now controls the nested branch. Collapsing an outer section
does not erase the nested preference. Scene and observation layout identities
remain keyed by the apparatus.
Offline Core reads only its cached fleet inventory, never queries the absent
hardware or shows an endless discovery spinner. Cached devices stay visible;
their current connection is unconfirmed, and mutations stay disabled.
## Computer replacement boundary
POST /fleet/{vehicle}/computer consumes a verified Node invitation and an
expected vehicle revision. The vehicle id/name/class/creation time stay intact.
The prior private binding row is archived transactionally in board_history;
monitor/configuration archives remain keyed by their original node identity.
The new computer does not inherit commands, sensor sessions, calibration or
claims of physical wiring from the previous computer. A new verified inventory
is required. Existing devices bound to another apparatus are not transferred.
Expired previews, stale revisions, pending pairing, active rover control and
unexpired device operations block replacement. Verification/listener failures
before commit leave the original row intact. At explicit replacement commit,
the old binding loses authority; handshake completion of the new binding is
shown separately. Failure does not silently restore or authorize the old one.
Each stream frame rechecks its binding. Command admission resolves the node
under the same fleet lock as replacement. Preview retry is idempotent only for
the exact resulting binding. This is not live hot-swapping acceptance.
## Validation
Synthetic pairing tests cover preserved vehicle identity/layout, archived old
binding, old-certificate rejection, duplicate node rejection, stale/expired
previews, idempotency, active operation rejection and transactional rollback.
Frontend checks and offline browser QA cover both plus scopes, nested folding,
offline inventory, K1 enrollment state, host settings and back navigation.
Acceptance on 25.09.2026: all 69 fleet tests, 978 Control Station tests and
the Node UI contract test passed. Both production builds passed TypeScript.
Browser QA on canonical port 8000 verified normal/expanded presentation,
Escape dismissal, list/computer/device action scopes, cached devices, and
folding persistence across navigation and a full page reload. The existing
LaunchAgent restarted the sole Core process; exact health recovered.
The real board is powered off: new pairing/replacement and live device operation
require subsequent hardware acceptance; no such action is performed here.