chore(k1): checkpoint connection recovery work

This commit is contained in:
DCCONSTRUCTIONS
2026-08-21 08:12:22 +03:00
parent a3138f3d71
commit c7843a3c7e
22 changed files with 969 additions and 293 deletions
+21 -12
View File
@@ -1,6 +1,6 @@
# K1 connection supervision canon
Status: canonical target and product acceptance contract, updated 2026-08-11;
Status: canonical target and product acceptance contract, updated 2026-08-20;
implementation/hardware conformance remains tracked by the acceptance manifest.
Operator recovery procedure: [`runbooks/K1_CONNECTION_RECOVERY.md`](runbooks/K1_CONNECTION_RECOVERY.md).
@@ -16,10 +16,12 @@ point packet means that the device is connected.
The product goal is a simple operator-owned lifecycle: every app start,
disconnect, explicit stop, or committed network-mode transition ends the old
connection session. A normal connection has one discovery intent and one
application intent: the operator explicitly searches, selects one fresh result,
enters the network data immediately, and presses **Применить** once. Opening the
UI, selecting a result and editing credentials are presentation inputs only.
live connection authority. The last exactly confirmed K1 and topology remain a
durable read-only fast-reconnect target until the operator changes the device or
network. A normal new connection has one discovery intent and one application
intent: the operator explicitly searches, selects one fresh result, enters the
network data immediately, and presses **Применить** once. Opening the UI,
selecting a result and editing credentials are presentation inputs only.
Changing mode is one explicit local scenario-reset operation; it performs no
device or host I/O and starts no discovery. Historical attempts are audit
evidence only. They never
@@ -109,9 +111,14 @@ terminology, physical-ledger state and protocol recovery names are not operator
ceremonies.
1. A cold plugin section shows the connection-mode selector and Step 01
**Подключение** with its explicit Bluetooth search action. Historical K1
identity never adds a reconnect prompt on cold entry. Opening or polling the
surface performs no action.
**Подключение**. When an exact last confirmed K1 and unchanged topology are
available, it shows the saved target with **Переподключиться** plus the
separate **Найти по Bluetooth** path. The reconnect action performs only a
server-pinned read-only observation; it sends no network settings, START or
STOP. A browser-cache reset or local scenario reset closes live authority but
does not erase this durable offer. Audit-only historical identities do not
create it. Without an eligible durable target, Step 01 shows only the explicit
Bluetooth search action. Opening or polling the surface performs no action.
Each selector/escape gesture sends one explicit CAS-fenced
`reset_scenario`; it may seal only old local receiver/camera/control owners
and retire their audit lineage, but performs no BLE, device/host Wi-Fi,
@@ -173,9 +180,11 @@ reopened record from fresh DeviceInfo plus non-retained DeviceStatus: READY
settles standby, while identity-bound initialized SCANNING settles active without replaying
START and materializes only a STOP-only control shell. A reset-owned settlement
never restarts the retired receiver, camera, evidence writer or acquisition.
Exact operator-facing physical recovery keeps its backend CAS and
transport pinning, but appears only inside an established session after an
actual interruption, never in cold entry or Bluetooth results.
Exact operator-facing physical recovery keeps its backend CAS and transport
pinning and remains distinct from cold fast reconnect. Fast reconnect only
re-proves an unchanged saved target; unresolved physical START/STOP state still
uses the stricter recovery path. Neither action appears inside Bluetooth result
rows.
Outside that reset-owned STOP-only settlement, fresh non-retained READY proof
resolves physical ambiguity as standby; fresh exact same-project SCANNING resolves it as active and exposes only the
@@ -248,7 +257,7 @@ not fabricate the state of another row.
| Boundary that can fail | Typical real scenario | Required response |
| --- | --- | --- |
| Browser ↔ local API | app refresh/restart, closed tab, suspended renderer, lost HTTP response | App restart/close ends its operator connection session without sending a device command; a single lost response never repeats an action. The next app session starts with discovery and selection. |
| Browser ↔ local API | app refresh/restart, closed tab, suspended renderer, lost HTTP response | App restart/close ends live operator authority without sending a device command; a single lost response never repeats an action. When an exact last confirmed K1/topology remains durable, the next app session offers an explicit read-only fast reconnect alongside fresh discovery. Otherwise it starts with discovery and selection. |
| Mission Core process | crash, upgrade, second process, stale worker thread | Close the active connection session on restart; retain terminal audit only; stable process locks prevent a second controller; old runtime generations cannot publish evidence into a new session. |
| CoreBluetooth discovery/GATT | K1 powers off, Mac sleeps, disconnect callback arrives, scan callback arrives late | Close and clear the selected session on proven disconnect. A later operator scan and selection establish a new GATT connection. Unselected rows remain stable for the latest admitted scan generation and are invalidated only by an explicit successor/reset, runtime-owner teardown or proven exact-target GATT failure. A row never grants mutation authority without exact-handle capture and live GATT validation. |
| Mac Wi-Fi association | Quick AP is left manually, Bridge router changes, same IP is reused by another AP | Rotate host epoch using an opaque OS association identity in addition to route/interface/source IP. Rebuild TCP and protocol proof. |
+34 -21
View File
@@ -12,16 +12,19 @@ the wizard the two step names are exactly **Подключение** and **Се
### Cold entry
On a clean cold entry show only:
On a clean cold entry show:
- the connection-mode selector;
- Step 01 **Подключение** with the explicit Bluetooth search action.
- Step 01 **Подключение**;
- when an exact last confirmed K1 and unchanged topology are durable,
**Переподключиться** and the separate **Найти по Bluetooth** path;
- otherwise only the explicit Bluetooth search action.
Historical K1 identity never adds a reconnect choice to cold entry. If local
session ownership or an older connection scenario exists, one explicit
`reset_scenario` CAS first closes only that local scenario. Only after the reset
is accepted does clean Step 01 expose **Найти по Bluetooth** as a separate
click; reset never starts Scan itself.
**Переподключиться** is a read-only fast connection to the server-pinned last
K1. It does not send network settings, START or STOP. Historical audit identity
alone never creates this choice. A browser-cache reset or accepted
`reset_scenario` closes the old live/local authority but does not erase the
last confirmed target. Reset never starts reconnect or Scan itself.
Do not render Step 02 yet and do not start discovery automatically. The mode
selector and same-mode **Подключить новый K1** escape remain available through
@@ -42,12 +45,14 @@ the request owns the current action it reads **Сбрасываем подклю
does not dispatch a second reset until that bounded request settles. It remains
available to supersede any other local action. The accepted revision always
returns the new local scenario and even a dirty browser selector to canonical
**Bridge**, clears the old browser/backend presentation and source owners, and
leaves Scan as a separate click. It performs no hidden Scan, Verify, Connect,
**Bridge**, clears old browser drafts and source owners, and leaves both an
eligible durable fast reconnect and Scan as separate clicks. It performs no
hidden Scan, Verify, Connect,
START, STOP, BLE or network write and never substitutes a passive `state.read`
for the reset mutation. After settlement the product surface contains no prior
UUID, result count, **Повторить поиск**, reconnect error, selected device,
credentials or recovery card; it returns to **Найти по Bluetooth**. Late
for the reset mutation. After settlement the product surface contains no stale
result count, **Повторить поиск**, reconnect error, selected draft or
credentials. It retains only the exact durable fast-reconnect card when one is
eligible, alongside **Найти по Bluetooth**. Late
Scan/Verify settlements from the retired scenario cannot repopulate it.
The retained reset marker fences only work that belonged to the retired
scenario. A newly correlated post-reset network attempt that fails or has an
@@ -196,8 +201,10 @@ CoreBluetooth object and live GATT validation before any write.
The selected session ends on proven disconnect, explicit lifecycle stop, a
committed network transition, selection of another device, backend restart or
proven native cleanup. A later connection always requires an explicit search
and **Выбрать**. Polling can update presentation but starts neither operation.
proven native cleanup. A later new-device connection requires an explicit search
and **Выбрать**. An unchanged exact last confirmed K1 may instead use the
explicit read-only fast reconnect. Polling can update presentation but starts
neither operation.
Bridge, Quick Connect and Direct Connect are separate topologies. In
any state, changing the mode or choosing another K1 in the same mode sends one
@@ -260,7 +267,8 @@ fenced.
| Event | Product result | Operator path |
| --- | --- | --- |
| Cold entry | Mode plus Step 01 and explicit Scan; zero device I/O before Scan | Start search explicitly |
| Cold entry with an exact last confirmed K1/topology | Mode plus Step 01, read-only **Переподключиться**, and separate explicit Scan; zero device I/O before a click | Fast reconnect or start search explicitly |
| Cold entry without an eligible durable target | Mode plus Step 01 and explicit Scan; zero device I/O before Scan | Start search explicitly |
| Disconnected/idle mode or same-mode new-device request with unresolved durable physical history | Local session/audit lineage is retired under one reset CAS; zero device/host I/O and no automatic Scan | Start the clean Step 01 search explicitly; the old physical outcome remains auditable |
| Mode reset while live, reconnecting or terminal cleanup still owns local sources | Reset supersedes recovery and locally seals receiver/camera/control; previous K1 may still scan | Wait for the bounded local cleanup or retry the same reset if local sealing fails |
| Search running | Step 01 spinner and visible countdown | Wait or let the bounded search end |
@@ -280,7 +288,8 @@ fenced.
| A physical START/STOP edge is unresolved | Mutation stays fenced; no technical wizard ceremony | Search/select remains explicit; backend admits only a safe exact path |
| Exact actively retired UUID is present after committed reset and successor Scan | The row exposes the same enabled **Выбрать** as every candidate | Select locally; Apply remains exact-handle/live-GATT gated and may append one internal local settlement checkpoint before its sole write; audit remains append-only and START/STOP stay denied until fresh read-only classification |
| Another candidate is selected while old authority is unavailable and no reset-owned new scenario exists | Selection stays local and Apply remains denied | Start an explicit new connection scenario, then Scan and select again |
| Browser refresh or backend restart | No automatic operation and no restored live selection | Begin from the cold progressive wizard |
| Browser refresh or cache reset | No automatic operation and no restored live authority; exact durable last target remains available for read-only fast reconnect | Reconnect explicitly or begin a fresh Bluetooth search |
| Backend restart without an eligible durable target | No automatic operation and no restored live selection | Begin from the cold progressive wizard |
## Physical START/STOP safety remains separate
@@ -336,15 +345,19 @@ loader belongs to the explicit action that created it and ends with it.
Software tests do not replace a real K1/macOS/router run. Accept sequentially:
1. Open cold and prove mode plus Step 01 and its explicit Scan action are
visible, while Step 02 is absent and no discovery starts automatically. With both empty and
1. Open cold and prove mode plus Step 01 are visible, while Step 02 is absent
and no discovery starts automatically. With an exact last confirmed K1 and
unchanged topology, prove **Переподключиться** and **Найти по Bluetooth** are
both available after reload and browser-cache reset; reconnect performs only
read-only verification. With no eligible durable target, prove only explicit
Scan is available. With both empty and
unresolved durable physical history, change the mode and prove one local
reset CAS, zero device/host calls and no automatic Scan. Repeat from active,
reconnecting and terminal `cleanup_pending` states; prove local sources are
sealed, the old K1 is not claimed stopped, and a local cleanup failure leaves
the exact reset retryable. With an exact prior connection, prove cold entry
contains no historical reconnect prompt; after one reset CAS and zero Scan,
a separate clean **Найти по Bluetooth** action remains clean after reload.
the exact reset retryable. With an exact prior connection, prove one reset
CAS and zero Scan preserve its fast-reconnect card and a separate clean
**Найти по Bluetooth** action after reload.
2. Start discovery and prove the spinner and seconds countdown remain visible
for the bounded search, then the exact result count appears.
3. With multiple advertisements, prove every ordinary connectable row keeps