feat(k1): stabilize LAB bridge and isolate onboard device integration
This commit is contained in:
@@ -0,0 +1,269 @@
|
||||
# K1 Bridge: архитектура, эксплуатационные сценарии и граница рефакторинга
|
||||
|
||||
Дата среза: 06.09.2026. Основание: просьба владельца полностью восстановить контекст K1 из Ops перед решением о рефакторинге. Это документальный и статический аудит текущей рабочей копии, а не новая аппаратная приёмка.
|
||||
|
||||
Исходный commit: `020a878915ea32c64963975447650fd8eee29071`. Рабочее дерево уже содержало изменения Node, K1, viewer и UI. При этом аудите исходники, прошивка, состояние сканера, службы и Ops не изменялись. Созданы только этот отчёт и индексы источников. Тесты, сборки, BLE-поиск, provisioning, START/STOP и отключения сети не запускались.
|
||||
|
||||
## 1. Вывод
|
||||
|
||||
Опасение о чрезмерной связности подтверждается кодом. `facade.py` — 35 996 строк; `connect()` — 1 971; `_adopt_existing_lan_connection()` — 1 224; `state()` — 1 028; `_reconcile_acquisition()` — 1 587. Экран `K1ProvisioningPipeline.tsx` — 3 732 строки, runtime hook — 2 135. В экран одновременно входят локальный черновик, выбор режима, scan, попытка provisioning, чтение статуса, восстановление, история физической команды и представление ошибок.
|
||||
|
||||
Однако это не означает, что все проверки лишние. Существенная часть сложности появилась после доказанных инцидентов: повторные команды после неизвестного результата, поздний ответ старого процесса, потеря сети после START, STOP без READY, восстановление камеры, гонка REST/WebSocket, удержание старого Rerun listener. Удалить эти проверки ради короткой функции подключения означало бы вернуть реальные дефекты.
|
||||
|
||||
Нужна декомпозиция владельцев и переходов при сохранении поведения. Уже существуют полезные границы: BLE transport, connection supervisor, network ledger, physical-command coordinator, recovery checkpoint, control transcript. Основной долг находится в их соединении внутри facade и в повторной интерпретации состояния разными UI.
|
||||
|
||||
Текущая ошибка подключения и архитектурный долг — связанные, но разные вопросы. Сам размер файла не доказывает причину ATT-ответа K1. Последние изменения улучшили диагноз и выход из ошибки; успешное подключение последней попытки ими не доказано.
|
||||
|
||||
## 2. Что найдено в Ops
|
||||
|
||||
Через прямой NODE.DC Ops MCP получены все 76 карточек MISSION CORE с описаниями и structured blocks. У 41 карточки есть метка `XGRIDS K1`; значительная часть — потребители уже записанного K1 evidence в LAB. Для 45 карточек получены все страницы активных комментариев: 128 комментариев, без оставшейся пагинации. Полный индекс названий, блоков, дат и comment IDs: [Ops index](2026-09-06-k1-bridge-ops-index.json).
|
||||
|
||||
Глубокая смысловая сверка выполнена для сетевого/control/recovery контура, Node и границ viewer. Индексация лабораторных карточек не выдаётся за повторный аудит всех алгоритмов CV.
|
||||
|
||||
| Карточка | Роль в этом разборе |
|
||||
|---|---|
|
||||
| MISSIONCOR-3 — Mission Core. Lixel K1 / XGRIDS Integration | Основная текущая приёмка K1; packet oracle, 14 операций, Bridge/Quick, сон/сеть, камера, baseline производительности |
|
||||
| MISSIONCOR-49 — K1 · Проблемы сканирования | История отдельных сетевых, control, producer и Rerun отказов; физические повторные циклы; причины предыдущих регрессий |
|
||||
| MISSIONCOR-76 — Mission Core Node · Архитектурные границы для бортового ПК | Владение устройствами на борту, связь Core/Node, reboot, журнал команд, Linux-паритет; уточнения владельца в комментариях |
|
||||
| MISSIONCOR-66 — Mission Core. Канон интеграции Rerun | Границы live, Saved Sessions и LAB; запрет менять общий lifecycle ради локальной лаборатории |
|
||||
| MISSIONCOR-74 — Additional Core · Переносимая кастомизация Rerun | Связанный реестр кастомизаций; индексирован, актуальные различия поверхностей прочитаны в свежем блоке #66 |
|
||||
| MISSIONCOR-7 — Mission Core. Milestone — canonical K1 control and durable archive acceptance | Историческая физическая START/live/STOP/READY/archive приёмка |
|
||||
| MISSIONCOR-10 — Operational Core — Real-time Record Limits | Исторические ограничения записи, durability, consumer backpressure; часть описания recovery устарела |
|
||||
| MISSIONCOR-51 — Технический долг Mission Core | Отдельные полевые и вычислительные ограничения; не источник разрешения ослабить K1 safety |
|
||||
| MISSIONCOR-1, -2, -5, -50 | Архитектурный и исторический контекст проекта, SDK и границ компонентов |
|
||||
| MISSIONCOR-4 — Archive. NDC_xgrids-k1-connector — historical evidence | Раннее физическое evidence; явно архивная архитектура |
|
||||
| MISSIONCOR-8, -11 и остальные K1 LAB-карточки | Downstream camera/perception/calibration/replay; учитывать как потребителей неизменного source-of-record |
|
||||
|
||||
В #76 комментарии 05.09 имеют решающее уточнение: первый бортовой K1 — **только Bridge**; существующий macOS/Quick остаётся тестовым путём. В теле старого baseline ещё написан последующий Linux Quick: это не новая задача. Все действия оператора должны проходить через GUI. Порядок пользовательских уточнений важнее старого шаблона карточки.
|
||||
|
||||
## 3. Восстановленная история и достоверность
|
||||
|
||||
| Дата / источник | Что действительно было подтверждено | Чего это не доказывает |
|
||||
|---|---|---|
|
||||
| 16.07, #3, #4 | BLE/Wi-Fi vertical slice; LixelGO/iPhone IP capture; MQTT/RTSP и packet map | IP capture не содержит BLE HCI; не является дампом самого 7f01 provisioning |
|
||||
| 19.07, #7 | Canonical START/live/STOP/READY и durable archive на одном K1 | Linux, второй K1, другая FW, многочасовая эксплуатация |
|
||||
| 28.07, #49 | Повторные Quick/Bridge циклы и вход после очистки cache | Успешное соединение не означало исправный Rerun receiver |
|
||||
| 06.08, #49 | Bridge физически работал; отдельно диагностирован Quick/CoreWLAN helper regression | Нельзя переносить причину Quick helper на Bridge |
|
||||
| 21–22.08, #49/#3 | Устранены starvation, camera churn, Rerun admission и state-channel проблемы; подтверждён STOP + READY | Число unit tests не заменяет отдельную аппаратную приёмку каждого recovery |
|
||||
| 23.08, #3 | Bridge → Quick → Bridge → Quick; сон во время запуска; сеть off → сон → wake → сеть on; камера и облако восстановились без повторного START/STOP | Полный reboot Node во время K1 записи, другая ОС/FW, универсальный SLA |
|
||||
| 05–06.09, #76 | Pairing, heartbeat offline/online при остановке Node-службы, сохранение identity, D455 этап | Аппаратный K1/Linux parity ещё не принят |
|
||||
| 06.09, локальные incident-аудиты | Есть успешный Bridge, затем ATT-отказы; отдельно найден неверный camera evidence-root | Последние подключения и камера после исправления ещё не приняты новым чистым UI-прогоном |
|
||||
|
||||
Внутренний быстрый baseline — `eaad9de`, `20260822T105904Z_viewer_live`: START до индикации калибровки <5 с, калибровка 21 с, облако +2 с, правая камера +4 с; callback→publication p50/p95 23,839/41,541 ms. Это один физический run на коротком ledger, не гарантированная задержка любого подключения. Recovery-capable baseline `1001a31` имеет другие задержки и расширенные гарантии. Подробности: [internal baseline](../lab/010_K1_MISSION_CORE_INTERNAL_LIVE_BASELINE_20260822.redacted.md).
|
||||
|
||||
### Обнаруженные расхождения источников
|
||||
|
||||
1. Manifest содержит 77 сценариев: 51 `software-covered`, 17 `partial`, 9 `planned`. Это значения файла, не результат новых тестов. Например, переключение Bridge/Quick и sleep/wake ещё отмечены как требующие hardware evidence, хотя более поздняя #3 описывает физическую приёмку.
|
||||
2. #10 пишет, что MQTT reconnect отсутствует; #3 и текущий recovery-код содержат ограниченное восстановление активного потока. Историческое ограничение нельзя применять как описание текущего пути.
|
||||
3. #49 хранит старый статус незакрытой Rerun-приёмки; более поздняя #3 закрывает конкретный принятый beta-профиль. Приёмку следует связывать с датой, commit, платформой и точным сценарием.
|
||||
4. В #3 краткий oracle-блок неточно объединяет финальный PCL и teardown около +0,951 с. Явная ERRATA в комментарии `c1ef70e2-d900-4d42-ad78-d460040a42d7` различает последний PCL PUBLISH +36,415 ms, pose +40,787 ms, RTSP media +54,057 ms и teardown около +951 ms. Эталон должен учитывать исправление.
|
||||
5. Документ supervision описывает фиксированный шестисекундный scan; текущий scanner использует одно непрерывное окно 6→20 с при отсутствии K1. Это документальный drift после сегодняшнего изменения.
|
||||
6. В документах есть разная формулировка browser close: завершение operator session и сохранение backend-owned capture. Это разные владельцы. Контракт CONN-62 и код не разрешают WebSocket disconnect инициировать scanner-команду. Для рефакторинга нужно явно зафиксировать отдельно draft, UI connection, lease и acquisition.
|
||||
|
||||
## 4. Bridge по слоям: нормальная последовательность
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
UI[Оператор: найти, выбрать K1, ввести сеть] --> Intent[Один intent: runtime, mode, discovery generation, operation ID]
|
||||
Intent --> Admission[Проверка владельца, текущего состояния и журналов]
|
||||
Admission --> BLE[Точный BLE handle: GATT contract и 7f02 baseline]
|
||||
BLE --> Journal[Сохранить границу dispatch до записи]
|
||||
Journal --> Write[Одна 99-byte запись в 7f01]
|
||||
Write --> Status[7f02: подтверждение требуемой сети и адреса]
|
||||
Status --> Applied[Durable network_applied]
|
||||
Applied --> Control[Read-only route, TCP, DeviceInfo bootstrap]
|
||||
Control --> Ready[Текущая identity и готовность управления]
|
||||
Ready --> Start[Отдельный явный START по каноническому диалогу]
|
||||
Start --> Raw[Локальная исходная запись]
|
||||
Start --> Preview[Ограниченный live preview: облако и камера]
|
||||
```
|
||||
|
||||
Для LAB весь runtime принадлежит компьютеру оператора. Для борта путь до `Intent` проходит Core → аутентифицированный Node channel → Node broker → тот же K1 runtime. Радио, проверка маршрута к K1, secret provider и recorder принадлежат БК. Сеть Core может отличаться от локальной сети Node/K1.
|
||||
|
||||
### Провода протокола
|
||||
|
||||
Общий Bridge profile FW 3.0.2: service `7f00`, write `7f01`, status `7f02`. Frame — ровно 99 bytes: длина SSID, 32-byte slot, длина password, 64-byte slot, конечный zero. Mission Core использует один `with_response`, как в принятом macOS run. Это не Quick AP-enable, где отдельный 100-byte frame. Источники: [reviewed profile](../04_K1_WIFI_PROVISIONING_PROFILE.md), `ble/wifi_provisioning.py:193`, вызов в `facade.py:11747`.
|
||||
|
||||
`write_gatt_char` ACK доказывает транспортный факт, но не готовность MQTT. Состояние сети, локальный route, TCP endpoint, DeviceInfo identity, control и свежие sensor data — самостоятельные доказательства. Нельзя заменить их единым `connected` или принимать «точки пришли» за право на новую команду.
|
||||
|
||||
Прикладной transcript остаётся непрерывной MQTT-сессией: ordinals 01–10 перед START; ordinal 11 — START; 12 — свежий initialized SCANNING; 13–14 — завершающее read-only обновление. Подготовка включает согласованную синхронизацию времени и не является целиком read-only. Recovery использует inspection-only путь, который её не повторяет.
|
||||
|
||||
Критическая граница сна: после ordinal 12 physical resolve и recovery checkpoint сохраняются до 13–14; public SCANNING/STOP удерживаются transition gate до завершения refresh. Потеря связи в этот момент не должна уничтожить уже доказанный START. Реализация: `protocol/application_session.py:1020`, `facade.py:24291`, тест `test_post_start_refresh_timeout_wakes_existing_read_only_recovery`.
|
||||
|
||||
## 5. Владельцы и состояния, которые нельзя смешать
|
||||
|
||||
| Владелец | Состояние / обязанность | Должен пережить |
|
||||
|---|---|---|
|
||||
| UI draft | Выбранный кандидат, SSID/пароль до отправки, локальное ожидание | Ничего, что могло бы восстановить право повторной записи после смены runtime |
|
||||
| BLE arbiter | Один нативный radio owner, точный handle, cleanup | Отмену coroutine до фактического освобождения нативной операции |
|
||||
| Network idempotency journal | Один operation ID и неизменность его запроса | Потерю HTTP-ответа и backend restart без повторного write |
|
||||
| Network mutation ledger | prepared/dispatching/observing/terminal и доказательства 7f02 | Crash на границе отправки |
|
||||
| Semantic topology store | Последняя подтверждённая конфигурация сети | Restart, но только как configured/offline, без live authority |
|
||||
| Connection supervisor | Intent, host epoch, route/TCP/DeviceInfo, отдельные control/data planes | Короткую потерю сети через отзыв зависимых полномочий |
|
||||
| Physical coordinator/ledger | START/STOP и доказанный либо неизвестный физический результат | Потерю сети, process crash и UI reset; не очищается вместе с формой |
|
||||
| Active recovery checkpoint | Точная lineage acquisition/device/project/evidence и gaps | Поддерживаемый rebind/restart без нового START |
|
||||
| Recorder/camera producer | Raw evidence и committed prefixes | Закрытие browser, медленного consumer, смену просмотрщика |
|
||||
| Viewer | Disposable receiver, canvas/media transport, профиль отображения | Пересоздание consumer без управления сканером |
|
||||
| Core/Node pairing | Постоянное доверие, heartbeat, binding | Обрыв канала и reboot; состояние K1 доказывается отдельно |
|
||||
|
||||
Часть журналов кажется дублированием только по названию: журнал сетевого запроса и журнал физического START отвечают на разные вопросы. Их физическое объединение без анализа crash consistency опасно. При этом хранение однотипных presentation-состояний в нескольких frontend-местах не даёт новых гарантий и является кандидатом на упрощение.
|
||||
|
||||
`state()` сейчас не чистая проекция: он выполняет локальную retirement/reconciliation работу и может инициировать уже разрешённый active-stream recovery. Это видно с `facade.py:7285`. Поэтому простое изменение частоты polling или перенос `state()` в новый UI может затронуть lifecycle. Чистую проекцию можно выделять только вместе с независимым владельцем reconciliation, сохранив все переходы и их порядок.
|
||||
|
||||
## 6. Матрица поведения, которое следует сохранить
|
||||
|
||||
Статусы ниже различают историческую физическую приёмку, наличие реализации/тестов и непроверенный Node parity. Наличие теста здесь не означает его нового запуска.
|
||||
|
||||
| Сценарий | Обязательное поведение | Основание / текущая граница |
|
||||
|---|---|---|
|
||||
| Первый scan не сразу видит включённый K1 | Одно ограниченное discovery; никаких скрытых connect/write; отдельное время первого candidate | `ble/scanner.py:1207`; сегодняшнее окно 6→20 с; свежая UI-приёмка нужна |
|
||||
| Выбор устройства, ввод сети | Только локальный draft; никаких аппаратных действий от selection | #3, canon CONN-06/-70/-74; frontend fences |
|
||||
| Apply | Не более одной reviewed сетевой mutation, exact captured handle | `wifi_provisioning.py:687`; durable callback перед write |
|
||||
| Отказ до write | Прямо сообщить отсутствие отправки; освободить завершённую попытку | Network journal/ledger и error annotations |
|
||||
| Потеря ACK или HTTP после write | Не повторять write; читать результат того же intent | `networkProvisioning.ts:105`; idempotency journal |
|
||||
| K1 подключился к Wi-Fi, MQTT ещё не готов | network_applied сохраняется; read-only bootstrap не превращается во второй Apply | `facade.py:12001`, `_schedule_control_bootstrap_continuation:4439` |
|
||||
| Неверная сеть / старый 7f02 | Не принимать чужой/старый target; ожидание и classifier должны согласоваться | `wifi_provisioning.py:709`, `_post_dispatch_network_target:35334`; найдено расхождение завершения polling |
|
||||
| Повторный scan после STOP | Новый acquisition без stale camera/ingress/session | #49 field acceptance; `test_next_scan_retires_stale_terminal_live_perception_ingress_before_start` |
|
||||
| Потеря только интернета при живой Node/K1 LAN | Локальный capture не зависит от Core preview; Core показывает потерю канала | #76 ownership; физический Linux K1 gate открыт |
|
||||
| Потеря маршрута Node/K1 во время записи | Отозвать control; сохранить exact lineage; bounded read-only rebind | #3 23.08 macOS; `test_control_first_loss_freezes_topology_before_ephemeral_retirement` |
|
||||
| Сеть off → sleep → wake → сеть on | Не повторять START; новый host epoch и новые identity/control proofs; вернуть data consumers | #3 физически принято на Mac; на Node отдельно |
|
||||
| Потеря сети между ordinal 12 и 13–14 | Сохранить durable START proof, выполнить существующий read-only recovery | `application_session.py:1020`, lifecycle test `test_post_start_refresh_timeout_wakes_existing_read_only_recovery` |
|
||||
| K1 выключился во время калибровки | Не висеть в ложном ожидании; SCAN_OVER завершает локально, physical ambiguity сохраняется | #3 calibration-loss; `_reconcile_acquisition` |
|
||||
| K1 вернулся READY после power cycle | Зафиксировать cessation, не объявлять успешный STOP и не начинать новый START | `test_active_stream_recovery_device_standby_never_restarts_scanner` |
|
||||
| По прежнему IP отвечает другой K1/сервис | Не принимать endpoint за identity; не переписывать pin автоматически | `test_active_stream_recovery_wrong_identity_blocks_without_retry_or_camera_reopen` |
|
||||
| Node/Core backend restart | Новое runtime поколение, сохранённая pairing/history; никакого replay команд | #76 heartbeat + separate K1 restart checkpoint |
|
||||
| Restart активной K1 acquisition | Только доказательная rehydration; новый writer/gap, first PCL admission; старое evidence неизменно | `tests/test_xgrids_active_acquisition_restart_rehydration.py:326`; 22 тест-функции в файле; Node E2E ещё не принят |
|
||||
| STOP ACK есть, READY нет | Не объявлять завершение; standby-unconfirmed/unknown, read-only reconciliation | #49; `test_stop_response_preserves_precise_unknown_outcome_through_predeadline_loss` |
|
||||
| Принудительное локальное завершение | Закрыть только локальных владельцев; не посылать STOP; поздний recovery не оживляет их | `test_local_force_finish_cleanup_failure_is_visible_retryable_and_fences_late_success` |
|
||||
| Две вкладки / поздний REST / смена mode | Старый runtime/revision не перетирает новый, команда не дублируется | `stateOrdering.ts`, lifecycle.ts; #3 REST/WS acceptance |
|
||||
| Browser refresh/cache reset/закрытие | Не влияет на физическую команду и recorder; UI заново читает backend | #49/#76; CONN-62; отдельная clean-cache GUI проверка обязательна |
|
||||
| Rerun не открылся, камера/данные живы | Viewer-only recovery; не переподключать K1 и не терять raw | #49 listener/admission; #3 live_receiver recovery |
|
||||
| Камера пропала, MQTT жив | Camera-only recovery точного source/evidence epoch | `test_camera_stall_snapshot_does_not_reconnect_live_mqtt_runtime`, camera watchdog tests |
|
||||
| Live consumer медленный / worker недоступен | Ограничивать preview, сохранять source-of-record и не менять scanner authority | #10, #66, #76; Node sustained-resource gate открыт |
|
||||
| Переключение live → Data → LAB | Разные admission/lifecycle/settings policies; исправление Bridge не меняет эти профили | #66; `viewerProfile.ts`, отдельный аудит live camera root |
|
||||
|
||||
Восстановление нельзя свести к правилу «никаких повторов»: запрещены автоматические физические mutations, но ограниченные read-only наблюдения и consumer-only reconnect нужны и уже приняты. Для active-stream exception backoff задан 0,5/1/2/4/5 с с пределом интервала; автоматического terminal timeout нет. Exact READY/SCAN_OVER/fault, изменение lineage и явное локальное завершение имеют разные исходы. Источник: `docs/20_K1_CONNECTION_SUPERVISION_CANON.md:574`.
|
||||
|
||||
## 7. Конкретные проблемы и риски текущей структуры
|
||||
|
||||
### F1 — Перегруженный coordinator и зависимость проекции от lifecycle
|
||||
|
||||
**Подтверждено статически.** `facade.py` соединяет protocol, OS/network, durable stores, process/native leases, acquisition/camera, recovery, ошибки и UI policy. `connect()` содержит Bridge, Direct, Quick, host association, reset/retirement и child bootstrap. В `state()` совмещены чтение и упорядоченное локальное завершение.
|
||||
|
||||
Практический риск: изменение unrelated presentation/polling влияет на момент cleanup или recovery; вынесенный callback может поменять порядок захвата gate и публикации proof. Размер файла сам по себе не обосновывает удаление guard. Первый допустимый structural шаг — выделение неизменных типов/проекций и изолированных функций с сохранением вызовов и их порядка.
|
||||
|
||||
### F2 — Нижний и верхний уровни по-разному заканчивают station observation
|
||||
|
||||
**Подтверждённое расхождение критериев; полевой причинный статус открыт.** `wifi_provisioning.py:725` завершает polling при любом адресе, кроме `None` и AP fallback. Верхний слой `facade.py:11792` проверяет requested network и допускаемый post-dispatch target. Если первая post-write выборка ещё описывает прежнюю LAN, helper больше не ждёт следующую, даже при оставшемся 45-second budget.
|
||||
|
||||
Для исправления потребуется единый reviewed terminal predicate либо передаваемый transport-слою observation criterion. До изменения нужен сценарий «старая LAN → переходный status → запрошенная LAN» с одной записью и несколькими чтениями. Это не объяснение сегодняшнего ATT4: ATT-исключение возникает на write, до этого polling.
|
||||
|
||||
### F3 — UI повторно собирает смысл операции и скрывает полезный контекст
|
||||
|
||||
**Подтверждено кодом и пользовательскими скриншотами.** Экран держит отдельные local attempt, candidate-unavailable, saved reconnect, applied recovery и physical recovery representations; один failure записывается в несколько presentation slots. Ошибка дублируется, а SSID из `ProvisioningAttemptPresentation` не показан в итоговой ошибке. Primary reconnect фактически выполняет проверку существующего состояния, а исправление сети требует другого пути.
|
||||
|
||||
Из успешного private evidence известна подтверждённая сеть; на раннем скриншоте введено другое написание. SSID последней неуспешной операции не сохранён, поэтому обвинять конкретно последний ввод нельзя. UI должен позволять проверить собственный ввод в текущем локальном intent, без публикации SSID в Ops/общие логи и без восстановления password. Требуется согласовать это с прежней формулировкой secret-free error contract, которая запрещает вставлять SSID из backend exception.
|
||||
|
||||
Кандидат на упрощение: одна typed presentation projection из server attempt + текущего локального draft, один операторский error и один явно названный следующий шаг. Backend policy остаётся authority.
|
||||
|
||||
### F4 — Новый Node-путь теряет часть уже существующего recovery-контракта
|
||||
|
||||
**Подтверждено статически; аппаратный Node/K1 запуск не выполнен.** `NodeBridge` переиспользует общий service — это правильная основа. Но `project()` (`node_bridge.py:55`) экспортирует краткие `connected`, `ready_to_start`, `phase`, candidates и runtime; отсутствуют exact connection attempt, typed failure, safe-next-action, child bootstrap progress и snapshot revision.
|
||||
|
||||
`network.provision` возвращает durable network ACK до завершения read-only bootstrap. `DeviceEnrollmentWindow.tsx` получает один projected result и не подписывается на последующее enrollment state; он может показать «связь пока не подтверждена», хотя bootstrap продолжает работать. Это не обязательно отказ соединения. Verify требует `device_id` в текущих candidates (`node_bridge.py:116`): cold saved reconnect после restart не эквивалентен принятому LAB пути.
|
||||
|
||||
Python `/operation` сворачивает все exceptions в 409 с одной фразой; Go `call()` заменяет non-200 ещё одной общей ошибкой, а `execute()` классифицирует её как unknown. Это безопасно по запрету replay, но стирает различие между stale-before-dispatch, отказом устройства и unknown-after-dispatch. Backend typed diagnostics, улучшенные в LAB, не доходят до Node UI.
|
||||
|
||||
Не следует копировать 3 732-строчный LAB-компонент в Node. Нужно довести общий typed operation/recovery контракт через транспортные оболочки и оставить две компактные поверхности над одной семантикой.
|
||||
|
||||
### F5 — Несогласованные deadlines между Node и K1 runtime
|
||||
|
||||
**Подтверждено статически; воспроизведение не проводилось.** Node UI выдаёт request deadline 170 с; Core/Go допускают до 180 с; Go HTTP client имеет 185 с, но сам вызов ограничен command deadline; общий `network.provision` journal задаёт 240 с. Timeout доставки может наступить до terminal outcome нижнего уровня. Политика unknown/no-auto-retry корректна, но оператору недоступно полноценное наблюдение той же операции после таймаута через урезанную проекцию.
|
||||
|
||||
Нужно разделять срок допуска до отправки, ожидание transport-ответа и наблюдение принятой операции. Увеличить все timeout не решает владение и корреляцию.
|
||||
|
||||
### F6 — Матрица приёмки и freezes плохо отражают актуальную систему
|
||||
|
||||
**Подтверждено.** Manifest ссылается в основном на целые test files, а не на конкретный test/scenario/physical run. Один lifecycle файл содержит 446 test-функций. `planned` в старом manifest не доказывает отсутствие реализации сегодня; `software-covered` не доказывает Node parity.
|
||||
|
||||
Guardrail закреплён на `c041a569...` и защищает packet oracle/frozen paths. Он полезен как защита от случайного protocol drift, но не как единственный критерий нового рефакторинга. Отдельные сегодняшние source changes уже выходят за старый файл-freeze. Нельзя просто переснять hashes, объявив поведение сохранённым.
|
||||
|
||||
### F7 — Профильность Rerun частично отделяет lifecycle, но не всю кастомизацию
|
||||
|
||||
**Подтверждено текущим кодом и предыдущим аудитом.** Есть разные live/recorded/LAB profile kinds и remount boundary. Но в Control Station общий `App.tsx:183` хранит `sceneSettings`; live и Saved Sessions используют общий канал настроек, тогда как LAB имеет отдельные result settings. Поэтому формулировка «все три профиля полностью изолированы» сейчас слишком сильна.
|
||||
|
||||
Это самостоятельный долг, не основание трогать viewer в рефакторинге Bridge. Протокол подключения, live camera producer и presentation-профиль нужно принимать отдельно. Исправление camera evidence-root сегодня также не является изменением Wi-Fi протокола.
|
||||
|
||||
## 8. Обязательные границы будущего рефакторинга
|
||||
|
||||
Это предложение для последующего решения, не начатая реализация.
|
||||
|
||||
1. Сохранить текущий рабочий snapshot и сопоставить каждый обязательный scenario с точным existing test, physical run и платформой. Отдельно перечислить Node-only проверки. Исторические версии Ops не переписывать как новую приёмку.
|
||||
2. Выделить один typed outcome: `not_dispatched`, `network_applied`, `control_ready`, `outcome_unknown`, конкретный отказ; не выводить результат из HTTP-кода. Во всех оболочках сохранять operation/runtime/target correlation и допустимое действие.
|
||||
3. Разделить normal Bridge provisioning и recovery orchestration. Нормальный путь может быть коротким; recovery обязан сохранять explicit target, physical ledger и ownership. Quick остаётся отдельной принятой strategy; его не переносить на борт и не удалять из LAB.
|
||||
4. Разделить state projection и reconciliation owner, только после фиксации существующего порядка переходов и lock ownership. Переносить по одной обязанности, без одновременного изменения protocol, camera и viewer.
|
||||
5. Упростить UI на основе общей семантики результата. Компактность достигается уменьшением повторной интерпретации, а не скрытием unknown или заменой всех отказов словом «подключение».
|
||||
6. После каждого узкого изменения — соответствующая автоматическая регрессия, затем отдельный физический UI-сценарий. По указанию владельца перед каждым аппаратным/UI-прогоном очистить cache; обычный reload не засчитывать. Сейчас очищенный прогон не выполнен: browser tool не предоставляет доступную очистку.
|
||||
7. Производительность измерять на одном и том же сценарии и ledger: click→BLE discovery, connect, write-return, status proof, network ACK, route/TCP/DeviceInfo, START, first PCL, first camera. У каждой стадии свой бюджет; таймер ожидания без стадии недостаточен.
|
||||
|
||||
Неприкосновенны: exact wire frame/command order, одна mutation на intent, durability до dispatch, запрет command replay, identity/runtime/host-epoch fences, отделение record от preview, gaps при restart, reader-only recovery и сохранение других Rerun профилей. Менять эти контракты можно только отдельным обоснованным решением, а не попутно при разрезании файлов.
|
||||
|
||||
## 9. Что пока нельзя утверждать
|
||||
|
||||
- Причина всех сегодняшних ATT-ошибок не установлена единым доказательством. FW-specific interpretation ATT4/6 полезна, но не заменяет exact entered-network evidence последнего intent.
|
||||
- Найденный ранний выход polling — статически установленный риск другого этапа; он не был воспроизведён на физическом K1.
|
||||
- Холодный reboot БК на несколько минут во время K1 capture не принят этим аудитом. В коде есть restart rehydration, в Ops принят heartbeat/recovery на отдельных конфигурациях; их Linux end-to-end композиция требует своей приёмки.
|
||||
- Все 77 scenario не перепроверены аппаратно и все тесты не перезапущены. Список нужен для предотвращения регрессии, а не для новой зелёной отметки.
|
||||
- Рефакторинг не начат. Нет изменения пакета, установки на Mini, restart сервиса или публикации в Ops.
|
||||
|
||||
## 10. Проверяемые источники
|
||||
|
||||
- [Полный индекс Ops](2026-09-06-k1-bridge-ops-index.json): все 76 карточек, 41 K1 label, 128 полученных активных комментариев; основные semantic источники указаны выше.
|
||||
- [Hashes текущего кода](2026-09-06-k1-bridge-code-snapshot.json): точные bytes критических модулей и test-файлов на момент чтения, без proprietary evidence или credentials.
|
||||
- [LixelGO IP protocol observation](../lab/002_LIXELGO_IPHONE_LOCAL_PROTOCOL_20260716.redacted.md), [Wi-Fi profile](../04_K1_WIFI_PROVISIONING_PROFILE.md).
|
||||
- [Connection supervision canon](../20_K1_CONNECTION_SUPERVISION_CANON.md), [acceptance manifest](../k1-connection-acceptance.manifest.json), [recovery runbook](../runbooks/K1_CONNECTION_RECOVERY.md), [physical recovery ADR](../adr/0015-k1-physical-state-recovery.md).
|
||||
- [Bridge incident](2026-09-06-k1-bridge-connection-incident.md), [station reply semantics](2026-09-06-k1-station-reply-semantics.md), [live camera root](2026-09-06-k1-live-reference-camera-root.md), [Node Bridge implementation and open gates](2026-09-06-node-k1-bridge-implementation.md).
|
||||
|
||||
Чтение Ops и исходников позволило восстановить рабочие гарантии и найти конкретные границы риска. Следующее решение должно выбирать одну такую границу и её приёмку; переписывание всего K1-контура сразу не имеет достаточного доказательного основания.
|
||||
|
||||
## 11. Уточнение цели: необязательная интеграция устройства и переносимость
|
||||
|
||||
Дополнительный запрос владельца: K1 должен быть необязательной интеграцией; macOS — первая принимаемая платформа, Ubuntu — следующая проверка переносимости. Другие модели XGRIDS не должны требовать встраивания их протоколов в Core. Ниже — архитектурное предложение, не новая реализация или аппаратная приёмка.
|
||||
|
||||
### Что уже отделено, а что ещё нет
|
||||
|
||||
- Есть manifest `DevicePlugin`, отдельный frontend пакета `plugins/xgrids-k1`, backend в `src/k1link/device_plugins/xgrids_k1`, нейтральный SDK и загрузчик с проверкой версии, набора действий и handshake. Это реальная существующая основа, которую следует сохранить.
|
||||
- [Composition frontend](../../apps/control-station/src/composition/devicePlugins.ts) явно включает K1 в сборку. Это правильное место выбора поставляемых модулей, но сейчас выбор статический; независимо устанавливаемый frontend этим не доказан.
|
||||
- [Backend composition](../../src/k1link/web/device_plugin_composition.py) допускает только `transitional-in-process`. [ADR 0011](../adr/0011-laboratory-plugin-runtime-handshake-and-transport-seam.md) прямо исключает из текущих гарантий crash containment, supervisor, portable media IPC и установку пакетов. Наличие runtime transport не означает, что отдельный процесс уже работает.
|
||||
- [Общий pyproject](../../pyproject.toml) включает BLE/MQTT-зависимости и K1 CLI. Общий Python wheel содержит весь `src/k1link`. Поэтому независимое удаление драйвера пока нельзя считать принятой возможностью.
|
||||
- [SensorWorkspace](../../packages/sensor-ui/src/SensorWorkspace.tsx) напрямую импортирует `K1Detail` и выбирает его по `device.kind === 'k1'`. Это конкретная зависимость общей поверхности от устройства; её место — регистрация визуального расширения интеграцией.
|
||||
- Анализ Python import graph обнаружил 14 модулей `compute`, прямо импортирующих K1 analysis/protocol/replay. Это главным образом работа с данными, а не управление радио. Их нельзя механически удалять вместе с драйвером: нужны отдельные границы чтения архивов и проверка LAB.
|
||||
- [NodeBridge](../../src/k1link/device_plugins/xgrids_k1/node_bridge.py) уже использует тот же compatibility service с Linux-адаптерами. Следовательно, второй реализации всей K1-логики сейчас нет и создавать её не требуется. Однако полноценная Ubuntu-приёмка и общий контракт проекции результата ещё не завершены.
|
||||
|
||||
Размер и связность `facade.py` — проблема внутреннего устройства плагина. Прямые зависимости общей установки, сенсорного UI и вычислительных модулей — проблема его внешней границы. Перенос одного большого файла в новую папку не решит ни ту, ни другую автоматически. Историческое имя Python namespace `k1link` само по себе не является доказательством зависимости от оборудования.
|
||||
|
||||
### Предлагаемая ответственность
|
||||
|
||||
| Слой | Ответственность |
|
||||
| --- | --- |
|
||||
| Mission Core / Node host | Реестр интеграций и execution node, разрешения, общий жизненный цикл операций, хранение evidence, маршрутизация нормализованных потоков, оболочка UI и профили просмотра. |
|
||||
| K1 domain | Точный протокол BLE/MQTT/RTSP, совместимость модели и прошивки, порядок подключения и acquisition, интерпретация ответов, восстановление и доказательства физического состояния K1. |
|
||||
| Адаптер платформы | BLE backend, наблюдение сетевого интерфейса и маршрута, доступ к секретам, запуск и остановка локального runtime. Реализации macOS и Ubuntu могут различаться. |
|
||||
| Представление интеграции | Поля подключения, возможности, настройки и статусы K1 через общий SDK; регистрация в host вместо веток K1 в общих компонентах. |
|
||||
| Чтение данных | K1 raw codec как явно объявленная зависимость чтения; нормализованные сохранённые данные читаются без активного драйвера оборудования. Совместимость существующих LAB проверяется отдельно. |
|
||||
|
||||
Переносимую логику разумно сохранить на текущем Python, отделяя платформенные вызовы через небольшие интерфейсы. Требование переносимости относится к поведению и контракту, а не к одному бинарнику для всех ОС. SDK уже экспортирует JSON Schema; смена языка возможна позднее при доказанной необходимости, но сейчас добавила бы повторную проверку протокола и recovery.
|
||||
|
||||
Целевая граница исполнения — отдельный runtime интеграции на том компьютере, рядом с которым находится устройство: локально для LAB, на БК для бортового сценария. Core обращается к выбранному execution node; Wi-Fi оператора не подменяет Wi-Fi около БК. Контракт должен покрывать не только команды, но и события операции, состояние, evidence и media. Объявление permissions в manifest не заменяет их фактическое ограничение.
|
||||
|
||||
Плагин имеет общий протокол взаимодействия с host, но собственную семантику устройства. Core не должен знать BLE UUID или порядок команд K1. И наоборот, плагин не должен владеть профилем лабораторного Rerun, глобальной навигацией или реестром аппаратов. Другие модели XGRIDS получают явные model/firmware profiles; общие vendor-компоненты выделяются по подтверждённому совпадению поведения, а не по одному бренду. Обобщение на одновременную работу нескольких устройств — отдельная приёмка, не свойство текущего single-session runtime.
|
||||
|
||||
### Последовательность и критерии завершения
|
||||
|
||||
1. Закрепить существующие сценарии и одинаковый контракт результата/событий для LAB и Node. Получение HTTP-ответа и физическое завершение операции остаются разными событиями.
|
||||
2. Выделить внутри K1 provisioning, наблюдение/recovery и проекцию состояния, сохранив durable dispatch boundary, владельца операции, блокировки и порядок команд. Не пересобирать одновременно acquisition, камеру и Rerun.
|
||||
3. Устранить прямые зависимости общих компонентов, разделить поставку драйвера и чтение записей. Проверить запуск Core без установленного K1 и работу других устройств и доступных архивов.
|
||||
4. Реализовать процессную границу за существующим transport с явными контрактами событий/media. Проверить зависание и падение плагина: Core остаётся доступным, состояние устройства честно меняется, другая запись не прерывается. Перезапуск runtime не переотправляет provisioning, START или STOP автоматически; сначала восстанавливаются журнал и наблюдаемое состояние.
|
||||
5. Принять macOS, затем Ubuntu Bridge на том же domain-коде. Перенос считается доказанным, когда меняются адаптеры и упаковка, а не K1-логика или Core. Допустимо начать с явного состава сборки; динамическая установка UI, магазин плагинов и hot reload не нужны для первой проверки границы.
|
||||
|
||||
Отдельные обязательные критерии: восстановление связи после потери сети, безопасное обнаружение рестарта устройства и БК, отсутствие повторной физической команды, отсутствие регрессии LAB/recorded/live профилей Rerun. Каждый аппаратный прогон выполняется через UI с предварительной очисткой браузерного кэша по указанию владельца. В этом дополнении выполнены только чтение кода и документирование; проверок на устройстве не было.
|
||||
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"schema_version": "missioncore.bridge-analysis-code-snapshot/v1",
|
||||
"base_commit": "020a878915ea32c64963975447650fd8eee29071",
|
||||
"working_tree": "dirty; source code not changed by this analysis",
|
||||
"analysis": "Static inspection and existing evidence only; no tests executed",
|
||||
"files": [
|
||||
{
|
||||
"path": "src/k1link/device_plugins/xgrids_k1/facade.py",
|
||||
"sha256": "24c4ec6c33c4d21a99065c4146df12bdc2d81efd5a9da769c077b1ab4bea896d",
|
||||
"lines": 35996
|
||||
},
|
||||
{
|
||||
"path": "src/k1link/device_plugins/xgrids_k1/connection_supervisor.py",
|
||||
"sha256": "2adbd7d61c34816d5be9a66baa788634408df7dc0e4a0d21520241d5e965806a",
|
||||
"lines": 2252
|
||||
},
|
||||
{
|
||||
"path": "src/k1link/device_plugins/xgrids_k1/ble/wifi_provisioning.py",
|
||||
"sha256": "f169a78f66e71d9c85d503e174d5f19f19e010aa04db3fe3ba8cf032d809de55",
|
||||
"lines": 818
|
||||
},
|
||||
{
|
||||
"path": "src/k1link/device_plugins/xgrids_k1/ble/scanner.py",
|
||||
"sha256": "ea6f1fed6db5b3b98735779e14237e0eaf40a3cbe2d0cd8f3196e95308c12153",
|
||||
"lines": 1306
|
||||
},
|
||||
{
|
||||
"path": "src/k1link/device_plugins/xgrids_k1/protocol/application_session.py",
|
||||
"sha256": "f91367b19b3f85ce2b47279acbdeff969cd5a10b64bc5ceb90b843840a1e6b26",
|
||||
"lines": 1930
|
||||
},
|
||||
{
|
||||
"path": "src/k1link/device_plugins/xgrids_k1/node_bridge.py",
|
||||
"sha256": "550f34cf5b947131ca1067a1dbe74731ca7a7601cb42bbb5a9c1a08ebfd9179b",
|
||||
"lines": 248
|
||||
},
|
||||
{
|
||||
"path": "src/k1link/device_plugins/xgrids_k1/linux_host.py",
|
||||
"sha256": "c2e3fa6884e97c9cbd7bbf29cfb1260e539f61fd2e7d18e0dc3c1ab94c59d4d4",
|
||||
"lines": 214
|
||||
},
|
||||
{
|
||||
"path": "src/k1link/fleet/device_enrollment.py",
|
||||
"sha256": "9856fd5638aaef88317ec82fa482b6e358efb5fd57deecf61fb9e9d0e14c4fa4",
|
||||
"lines": 173
|
||||
},
|
||||
{
|
||||
"path": "apps/node-agent/internal/node/device_enrollment.go",
|
||||
"sha256": "9e92379ed9e262d6ee4b16c754df7852d129bf6daec324140e85a4b15af9c5e0",
|
||||
"lines": 323
|
||||
},
|
||||
{
|
||||
"path": "plugins/xgrids-k1/frontend/src/components/K1ProvisioningPipeline.tsx",
|
||||
"sha256": "d6205ce8be004238791471fb7c3eb2eccb0676bedb81a06514771c26419cc0ab",
|
||||
"lines": 3732,
|
||||
"hook_mentions": {
|
||||
"useState": 17,
|
||||
"useEffect": 13,
|
||||
"useRef": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "plugins/xgrids-k1/frontend/src/useXgridsK1Runtime.ts",
|
||||
"sha256": "dcc0c1e5ca1c841e963ddc18d24628799ec97d036bc1095eb6f22124cf000565",
|
||||
"lines": 2135
|
||||
},
|
||||
{
|
||||
"path": "plugins/xgrids-k1/frontend/src/networkProvisioning.ts",
|
||||
"sha256": "45ba4eaf111b1cf8fa0945884aaf95e3920a274cd1d7f0e4797f8da2ee7fd204",
|
||||
"lines": 241
|
||||
},
|
||||
{
|
||||
"path": "packages/sensor-ui/src/DeviceEnrollmentWindow.tsx",
|
||||
"sha256": "de02c9443f606bd0d73ec476c34f095c05851005497426431271dbe1ba36f064",
|
||||
"lines": 49,
|
||||
"hook_mentions": {
|
||||
"useState": 10,
|
||||
"useEffect": 1,
|
||||
"useRef": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "packages/sensor-ui/src/enrollment.ts",
|
||||
"sha256": "ad97fbe9ecc1b6567d8ee67d6d32c4cc39a31ce95bf0eb48ef9cbc40a884a16f",
|
||||
"lines": 37
|
||||
},
|
||||
{
|
||||
"path": "docs/k1-connection-acceptance.manifest.json",
|
||||
"sha256": "477615cdd34685bc10d9815de0c9642fbdfcdea2eb05e04b5e07b7c87a2e9854",
|
||||
"lines": 93
|
||||
},
|
||||
{
|
||||
"path": "tests/test_xgrids_acquisition_lifecycle.py",
|
||||
"sha256": "97728c489551f16a6ccf2606a19ca077f5a2926adc67681f367666188df008ef",
|
||||
"lines": 34794
|
||||
},
|
||||
{
|
||||
"path": "tests/test_xgrids_active_acquisition_restart_rehydration.py",
|
||||
"sha256": "3fc85fba32eab54fda12625ac77d54f7e3ae8a454708b4167b71e0ffd4271d61",
|
||||
"lines": 2482
|
||||
},
|
||||
{
|
||||
"path": "tests/test_node_k1_bridge.py",
|
||||
"sha256": "25af4bd6d40b8df487b231e599730117b62598a78b838d20e5443395920c2c48",
|
||||
"lines": 204
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
# K1 Bridge: provisioning recovery and first-scan discovery
|
||||
|
||||
Scope: the operator-local K1 connection in LAB on canonical Core `8000`.
|
||||
The onboard installation is a separate pending acceptance task.
|
||||
|
||||
The later [firmware callback audit](2026-09-06-k1-station-reply-semantics.md)
|
||||
explains the two fresh 19:22/19:23 UTC failures and supersedes the earlier
|
||||
generic ATT interpretation below with a bounded FW 3.0.2 station diagnosis.
|
||||
|
||||
## Evidence and diagnosis
|
||||
|
||||
Three explicit Bridge attempts returned HTTP 502 on 2026-09-06 at
|
||||
18:04:50, 18:05:13 and 18:05:43 UTC. All failed at `gatt-write` with
|
||||
`BleakGATTProtocolError`, ATT 4 `INVALID_PDU`. Each attempted one 99-byte
|
||||
write-with-response; acknowledgement and a joined Wi-Fi address were absent.
|
||||
The characteristic advertised `read, write`; the observed write-without-response
|
||||
capacity was 253 bytes. This capacity alone does not prove a successful ATT write.
|
||||
|
||||
A separately admitted, exact-target Bridge `connection.verify` at 18:15 UTC
|
||||
read K1 state without a provisioning write or host Wi-Fi switch. K1 answered,
|
||||
but supplied no shared-LAN address (`connection-verify-address-unavailable`).
|
||||
A preceding request was rejected at input validation because of the diagnostic
|
||||
operation identifier format; it performed no device I/O.
|
||||
|
||||
The same ATT error exists in August's private logs. The accepted August Bridge
|
||||
record uses the same Bleak 3.0.2, write mode, frame length and capacity. Successful
|
||||
historical writes also include an empty network baseline, so an empty baseline
|
||||
does not justify adding an AP-enable command or rejecting Bridge in advance.
|
||||
The exact reason for the peripheral's rejection remains unproved; a wrong Wi-Fi
|
||||
password, frame-format regression or MTU failure must not be asserted from this
|
||||
error alone. No alternate transport mode, frame, retry, START or STOP was sent.
|
||||
|
||||
Private evidence is retained under the ignored incident directory, with UTC and
|
||||
monotonic timestamps, redacted events and SHA-256 artifact index. Credentials and
|
||||
raw packet contents are absent from this report and source changes.
|
||||
|
||||
## Software causes and refactor
|
||||
|
||||
- The Connect hook had three overlapping result/error branches. Its failed
|
||||
HTTP path accepted the server snapshot but returned `observedState: null` to
|
||||
the form; the form therefore lost the exact operation and its ATT diagnosis.
|
||||
- A completed Bluetooth search suppressed the recovery surface even after the
|
||||
subsequent, click-owned Connect failed. The disabled credential form instead
|
||||
promised a future safe continuation despite there being no running operation.
|
||||
- Recovery guidance claimed an automatic recovery or a completed UI reset that
|
||||
had not occurred. The lead status fell back to idle despite a terminal failure.
|
||||
- Durable JSON logging omitted the write-mode, frame-size and GATT-property
|
||||
fields already supplied by the BLE implementation.
|
||||
|
||||
`networkProvisioning.ts` now owns the single submission, bounded observation of
|
||||
that exact idempotency key, monotonic snapshot selection and reviewed failure
|
||||
copy. An existing operation is only observed, never resubmitted. The runtime
|
||||
hook retains exact applied-network/control-authority checks and React action
|
||||
ownership. The form renders the returned operation and allows explicit recovery
|
||||
after its failed attempt even when its preceding search is complete. It still
|
||||
clears the credential immediately upon submission.
|
||||
|
||||
The frozen BLE frames and canonical MQTT dialogue are unchanged. Ops reference:
|
||||
MISSIONCOR-3, “Mission Core. Lixel K1 / XGRIDS Integration”, and
|
||||
`docs/lab/002_LIXELGO_IPHONE_LOCAL_PROTOCOL_20260716.redacted.md`.
|
||||
That iPhone capture begins at the IP stack and does not contain Bluetooth HCI.
|
||||
The reviewed BLE profile remains `docs/04_K1_WIFI_PROVISIONING_PROFILE.md`.
|
||||
|
||||
## Validation and remaining acceptance
|
||||
|
||||
Focused frontend: 227 passed, including new behavioral cases for HTTP 502,
|
||||
existing-key non-replay, lost-response success, pending settlement, stale REST
|
||||
versus newer WebSocket, superseded intent and unrelated journal rows. The
|
||||
rendered regression covers completed search followed by failed Connect and
|
||||
requires enabled recovery choices instead of the trapped form.
|
||||
|
||||
Full frontend suite: 772 passed sequentially; TypeScript and final production build passed. Application architecture: 4 passed.
|
||||
|
||||
BLE and persistent-diagnostic tests: 34 passed. Canonical guardrail: four
|
||||
immutable LixelGO captures verified in their original checkout, frozen
|
||||
protocol/Bridge contour unchanged, 32 synthetic sentinels passed. The convenience
|
||||
script initially failed because raw captures are deliberately absent from the
|
||||
active worktree; its original-location integrity check passed without copying
|
||||
or changing the captures. Ruff and diff checks passed.
|
||||
|
||||
A later runtime journal records an explicit successful Bridge write at 18:42 UTC,
|
||||
followed by application-control acceptance, canonical START and the first point
|
||||
frame, then a confirmed STOP at 18:44 UTC. These actions occurred while the
|
||||
assistant was editing/testing discovery; they were not dispatched by this
|
||||
investigation. The success followed the recovery-UI refactor and preceded the
|
||||
new discovery implementation. It proves a subsequent successful physical
|
||||
connection, not the cause or permanent resolution of the intermittent ATT error.
|
||||
Do not automatically replay the earlier failed attempts.
|
||||
|
||||
## First-scan miss
|
||||
|
||||
The operator reported that an already active K1 is absent from the first search
|
||||
and appears on the second. This was reproduced on the original six-second
|
||||
implementation: 18:32 UTC returned four devices and zero K1 candidates; a second
|
||||
explicit scan at 18:35 UTC returned five devices including the expected K1.
|
||||
These observations do not distinguish radio advertisement latency from macOS
|
||||
state and do not establish that the camera was powered off.
|
||||
|
||||
Discovery now opens one native Bleak scanner context. It listens for an initial
|
||||
six-second window and, if no K1 name has appeared, continues the same context
|
||||
up to a twenty-second bound, stopping on a later K1 candidate. There is no hidden
|
||||
second scan, GATT connection, provisioning retry or cache-based candidate
|
||||
promotion. Name matching only ends discovery; compatibility and connection
|
||||
still require their separate evidence. The owner arbiter, generation revocation,
|
||||
native-handle capture and cancellation cleanup remain in place. A caller's
|
||||
explicit shorter duration is respected.
|
||||
|
||||
The shared frontend request/countdown, backend default and Node Bridge source
|
||||
use the twenty-second bound. Completed operations and private structured logs
|
||||
now include scanner startup, total elapsed time, first-candidate time and whether
|
||||
the initial window was extended. They contain no Wi-Fi credentials.
|
||||
|
||||
After the new canonical process started, the first explicit UI search at
|
||||
18:51 UTC found the expected K1. Native startup took 766 ms, first K1 detection
|
||||
2023 ms from scanner construction, and total discovery 6768 ms. Extension was
|
||||
not needed. The browser showed the fresh candidate and the arbiter returned idle.
|
||||
This is one successful process-restart test; the browser cache was not cleared.
|
||||
The operator requested a separate test after browser-cache clearing. That
|
||||
acceptance is pending: the available in-app browser automation exposes no cache
|
||||
clearing capability, and the clear-browsing-data keyboard shortcut had no effect.
|
||||
The operator was asked to clear it; no cache clearing is claimed.
|
||||
|
||||
Additional discovery validation: 73 backend tests passed (scanner, owner arbiter,
|
||||
persistent diagnostics and Node Bridge), seven facade discovery tests passed,
|
||||
138 focused frontend tests passed, and architecture checks passed. TypeScript
|
||||
and production build passed. Final focused discovery checks, Ruff, whitespace
|
||||
and the frozen protocol/Bridge comparison passed. The previously completed full
|
||||
772-test frontend pass applies to the recovery refactor; only affected suites
|
||||
were rerun for the subsequent discovery change.
|
||||
|
||||
Canonical Core remains on port 8000; no listener exists on 8765. Onboard source
|
||||
is synchronized separately; this investigation does not install or rebuild the
|
||||
pending Node package.
|
||||
|
||||
## Ops publication
|
||||
|
||||
The existing Ops canon was read. Automatic approval review rejected the attempted
|
||||
card update because consultation was authorized but publication of internal
|
||||
technical details was not. No Ops card was changed; this local report is the
|
||||
reviewable result pending explicit publication authorization.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,129 @@
|
||||
# K1 live reference comparison and missing camera
|
||||
|
||||
Scope: operator-local K1 Bridge data acquisition on canonical Mission Core 8000.
|
||||
No new physical START, STOP, provisioning or RTSP request was sent during this
|
||||
investigation. The comparison uses the existing 6 September capture.
|
||||
|
||||
## Authoritative references found in Ops
|
||||
|
||||
MISSIONCOR-3, “Mission Core. Lixel K1 / XGRIDS Integration”, block “Внутренние
|
||||
эталоны Mission Core” and its comment dated 22 August 2026 identify:
|
||||
|
||||
- Fast reference A: eaad9de, 20260822T105904Z_viewer_live. START to calibration
|
||||
under 5 seconds; calibration 21 seconds; after calibration cloud 2 seconds,
|
||||
right camera 4 seconds; STOP to physical onset 1 second.
|
||||
- Recovery reference B: 1001a31, 20260822T130323Z_viewer_live. START 14 seconds;
|
||||
calibration 22 seconds; cloud +1 second, camera +8 seconds; STOP onset 7 seconds.
|
||||
|
||||
The linked canonical report is
|
||||
[Lab 010](../lab/010_K1_MISSION_CORE_INTERNAL_LIVE_BASELINE_20260822.redacted.md).
|
||||
These are measured internal references for one K1 FW 3.0.2 and Bridge/direct LAN,
|
||||
not a transferable SLA or a standalone Rerun preset. A used a short physical
|
||||
ledger; B retained mature recovery history. Source quality was not reduced.
|
||||
|
||||
MISSIONCOR-66, “Mission Core. Канон интеграции Rerun”, and MISSIONCOR-74,
|
||||
“Additional Core · Переносимая кастомизация Rerun”, define the separate
|
||||
presentation contracts:
|
||||
|
||||
| Surface | Current profile | Clock and media |
|
||||
| --- | --- | --- |
|
||||
| Live acquisition | live-acquisition | stream_time; upstream live Rerun; right camera through the independent RTSP → durable fMP4 → MSE path |
|
||||
| Saved Sessions / Data | recorded-session | session_time; progressive recorded admission; RecordedFmp4Player follows the shared playback clock |
|
||||
| Canonical LAB result | laboratory-result | session_time; result-specific settings; merged RRD with native AssetVideo/VideoFrameReference; separate presentation gate |
|
||||
|
||||
The live receiver does not inherit the LAB full-readiness gate. Changing point
|
||||
size, accumulation or blueprint cannot repair a camera producer that never
|
||||
started. The current live display settings include accumulation 47 seconds,
|
||||
point size 1 and height/viridis; no evidence identifies those visual values as
|
||||
part of the fast reference, so they were not arbitrarily reset.
|
||||
|
||||
### Settings isolation limitation found during the audit
|
||||
|
||||
Distinct profile kinds, clocks and media admission do not prove complete
|
||||
settings isolation. App.tsx still owns one sceneSettings/displayDraft pair for
|
||||
live and Data, and useWorkspaceLayoutProfile() loads and saves one
|
||||
observation.spatial profile containing scene settings. The normal settings
|
||||
committer suppresses backend writes while recorded replay is presented, but it
|
||||
still updates the common in-memory settings. The persisted layout restore/apply
|
||||
path also has no profile-kind namespace. LAB uses its own resultId-scoped draft
|
||||
and durable view profile.
|
||||
|
||||
Therefore this audit confirms distinct presentation contracts and LAB settings
|
||||
ownership, not full live/Data settings isolation. No shared settings, layout,
|
||||
Rerun renderer or replay code was changed for this camera repair. The focused
|
||||
profile tests below do not cover the remaining live/Data settings coupling.
|
||||
Separating that storage and state requires its own transition/race and browser
|
||||
regression checks; it must not be folded into a camera-path fix implicitly.
|
||||
|
||||
## Observed 6 September failure
|
||||
|
||||
Existing session 20260906T184240Z_viewer_live:
|
||||
|
||||
| Metric | Fast A | Recovery B | Recent run |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| MQTT callback → publication p50 | 23.839 ms | 83.934 ms | 93.151 ms |
|
||||
| MQTT callback → publication p95 | 41.541 ms | 223.589 ms | 208.989 ms |
|
||||
| Preview drops | 0 | 70 | 58 |
|
||||
| Point decode errors | 0 | 0 | 0 |
|
||||
| Camera archive | complete | complete | absent |
|
||||
|
||||
Run lengths differ; drop counts are not normalized performance rates. Device
|
||||
calibration onset and first visible pixels were not independently measured in
|
||||
the recent run, so the historical operator timings are not falsely compared to
|
||||
backend timestamps. The recent run published 1,028,061 points in 386 PCL frames.
|
||||
|
||||
At 18:43:15 UTC the browser admitted a Rerun store; this alone is not proof of
|
||||
visible point pixels. Between 18:43:20 and 18:43:50 the backend logged 22 failed
|
||||
post-authoritative-PCL camera activations. First-PCL admission took 4–34 ms.
|
||||
No camera producer activation success or camera media artifact exists in this
|
||||
session. The private formatter discarded exception details, preventing recovery
|
||||
of each historical exception stack from those records.
|
||||
|
||||
## Reproduced storage defect and bounded repair
|
||||
|
||||
The running checkout is separate from MISSIONCORE_DATA_DIR. Acquisition uses
|
||||
resolve_missioncore_evidence_dir(), but XgridsK1CameraGateway previously confined
|
||||
session paths to repository_root. The actual session is outside the checkout.
|
||||
An offline call using the real existing session directory deterministically
|
||||
raised “camera recording root must stay inside the repository” before authority
|
||||
reservation, FFmpeg preparation or network I/O. Camera remained idle, matching
|
||||
the observed pre-producer failure. This mismatch necessarily blocks recording
|
||||
at the configured path even though the original exception stacks were lost.
|
||||
|
||||
The gateway now receives an explicit evidence_root from the existing service
|
||||
composition. It confines both acquisition-owned and selected-preview recording
|
||||
to that root after resolving paths. It rejects sibling directories and symlink
|
||||
escapes. The source checkout remains the FFmpeg-binary lookup root; the fallback
|
||||
for standalone gateway callers preserves their existing repository confinement.
|
||||
No RTSP arguments, video quality, stream choice, camera producer lifecycle,
|
||||
START/STOP, MQTT dialogue, Rerun blueprint or LAB/archive viewer policy changed.
|
||||
|
||||
Private exception diagnostics now retain only the exception class and final
|
||||
filename/line/function. They omit exception text, locals, source lines and
|
||||
absolute paths. This makes future failures attributable without leaking data.
|
||||
|
||||
## Validation and remaining physical acceptance
|
||||
|
||||
- Camera gateway suite: 42 passed, including seven new external-root,
|
||||
composition-wiring and path-confinement cases. Synthetic FFmpeg produced
|
||||
and archived media in the configured external directory.
|
||||
- Camera acquisition lifecycle: 37 passed.
|
||||
- Persistent diagnostics suite: 8 passed, including exception-location redaction.
|
||||
- Focused frontend profile, environment, LAB view profile and recorded-camera
|
||||
journal checks: 14 passed. These are contract-level checks, not physical
|
||||
playback acceptance or proof of complete settings isolation.
|
||||
- Mypy on camera.py and runtime_diagnostics.py: passed.
|
||||
- Ruff and git diff --check: passed.
|
||||
- Protocol, BLE provisioning/AP, physical ledger/coordinator, MQTT,
|
||||
connection supervisor, runtime and archive remain identical to c041a56.
|
||||
- The broad frozen-contour guard also includes camera.py, so it now deliberately
|
||||
detects this narrow camera storage change. Its baseline was not advanced or
|
||||
weakened. This is not a claim that the full freeze check passes unchanged.
|
||||
|
||||
The canonical idle service was refreshed and its replacement was confirmed
|
||||
ready on port 8000 at 19:10:22 UTC; no frontend rebuild was required. A new
|
||||
operator-started physical run is still
|
||||
needed to verify right-camera appearance, durable media and browser playback.
|
||||
The existing failed session is preserved and is not retroactively repaired.
|
||||
|
||||
Ops was consulted read-only. This local report was not published to a card.
|
||||
@@ -0,0 +1,95 @@
|
||||
# K1 station replies: misleading ATT error and fresh failures
|
||||
|
||||
Scope: the two operator-started Bridge attempts on Core 8000 following the
|
||||
operator-reported browser cache clear. No device write, START/STOP, Wi-Fi switch,
|
||||
subnet scan or firmware execution was performed by this investigation.
|
||||
|
||||
## Fresh evidence
|
||||
|
||||
| Attempt, UTC | K1 first advertisement | Complete scan | Connect operation | Reply |
|
||||
| --- | ---: | ---: | ---: | --- |
|
||||
| 19:22:25 | 1.308 s | 6.366 s | 51.399 s | ATT 4 INVALID_PDU |
|
||||
| 19:23:55 | 2.208 s | 6.023 s | 7.587 s | ATT 4 INVALID_PDU |
|
||||
|
||||
Both attempts reached the validated GATT contract, baseline status read and
|
||||
one 99-byte write-with-response. Neither write was acknowledged as successful.
|
||||
The observed command capacity was 253 bytes. Empty passwords are rejected
|
||||
before this path; presence does not prove correctness. Individual connect and
|
||||
write durations were not recorded, so total latency is not falsely attributed
|
||||
entirely to the Wi-Fi operation or entirely to CoreBluetooth.
|
||||
|
||||
Private snapshots, operation identities and the disassembly are retained under
|
||||
the ignored `.runtime/k1-connect-incident-20260906/fresh-1922/` directory with
|
||||
0600 files, UTC/monotonic timestamps and a SHA-256 artifact index.
|
||||
|
||||
## Firmware-level explanation
|
||||
|
||||
The reviewed official FW 3.0.2 artifact is the immutable source documented in
|
||||
[Lab 004](../lab/004_K1_FW302_AP_CREDENTIAL_PROVIDER_20260720.redacted.md).
|
||||
Its extracted `lixel_nman` executable is 323,464 bytes, SHA-256
|
||||
`aead745e4e0073ae99e84e851e5560161f2d560d5804f21e5268116efbb1dc42`,
|
||||
ELF build ID `205fb546e44667ca0e74159a7672269a81b754f5`.
|
||||
|
||||
Bounded offline AArch64 disassembly established:
|
||||
|
||||
1. At 0x14588 the service registers characteristic 7f01, with write callback
|
||||
0x17e48 selected at 0x14598.
|
||||
2. Its station branch calls `wifi_connect` at 0x18034 → 0x15440. The return
|
||||
value is retained in w28 at 0x18038 and passed as the write-result error at
|
||||
0x17fc4 → 0x291a8.
|
||||
3. `wifi_connect` returns 4 in its network-not-found branches: 0x16190 and
|
||||
0x16a2c. Adjacent diagnostics refer explicitly to the SSID not being found.
|
||||
4. The branch matching NetworkManager's credentials-required output returns
|
||||
6 at 0x1641c. This describes an unsuccessful credential path, not proof
|
||||
of the exact incorrect character or how the form was populated.
|
||||
|
||||
Thus the device's application return codes collide with generic ATT names:
|
||||
4 is displayed as INVALID_PDU and 6 as REQUEST_NOT_SUPPORTED. The existing
|
||||
Mission Core message discarded the reviewed device meaning. Neither changing
|
||||
the 99-byte layout nor switching write mode follows from these observations.
|
||||
The callback performs Wi-Fi work before returning, so its response is not merely
|
||||
an instantaneous transport acknowledgment.
|
||||
|
||||
This is an interpretation under the selected exact firmware profile. It is not
|
||||
a universal ATT error mapping, proof of the live firmware before DeviceInfo,
|
||||
or confirmation that the network configuration remained unchanged. The actual
|
||||
SSID/radio condition still needs a corrected operator-run connection test.
|
||||
The old screenshot displays a network name whose exact spelling was queried;
|
||||
this report does not assume that spelling is a typo or assume K1 can see it.
|
||||
|
||||
## Bounded correction
|
||||
|
||||
`wifi_failure.py` owns the pure profile-scoped classification. The service uses
|
||||
it only for Bridge/Direct, selected FW 3.0.2, an annotated gatt-write failure,
|
||||
a dispatched/unconfirmed 99-byte write-with-response and BleakGATTProtocolError.
|
||||
It retains the original exception code and raw ATT facts. Quick Connect,
|
||||
baseline-read failures, other firmware and unrelated errors retain their
|
||||
original diagnostics. Retry flags, mutation ledger and ambiguity remain intact.
|
||||
|
||||
The frontend shares the station guidance between the form and recovery panel.
|
||||
It offers “Указать сеть Wi-Fi заново” through the existing explicit reset and
|
||||
scan flow. The pending stage explains K1 Wi-Fi connection. It does not populate,
|
||||
store, inspect or change the password. The GATT helper, packet bytes, MQTT,
|
||||
physical command control and all Rerun profiles are unchanged in this increment.
|
||||
|
||||
## Validation
|
||||
|
||||
- 19 focused backend cases passed: 16 classification boundary cases, two
|
||||
service-level reply cases and the existing ambiguous-write recovery case.
|
||||
They verify one submission, retained raw codes, failed/unconfirmed state and
|
||||
an unresolved ledger rather than promoting an error to success.
|
||||
- 24 classification/diagnostic cases passed; mypy and Ruff passed.
|
||||
- 775 frontend tests passed, including rendered station guidance and the
|
||||
explicit same-device network setup action. Architecture: 4 passed.
|
||||
- Production typecheck and build passed. The replacement canonical Core was
|
||||
confirmed ready at 2026-09-06T19:41:10.665304+00:00.
|
||||
- Browser check: the real Core 8000 Test Devices page loaded; normal and expanded
|
||||
sizes worked. The page was left open. No BLE action was clicked. The new error
|
||||
messages were covered by rendered tests, not represented as a new hardware run.
|
||||
- Hardware Wi-Fi acceptance remains pending the exact target SSID and an
|
||||
operator-started attempt. macOS networksetup did not provide the current SSID;
|
||||
its output was not treated as proof that the host network is down.
|
||||
|
||||
Ops MISSIONCOR-3 was read through the direct MCP. Its iPhone network capture
|
||||
starts at IP and does not contain Bluetooth HCI; it cannot establish the meaning
|
||||
of this GATT callback. No Ops card was written.
|
||||
@@ -0,0 +1,164 @@
|
||||
# K1 Bridge on the onboard computer
|
||||
|
||||
Authority: the owner's 2026-09-06 request and two annotated Fleet screenshots.
|
||||
The earlier Node context is historical evidence, not a new instruction. X4 work
|
||||
is paused while its battery charges. Hardware acceptance below remains pending
|
||||
until the owner powers on K1 and supplies the target WLAN in the application.
|
||||
|
||||
## Product surface decision
|
||||
|
||||
The operator occasionally adds a wireless sensor to one selected onboard
|
||||
computer. The primary entity is that computer's device inventory. Discovery,
|
||||
network observations, credentials and device control belong to that computer;
|
||||
the operator's Mission Core is a paired remote console. Node's local console
|
||||
uses the same form and backend contract.
|
||||
|
||||
Selected composition: a plus beside refresh in the device inventory opens the
|
||||
canonical modal Window, titled «Подключение устройства к БК». The target board
|
||||
is visible before discovery or credentials. K1 is selected from an explicit
|
||||
scan on that board. The operator chooses a board-observed WLAN or enters an
|
||||
SSID, then submits one Bridge operation. Success requires observed device
|
||||
connectivity; dispatch alone is never shown as connected.
|
||||
|
||||
An independent Fleet connection workspace was considered: it separates the
|
||||
action from its board and can imply operator-local radios. A new primary root
|
||||
was also unnecessary. The owner explicitly selected the inventory plus and
|
||||
authorized moving the existing operator-local connection workspace into LAB,
|
||||
with all its current modes and functions, under «Тестовые устройства».
|
||||
The stable workspace ID is retained for saved navigation.
|
||||
|
||||
This is domain content in admitted list/detail and modal compositions. Reuse
|
||||
`Window`, `WindowFooterActions`, `TextField` (including password), `Select`,
|
||||
`Button`, `IconButton`, `ResourceRow`, `SettingsCard`, `StatusBadge`,
|
||||
`ActivityIndicator`, `ToastStack`; icons `plus`, `refresh`, `network`, `camera`,
|
||||
`eye`, `settings`, `close`, `play`, `stop`. These exist in the sibling Design
|
||||
Guideline registries. No new generic visual entity is introduced.
|
||||
|
||||
State grammar: board unavailable; service unavailable; ready to scan;
|
||||
scanning; no candidates; candidate selected; network list unavailable with
|
||||
manual entry; ready to connect; connecting; observed connected; failed before
|
||||
write; outcome unknown after possible write. A stale discovery/runtime or
|
||||
changed board invalidates the form. Closing the modal clears its password.
|
||||
Closing after dispatch does not claim cancellation of a physical operation.
|
||||
|
||||
## Execution and security boundaries
|
||||
|
||||
Only Bridge is admitted on Node. Reuse the reviewed firmware 3.0.2 profile and
|
||||
99-byte 7f01 operation with 7f02 observation and exact endpoint verification.
|
||||
No Quick Connect, host association, AP enable, subnet scan or firmware action
|
||||
is exposed by the Node adapter. BLE notifications, where used by the existing
|
||||
profile, can entail the standard temporary CCCD subscription write.
|
||||
|
||||
Use the existing authenticated Node/Core channel. A device-enrollment command
|
||||
targets the paired node and a worker runtime/discovery generation, even before
|
||||
a device session exists. Wi-Fi credentials must not enter the existing durable
|
||||
sensor-command journal, Fleet database, error text, evidence or argv. Pending
|
||||
secret payloads are short-lived memory only. A restart, expired command or
|
||||
uncertain dispatch never retries a provisioning write with a new identity.
|
||||
The Node worker is the sole hardware owner for both consoles.
|
||||
|
||||
Linux adapters must observe BlueZ and NetworkManager on the board. Host-route
|
||||
verification uses the kernel route to the exact K1 address; a tunnel/default
|
||||
route cannot silently qualify as the required local Bridge path. Existing
|
||||
macOS adapter behavior and the accepted local LAB workflows are retained.
|
||||
|
||||
## Rerun profile boundary
|
||||
|
||||
| Profile | Source / clock | Settings authority | Lifetime |
|
||||
| --- | --- | --- | --- |
|
||||
| Live acquisition | Current board camera/LiDAR / stream_time | Live preview settings | Active acquisition and execution binding |
|
||||
| Recorded session | Immutable admitted recording / session_time | Session replay, trajectory, time and playback settings | Recording identity |
|
||||
| Laboratory result | Immutable admitted result and recording / session_time | Result-specific scene, evidence layers, diagnostic selections | Result identity |
|
||||
|
||||
The native renderer and recorded data pipeline can be shared, but each profile
|
||||
has an explicit discriminator. Crossing profiles remounts the renderer so its
|
||||
refs, subscriptions and pending recovery cannot leak into another profile.
|
||||
LAB uses its own factory and result identity. Source recording/application IDs
|
||||
remain unchanged: profile separation does not rewrite evidence lineage.
|
||||
|
||||
The Node publishes native RRD through ordered WebRTC data channels and the
|
||||
existing camera gateway publishes H.264/fMP4 through a second channel. Signalling
|
||||
uses paired sensor operations; ICE admits private LAN/Tailscale host candidates
|
||||
only, with no STUN/TURN. The Node Rerun sink opens no gRPC port. Both hosts inject
|
||||
the existing isolated native Rerun renderer into the shared sensor UI.
|
||||
|
||||
Two viewers at most are admitted. Decoded preview envelopes use latest-value
|
||||
queues; encoded RRD bytes are never dropped within an open recording. Slow
|
||||
consumers are closed. Camera preview leases share the canonical recording
|
||||
producer. Closing a viewer releases its peer and delivery lease; acquisition
|
||||
STOP remains a separate explicit operator action. These bounds concern delivery;
|
||||
sustained native-viewer CPU/GPU/memory acceptance requires the real board test.
|
||||
|
||||
Node live settings (point size, accumulation, color, palette, points, trajectory,
|
||||
grid) invoke only `viewer.settings.update` behind `profile=live-acquisition`.
|
||||
Recorded and LAB profiles keep their own controls. LAB result changes remount
|
||||
the renderer as well as changes between the three profile kinds.
|
||||
|
||||
## Validation and current acceptance
|
||||
|
||||
- Focused backend tests: board binding, expiration, replay prevention, secret
|
||||
non-persistence, restart/unknown outcomes and Linux route classifications.
|
||||
- Architecture gate; frontend typecheck, unit tests and build sequentially.
|
||||
- Node package build and installation provenance; service health after reboot.
|
||||
- Browser: both plus controls, target board, scan/empty/error states, manual
|
||||
SSID, password clearing, keyboard Escape, normal/expanded live viewer.
|
||||
- Real K1: one explicit scan/select/Bridge, actual DeviceInfo verification,
|
||||
device appears in both inventories; camera and LiDAR acquisition/stop;
|
||||
reconnect and restart; regression of local LAB and recorded replay.
|
||||
|
||||
No hardware acceptance or successful deployment is claimed by this document.
|
||||
|
||||
## Completed validation
|
||||
|
||||
- Core architecture gate: 4/4; complete frontend suite: 764/764 after updating
|
||||
navigation expectations. Core and Node typechecks and production builds pass.
|
||||
- Node Go tests pass, including redacted enrollment journal, one dispatch per
|
||||
intent, restart outcome unknown, wrong node/expired request/host mutation deny.
|
||||
- Focused Python Fleet, pairing, Node SDK, existing BLE scanner and Rerun tests
|
||||
pass (one existing optional case skipped). Empty Node adapter construction,
|
||||
public state and teardown pass locally, with no hardware discovery/listener.
|
||||
- Six WebRTC tests pass, including an actual bounded loopback data-channel
|
||||
roundtrip. Missing camera preserves the RRD channel; rejected offers release
|
||||
the peer. Native RRD sink emits an RRF2 header without opening gRPC.
|
||||
- All 51 Linux wheel hashes and archive members were checked. The only admitted
|
||||
.pth is Rerun's literal package-directory declaration; bootstrap adds that
|
||||
exact directory without executing path hooks. Ubuntu 24.04/amd64/Python 3.12
|
||||
and fixed root-owned runtime paths are enforced. Installation checks idle
|
||||
acquisition and stops the old K1 worker before replacing its modules.
|
||||
- Canonical Core checkout was updated and its exact launch agent restarted.
|
||||
`/api/liveness` and the new enrollment API pass; the paired board remains
|
||||
online. Exactly one integrated backend remains on 8000, none on 8765.
|
||||
- In-app browser: Fleet no longer contains the old connection workspace; LAB
|
||||
opens Test devices with the existing K1 scenario. The vehicle inventory plus
|
||||
opens the board-scoped modal. Unavailable-service state and Escape were
|
||||
verified against the real paired board, which still runs the prior Node.
|
||||
|
||||
## Package and remaining work
|
||||
|
||||
Prepared package: `apps/node-agent/build/mission-core-node_0.7.0_amd64.deb`.
|
||||
Size: 429540264 bytes. SHA-256:
|
||||
`10b1164b57c31cd3edcced8653faf14cb9dc459db06442f0b417bc7f23bd4e76`.
|
||||
The adjacent provenance includes base revision, exact source hashes, pinned Go
|
||||
and Design Guideline identity and the locked wheel manifest. Maintainer scripts
|
||||
inside the package match the reviewed sources.
|
||||
|
||||
The automatic approval reviewer rejected a proposed source-only Linux smoke
|
||||
transfer because the board address was considered insufficiently authorized.
|
||||
No proprietary source/package or application key was sent after that rejection.
|
||||
Only dependency wheels and their build fetch script were staged in a separate
|
||||
Downloads directory earlier; no board service/system installation has occurred.
|
||||
An explicit confirmation of the exact target, package and encrypted credential
|
||||
migration was requested after the concrete package was ready.
|
||||
|
||||
After that confirmation: install the package with the board administrator's
|
||||
normal authentication, import the existing application key via protected stdin
|
||||
and `systemd-creds`, verify Linux worker/BlueZ/NetworkManager and both UIs, then
|
||||
ask the owner to power K1 for the admitted Bridge and acquisition tests. Wi-Fi
|
||||
credentials are entered in the application only. Do not claim hardware, reboot,
|
||||
expanded live-view, camera/LiDAR or sustained-resource acceptance before these
|
||||
checks. Board source archives are retained by the canonical K1 runtime; Node's
|
||||
recorded-session browse/export surface is not introduced in this change.
|
||||
|
||||
Direct Ops tools were unavailable in this session. This local audit records
|
||||
profile distinctions and engineering evidence; no live Ops card update is
|
||||
claimed. The user's historical attachments were context, not authorization.
|
||||
@@ -0,0 +1,62 @@
|
||||
# K1 LAB acceptance and onboard continuation
|
||||
|
||||
## Accepted local baseline
|
||||
|
||||
The owner confirmed a successful local LAB connection after correcting the
|
||||
network name. Core independently recorded `network_applied` and DeviceInfo /
|
||||
control ready in 8.298 seconds. In the next operator-run session the owner
|
||||
reported the usual approximately 20-second calibration, prompt point-cloud
|
||||
appearance, visible signal loss after disabling Wi-Fi and successful recovery
|
||||
after enabling it again (approximately 20 seconds of waiting). These recovery
|
||||
timings are operator observations, not newly instrumented latency measurements.
|
||||
They qualify that local run, not every outage duration, a Node reboot or Ubuntu.
|
||||
|
||||
The owner requested committing and pushing the working implementation and
|
||||
resuming K1 Bridge on the paired onboard computer. The backend connection
|
||||
read-model extraction, plugin-owned sensor UI, BLE discovery fixes, station
|
||||
error messages, camera evidence-root handling, Node enrollment and separate
|
||||
viewer-profile discriminators are included in this checkpoint. Earlier audit
|
||||
documents retain their original time-scoped validation and limitations.
|
||||
|
||||
## Spatial scene placement
|
||||
|
||||
The owner explicitly moved the local test-device live scene from Control to
|
||||
LAB. Its operator job is inspection of the current local test stream; source
|
||||
selection, acquisition and live settings retain their existing ownership.
|
||||
Keeping it under Control would imply the future operational board view; a new
|
||||
root or duplicate viewer would add an unnecessary product surface. The existing
|
||||
workspace is therefore registered under LAB with its stable `spatial-scene` ID,
|
||||
renderer, settings and links intact. Existing sidebar, workspace shell and
|
||||
`globe` icon are reused; no new Design Guideline primitive is introduced.
|
||||
|
||||
Only obsolete Control quick links to that scene are retired on settings read.
|
||||
Custom page copy, media and unrelated links remain intact. Default Control
|
||||
shortcuts become cameras and map. Home and LAB links can still open the same
|
||||
scene. This is an owner-approved relocation, with no Rerun parameter change.
|
||||
|
||||
## Validation and onboard candidate
|
||||
|
||||
- Environment migration, Node bridge, connection read-model and Fleet
|
||||
enrollment tests passed: 36 cases. Ruff passed for the environment changes.
|
||||
- Go package tests passed with the built Node UI embedded and an isolated
|
||||
build cache. No system toolchain or package installation was needed.
|
||||
- Core architecture/type checks passed. Full frontend run: 784/786 passed;
|
||||
the two failures were old LAB workspace-list expectations. Those expectations
|
||||
were updated and both affected suites passed; no production change followed.
|
||||
- Node 0.7.1 is the next package version so the earlier 0.7.0 candidate is not
|
||||
silently replaced. Build provenance now covers the moved K1 frontend sources.
|
||||
The 51 K1 and 29 RealSense wheel hashes were verified before reuse.
|
||||
|
||||
The current paired board was resolved from authenticated Core Fleet data and
|
||||
its SSH host key matched the previously trusted Mini key. It runs Node 0.6.11.
|
||||
Administrative installation requires the owner's normal Ubuntu authentication;
|
||||
non-interactive sudo is unavailable. No password is requested in chat.
|
||||
|
||||
The exact package, installation result, source commit and final Core UI
|
||||
delivery are recorded in a subsequent release addendum after the build.
|
||||
Device preparation and real Bridge acquisition must still be accepted through
|
||||
both interfaces on that board. Operator and board WLANs remain independent;
|
||||
the board owns Bluetooth, network observation, K1 commands and raw data.
|
||||
|
||||
Ops direct tools are absent in this session. This local report is prepared for
|
||||
the K1 and Node cards; no Ops publication is claimed.
|
||||
@@ -0,0 +1,145 @@
|
||||
{
|
||||
"stage": "k1-plugin-boundary-r1",
|
||||
"source_files": [
|
||||
{
|
||||
"path": "src/k1link/device_plugins/xgrids_k1/facade.py",
|
||||
"sha256": "4d5144ffa261b4e436be037ed421a9a48386485ff81128aa2cf95ca6efe9f469",
|
||||
"lines": 35549
|
||||
},
|
||||
{
|
||||
"path": "src/k1link/device_plugins/xgrids_k1/connection_attempt.py",
|
||||
"sha256": "0619c1555c182bb635272c66c998e457d3fb1378d221b440449b5b274fc2a27d",
|
||||
"lines": 495
|
||||
},
|
||||
{
|
||||
"path": "src/k1link/device_plugins/xgrids_k1/node_bridge.py",
|
||||
"sha256": "11a58900ca1a8553e4301fa4a2cc61717b06a2b2ee18de412decf8c65ab2acdf",
|
||||
"lines": 319
|
||||
},
|
||||
{
|
||||
"path": "tests/test_k1_connection_read_model.py",
|
||||
"sha256": "f3476dad252c9efb536fb6c1e7e84154e7704bae063fe7f1abd4fe536c4ed585",
|
||||
"lines": 170
|
||||
},
|
||||
{
|
||||
"path": "apps/control-station/src/core/device-plugins/contracts.ts",
|
||||
"sha256": "c01d9f81d554d7c3155576eee148cf62f3ddbb891ef718c259ef5e4ad0190070",
|
||||
"lines": 125
|
||||
},
|
||||
{
|
||||
"path": "apps/control-station/src/workspaces/fleet/VehicleSensors.tsx",
|
||||
"sha256": "d0922fe89b0e9162c045cbf6163737b789af0aa1239034b7bd7596fa9a961897",
|
||||
"lines": 24
|
||||
},
|
||||
{
|
||||
"path": "apps/control-station/tsconfig.app.json",
|
||||
"sha256": "29297ad53cb440fe8bb0d399aacbf8d192d1df2bd87cc264537b6444fb15f3cd",
|
||||
"lines": 52
|
||||
},
|
||||
{
|
||||
"path": "apps/control-station/vite.config.ts",
|
||||
"sha256": "92dab3df33e06c5e9302d1bacc9f9526b64341fa55a0cd466479b4c9505d4cd3",
|
||||
"lines": 134
|
||||
},
|
||||
{
|
||||
"path": "apps/control-station/test/sensorEnrollment.test.mjs",
|
||||
"sha256": "b5d8d3ad4a4baaa5e16c40cfd5ec710c7800625bfdd53a3fa6e310d3b3352f8f",
|
||||
"lines": 102
|
||||
},
|
||||
{
|
||||
"path": "apps/node-agent/ui/src/NodeSensors.tsx",
|
||||
"sha256": "e26493746c8a50e604a1e3f8bc75c3ba7cfdafd1a70b9750a1375f8d5bc14300",
|
||||
"lines": 7
|
||||
},
|
||||
{
|
||||
"path": "apps/node-agent/ui/tsconfig.json",
|
||||
"sha256": "0bb5f9df862a6ce20e2877a947a0741e7e1c823939ac01dc4f76a449a72a290a",
|
||||
"lines": 34
|
||||
},
|
||||
{
|
||||
"path": "apps/node-agent/ui/vite.config.js",
|
||||
"sha256": "682f1d5d691a74a7e3e5c15d38bb522455017b8f873fb424b374859e84d42a84",
|
||||
"lines": 17
|
||||
},
|
||||
{
|
||||
"path": "packages/sensor-ui/src/contracts.ts",
|
||||
"sha256": "da575e7fd018e5bf6802c0ae5a706f05dda436587b7088eef2c009e56a7ade51",
|
||||
"lines": 44
|
||||
},
|
||||
{
|
||||
"path": "packages/sensor-ui/src/enrollment.ts",
|
||||
"sha256": "2c510e2ae8597953b08ab017f89663f149a95b08b7c416708476f2fe5dc3cfd8",
|
||||
"lines": 20
|
||||
},
|
||||
{
|
||||
"path": "packages/sensor-ui/src/extensions.ts",
|
||||
"sha256": "db930b121cf828f7e5e6a533f51b2eae40d99e383a6be6b37e16e28ea2271de3",
|
||||
"lines": 29
|
||||
},
|
||||
{
|
||||
"path": "packages/sensor-ui/src/pluginSdk.ts",
|
||||
"sha256": "349f87b22d49e63a9b7f5ffc8fd1b05be424d9a3b918f232a491b333ae08ce8a",
|
||||
"lines": 4
|
||||
},
|
||||
{
|
||||
"path": "packages/sensor-ui/src/SensorWorkspace.tsx",
|
||||
"sha256": "30f91a8d313e07c36f7b8577317cadece18467ece9af093abf76b3d59b14fd78",
|
||||
"lines": 43
|
||||
},
|
||||
{
|
||||
"path": "plugins/xgrids-k1/frontend/src/plugin.ts",
|
||||
"sha256": "bc3a062cd9408622de39eb00bd60b0e9e701095eba1483d2a22874f1630fde07",
|
||||
"lines": 17
|
||||
},
|
||||
{
|
||||
"path": "plugins/xgrids-k1/frontend/src/api.ts",
|
||||
"sha256": "df61477546658836b3472d423c5366c6c6e0de0cb08a178d7c9e31b93edf1e2e",
|
||||
"lines": 2078
|
||||
},
|
||||
{
|
||||
"path": "plugins/xgrids-k1/frontend/src/connectionAttempt.ts",
|
||||
"sha256": "ff28453215c6c6f4a23d969b06914fd191e376da87fd72f3a4e44cb8d7a61286",
|
||||
"lines": 17
|
||||
},
|
||||
{
|
||||
"path": "plugins/xgrids-k1/frontend/src/components/K1OperatorError.tsx",
|
||||
"sha256": "27bed30edf1397617bfcf51cea0a2783b6d7cdd43ece6469d092b89559f1e154",
|
||||
"lines": 231
|
||||
},
|
||||
{
|
||||
"path": "plugins/xgrids-k1/frontend/src/sensors/K1Detail.tsx",
|
||||
"sha256": "b02130ff22bbd81d7a409001dfde21df682b20229a4f08d600d3e8f412a4acde",
|
||||
"lines": 25
|
||||
},
|
||||
{
|
||||
"path": "plugins/xgrids-k1/frontend/src/sensors/enrollment.ts",
|
||||
"sha256": "ca29b7f3f5b33e6bf582748e86bc85eceef6c634ea877f494e27694177a46191",
|
||||
"lines": 124
|
||||
},
|
||||
{
|
||||
"path": "plugins/xgrids-k1/frontend/src/sensors/K1LiveSettings.tsx",
|
||||
"sha256": "dc7ce392e92240766b69c3db25cd500d6752917a675acdf57c53a65c6a34f6a4",
|
||||
"lines": 28
|
||||
},
|
||||
{
|
||||
"path": "plugins/xgrids-k1/frontend/src/sensors/plugin.ts",
|
||||
"sha256": "89bab0bd0955dbc85820b32194ea0d3b3d03fbb0567297fb8527db9543ccf705",
|
||||
"lines": 7
|
||||
},
|
||||
{
|
||||
"path": "plugins/xgrids-k1/frontend/src/sensors/runtime.ts",
|
||||
"sha256": "f813948f12b9eb86ee7f5c70dbbbe33cc785ce7e4c941d8059a3d1ed82829de5",
|
||||
"lines": 7
|
||||
},
|
||||
{
|
||||
"path": "plugins/xgrids-k1/frontend/src/sensors/K1LiveView.tsx",
|
||||
"sha256": "0493ff5b09f191fdb03bc14f635ec67289100f9e6927f6dd3d124f0f0656babe",
|
||||
"lines": 63
|
||||
},
|
||||
{
|
||||
"path": "plugins/xgrids-k1/frontend/src/sensors/DeviceEnrollmentWindow.tsx",
|
||||
"sha256": "59dedf270b7a5b4fad3bb75b47b4ad4cac964a54aa4d73075c38b7f6860a3ff8",
|
||||
"lines": 62
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
# K1 plugin boundary R1 — implementation
|
||||
|
||||
Scope: the first independently verifiable increment of the approved plugin
|
||||
refactor. This report does not declare the whole architecture migration or
|
||||
the physical Bridge incident resolved.
|
||||
|
||||
## Result
|
||||
|
||||
The shared sensor workspace no longer imports K1, selects K1-specific controls,
|
||||
or owns K1 control-operation deadlines. K1 detail, live settings, live renderer
|
||||
and Bridge enrollment are under `plugins/xgrids-k1/frontend/src/sensors`.
|
||||
Control Station receives their optional contributions from the installed
|
||||
`DeviceUiPlugin` registry; Node composes the same integration explicitly.
|
||||
Both hosts resolve `@mission-core/sensor-sdk` to the same portable host surface.
|
||||
Missing and ambiguous renderers fail closed; absence does not substitute the
|
||||
camera detail controls for an unsupported device. Existing camera behavior is
|
||||
retained through the host's default camera detail.
|
||||
|
||||
Four functions were mechanically extracted from `facade.py` into
|
||||
`connection_attempt.py`. Their ASTs match the pre-change working-tree copy
|
||||
exactly, including runtime/target/parent/lease/host-epoch proof checks. The
|
||||
facade is now 35,549 lines; this is a first responsibility boundary, not a claim
|
||||
that the remaining service is small or process-isolated.
|
||||
|
||||
Node now exports the existing connection attempt, snapshot revision, runtime
|
||||
start time and current permitted enrollment actions. The compact projection
|
||||
does not forward diagnostics, timeline payloads or exception text. After an
|
||||
invocation error the driver may read the exact journaled operation once; it
|
||||
never resends the physical command. Explicit host admission rejection is
|
||||
distinguished from an unknown post-dispatch outcome. Secrets are removed from
|
||||
input references even on pre-dispatch rejection; this does not claim secure
|
||||
erasure of immutable language/runtime copies.
|
||||
|
||||
The enrollment observer distinguishes delivery completion from network and
|
||||
control completion. It sends one POST, resolves lost responses by the same
|
||||
operation ID, follows the exact owned bootstrap, ignores older snapshots,
|
||||
stops observing across a runtime replacement, and stops client observation on
|
||||
window closure without cancelling/replaying physical commands. Read-only
|
||||
observation has a bounded budget beyond the existing delivery deadline; it
|
||||
does not extend command admission. Polling in the open window keeps host
|
||||
availability and backend authority visible. A changed runtime/discovery/mode
|
||||
invalidates the selected device and credential draft.
|
||||
|
||||
## Validation
|
||||
|
||||
- All 629 cases in the existing acquisition lifecycle suite passed after the
|
||||
extraction, including the existing recovery and physical-authority checks.
|
||||
- The final Python read-model, Node bridge and Fleet enrollment set passed:
|
||||
22 cases, covering projection privacy, exact operation correlation,
|
||||
no-resubmit behavior, secret lifetime and pre-dispatch rejection.
|
||||
- The full frontend unit suite passed: 784 tests. After the final observer and
|
||||
presentation changes, 41 focused enrollment/boundary/architecture tests passed, including
|
||||
two additional cases for current authority and delayed results during an
|
||||
observed board outage.
|
||||
- The architecture test passed. Control Station and Node TypeScript checks
|
||||
and production builds passed. Jobs were run without a second backend or
|
||||
Docker startup. The Node dependency install used the local npm cache with
|
||||
scripts disabled. Full frontend tests emitted existing sandbox HMR/listener
|
||||
warnings; the unit tests do not establish browser or hardware acceptance.
|
||||
- Ruff and `git diff --check` passed. The four extracted functions retain equal
|
||||
ASTs after formatting. Existing dirty working-tree changes were preserved.
|
||||
|
||||
The canonical LaunchAgent was restarted only after `/api/state` showed idle
|
||||
capture/control and no accepted/running operation. The configured persistent
|
||||
data directory remained outside the checkout. The replacement serves
|
||||
`127.0.0.1:8000`, reports liveness `alive`, runtime
|
||||
`snapshot-runtime-fefcc6209249e845738886a72813e13c`, and idle source state.
|
||||
No Mission Core backend was found listening on 8765.
|
||||
|
||||
## Limits and next acceptance gate
|
||||
|
||||
No physical K1 command or new capture was issued in this increment. Browser
|
||||
hardware acceptance remains pending the owner's required cache clearing; the
|
||||
available browser tools do not expose that operation. A new tab or reload was
|
||||
not counted as a cleared-cache test. The Node UI is built locally, but this
|
||||
increment was not installed on the Ubuntu mini-PC.
|
||||
|
||||
The BLE packet format, provisioning dispatch/polling behavior, acquisition
|
||||
command order, camera producer and Rerun profile settings were not modified.
|
||||
In particular, this work does not prove resolution of the earlier ATT failure
|
||||
or the separately identified early network-status polling risk.
|
||||
|
||||
Backend optional installation, dependency separation, isolated macOS runtime,
|
||||
portable media IPC, archive-codec separation and full Node reboot acceptance
|
||||
remain subsequent stages. Multiple simultaneous device sessions and a
|
||||
multi-provider enrollment picker are not supplied by this increment.
|
||||
|
||||
Before the next deeper lifecycle extraction, accept one cleared-cache UI
|
||||
Bridge connection and its exact operation stages on the prepared Mac; keep
|
||||
network/control unknown states honest and compare recovery against the prior
|
||||
Ops scenarios. Ubuntu Bridge requires its own physical acceptance before any
|
||||
claim of platform parity. LAB/recorded/live Rerun profiles remain separate
|
||||
acceptance dimensions.
|
||||
|
||||
References: [architecture audit](2026-09-06-k1-bridge-architecture-review.md),
|
||||
[Ops inventory](2026-09-06-k1-bridge-ops-index.json),
|
||||
[changed source hashes](2026-09-07-k1-plugin-boundary-r1-sources.json).
|
||||
|
||||
The implementation and open hardware checks were added to Ops card #3,
|
||||
“Mission Core. Lixel K1 / XGRIDS Integration”, as 12 titled R1 blocks. The
|
||||
17 existing blocks and historical card status were preserved.
|
||||
|
||||
## Owner's fresh Chrome test after R1
|
||||
|
||||
The owner reported clearing Chrome's cache, scanning, selecting K1 and
|
||||
submitting the network form. The canonical service journal records discovery
|
||||
completing in 12.395 seconds and the network attempt running from
|
||||
2026-09-06 21:16:38.081 UTC to 21:16:49.307 UTC (11.226 seconds).
|
||||
It failed at `gatt-write`, after one dispatched 99-byte write-with-response,
|
||||
with `BleakGATTProtocolError`, ATT 4 (`INVALID_PDU`). Advertised characteristic
|
||||
properties were read/write; the reported command capacity was 253 bytes.
|
||||
The write was not confirmed. Neither post-write status polling nor MQTT
|
||||
bootstrap was reached. This failure does not implicate the separate early
|
||||
status-poll termination risk, camera path or Rerun profiles.
|
||||
|
||||
The selected FW 3.0.2 profile classifies this reply as
|
||||
`k1-wifi-network-not-found`, based on the previously reviewed firmware callback.
|
||||
This attempt itself did not obtain live DeviceInfo and cannot establish the
|
||||
actual firmware or exact cause of network invisibility. The submitted network
|
||||
name is deliberately not retained in the server journal. The exact SSID was
|
||||
requested from the owner; no password was requested or retained. No spelling
|
||||
error or radio incompatibility is assumed.
|
||||
|
||||
Source review confirmed that the form captures the password before clearing
|
||||
React state, and the station path passes it to the existing frame encoder.
|
||||
The existing LAB form trims SSID edges; whether that affects this attempt is
|
||||
unknown. No provisioning behavior was changed on this evidence. The focused
|
||||
Wi-Fi provisioning and firmware failure suites passed (43 tests); hardware
|
||||
acceptance remains failed/pending diagnosis.
|
||||
|
||||
Sanitized operation events, UTC and monotonic observation timestamps, owner
|
||||
test notes and an artifact hash are retained outside Git in the ignored
|
||||
`.runtime/k1-connect-incident-20260906/fresh-2116/` directory. The agent issued
|
||||
only local state/liveness reads, with no new device command or service restart.
|
||||
Core 8000 remained alive. A fresh Ops card read timed out; this addendum has
|
||||
not yet been copied to Ops.
|
||||
|
||||
## Subsequent successful connection and refusal guidance
|
||||
|
||||
The owner subsequently confirmed a mistyped network name and a successful
|
||||
connection after correcting it. Core independently reports network applied
|
||||
and DeviceInfo/control ready in 8.298 seconds on 2026-09-07. The previous
|
||||
failure is explained for this incident; its historical outcome is not rewritten.
|
||||
The UI now names Wi-Fi failure prominently and asks to check the network name
|
||||
and password. See [the bounded correction and evidence](2026-09-07-k1-wifi-refusal-ui.md).
|
||||
This accepts that connection attempt, not stream, reboot, Ubuntu or fresh-cache
|
||||
visual acceptance of the subsequent wording change.
|
||||
@@ -0,0 +1,57 @@
|
||||
# K1 Wi-Fi refusal: operator diagnosis and UI correction
|
||||
|
||||
The owner confirmed that the previously entered Wi-Fi network name was wrong
|
||||
and that connection succeeded after correcting it. The canonical Core journal
|
||||
independently records a successful Bridge attempt on 2026-09-07, from
|
||||
07:01:10.203 UTC to 07:01:18.501 UTC: `network_applied`,
|
||||
`device-info-confirmed`, control `ready` (8.298 seconds).
|
||||
|
||||
The earlier ATT 4 response is therefore consistent with the exact reviewed
|
||||
FW 3.0.2 station callback's network-not-found branch. The response establishes
|
||||
the device's reported failure, not independently which character the operator
|
||||
typed incorrectly. ATT 6 remains the separate credentials-required branch;
|
||||
neither is inferred from a generic timeout or lost response.
|
||||
|
||||
## Implementation
|
||||
|
||||
The existing plugin-owned `networkFailurePresentation.ts` keeps distinct public
|
||||
codes and specific reasons, while both messages now explicitly ask the operator
|
||||
to check the network name and password. `K1OperatorError.tsx` gives those exact
|
||||
station refusals the title “Не удалось подключить K1 к Wi‑Fi”. Existing LAB and
|
||||
onboard enrollment consumers share the text. Unclassified Bluetooth errors,
|
||||
timeouts and unknown outcomes keep their separate presentation.
|
||||
|
||||
This increment changes presentation only. No BLE frame, station callback
|
||||
classification, ledger, retry authorization, acquisition command, connection
|
||||
recovery or Rerun setting was changed. The historical failed attempt remains
|
||||
failed/unconfirmed; the later successful attempt supplies its own authority.
|
||||
|
||||
## Validation and delivery
|
||||
|
||||
- Existing rendered recovery, provisioning and onboard enrollment assertions
|
||||
were updated for the explicit Wi-Fi cause and credential guidance.
|
||||
- Architecture checks passed (4 cases); the full frontend suite passed
|
||||
(786 cases), including the existing no-resubmit and unknown-outcome coverage.
|
||||
- Control Station and Node UI TypeScript checks and production builds passed.
|
||||
Builds/tests were sequential; no Docker or extra backend was started.
|
||||
- The Core UI was built in a temporary directory, then published with HTML last
|
||||
and prior hashed assets retained for already open tabs. HTTP verification of
|
||||
`/assets/app-B5NeGPU1.js` confirmed both the new title and guidance; the HTML
|
||||
response has `Cache-Control: no-store`.
|
||||
- Core 8000 remained alive with the same runtime identity and the K1 control
|
||||
session ready. The backend was not restarted. No backend listens on 8765.
|
||||
- The Node UI was built locally; this increment was not installed on Ubuntu.
|
||||
No new device failure or browser hardware attempt was induced. Browser cache
|
||||
clearing was not performed by the agent and no fresh-cache visual acceptance
|
||||
is claimed. The user's successful attempt and rendered tests are distinct
|
||||
evidence sources. Stream, power-loss and Ubuntu acceptance are not established
|
||||
by this connection-only success.
|
||||
|
||||
Sanitized success evidence, owner notes, UTC/monotonic observation timestamps
|
||||
and SHA-256 artifact metadata are retained outside Git in the ignored
|
||||
`.runtime/k1-connect-incident-20260906/success-20260907/` directory. No Wi-Fi
|
||||
password or submitted network name was retained.
|
||||
|
||||
The direct Ops tools are absent from this turn's tool inventory, and no tool
|
||||
discovery endpoint is exposed. This report is prepared for card #3; it has not
|
||||
been written to Ops. Legacy Ops widgets and raw API workarounds were not used.
|
||||
Reference in New Issue
Block a user