# Lab 003 — K1 application-control physical acceptance

- Date: 2026-07-18
- Device scope: one owner-controlled LixelKity K1, firmware `3.0.2`
- Result: **GO — staged START, live acquisition, STOP and READY recovery accepted**

This report contains no device identity, application authority, address,
credential, raw MQTT payload or project contents. Private evidence remains in
ignored session storage.

## Safety boundary

The operator was present and confirmed ownership, steady-green standby,
battery/storage, exact direct-LAN target and closed LixelGO. Every request used
QoS 2, retain false, one connection attempt and no automatic retry. The local
point/pose receiver was read-only and independent of the control client.

## Attempt 1

The control client emitted bootstrap ordinals 1–6. All six QoS2 transactions
completed and five required responses arrived, but the batch-3 response did not
pass exact correlation. The executor stopped before START. K1 remained READY
and steady green; point/pose counts stayed zero.

Subsequent comparison with the independent fast capture proved that ordinal 3
is not an application-response barrier: ordinals 4–6 may be published before
its response. Ordinals 3 and 6 share a response topic but use distinct unbound
and bound sessions. The repaired collector therefore routes by operation plus
session/device identity and waits for the combined ordinal 2–6 response window.

## Attempt 2

After adding redacted live-response diagnostics, one new explicit permit was
used. All ten pre-START requests and START completed QoS2; all required
responses, including the START response, passed identity/session/action/result
correlation. The eleven publishes completed in approximately `365 ms`.

The device then reported `SCAN_STARTING` for approximately 24 seconds. No point
cloud or pose message arrived. It emitted two identical
`lixel/application/report/system_error` messages with code `0x32040133`, reset
to `OTHER_STATUS` without a project binding and showed a steady-red indicator.
The bounded decoder maps the observed namespace/value as
`0x32040000 + 307 = ALGORITHM_ERROR`. A normal operator power cycle restored
steady green. No guessed STOP or repeated START was sent.

## Attempt 3

The production plugin UI completed BLE selection and one reviewed Wi-Fi
provisioning operation, then opened the staged control session from one explicit
operator launch intent. The MQTT owner completed six QoS2 publishes and
correlated five required responses. K1 remained in `READY`; no ModelingRequest,
START or STOP was emitted. The session failed closed on ordinal 4
`DeviceConfigRequest`, whose correlated response carried result `0x32040002`.
Automatic and same-session retry remained disabled.

Re-reading the original clean-cycle PCAP exposed an encoder defect rather than
a lifecycle-order failure. The captured time value is protobuf `sint64`: wire
varint `3568430060` ZigZag-decodes to Unix epoch `1784215030` (`2026-07-16
15:17:10 UTC`). Mission Core had encoded the current positive epoch directly as
an unsigned varint. The timezone and surrounding request structure matched, but
the device therefore received the wrong timestamp semantics and rejected the
time-sync request. The encoder now applies canonical `sint64` ZigZag encoding,
with an exact captured-value regression test. A new physical attempt remains
operator-gated and requires a fresh steady-green check.

## Attempt 4

After fresh provisioning and an operator-confirmed steady-green check, one
explicit plugin UI launch used the repaired staged session. All 14 canonical
requests completed on one MQTT connection: 14 QoS2 transactions, 12 correlated
application responses and the two evidence-backed optional-response cases. K1
reported `SCANNING`, a bound project and `init_ready=true`; the live receiver
decoded point cloud and pose data and the operator saw the scene on screen. No
device system error was reported.

The operator then issued exactly one STOP from the plugin UI. Its QoS2 and
correlated application response completed on the same control session. K1
reported `READY`, cleared its project binding and initialization flag, stopped
the live streams, saved, blinked during finalization and returned to a stable
green indicator. No retry, fallback STOP or second device command was sent.

The local evidence session sealed with 6,088 raw MQTT messages and 73,260,759
payload bytes, including 2,529 point-cloud and 2,680 pose messages. The durable
manifest recorded 7,670,356 decoded points, zero decode errors, 36.029 m of
route and a 302.677 s observation interval. Background preparation produced a
107,002,392-byte digest-bound Rerun recording and admitted two recorded-camera
sources. Ranged reads of the RRD, camera manifests, MP4 initialization data and
media segments all passed their generation and content-digest checks.

Natural RTSP closure during K1 STOP exposed a local finalization race: the
camera gateway initially classified the expected source end as an acquisition
failure before the control owner could enter its standby-confirmation phase.
The raw capture and all committed camera segments were already durable, and the
device STOP had succeeded. The gateway now marks device-STOP source termination
as expected, closes that camera generation as complete and preserves the
separate unexpected-EOF failure path. A recovery boundary also permits the
already-observed unbound READY state to retire an already-terminal local
acquisition without ever sending a second STOP. Synthetic regression coverage
exercises both paths.

One independent product check remains outside this control acceptance: confirm
that the vendor-native project is reusable through LixelGO or USB. The
application START/STOP, local archive and offline byte-serving gates are closed.

## Canonical-diff finding

Re-audit of two independent owner-operated LixelGO START captures found the
same 14-operation order in both:

1. unbound DeviceInfo;
2. unbound ModelingStatus;
3. unbound GetRtkAdvance;
4. bound time/timezone DeviceConfig;
5. bound DeviceInfo;
6. bound GetRtkAdvance;
7. bound GetNtripProfile;
8. bound GetCloudServerConfig;
9. bound GetRtkAdvance;
10. bound DeviceInfo;
11. bound START;
12. bound ModelingStatus;
13. bound DeviceInfo after initialization;
14. bound ModelingStatus after initialization.

The first retained capture reached START at `+202.423 s`; the faster one at
`+54.130 s`. This is variable operator/UI time, not a fixed scanner timeout.
Both preserved the stage order, sent operation 12 within about one second of
START, sent operations 13–14 about 24–25 seconds after START and kept the same
control MQTT connection open throughout. Neither capture disconnected or
unsubscribed during initialization. Neither capture contains a response to
operation 12; each contains exactly one bound ModelingStatus response in this
interval, immediately after operation 14. The repaired correlator therefore
retires an unanswered optional operation-12 owner before admitting required
operation 14.

The same re-audit shows that those wall-clock gaps are not protocol timers.
Requests 1–6 follow connection, request 7 follows explicit workspace entry,
requests 8–10 follow opening the project prompt, and START follows the operator's
confirmation. No separate project-name save request exists; the name first
appears in START. In both captures operations 13–14 coincide with K1's own
`SCANNING`, bound-project and `init_ready=true` DeviceStatus transition.

Attempt 2 matched the first eleven payload/topic operations but violated the
larger lifecycle: it collapsed the UI stages, omitted operations 12–14 and did
not preserve a long-lived control-session owner. The evidence does not prove
which individual deviation caused the device algorithm failure, so none is
treated as the sole cause.

## Code decision

The legacy collapsed `run_bootstrap()` executor now rejects before emission.
The replacement acceptance-only executor uses explicit connection/workspace/
project-prompt checkpoints with no captured-delay replay. It owns operations
1–14, gates the final refresh on live K1 state, surfaces redacted system errors,
keeps the same socket serviced through explicit STOP and waits for unbound
READY. The socket is also serviced between every UI action;
START/STOP permits begin only at their respective confirmations. Standalone
START/STOP are rejected. Plugin v0.5.0 installs the staged boundary behind
explicit operator UI actions; no state read, poll, navigation event or test may
authorize a physical attempt. Attempt 4 proved that path through live scanning,
one explicit STOP and stable-green standby. A 2026-07-19 UX review removed the
post-READY button because the live READY/unbound report already proves the state;
the explicit pre-STOP operator action remains. Physical double-click remains the
operational fallback; native-project reuse is still checked separately. The
follow-up archive/UX findings are recorded in Lab 004.
