# Implementation plan

This plan supersedes the app-dependent experiment order in the reference Bible.
Each gate produces evidence and an explicit GO, PAUSE or BLOCKED result.

## Current checkpoint — 2026-07-24

| Stage | Result |
| --- | --- |
| Stage 0 host/repository | GO — isolated Python 3.12 environment and private Git |
| Gate 1 physical operation | GO — autonomous double-click start/stop verified |
| Stage 1 BLE discovery | GO — repeatable advertisement and GATT profile |
| Stage 2 BLE provisioning | GO — reviewed 99-byte profile, LAN association confirmed |
| Stage 2 local connection matrix | GO for Bridge/direct-LAN; LAB-ONLY for Quick Connect — firmware review proved the K1 3.0.2 AP credential is firmware-constant and the SSID follows the `XGR-` identity/MAC rule. One prepared Mac completed AP-enable, AP-ready, CoreWLAN association and the normal control lifecycle. The successful host had been seeded from the reviewed firmware archive; a copied build on a clean host cannot acquire that material automatically. Automatic firmware download, iPhone extraction and hard-coding were rejected. Bridge remains the product path; Direct Connect remains physically pending |
| Stage 3 application session | GO — MQTT 3.1.1 on confirmed K1 TCP 1883 |
| Stage 4 artifacts/flows | GO — bounded capture, hashes and negative control |
| Stage 5 point cloud | GO — raw-LZ4 protobuf, 1,140 live frames decoded |
| Stage 5 pose | GO — 1,215 live frames decoded and motion-correlated |
| Stage 5 modeling telemetry | GO (read-only) — bounded `ModelingReport` decoder and device-reported scan time/distance/speed are integrated before the preview queue |
| Stage 5 camera | GO (live) — left/right RTSP/H.264 preview observed, read-only runtime adapter and physical UI acceptance completed |
| Stage 6 live viewer | GO — React Control Station, embedded self-hosted Rerun cloud/trajectory, plugin-owned spatial controls and live device/Mac metrics |
| Stage 7 observation archive | GO (point/pose/telemetry contract) — durable catalog, recovery, capture-clock-bounded RRD preparation, archived metric time series, saved-session timeline and atomic playback are implemented |
| Stage 7 recorded cameras | GO — the accepted physical session sealed real fMP4 camera data, produced two digest-bound recorded sources and passed manifest/init/segment range admission alongside a 107 MB RRD; browser QA opened real frames from the 170 MB camera epoch at multiple shared-timeline positions |
| Plugin isolation | GO (laboratory control plane) — vendor backend/frontend and optional scene controls are plugin-owned; manifest/runtime descriptor parity, versioned handshake, lifecycle health and transport correlation fail closed while execution remains in-process |
| K1 application control | GO (physical staged cycle) — after fixing the PCAP-proven `sint64` time field, one explicit UI launch completed all 14 canonical operations on one control session, reached live `SCANNING + project + init_ready`, displayed real points, then one explicit STOP returned K1 to unbound `READY`. No retry or fallback command was sent. Native-project reuse through LixelGO/USB remains an independent verification |
| Stage 8 product storage | PAUSE — retention, replication, encryption, capacity monitoring and long-run browser/WASM stress remain deployment gates |
| Simulation Polygon | SIM S0 GO; S1A–S1D lifecycle/browser/Ackermann command slices PASS on the accepted Gazebo/PX4 regression profile. S1E now makes provider interchangeability executable through a strict provider profile, generic live-state source and generic command acceptance. Gazebo remains the frozen low-cost regression tier; ADR 0017 gates a measured native Unreal/CARLA/Project AirSim U0 selection before any production bridge. Differential Rover, calibrated closed-loop speed, canonical PX4/ROS 2 telemetry, pause/step/reset, heartbeat/offboard/link-loss cases, navigation/safety acceptance, shared-deployment auth/RBAC and real actuator authority remain absent |

USB project copying remains optional ground truth rather than a blocker for the
now-verified network path. Owner-operated LixelGO traffic verifies the MQTT
start/stop mapping and RTSP camera transport. The exact start/stop protobuf
encoder, response correlator and device-status state machine now run in the
separately gated interactive publisher; the legacy shadow publisher remains
disabled. Retained PCAP plus client static analysis prove that OpenAPI is one
private application-level value in this LixelGO build, not a per-scanner
credential. The selected BLE peripheral returns its own LAN IP; the initial
unbound `DeviceInfoRequest` then returns vendor ID, serial, model, activation and
version facts for that live K1. Mission Core's shadow bootstrap reproduces the
10 observed pre-START requests byte-for-byte across four response windows and
five observed publish groups: initial DeviceInfo binding; then the ordered
ModelingStatus/RTK and time-sync/DeviceInfo/RTK groups without an invented
response barrier between them; NTRIP read; then cloud/RTK/DeviceInfo reads. The first
ModelingStatus request has no mandatory synchronous response barrier; readiness
is separately attested through live DeviceStatus. A fixed macOS Keychain loader
now reads the exact 36-byte authority without environment/file/browser fallback.
The response orchestrator correlates required headers/results and rejects
identity/profile drift. The legacy shadow publisher remains a separate
write-disabled type that cannot call its injected sink. Its dormant coordinator
is wired into the XGRIDS facade only for redacted planning/state. Separately,
plugin v0.5.0 installs the reviewed physical-acceptance transport behind explicit
operator actions and one continuous background session owner. Each operation
key is consumed before publish, automatic reconnect and application retry are
forbidden, and any unknown outcome poisons the transport. Public state contains
only bounded lifecycle, counter and failure metadata; authority and device
identity remain private.
The one-time `k1link authority provision` command delegates secret entry to the
macOS Keychain TTY prompt and validates through the production loader without
receiving the value in argv/environment/file/browser state. During the first
physical attempt that CLI loader did not receive macOS approval, so a temporary
process-local Security.framework lab adapter supplied the already reviewed
value. The transport emitted bootstrap ordinals 1–6 and received all five
required responses through the old third window, but topic-only routing
misassociated the unbound and bound RTK responses and the orchestrator raised
`ApplicationBootstrapError`. No START was emitted;
live DeviceStatus stayed READY and the local point/pose counts remained zero.
The local capture was sealed and the temporary Keychain item was deleted.
The second physical attempt then correlated all ten bootstrap requests and one
START success response. It nevertheless did not reproduce the full LixelGO
lifecycle: all eleven publishes completed in 365 ms, the process did not retain
the control session through initialization, and it omitted the immediate and
post-initialization status reads present in both retained LixelGO START captures.
K1 reported `SCAN_STARTING` for about 24 seconds, emitted system error
`0x32040133` twice (the recovered `ALGORITHM_ERROR` namespace/value), returned
to `OTHER_STATUS`, produced zero point/pose frames and showed steady red. The
operator restored steady green through a normal power cycle. The legacy
`run_bootstrap()` path now rejects before emission. The offline acceptance path
maps requests 1–6 to connection, request 7 to explicit scan-workspace entry,
requests 8–10 to explicit project-prompt opening and request 11 to the operator's
START confirmation. Captured human delays are not replayed or treated as minimum
timeouts. After the immediate operation-12 read, operations 13–14 wait for the
same bound K1 to report `SCANNING`, a bound project and `init_ready=true`.
The same transport is then serviced until an explicit STOP request; post-STOP
ownership waits for live unbound `READY`. System-error
and status reports are decoded into redacted safety state. The next staged
physical attempt, after correcting the protobuf `sint64` time field, completed
all 14 canonical operations, reached live scanning with real point/pose data and
then completed one explicit STOP. K1 returned to unbound `READY`; no retry or fallback device command was
sent. The durable local session is catalogued as ready/replayable, its RRD and
two camera sources pass digest-bound range admission, and physical double-click
remains the fallback. Reuse of the vendor-native project is still verified
separately through LixelGO or USB.

The Stage 6 live path uses a bounded raw-first bridge: loss in the visualization
queue cannot discard MQTT evidence. The queue holds four preview messages;
`ModelingReport` is consumed by a plugin-injected observer before that queue and
does not compete with point/pose frames. A project name is mandatory at
preparation, NFKC-normalized/trimmed on both sides, rejected for control
or surrogate characters or more than 96 characters, and retained only as
display metadata.

Stage 7 adds an independent durable observation-session lifecycle. New native
captures durably publish the clock origin before camera production, retain a
transport-scoped provisional envelope and atomically switch the capture-summary
pointer to a content-addressed session envelope after all producers stop.
Completed or recovered captures are prepared once by a bounded backend worker
into digest-bound RRD/cache-v9 and camera-manifest generations;
a browser replay request never performs conversion. The RRD materializes real
origin/end rows plus archived device-reported distance, speed and scan-time
series. The saved scene, controller and timeline remain hidden until the
complete RRD range and every declared camera pass admission. Sensor-to-display
latency and camera/LiDAR sensor-clock alignment remain separate correlation
tests.

The generic host clears a prior manual or archived spatial source only after a
plugin-owned live/file-replay start succeeds; a failed start preserves the
current scene. Once an acquisition is nonterminal, a fail-closed guard blocks
saved-session switching, persisted replay reattach and manual source
input/apply/reset until that acquisition has been finalized. The successful
automatic-source action is an internal start-result boundary and intentionally
does not masquerade as an operator source switch. The K1 scene-level stop
control is plugin-owned. A plugin-commanded acquisition sends one canonical STOP
and seals locally after protocol-reported standby; an operator-manual acquisition
still stops and seals only local reception and reports scanner state as unknown.

## Parallel branch — Simulation Polygon

The Polygon branch follows
[`docs/12_SIMULATION_POLYGON_PRODUCT_AND_SRS.md`](12_SIMULATION_POLYGON_PRODUCT_AND_SRS.md)
and
[`ADR 0015`](adr/0015-simulation-polygon-qualification-boundary.md). Component
placement follows [`ADR 0016`](adr/0016-distributed-product-edge-and-worker-topology.md);
provider neutrality and the Unreal evaluation follow
[`ADR 0017`](adr/0017-provider-neutral-simulation-and-unreal-evaluation.md).
It does not reorder or weaken the K1 physical-evidence gates in this document.
Its first gate, SIM S0, is accepted:

- exact Windows 11 / dedicated `MissionCore-Sim` WSL2 Ubuntu 24.04 worker
  profile;
- all mutable runtime bytes physically on D;
- ROS 2 Jazzy, Gazebo Harmonic, PX4 v1.17.0, matching `px4_msgs`,
  Micro XRCE-DDS and Nav2 compatibility;
- authoritative Gazebo `/clock`;
- loopback port and deterministic process ownership;
- target-worker rover/telemetry/pause/2 ms step/1×/2× resource evidence;
- target doctor `GO` for profile SHA-256
  `aeecf5005301db230e1340b85215cbbd862b72974a22b8c43767c28be8453e2f`.

The repository doctor is side-effect free:

```text
uv run missioncore-sim s0 doctor --json
```

Without the exact admitted target-worker evidence its correct result remains
`INCOMPLETE`. S1 starts from the accepted S0 version/storage/time/process
boundary. Its S1A increment implements server-owned immutable run identity,
legal lifecycle transitions, canonical rover command envelopes, authority/TTL
admission, append-only artifacts and restart reconciliation. The S1B increment
adds idempotent application lifecycle, single active ownership, an
exact-profile/D-only worker guard, health-gated real provider specs and
deterministic POSIX PGID supervision. Exact generation `6cb1495` passed on
`MissionCore-Sim`: Micro XRCE-DDS Agent and PX4/Gazebo stock rover ran inside a
fresh loopback-only namespace, the run reached `completed`, and both provider
groups stopped without residue. S1C commit `b7ccca3` adds a persistent
unprivileged worker agent in the same isolation boundary, a bounded Unix-socket
gateway, an explicit `internal-virtual-only` backend gate and a browser-native
live panel. UI-2 commit `60e7916` promotes that panel into a top-level product
root and replaces the diagnostic 2D marker with a
browser-native Three.js Ackermann Rover scene. The exact D-only generation
passed 35 target tests and one UI-driven provider run with diagnostic Gazebo
`VehicleState`, eight persisted lifecycle events, terminal `completed` and zero
process residue.

S1D exact code generation `49b0f47` adds the bounded
`missioncore.ackermann-command/v1` browser-to-worker path and the first physical
motion evidence. Mission Core keeps the external command in metres per second
plus normalized steering, journals it append-only, applies a 250 ms TTL and
maps it on the worker to stock-model PX4 rover Offboard throttle/steering
setpoints. PX4 still owns arming, Offboard state and control allocation; Mission
Core does not publish direct actuator topics. The exact D-only target passed 40
focused tests. Its first cold start took 32.08 seconds, and run
`s1c-49b0f47-20260724t195259z-62edd2` proved straight, turn, reverse, TTL stop
and zero-residue lifecycle shutdown. This closes only the primary Ackermann
command/motion slice. Differential Rover, calibrated speed tracking, canonical
PX4/ROS 2 telemetry, pause/step/reset, heartbeat/offboard/link-loss and
failsafe cases remain open before S1 can pass.

The archival surface consumes the same append-only run repository through
`GET /api/v1/polygon/runs` and
`GET /api/v1/polygon/runs/{run-id}`. `QualificationRunStore(read_only=True)`
does not create, chmod or mutate the configured repository and rejects every
write transition. When `/api/v1/polygon/worker` returns an admitted available
worker, the Control Station adds `Полигон` as a seventh header root. One header
click opens the single `polygon-datasets` workspace without a System launcher
panel. The old `?workspace=polygon-run[&run=<id>]` URL remains a compatibility
redirect into the dataset workspace; there is no separate Runs item in the
operator navigation.
Browser QA loaded the accepted archive and the S1C live worker, rotated and
zoomed the 3D rover, observed live run/provider status, Gazebo sim time and
ENU/FLU pose, then completed a clean stop. Start/stop stays behind the backend
gate and reaches the worker through a local Unix socket; SSH remains
qualification bootstrap, never the product data plane. A shared
operator-facing deployment still requires authentication/RBAC, service
supervision and an accepted routed worker transport.

The Dataset Gateway is now multi-source. RELLIS S0 pins official repository
frame `000104`, verifies `131,072` Ouster XYZI points against aligned labels and
publishes a bounded semantic/ground-target preview beside GOOSE in the same
`Полигон → Датасеты` workflow. This closes only reader, axes, ontology,
licensing and versioned ignore-policy compatibility. Full RELLIS Ouster scans,
labels and poses remain D-only admission work before the cross-dataset
Current/Patchwork++ decision.

Mission Core remains a distributed web product. React is served by a long-lived
Gateway/API and renders canonical state through browser-native components.
Gazebo/PX4/ROS 2/Nav2 run headless on a registered Linux simulation worker; the
native Gazebo GUI is optional worker-local diagnostics. A future field vehicle
runs a headless Linux Edge Agent with local-first evidence and store-and-forward
synchronization. The browser never talks directly to PX4, and field
execution/failsafe cannot depend on a browser or WAN connection.

## Stage 0 — repository and host baseline

Deliverables:

- local Python 3.12 `.venv` and lock-file;
- `k1link doctor` with no side effects;
- immutable reference documents and checksums;
- safety policy, artifact policy, session manifest schema;
- no Homebrew changes and no K1 interaction.

Acceptance:

- `uv sync --group dev` succeeds;
- `uv run k1link doctor` identifies a local `.venv`;
- tests, Ruff and mypy pass;
- repository contains no secrets or real captures.

## Gate 1 — physical and USB baseline

Code needed: none initially.

Actions:

1. Record masked device identity, LED boot sequence, battery and visible state.
2. Determine whether the unit is activated; solid-yellow or an activation prompt
   is treated as a distinct blocker.
3. Run one short autonomous button scan: start, keep still for at least 20 seconds,
   move through a simple scene, stop.
4. Enter USB mode using the documented physical shortcut if available.
5. Copy a project tree and selected metadata/logs into an ignored session path.
6. Hash every copied file and create a redacted manifest.

GO: the unit boots normally and records a usable autonomous project.

PAUSE: USB access is unavailable but the scan succeeds; continue BLE while
documenting the missing ground truth.

STOP: serious fault, overheating, activation lock, or destructive/ambiguous
device state.

## Stage 1 — BLE non-mutating discovery and metadata toolkit

Minimal dependencies: `bleak` plus the existing CLI stack. Add them only in the
repository-local environment.

Commands:

```text
k1link ble scan
k1link ble gatt-dump
k1link ble listen
```

Requirements:

- preserve advertisement snapshots, manufacturer/service data and RSSI;
- describe macOS scanning as active discovery: CoreBluetooth does not support
  passive scan mode, although discovery does not change K1 configuration;
- identify by observed name + macOS UUID + advertisement fingerprint;
- inspect standby, scanning and USB states separately;
- start with standard Device Information/Battery services;
- custom characteristic reads are opt-in because proprietary reads may have
  side effects;
- notifications are subscribed one characteristic at a time and logged to
  timestamped JSONL;
- no arbitrary characteristic or provisioning writes in this stage. Enabling a
  notification normally updates the standard CCCD descriptor; this temporary,
  reversible protocol write is disclosed and handled separately from device
  configuration.

GO: reproducible K1 advertisement and useful GATT map.

PAUSE: permission or pairing/authentication is required.

## Gate 2 — existing network behavior

Do not change Deco settings first.

Actions:

1. Save the Mac interface and route table.
2. Observe router client list and Mac ARP/neighbor state with K1 off.
3. Repeat after K1 boot, during standby, scanning and after one power cycle.
4. Check for new Wi-Fi SSIDs exposed by K1 without attempting to join unknown
   networks automatically.
5. Confirm any candidate by disappearance/reappearance with K1 power state.

GO-A: K1 joins a remembered LAN. Provisioning leaves the immediate critical
path and work moves to the application-session gate.

GO-B: K1 exposes its own AP. Document it as a separate topology and determine
whether Mac can join without modifying router configuration.

BLOCKED: neither association nor AP exists; provisioning becomes Stage 2.

## Stage 2 — no-app provisioning research

This stage answers whether SSID/PSK can be sent safely from Mac. A generic BLE
transport does not define the vendor payload.

Evidence sources allowed before a write:

- GATT UUIDs, properties and descriptors;
- standard service identification;
- read/notify state transitions correlated with network state;
- owned K1 project logs and metadata;
- public official firmware/documentation static evidence if separately reviewed;
- deterministic structure inferred without sending device mutations.

Before enabling any write profile, document:

- exact service/characteristic and required security mode;
- packet framing, encoding, ordering and length rules;
- SSID/PSK redaction and secure input path;
- checksum, nonce, token, certificate or signature fields;
- commit/connect command and status response;
- timeout, failure state, power-cycle behavior and rollback;
- one explicit confirmation flag in addition to a named reviewed profile.

GO: a deterministic provisioning profile exists with an explained expected
state transition and safe rollback.

HARD BLOCK: K1 knows no network, exposes only opaque vendor GATT, and no evidence
source reveals the protocol. Random writes are not an alternative.

## Stage 3 — application session and targeted capture

Prerequisite: confirmed K1 IP or direct AP topology.

Implementation order:

1. Passive ARP/mDNS/SSDP and K1-filtered `tcpdump` baseline.
2. Small targeted TCP connect check against the confirmed IP.
3. Service detection only where the prior step supplies evidence.
4. Parse K1 logs for daemon names, ports and protocol strings.
5. Add protocol-specific connection attempts only for demonstrated endpoints.
6. Model `WIFI_ASSOCIATED_BUT_NO_DATA_SESSION` explicitly.

`k1link net capture` must show the exact interface/filter, request privilege in
the foreground, handle Ctrl+C, and never configure persistent `sudo` or ChmodBPF.

GO: Mac becomes a K1 endpoint or receives a reproducible scan-correlated flow.

BLOCKED: application session requires an unavailable mutual-auth token,
certificate or undocumented handshake.

## Stage 4 — session artifacts and flow analysis

Implement only after real traffic exists:

- versioned session manifest;
- UTC and monotonic timeline events;
- SHA-256 inventory;
- TCP reassembly and UDP flow grouping;
- packet/byte rates, length histograms, entropy and first-byte fingerprints;
- idle versus scanning correlation;
- negative-control flows to reduce false positives.

GO: repeatable flows can be separated and ranked without losing raw evidence.

## Stage 5 — decoders by evidence priority

Priority:

1. device/scan status;
2. point-cloud preview;
3. pose/trajectory;
4. camera/panorama/video.

Add dependencies per decoder extra, not to the base environment. Point-cloud
and video signature probes operate on reassembled flows, not isolated packet
payloads. A candidate becomes a decoder only after a controlled physical test
confirms it.

MVP GO: K1-to-Mac scan-correlated data can be captured reliably and at least one
useful stream is decoded or structurally identified.

## Stage 8 — replaceable external perception worker

Stages 6–7 established the raw-first live bridge and durable combined session
archive described above. Stage 8 introduces a replaceable GPU executor without
giving it K1 command authority or the authoritative archive.

Accepted recorded gate, 2026-07-19:

- the Mac validates one sealed camera epoch through the same digest and
  ISO-BMFF timing inspector used by saved-session replay;
- `k1link compute prepare-camera-job` publishes an immutable
  `missioncore.compute-job/v1` below ignored runtime storage;
- the first TEST007 job contains only one bounded 56-frame camera epoch, not the
  MQTT archive or another K1 control path;
- Windows revalidates all 59 job files, reconstructs the exact canonical stream
  digest and decodes all 56 frames;
- pinned Apache-2.0 YOLOX-S executes through pinned Triton 2.70.0 on the RTX
  4090 and atomically publishes one content-addressed
  `missioncore.compute-result/v1`;
- repeating the exact result identity returns the existing result without
  increasing Triton inference count;
- Mission Core revalidates the returned generation, projects the exact camera
  frames and boxes onto `session_time`, and exposes the optional native Rerun
  view only after its complete RRF2 layer is accepted.

The accepted result is perception evidence only. The larger TEST007 epoch,
direct/routed worker transport and bounded live fan-out remain separate gates.
SSH/SCP is allowed only as the recorded laboratory bootstrap. The worker never
connects to K1.

See [ADR 0014](adr/0014-bounded-external-perception-worker.md), the
[worker contract](10_EXTERNAL_PERCEPTION_WORKER.md) and
[Lab 005](lab/005_FIRST_RECORDED_PERCEPTION_20260719.redacted.md).

## Deferred work

- ROS2/MCAP bridge;
- onboard computer and drone integration;
- automated scan-button electronics;
- OpenWrt/monitor-mode infrastructure;
- firmware or internal-Linux analysis;
- browser-visible end-to-end shared-timeline playback of the accepted archive
  containing point cloud plus one selected K1 camera (backend preparation and
  digest-bound byte admission are already accepted);
- OS-independent plugin/edge authority provider, PCAP-to-executor transcript
  regression and runtime ownership integration;
- long-running large-session WebViewer/WASM memory telemetry;
- production retention, replication, encryption and cross-platform packaging.
