Unify K1 discovery ownership and verification across enrollment paths
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
# K1 connection-path audit and R15
|
||||
|
||||
## Observed failure sequence
|
||||
|
||||
The owner reported four failed enrollment results between 19:08 and 19:11 MSK
|
||||
on 2026-09-07. The board is running the matched R14 pair: Node 0.8.11 and K1
|
||||
0.1.11. The K1 service entered active state at 19:06:33 MSK with zero restarts.
|
||||
This is not an old-package or service-restart explanation.
|
||||
|
||||
| Journal time (MSK) | Recorded failure boundary | Device Wi-Fi write |
|
||||
| --- | --- | --- |
|
||||
| 19:08:36 | `BleakDBusError`, `ensure_device_for_gatt → find_device_by_address → active_scan:477 → assert_reply` | Not reached |
|
||||
| 19:08:51 | `NetworkProvisioningConflict`, exact capture admission at `facade.connect:11127` | Not reached |
|
||||
| 19:09:51 | Same nested discovery boundary as 19:08:36 | Not reached |
|
||||
| 19:10:41 | Same revoked-capture admission as 19:08:51 | Not reached |
|
||||
|
||||
The first and third failures occur before `BleakClient.__aenter__`, service
|
||||
validation, baseline read and the journalled Wi-Fi write. The second and fourth
|
||||
occur even earlier. A wrong Wi-Fi password cannot cause these four failures;
|
||||
this does not prove the supplied credentials correct, because K1 never received
|
||||
them in these calls. No credential or raw exception payload is copied here.
|
||||
|
||||
R14 held a `BleakScanner` open while its nested resolver started a second
|
||||
scanner. Bleak 3.0.2 uses one BlueZ manager/D-Bus client per loop. BlueZ permits
|
||||
one discovery session per client and adapter, not one per Python scanner.
|
||||
See the [BlueZ Adapter API](https://github.com/bluez/bluez/blob/master/doc/org.bluez.Adapter.rst).
|
||||
The retained path was absent, so these calls took that nested branch.
|
||||
|
||||
The Wi-Fi function then revoked the exact selected capture in `finally` simply
|
||||
because no GATT baseline had completed. Discovery failure was thus incorrectly
|
||||
treated as a failed GATT exchange. The UI classified only `connect-failed` as
|
||||
a Bluetooth failure, leaving the form usable after `resolution-failed`.
|
||||
The next explicit Apply met the already-revoked capture and produced the
|
||||
misleading expired-search message. This is explicit invalidation, not a form
|
||||
timeout or a Wi-Fi refusal.
|
||||
|
||||
The old journal formatter omitted structured `extra` fields and native D-Bus
|
||||
codes. The exact native code of the historical calls was not retained. A test
|
||||
using the installed Bleak manager and a synthetic D-Bus wire enforcing BlueZ's
|
||||
documented single-session rule reproduces R14's exact call chain with
|
||||
`org.bluez.Error.InProgress`. This is reproduction evidence, not a claim that
|
||||
the missing historical code was recovered. The bounded Bluetooth service
|
||||
journal contains no additional entries for this interval. Protected stores
|
||||
were not bypassed to obtain more diagnostics.
|
||||
|
||||
## Path ownership review
|
||||
|
||||
| Path | Owner and final authority | Result of review |
|
||||
| --- | --- | --- |
|
||||
| Search / select in enrollment | UI selects one row from runtime + discovery + mode revision; selecting a row performs no physical connection | Retained; no automatic Apply or scan added |
|
||||
| UI → Core Fleet → Node Go broker | One operation ID, durable delivery journal, deadline, exact node/runtime binding; lost response observes the same ID | Retained; delivery completion does not mean device connected |
|
||||
| Node bridge → plugin facade | One bridge lock; plugin journal, lifecycle/dispatch gates and OS BLE arbiter | Retained; no second writer or recovery loop introduced |
|
||||
| Fresh / retained / durable BLE identity | Exact address/native path, owner epoch and scan/session lineage; these identities have distinct admission purposes | Retained; age alone does not revoke a form selection |
|
||||
| Native path resolution → Connect | Previously separate holder plus nested resolver | Replaced by one `selected_device_discovery` context; exact observations come from its own scanner; held through Connect and released before GATT I/O |
|
||||
| Station provisioning / status read / AP activation | Different reviewed payloads and policies, same native connection lifecycle | All three use that single discovery context; one Connect, no automatic write repeat |
|
||||
| Failure / cancellation cleanup | Transport lease, native callbacks and command journal have separate ownership | Removed duplicate revocation in exception and finally paths; only an attempted Connect/GATT exchange without baseline revokes a capture |
|
||||
| Verify in enrollment / Verify in device detail | Facade policy, ledger lineage, exact persisted target, route, DeviceInfo and physical reconciliation | Removed the adapter-level divergence: both use the same `verification_parameters`; another selected device cannot inherit the saved target |
|
||||
| Host Wi-Fi network list | Linux host network manager, read-only listing | Separate from BLE discovery; not a second K1 scanner |
|
||||
| Wi-Fi response → control readiness | Same parent intent and owned DeviceInfo bootstrap; network-applied and control-ready are distinct | Retained; missing control confirmation never authorizes resending Wi-Fi |
|
||||
| START / STOP / preview | Named acquisition, physical command journal, active session and persistent preview recording | Existing R13/R14 behavior retained; this change issues no physical test commands |
|
||||
|
||||
The simplification removes overlapping native discovery, duplicate failure
|
||||
cleanup, divergent Node verification selection and duplicated password markup.
|
||||
It does not combine different physical modes into a guessed fallback or erase
|
||||
their side-effect/identity fences.
|
||||
|
||||
## Operator presentation and diagnostics
|
||||
|
||||
- Both direct and onboard enrollment consume `K1WifiPasswordField`, extracted
|
||||
from the existing direct composition using Design Guideline `TextField`,
|
||||
`IconButton` and eye icons. Visibility resets when the value is cleared.
|
||||
Passwords remain absent from command journals and are cleared after submission.
|
||||
- Search busy, unavailable Bluetooth, missing selected K1, and other discovery
|
||||
failures have distinct shared messages. They state that Wi-Fi was not sent
|
||||
only with `side_effect_status=none`. Unknown/after-write outcomes keep the
|
||||
existing read-only reconciliation path.
|
||||
- Legacy resolution errors are recognized as Bluetooth failures so an old
|
||||
result cannot leave the same invalid form open for another Apply.
|
||||
- Node failure logs now print a finite allowlist of stage, native BlueZ error
|
||||
and write-attempt/confirmation booleans alongside the existing causal stack.
|
||||
Exception messages, SSID, password, frame data and locals are excluded.
|
||||
|
||||
## Verification and delivery
|
||||
|
||||
The regression first failed on unmodified R14 with the same nested stack, then
|
||||
passed after replacing the resolver. The test uses the real pinned Bleak BlueZ
|
||||
manager/scanner and dbus-fast with a synthetic wire; no adapter is accessed.
|
||||
Tests cover cached/missing paths, timeout, busy reply, cancellation, connection
|
||||
failure and callback/session release for all three BLE operation kinds. Other
|
||||
tests cover exact address/adapter, owner and capture changes, write boundary,
|
||||
single submission, shared Verify admission, and password visibility.
|
||||
|
||||
The dev dependency explicitly includes the same pinned dbus-fast version so
|
||||
this contract is also tested on macOS. Linux runtime dependencies are unchanged.
|
||||
Detailed final check counts and artifact provenance are recorded after the
|
||||
release checks. R15 is Node 0.8.12 plus K1 0.1.12 and uses the existing encrypted
|
||||
onboard application credential. Installation and physical UI acceptance are
|
||||
separate from synthetic checks; neither is claimed from a successful build.
|
||||
|
||||
### Source acceptance
|
||||
|
||||
- Backend connection/arbiter/journal suite: 189 passed.
|
||||
- Installer and package lifecycle tests: 14 passed.
|
||||
- Complete Control Station unit suite: 808 passed, including direct K1
|
||||
recovery, frontend boundaries and shared password visibility.
|
||||
- Node UI boundary test: 1 passed.
|
||||
- Node Go enrollment tests passed.
|
||||
- Control Station production build and TypeScript check passed (8.00 s).
|
||||
- Ruff and Git whitespace checks passed.
|
||||
|
||||
The board was online and published idle acquisition before staging. The local
|
||||
Docker daemon was not running; no Docker startup or parallel heavy builds were
|
||||
used. The original canonical Core process remained on port 8000.
|
||||
@@ -93,3 +93,12 @@ Mission Core returned HTTP 200; the updated home page rendered in the in-app
|
||||
browser. The single canonical process remains on 8000, with no listener on 8765.
|
||||
The native “Mission Core · K1 R14” installation window is open for owner-entered
|
||||
sudo. Installation exit status and clean-cache physical acceptance are pending.
|
||||
|
||||
## Subsequent installation and failed acceptance
|
||||
|
||||
On 2026-09-07 the board reported installed Node 0.8.11 and K1 0.1.11.
|
||||
The K1 service entered active state at 19:06:33 MSK with NRestarts=0.
|
||||
Owner testing at 19:08–19:11 failed enrollment. R14 is therefore installed but
|
||||
not accepted. Its synthetic scanner test permitted nested discovery sessions
|
||||
that the actual Bleak/BlueZ client does not support. See the R15 connection
|
||||
audit for the exact sequence, reproduction and replacement of that mechanism.
|
||||
|
||||
Reference in New Issue
Block a user