Skip to content

Commit 05d7092

Browse files
committed
[docs] Bind board identity to connection sessions
Signed-off-by: Viwat Vchirawongkwin <viwat.v@chula.ac.th>
1 parent f1bfa21 commit 05d7092

4 files changed

Lines changed: 7 additions & 3 deletions

File tree

‎docs/specifications/App/TDD.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ the host `file` classifier reports as `script text executable` (BLD-11).
324324
- **Neutral scan seam (`lib/pble`).** `class ScanHit { final String id; final String name; final int rssi; }` — the advertised board, retyped 1:1 off `BleScanResult` (`name` is the advertised name **verbatim**: the user-set label, else `PyBLE-XXXX`; value equality by `id`). `abstract interface class Scanner { Stream<List<ScanHit>> scan({Duration? timeout}); Future<void> stopScan(); }` — emits an **accumulating, deduped-by-`id`, latest-RSSI snapshot list** (accumulation lives in `lib/pble`, not the UI). A shipped `FakeScanner implements Scanner` scripts snapshots for tests. **This supersedes the illustrative `DiscoveredBoard`/`Scanner`-in-`lib/connect` sketch** — the seam is a neutral `lib/pble` type, exported through the `pble` barrel.
325325
- **Readiness seam.** `BleReadinessSource` (`BleReadiness current` + `Stream<BleReadiness> readiness`, [§7.4](#74-permissions--platform-quirks)) is **re-exported through the `pble` barrel** (the enum already is), so the connect UI observes adapter/permission transitions without importing `lib/ble`.
326326
- **`ConnectionManager` (neutral, `lib/pble`).** Holds the session; composes a `Scanner`, a `BleReadinessSource`, and a `ConnectionFactory` (`typedef ConnectionFactory = Future<Connection> Function(String deviceId)`). Surface: `Connection get connection` (the **stable facade**, [§6.2](#62-binding-pattern)); `Stream<List<ScanHit>> get scanResults`; `BleReadinessSource get readiness`; `ValueListenable<ConnectPhase> get phase`; `ScanHit? get selected`; `Object? get lastError`; and `startScan({Duration? timeout})` / `stopScan()` / `connect(String id)` / `disconnect()` / `dispose()`. `enum ConnectPhase { idle, scanning, connecting, connected, failed }` is the **connect-flow session phase — distinct from `ConnState`** (`scanning`/`connected` are deliberately not `ConnState` members, [§5.2](#52-shared-types)). `connect(id)` calls the `ConnectionFactory`, publishes the resulting `Connection`, and lets `PbleConnection.fromLink`'s link observation drive `connecting → ready`. The production `PbleConnectionManager.production({required String appName, required String appVersion})` wires the real `flutter_blue_plus` stack (`FbpBleAdapter` as scan source **and** connector, `FbpBleReadinessSource`) **internally**, so `main.dart` names no `lib/ble` type ([§6.2](#62-binding-pattern)).
327-
- **Connect controller + view (`lib/connect`).** A `ConnectController` (Riverpod `Notifier`, ADR-0007) projects the manager into an immutable `ConnectState { ConnectPhase phase, BleReadiness readiness, List<ScanHit> hits, ScanHit? selected, DeviceInfo? deviceInfo, Object? error }` (with `int get hitCount`), exposing `startScan`/`stopScan`/`connect(id)`/`disconnect`. On reaching `connected` it reads `connection.deviceInfo()` and stores it — the **DeviceInfo proof** the UI shows. `ConnectScreen` (the surface, hosted by `lib/app/pages/connect_page.dart` as a thin delegator) renders: the readiness banner (adapter-off / unauthorized / unsupported with localized rationale, FR-CONNECT-3); the scan CTA → live `ScanHit` list (name + RSSI, FR-CONNECT-1) → connecting indicator → connected `DeviceInfo` card (board ID / PyBLE firmware / chip / mpy / free-mem, FR-CONNECT-2) → disconnect; the unsupported-`proto_version` update prompt (FR-CONNECT-6); and a **live diagnostics panel** (adapter state, scanning yes/no, hit count, last-error detail) for on-hardware debugging (FR-CONN-6). The board-ID row reads only `DeviceInfo.deviceId` (never `ScanHit.id`); board ID and agent version are verbatim technical values. Empty compatibility defaults render localized **Not reported** rows and do not gate the connection. Because ADR-0011 replaces the Connect surface with the IDE at `ready`, the connected shell's `ConnectionStatusPill` also watches `connectControllerProvider`; this keeps the auto-disposed controller alive, defensively fetches `deviceInfo()` for an already-attached board, and appends its board ID + agent version to the state signal. The pill shows state alone while the fetch is pending, preserves the full state word under width pressure, and exposes the complete labeled summary through semantics.
327+
- **Connect controller + view (`lib/connect`).** A `ConnectController` (Riverpod `Notifier`, ADR-0007) projects the manager into an immutable `ConnectState { ConnectPhase phase, BleReadiness readiness, List<ScanHit> hits, ScanHit? selected, DeviceInfo? deviceInfo, Object? error }` (with `int get hitCount`), exposing `startScan`/`stopScan`/`connect(id)`/`disconnect`. On reaching `connected` it reads `connection.deviceInfo()` and stores it — the **DeviceInfo proof** the UI shows. `ConnectScreen` (the surface, hosted by `lib/app/pages/connect_page.dart` as a thin delegator) renders: the readiness banner (adapter-off / unauthorized / unsupported with localized rationale, FR-CONNECT-3); the scan CTA → live `ScanHit` list (name + RSSI, FR-CONNECT-1) → connecting indicator → connected `DeviceInfo` card (board ID / PyBLE firmware / chip / mpy / free-mem, FR-CONNECT-2) → disconnect; the unsupported-`proto_version` update prompt (FR-CONNECT-6); and a **live diagnostics panel** (adapter state, scanning yes/no, hit count, last-error detail) for on-hardware debugging (FR-CONN-6). The board-ID row reads only `DeviceInfo.deviceId` (never `ScanHit.id`); board ID and agent version are verbatim technical values. Empty compatibility defaults render localized **Not reported** rows and do not gate the connection. Because ADR-0011 replaces the Connect surface with the IDE at `ready`, the connected shell's `ConnectionStatusPill` also watches `connectControllerProvider`; this keeps the auto-disposed controller alive, defensively fetches `deviceInfo()` for an already-attached board, and appends its board ID + agent version to the state signal. Each fetch captures `connectionSessionStampOf(manager.connection)` and may publish its result only while that opaque local stamp is still current; a late prior-session result is discarded, and a replacement session may fetch concurrently rather than being suppressed by the old request. The pill shows state alone while the current-session fetch is pending, preserves the full state word under width pressure, and exposes the complete labeled summary through semantics.
328328
- **Scope of this freeze.** Delivers FR-CONNECT-1/2/3/5/6 + FR-CONN-6. **Saved-boards persistence** (FR-CONNECT-4 list/reconnect-by-remembered-id) depends on Drift `board_ref` (`lib/data`, A-24) which does not exist yet — auto-reattempt on an *unexpected* drop works today (A-03 `ReconnectingBleLink` + re-HELLO), but the persisted saved-board **list** is A-24/S5. The **screenless-identity UX** (rename / Identify, `setLabel`/`setIdentifyLed`/`identify`) is A-36/A-37/S7 and is **not** in this increment.
329329

330330
**Public API (sketch):** `ConnectController` (Riverpod `Notifier`) over the neutral `ConnectionManager` above; plus (A-36/A-37) `renameBoard(String label)`, `configureIdentifyLed(IdentifyLed)`, and `identify()` delegating to `Connection`.

‎docs/specifications/App/design-system.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ The primary state signal. Binds to `connStateProvider` (read-only, `ValueListena
253253
| `running` | `Icons.play_circle` (live) |
254254

255255
The glyph is the state color at full strength (≥ 3:1 graphic).
256-
- **Contents** (row, `SignalSpacing.sm` gap, `EdgeInsets.symmetric(horizontal: SignalSpacing.md, vertical: SignalSpacing.xs)`): the state glyph + the **localized state label** in **`colorScheme.onSurface`** (`labelMedium`, scaler ≤ 1.3). On `ready`/`running`, once `DeviceInfo` is available, a localized compact pattern appends its verbatim board ID + PyBLE agent version (English: `Ready · 5646 · 0.6.0`); empty values use the localized **Not reported** fallback. The label uses `onSurface` (not the state color) so text is AA-safe at any hue (§1.3). The full **state word never ellipsizes** — under width pressure only the appended identity summary may ellipsize, and the pill's minimum content remains glyph + full word (§7.1 shrink order).
256+
- **Contents** (row, `SignalSpacing.sm` gap, `EdgeInsets.symmetric(horizontal: SignalSpacing.md, vertical: SignalSpacing.xs)`): the state glyph + the **localized state label** in **`colorScheme.onSurface`** (`labelMedium`, scaler ≤ 1.3). On `ready`/`running`, once the current connection session's `DeviceInfo` is available, a localized compact pattern appends its verbatim board ID + PyBLE agent version (English: `Ready · 5646 · 0.6.0`); empty values use the localized **Not reported** fallback. A late result from a detached session is never rendered. The label uses `onSurface` (not the state color) so text is AA-safe at any hue (§1.3). The full **state word never ellipsizes** — under width pressure only the appended identity summary may ellipsize, and the pill's minimum content remains glyph + full word (§7.1 shrink order).
257257
- **Motion:** `connecting`/`running` glyphs pulse (`SignalMotion.pulse`, opacity 0.55↔1.0) under the reduced-motion gate (§6). Color transitions animate over `SignalMotion.standard`.
258258
- **Screen reader (folded from review):** wrap the pill in one `Semantics` node. Without live metadata its label remains `l10n.connStatusSemanticLabel(stateWord)` → "Connection status: {state}". With metadata it uses a localized label that announces the complete state, **Board ID**, and **PyBLE firmware** values with context; visual ellipsis never truncates semantics.
259259
- Hit area ≥ 48 dp tall (transparent padding) so it can later become a tappable connection summary.

‎docs/specifications/App/specs.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,7 @@ implementation:
822822
- **AC-3 (additive compatibility)** — When either additive field is empty, its row MUST remain visible with a localized **Not reported** value; absence MUST NOT fail the connection, hide the existing DeviceInfo proof, or gate any action. MUST
823823
- **AC-4 (existing proof retained)** — The chip, MicroPython version, free-memory value, and Disconnect action MUST remain present and retain their current semantics. MUST
824824
- **AC-5 (connected-shell reachability)** — On `ready` and `running`, the always-visible status pill MUST append the same Board ID and PyBLE firmware values after its full localized state word. Tight layouts MAY ellipsize only the appended summary, never the state word; the semantics label MUST expose the complete state, **Board ID**, and **PyBLE firmware** values with context. While `DeviceInfo` is still loading the pill MAY show the state alone. MUST
825+
- **AC-6 (connection-session consistency)** — A `DeviceInfo` result MUST apply only to the local connection session that requested it. After disconnect/reconnect or replacement of the active board, a late result from the prior session MUST be discarded; the pill MUST remain state-only until the current session's `DeviceInfo` arrives and MUST never flash or retain the prior board's ID or firmware. MUST
825826

826827
### 4.15 About & Open-Source Information (`lib/app/`) — FR-ABOUT
827828

‎docs/specifications/app.md‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,10 @@ empty value, the card shows a localized **Not reported** fallback and keeps the
9191
connection usable. When the ready/running shell replaces the pre-connection
9292
Connect surface, its always-visible connection-status pill retains a compact
9393
board-ID + PyBLE-firmware summary from the same `DeviceInfo`; the state word
94-
remains fully visible and the complete labeled values remain in semantics.
94+
remains fully visible and the complete labeled values remain in semantics. A
95+
pending `DeviceInfo` response belongs only to the local connection session that
96+
requested it: after detach/reconnect, the app discards any late prior-session
97+
response and shows state alone until the current board reports its own info.
9598

9699
`DeviceInfo.chip == "esp32-s3"` does not identify which provisioning image is
97100
installed and cannot distinguish the lean `esp32-s3-n16r8` image from the

0 commit comments

Comments
 (0)