54 KiB
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.
Глубокая смысловая сверка выполнена для сетевого/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.
Обнаруженные расхождения источников
- Manifest содержит 77 сценариев: 51
software-covered, 17partial, 9planned. Это значения файла, не результат новых тестов. Например, переключение Bridge/Quick и sleep/wake ещё отмечены как требующие hardware evidence, хотя более поздняя #3 описывает физическую приёмку. - #10 пишет, что MQTT reconnect отсутствует; #3 и текущий recovery-код содержат ограниченное восстановление активного потока. Историческое ограничение нельзя применять как описание текущего пути.
- #49 хранит старый статус незакрытой Rerun-приёмки; более поздняя #3 закрывает конкретный принятый beta-профиль. Приёмку следует связывать с датой, commit, платформой и точным сценарием.
- В #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. Эталон должен учитывать исправление. - Документ supervision описывает фиксированный шестисекундный scan; текущий scanner использует одно непрерывное окно 6→20 с при отсутствии K1. Это документальный drift после сегодняшнего изменения.
- В документах есть разная формулировка browser close: завершение operator session и сохранение backend-owned capture. Это разные владельцы. Контракт CONN-62 и код не разрешают WebSocket disconnect инициировать scanner-команду. Для рефакторинга нужно явно зафиксировать отдельно draft, UI connection, lease и acquisition.
4. Bridge по слоям: нормальная последовательность
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, 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. Обязательные границы будущего рефакторинга
Это предложение для последующего решения, не начатая реализация.
- Сохранить текущий рабочий snapshot и сопоставить каждый обязательный scenario с точным existing test, physical run и платформой. Отдельно перечислить Node-only проверки. Исторические версии Ops не переписывать как новую приёмку.
- Выделить один typed outcome:
not_dispatched,network_applied,control_ready,outcome_unknown, конкретный отказ; не выводить результат из HTTP-кода. Во всех оболочках сохранять operation/runtime/target correlation и допустимое действие. - Разделить normal Bridge provisioning и recovery orchestration. Нормальный путь может быть коротким; recovery обязан сохранять explicit target, physical ledger и ownership. Quick остаётся отдельной принятой strategy; его не переносить на борт и не удалять из LAB.
- Разделить state projection и reconciliation owner, только после фиксации существующего порядка переходов и lock ownership. Переносить по одной обязанности, без одновременного изменения protocol, camera и viewer.
- Упростить UI на основе общей семантики результата. Компактность достигается уменьшением повторной интерпретации, а не скрытием unknown или заменой всех отказов словом «подключение».
- После каждого узкого изменения — соответствующая автоматическая регрессия, затем отдельный физический UI-сценарий. По указанию владельца перед каждым аппаратным/UI-прогоном очистить cache; обычный reload не засчитывать. Сейчас очищенный прогон не выполнен: browser tool не предоставляет доступную очистку.
- Производительность измерять на одном и том же сценарии и 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: все 76 карточек, 41 K1 label, 128 полученных активных комментариев; основные semantic источники указаны выше.
- Hashes текущего кода: точные bytes критических модулей и test-файлов на момент чтения, без proprietary evidence или credentials.
- LixelGO IP protocol observation, Wi-Fi profile.
- Connection supervision canon, acceptance manifest, recovery runbook, physical recovery ADR.
- Bridge incident, station reply semantics, live camera root, Node Bridge implementation and open gates.
Чтение 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 явно включает K1 в сборку. Это правильное место выбора поставляемых модулей, но сейчас выбор статический; независимо устанавливаемый frontend этим не доказан.
- Backend composition допускает только
transitional-in-process. ADR 0011 прямо исключает из текущих гарантий crash containment, supervisor, portable media IPC и установку пакетов. Наличие runtime transport не означает, что отдельный процесс уже работает. - Общий pyproject включает BLE/MQTT-зависимости и K1 CLI. Общий Python wheel содержит весь
src/k1link. Поэтому независимое удаление драйвера пока нельзя считать принятой возможностью. - SensorWorkspace напрямую импортирует
K1Detailи выбирает его поdevice.kind === 'k1'. Это конкретная зависимость общей поверхности от устройства; её место — регистрация визуального расширения интеграцией. - Анализ Python import graph обнаружил 14 модулей
compute, прямо импортирующих K1 analysis/protocol/replay. Это главным образом работа с данными, а не управление радио. Их нельзя механически удалять вместе с драйвером: нужны отдельные границы чтения архивов и проверка LAB. - NodeBridge уже использует тот же 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.
Последовательность и критерии завершения
- Закрепить существующие сценарии и одинаковый контракт результата/событий для LAB и Node. Получение HTTP-ответа и физическое завершение операции остаются разными событиями.
- Выделить внутри K1 provisioning, наблюдение/recovery и проекцию состояния, сохранив durable dispatch boundary, владельца операции, блокировки и порядок команд. Не пересобирать одновременно acquisition, камеру и Rerun.
- Устранить прямые зависимости общих компонентов, разделить поставку драйвера и чтение записей. Проверить запуск Core без установленного K1 и работу других устройств и доступных архивов.
- Реализовать процессную границу за существующим transport с явными контрактами событий/media. Проверить зависание и падение плагина: Core остаётся доступным, состояние устройства честно меняется, другая запись не прерывается. Перезапуск runtime не переотправляет provisioning, START или STOP автоматически; сначала восстанавливаются журнал и наблюдаемое состояние.
- Принять macOS, затем Ubuntu Bridge на том же domain-коде. Перенос считается доказанным, когда меняются адаптеры и упаковка, а не K1-логика или Core. Допустимо начать с явного состава сборки; динамическая установка UI, магазин плагинов и hot reload не нужны для первой проверки границы.
Отдельные обязательные критерии: восстановление связи после потери сети, безопасное обнаружение рестарта устройства и БК, отсутствие повторной физической команды, отсутствие регрессии LAB/recorded/live профилей Rerun. Каждый аппаратный прогон выполняется через UI с предварительной очисткой браузерного кэша по указанию владельца. В этом дополнении выполнены только чтение кода и документирование; проверок на устройстве не было.