You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit bc3f87f
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: docs/specifications/App/TDD.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -311,7 +311,7 @@ the host `file` classifier reports as `script text executable` (BLD-11).
311
311
312
312
### 4.8 `lib/connect/` — scan/connect flow UI
313
313
314
-
**Responsibility:** present a scan filtered to the PyBLE service UUID, listing each board by its **advertised name + RSSI** — the user-set device **label** when set, otherwise the default `PyBLE-XXXX` ([§7.1](#71-scan--connect)) — never a raw list; drive connect (GATT → TX subscribe → MTU 247 → INFO/HELLO → show `DeviceInfo` incl. `deviceId`/`label`) before enabling editor/console/file actions; surface permission/adapter state with localized rationale; list and reconnect saved boards; refuse an unsupported `proto_version` with an update prompt; scan→connect→use with no pairing-code/account/lease. It also hosts the post-connect **screenless-identity** UX: a **rename-board** action ([§11.5](#115-screenless-identity--rename--identify)) and a cap-gated **Identify** action.
314
+
**Responsibility:** present a scan filtered to the PyBLE service UUID, listing each board by its **advertised name + RSSI** — the user-set device **label** when set, otherwise the default `PyBLE-XXXX` ([§7.1](#71-scan--connect)) — never a raw list; drive connect (GATT → TX subscribe → MTU 247 → INFO/HELLO → show `DeviceInfo` incl. `deviceId`/`agentVersion`/`label`) before enabling editor/console/file actions; surface permission/adapter state with localized rationale; list and reconnect saved boards; refuse an unsupported `proto_version` with an update prompt; scan→connect→use with no pairing-code/account/lease. It also hosts the post-connect **screenless-identity** UX: a **rename-board** action ([§11.5](#115-screenless-identity--rename--identify)) and a cap-gated **Identify** action.
@@ -324,7 +324,7 @@ the host `file` classifier reports as `script text executable` (BLD-11).
324
324
-**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.
325
325
-**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`.
326
326
- **`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 (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).
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.
328
328
-**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.
329
329
330
330
**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`.
-**FR-CONNECT-1** — The connect screen MUST present a scan **filtered to the PyBLE service UUID**, listing each board by its **advertised name with RSSI** — the user-set device **label** when one is set, otherwise the default `PyBLE-XXXX` ([protocol.md §2](../protocol.md#2-ble-transport-gatt)) — and MUST NOT present a raw, unfiltered BLE device list. MUST (*source: PRD §9.6, §19.3, §8.1, protocol.md §2; verify: widget; story: A-22*)
812
-
-**FR-CONNECT-2** — On Connect, the app MUST open GATT, subscribe to TX notify, request MTU 247, read INFO / send HELLO, and display `DeviceInfo` (chip, MicroPython version, free memory) before enabling editor/console/file actions. MUST (*source: PRD §9.6, §19.3, §8.1; verify: integration; story: A-22*)
812
+
-**FR-CONNECT-2** — On Connect, the app MUST open GATT, subscribe to TX notify, request MTU 247, read INFO / send HELLO, and display `DeviceInfo` (display-only board ID, PyBLE agent firmware version, chip, MicroPython version, and free memory) before enabling editor/console/file actions. The board ID MUST be `DeviceInfo.deviceId`, never the platform scan identifier; board ID and agent version MUST be rendered verbatim, MUST NOT be derived or used as an authorization/profile gate, and an empty value MUST render a localized **Not reported** fallback without blocking use. MUST (*source: PRD §9.6, §19.3, §8.1, protocol.md §7; verify: widget/integration; story: A-22*)
813
813
-**FR-CONNECT-3** — The screen MUST surface BLE permission and adapter state with localized rationale (iOS `NSBluetoothAlwaysUsageDescription`; Android `BLUETOOTH_SCAN`/`BLUETOOTH_CONNECT` + older-Android location). MUST (*source: PRD §9.6, §19.3, app.md §5; verify: widget/integration; story: A-04/A-22*)
814
814
-**FR-CONNECT-4** — The screen MUST list **saved boards** and reconnect them by remembered identifier; on link loss the app MUST auto-reattempt and in-flight transfers MUST resume per [protocol.md §5](../protocol.md#5-file-transfer-the-reliability-core). MUST (*source: PRD §8.1, §19.3, §13.1; verify: integration; story: A-03/A-22*)
815
815
-**FR-CONNECT-5** — The flow MUST be scan → connect → use only: no pairing-code step, no account, no session-lock/heartbeat, and no board-identity gating (the non-goals of [PRD §4.3](../prd.md)). MUST (*source: PRD §8.1, §9.6, §19.3, §4.3; verify: widget; story: A-22*)
816
816
-**FR-CONNECT-6** — The connect flow MUST refuse a board with an unsupported `proto_version` and prompt to update the firmware/app rather than failing silently. MUST (*source: PRD §18.3; verify: widget/integration; story: A-10/A-22*)
817
817
818
+
**Connected Board-info identity slice — FROZEN (`[docs]` 2026-08-22, additive to FR-CONNECT-2).** The testable acceptance criteria are:
819
+
820
+
-**AC-1 (board ID source)** — The Board-info card MUST label and show `DeviceInfo.deviceId` verbatim. It MUST NOT expose `ScanHit.id`, which is a platform connection identifier (an iOS UUID or Android MAC-form string), or substitute the advertised name/label. MUST
821
+
-**AC-2 (firmware distinction)** — The card MUST label and show `DeviceInfo.agentVersion` verbatim as **PyBLE firmware**, distinct from the existing **MicroPython** version row. It MUST NOT derive the value from app metadata or `mpyVersion`. MUST
822
+
-**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
823
+
-**AC-4 (existing proof retained)** — The chip, MicroPython version, free-memory value, and Disconnect action MUST remain present and retain their current semantics. MUST
824
+
818
825
### 4.15 About & Open-Source Information (`lib/app/`) — FR-ABOUT
819
826
820
827
**FROZEN (X-11 About-page subset · `[docs]` 2026-07-28).** This subset makes
Copy file name to clipboardExpand all lines: docs/specifications/app.md
+8-1Lines changed: 8 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -74,7 +74,7 @@ Widgets receive a `Connection` (or narrow callbacks derived from it). Tests inje
74
74
## 4. Connect flow
75
75
76
76
1.**Scan** — `flutter_blue_plus` scan filtered to the PyBLE service UUID; show advertised `PyBLE-XXXX` names + RSSI.
77
-
2.**Connect** — open GATT, subscribe to TX notify, request MTU 247, read INFO / send HELLO, show `DeviceInfo`.
77
+
2.**Connect** — open GATT, subscribe to TX notify, request MTU 247, read INFO / send HELLO, show `DeviceInfo`, including its display-only board ID and PyBLE agent firmware version.
78
78
3.**Use** — editor/console/files bind to the `Connection`.
79
79
4.**Reconnect** — on link loss, auto-reattempt; in-flight file transfers resume via PBLE/1 §5. Saved boards reconnect by remembered identifier.
80
80
@@ -83,6 +83,13 @@ renders unknown future values verbatim and continues from advertised PBLE/1
83
83
capabilities; a missing in-app pin reference is informational and MUST NOT
84
84
block the connection.
85
85
86
+
The connected Board-info card renders `DeviceInfo.deviceId` as **Board ID** and
87
+
`DeviceInfo.agentVersion` as **PyBLE firmware**, verbatim. It MUST NOT substitute
88
+
the platform scan identifier, derive either value, or use either value as an
89
+
authorization/profile gate. When a legacy or nonconforming agent reports an
90
+
empty value, the card shows a localized **Not reported** fallback and keeps the
91
+
connection usable.
92
+
86
93
`DeviceInfo.chip == "esp32-s3"` does not identify which provisioning image is
87
94
installed and cannot distinguish the lean `esp32-s3-n16r8` image from the
88
95
separate `waveshare-esp32-s3-lcd-147b` image. The app therefore MUST NOT infer
0 commit comments