Skip to content

Commit 5453c58

Browse files
committed
[docs] start app 0.2.0 beta train
Signed-off-by: Viwat Vchirawongkwin <viwat.v@chula.ac.th>
1 parent 134ba8b commit 5453c58

6 files changed

Lines changed: 133 additions & 13 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@
33
Notable public changes to PyBLE are recorded here. App and firmware versions
44
are released independently from this monorepo.
55

6-
## Unreleased
6+
## Unreleased — App 0.2.0 beta train
77

88
These app, website, and repository-source changes do not designate a new app
99
or firmware release. The qualified firmware release remains v0.6.0, built from
1010
its annotated tag rather than from the newer branch tip.
1111

12+
The current compatible app feature set now targets `0.2.0`; its first local
13+
beta candidate is build `5`. This candidate identity improves test and
14+
evidence traceability but does not designate a qualified store release or
15+
TestFlight handoff.
16+
1217
### App source
1318

1419
- Added Python syntax highlighting and synchronized one-based line numbers to

‎docs/ROADMAP.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ promising dates; accepted work is tracked through GitHub issues and milestones.
1515

1616
## Near term
1717

18+
- Qualify the app 0.2.0 beta train, starting at globally monotonic build 5,
19+
across the retained iPad/Android and five-profile hardware test scope
1820
- Complete the app, PBLE/1, resource, and remaining firmware release
1921
qualification for the exact v0.4.2 bytes
2022
- Complete real-hardware qualification before enabling the ESP32-C3 installer
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# ADR-0044 — Start the app 0.2.0 beta train at build 5
2+
3+
- Status: **Accepted**
4+
- Date: 2026-08-27
5+
- Extends: [ADR-0040](0040-sha-pinned-connected-github-import.md)
6+
- Extends: [ADR-0042](0042-prefill-official-examples-and-discover-branches.md)
7+
- Extends: [ADR-0043](0043-session-bound-visible-file-multi-delete.md)
8+
9+
## Context
10+
11+
The installed app identity remains `0.1.0+4`, but build 4 is a retained
12+
TestFlight handoff bound to an earlier source commit. Since that handoff, the
13+
app source has gained a coherent set of compatible beta features: connected
14+
board identity, the numbered and adjustable rich editor, SHA-pinned GitHub
15+
import, the editable official examples-repository default with branch-only
16+
discovery, session-bound visible-file multi-delete, and a one-row responsive
17+
Files toolbar.
18+
19+
Reinstalling a different source state under the same version and build works
20+
for local development, but it makes About, screenshots, HIL evidence, and
21+
tester reports unable to distinguish the artifacts. A future TestFlight upload
22+
also needs a unique build identity, and the shared Flutter build number becomes
23+
Android's monotonically increasing `versionCode`.
24+
25+
The project reserves app `1.0.0` for its first production release and has not
26+
previously assigned a numbered milestone to the current Unreleased app work.
27+
28+
## Decision
29+
30+
1. **Start a new compatible beta milestone.** The current coherent Unreleased
31+
feature set becomes the **PyBLE app 0.2.0 beta train**. This changes neither
32+
the independently versioned firmware agent (`0.6.0`) nor PBLE/1.
33+
34+
2. **Use build 5 for the first candidate.** The first local beta candidate is
35+
`0.2.0+5`, displayed as `0.2.0 (5)`. Later candidate artifacts increment the
36+
build number (`+6`, `+7`, …) even when the base version stays `0.2.0`.
37+
PyBLE does not reset the shared Flutter build sequence when the base version
38+
changes, preserving one simple iOS/Android ordering rule.
39+
40+
3. **Keep `pubspec.yaml` authoritative.** `app/pubspec.yaml` owns the complete
41+
base-version/build-number pair. The base version is mirrored at compile time
42+
by `kAppVersion` for PBLE/1 HELLO because the wire layer must not depend on
43+
runtime package metadata. The public GitHub request User-Agent derives from
44+
the same `kAppName` and `kAppVersion` constants instead of carrying another
45+
independently authored version literal.
46+
47+
4. **Make synchronization executable.** An automated test parses the package
48+
version, pins the declared 0.2.0 build-5 candidate, and proves its base
49+
version equals `kAppVersion`. GitHub-client tests require the exact
50+
`PyBLE/<kAppVersion>` User-Agent. A later version bump must update the
51+
candidate expectation and cannot silently drift package, HELLO, and HTTP
52+
identities.
53+
54+
5. **Distinguish a candidate from a qualified release.** A local install or
55+
TestFlight build carrying `0.2.0 (5)` is a beta candidate, not evidence that
56+
the app has completed release qualification. Store-signable artifacts,
57+
platform parity, compatibility declarations, automated gates, and required
58+
physical validation remain governed by the existing release requirements.
59+
60+
6. **Bind every external handoff to source and evidence.** If build 5 advances
61+
from local testing to TestFlight or another external handoff, its test plan
62+
records the exact accepted source commit, qualified agent release, PBLE/1
63+
baseline, focused test scope, and known boundaries. About must report the
64+
installed package identity, and physical evidence must name both app
65+
version/build and board firmware identity.
66+
67+
7. **Preserve historical identities.** The retained `0.1.0 (4)` TestFlight
68+
plan, prior changelog entries, and the `source-2026.08.23` citation snapshot
69+
remain unchanged. They describe immutable earlier artifacts rather than the
70+
moving branch tip.
71+
72+
8. **Keep delivery test-first and local until accepted.** The synchronization
73+
test lands red before the version implementation. The exact release build is
74+
installed and physically reviewed on iPad before any pull request or push
75+
for this combined feature branch.
76+
77+
## Consequences
78+
79+
- Testers can distinguish the new feature set from retained build 4 in About,
80+
screenshots, diagnostics, and any later TestFlight feedback.
81+
- PBLE/1 sees app version `0.2.0` while remaining protocol-compatible with the
82+
qualified `0.6.0` agents.
83+
- The official examples repository remains independently versioned and
84+
untrusted at the importer boundary.
85+
- Future candidate builds pay the small cost of updating one explicit build
86+
expectation in exchange for deterministic cross-platform traceability.
87+
- `1.0.0` remains unavailable until the production acceptance gates pass.
88+
89+
## Alternatives considered
90+
91+
- **Use `0.1.0+5`.** Valid for another iteration of the same beta milestone,
92+
but rejected because the accumulated compatible feature set now warrants an
93+
explicit minor beta milestone.
94+
- **Reset to `0.2.0+1`.** Accepted by iOS for a new base version, but rejected
95+
because Android requires a greater `versionCode` and PyBLE ships one Flutter
96+
identity at platform parity.
97+
- **Override only the build command.** Rejected because About could report an
98+
overridden package version while PBLE/1 HELLO and the GitHub User-Agent still
99+
advertise stale compile-time values.
100+
- **Jump to `1.0.0`.** Rejected because version 1.0 is the first fully qualified
101+
production release, not a development or TestFlight milestone.
102+
103+
## Related
104+
105+
- [App build and version requirements](../specifications/App/specs.md#9-build-versioning--distribution--bld)
106+
- [PRD release and versioning](../specifications/prd.md#18-release--versioning)
107+
- [PyBLE 0.1.0 (4) retained TestFlight handoff](../testing/testflight/0.1.0-build-4.md)
108+
109+
<!-- SPDX-License-Identifier: MIT -->

‎docs/decisions/README.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,6 @@ Each ADR captures one significant decision: its context, the choice, and the con
4747
| [0041](0041-separate-official-examples-repository.md) | Maintain the official user-facing runnable examples in a separately governed repository while PyBLE retains only its importer fixtures and bundled offline examples | Accepted |
4848
| [0042](0042-prefill-official-examples-and-discover-branches.md) | Prefill the editable official examples repository and provide bounded, branch-only discovery while retaining advanced explicit refs | Accepted |
4949
| [0043](0043-session-bound-visible-file-multi-delete.md) | Bind visible regular-file multi-delete to one board session with sequential fail-fast execution and honest partial results | Accepted |
50+
| [0044](0044-start-app-v020-beta-train-at-build-5.md) | Start the app 0.2.0 beta train at build 5 with synchronized package, PBLE/1 HELLO, and GitHub request identities | Accepted |
5051

51-
Decided 2026-06-30 (0001–0005); 0006 accepted 2026-07-01; 0007, 0008, 0009 and 0010 accepted 2026-07-02; 0011 accepted 2026-07-03; 0012 accepted 2026-07-25; 0013 accepted 2026-07-27; 0014, 0015, 0016, and 0017 accepted 2026-07-28; 0018–0022 accepted 2026-07-29; 0023 and 0024 accepted 2026-08-01; 0025 and 0026 accepted 2026-08-02; 0027–0029 accepted 2026-08-03; 0030 accepted 2026-08-11; 0031–0033 accepted 2026-08-12; 0034 accepted 2026-08-13; 0035 and 0036 accepted 2026-08-16; 0037 and 0038 accepted 2026-08-20; 0039 accepted 2026-08-21; 0040 accepted 2026-08-22; 0041 accepted 2026-08-25; 0042 and 0043 accepted 2026-08-27.
52+
Decided 2026-06-30 (0001–0005); 0006 accepted 2026-07-01; 0007, 0008, 0009 and 0010 accepted 2026-07-02; 0011 accepted 2026-07-03; 0012 accepted 2026-07-25; 0013 accepted 2026-07-27; 0014, 0015, 0016, and 0017 accepted 2026-07-28; 0018–0022 accepted 2026-07-29; 0023 and 0024 accepted 2026-08-01; 0025 and 0026 accepted 2026-08-02; 0027–0029 accepted 2026-08-03; 0030 accepted 2026-08-11; 0031–0033 accepted 2026-08-12; 0034 accepted 2026-08-13; 0035 and 0036 accepted 2026-08-16; 0037 and 0038 accepted 2026-08-20; 0039 accepted 2026-08-21; 0040 accepted 2026-08-22; 0041 accepted 2026-08-25; 0042–0044 accepted 2026-08-27.

‎docs/specifications/App/TDD.md‎

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ UI reads down (watch providers) and writes up (call `Connection` methods / DAO m
501501

502502
**Runtime session — FROZEN (A-22 · `[docs]` 2026-07-02, [ADR-0009](../../decisions/0009-runtime-connection-manager.md)).** The active `Connection` changes at runtime, so the seam is preserved as a **stable facade** rather than a swapped root value:
503503

504-
- **Root injection moves up to `connectionManagerProvider`** (`Provider<ConnectionManager>`, throws until overridden). `main.dart` overrides it with `PbleConnectionManager.production(appName: kAppName, appVersion: kAppVersion)` — importing `package:pyble/pble/…` only, so `main.dart` names no `lib/ble` type (the import-boundary gate scans `main.dart`). `kAppName` (`"PyBLE"`) / `kAppVersion` are ASCII HELLO `app_name`/`app_version` constants ([protocol.md §7](../protocol.md#7-hello--capabilities)) held in `lib/app/` and kept in sync with the `pubspec` base version by hand. No runtime metadata dependency crosses into `lib/pble`; `package_info_plus` is confined to the About-page adapter ([§4.12](#412-libapp--about-and-open-source-information)) and never supplies a wire value. The constants are wire identifiers, not display text (FR-I18N-4, FR-ABOUT-3).
504+
- **Root injection moves up to `connectionManagerProvider`** (`Provider<ConnectionManager>`, throws until overridden). `main.dart` overrides it with `PbleConnectionManager.production(appName: kAppName, appVersion: kAppVersion)` — importing `package:pyble/pble/…` only, so `main.dart` names no `lib/ble` type (the import-boundary gate scans `main.dart`). `kAppName` (`"PyBLE"`) / `kAppVersion` are ASCII HELLO `app_name`/`app_version` constants ([protocol.md §7](../protocol.md#7-hello--capabilities)) held in `lib/app/`. `app/pubspec.yaml` remains the complete app-version/build authority; a unit contract parses its base version and requires exact equality with `kAppVersion`, while the public GitHub User-Agent derives from those same constants ([ADR-0044](../../decisions/0044-start-app-v020-beta-train-at-build-5.md)). No runtime metadata dependency crosses into `lib/pble`; `package_info_plus` is confined to the About-page adapter ([§4.12](#412-libapp--about-and-open-source-information)) and never supplies a wire value. The constants are wire identifiers, not display text (FR-I18N-4, FR-ABOUT-3).
505505
- **`connectionProvider` is preserved, now derived:** `Provider<Connection>((ref) => ref.watch(connectionManagerProvider).connection)`. `ConnectionManager.connection` is a **stable facade** whose identity never changes: its `state` reflects the whole session (`disconnected` while idle/scanning/failed, `connecting` during GATT+HELLO, then the live board's `ready`/`running`), and its verbs delegate to the live board when connected and throw a **typed not-connected error** otherwise (never a silent success). `connStateProvider` is **unchanged** (`connectionProvider.state`). Every S1–S3 widget (`ConnectionStatusPill`, `PinReferencePage` `FutureBuilder`, `FilesPage`) keeps compiling and now shows real state.
506506
- **Test compatibility:** a test overriding `connectionProvider` directly with a `FakeConnection` still **wins** (the override replaces the derived body), so the existing widget/golden suite (`shell_harness.pumpShell`) is untouched. New connect-flow tests inject `FakeScanner` + a `ConnectionFactory` returning `FakeConnection` + a fake `BleReadinessSource` through a `PbleConnectionManager`, or override `connectionManagerProvider` with a fake manager — no fake radio / fake `BleLink` needed (FR-CONN-7, D5).
507507
- The `ConnectController` (`Notifier`) projecting the manager into `ConnectState` for the connect surface lives in `lib/connect` ([§4.8](#48-libconnect--scanconnect-flow-ui)); it is the one place with the connect-flow intents.
@@ -785,16 +785,18 @@ request. The canonical value displayed back to the user is
785785

786786
`GithubApi` talks only to HTTPS on exact host `api.github.com`, with redirects
787787
disabled (or independently revalidated and rejected unless the destination is
788-
the same exact host). It sends GitHub's required API version/accept/user-agent
789-
headers but no authorization, token, cookie, board fact, or user source. It
790-
does not follow `download_url`, HTML links, or any arbitrary URL returned in a
791-
response. A response rejected from headers alone (including status or declared
792-
body length) has its body stream cancelled before the adapter publishes the
793-
typed failure, so repeated hostile responses cannot strand transport resources
794-
(SEC-10). Each REST GET also owns one non-extending absolute wall-clock
795-
deadline that starts before the request is sent and covers both headers and the
796-
complete bounded body. Receiving a slow trickle never extends that deadline;
797-
expiry asks the transport to abort and maps to the neutral offline failure.
788+
the same exact host). It sends GitHub's required API version and accept headers
789+
plus exact User-Agent `PyBLE/<kAppVersion>`, derived from the compile-time app
790+
identity rather than an independent version literal. It sends no authorization,
791+
token, cookie, board fact, or user source. It does not follow `download_url`,
792+
HTML links, or any arbitrary URL returned in a response. A response rejected
793+
from headers alone (including status or declared body length) has its body
794+
stream cancelled before the adapter publishes the typed failure, so repeated
795+
hostile responses cannot strand transport resources (SEC-10). Each REST GET
796+
also owns one non-extending absolute wall-clock deadline that starts before the
797+
request is sent and covers both headers and the complete bounded body.
798+
Receiving a slow trickle never extends that deadline; expiry asks the transport
799+
to abort and maps to the neutral offline failure.
798800

799801
Resolution is a two-step logical operation:
800802

‎docs/specifications/App/specs.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,6 +1141,7 @@ The local Drift database ([PRD §12.1](../prd.md), `lib/data/`) MUST model exact
11411141
- **BLD-1** — The app MUST be a single Flutter codebase targeting iPadOS and Android tablets (one package, [app.md §2](../app.md#2-packages--directories)). MUST (*source: PRD §6.1, §16.1; verify: integration; story: A-20*)
11421142
- **BLD-2** — App dependencies MUST be governed by `pubspec.yaml` with an approved-entry record (rationale, license, minimum version) per dependency; each MUST be MIT/Apache-2.0/BSD-compatible; a committed `pubspec.lock` MUST pin versions and CI MUST build against the lock. MUST (*source: PRD §17.2; verify: unit/integration; story: X-03*)
11431143
- **BLD-3** — The app MUST follow SemVer; v1.0 is the first production release and a breaking change bumps MAJOR. MUST (*source: PRD §18.1; verify: unit; story: X-11*)
1144+
- **App 0.2.0 beta train — FROZEN (`[docs]` 2026-08-27, [ADR-0044](../../decisions/0044-start-app-v020-beta-train-at-build-5.md)).** The current compatible Unreleased feature set advances the app base version to `0.2.0`; its first candidate uses globally monotonic Flutter build number `5`. `app/pubspec.yaml` owns `0.2.0+5`; an automated unit contract MUST keep that base synchronized with the compile-time PBLE/1 HELLO identity and the exact public GitHub User-Agent. This candidate designation does not change firmware `0.6.0`, PBLE/1, the store-parity rule, or the qualification required before calling an app build released. Version `1.0.0` remains reserved for the first production release.
11441145
- **BLD-4** — The app MUST be distributed free on the App Store and Google Play, submitted **at parity** (neither store ahead of the other); no account, paywall, or in-app purchase. MUST (*source: PRD §15.3, §18.2; verify: integration; story: X-11*)
11451146
- **BLD-5** — The locale-parity check MUST block any merge that breaks ARB parity. MUST (*source: PRD §9.10, §21.1; verify: locale; story: X-12*)
11461147
- **BLD-6** — The build MUST ship a `THIRD_PARTY_LICENSES`/notices set generated mechanically, and the app MUST present an in-app Open-Source Notices screen reachable from About on both platforms. The earlier delivery of a reachable `LicenseRegistry` page is an incremental surface and does not close the complete generated-notices requirement. MUST (*source: PRD §15.2; verify: widget/integration; story: X-11*)

0 commit comments

Comments
 (0)