docs: correct the false Wi-Fi premise; add UI and live-tracking phases
CI / Repo hygiene (pull_request) Successful in 2s
CI / Web (lint, typecheck, build) (pull_request) Successful in 14s
CI / Migrations reversible (pull_request) Successful in 6s
CI / API (lint, types, tests) (pull_request) Successful in 53s

The plan's headline section claimed the Rider 650 has on-device Wi-Fi and a
Data Sync menu that uploads to Bryton's cloud with no phone involved. It
does not. The unit has ANT+ and Bluetooth only; its sole sync route is BLE
to the Bryton Active app. Confirmed on the physical device, corroborated by
BikeRadar's hands-on. Likely origin: conflation with the Rider 750 / S800,
which do have Wi-Fi.

Impact is narrower than it first appears and no built code is invalidated:
everything downstream of Bryton's cloud never depended on how a ride got
into that cloud, so the poller, ingestion, schema, wear engine and all of
Phase 0 stand. What was invalidated is the product promise — Phase 1 was
called "Zero-touch ride history" and claimed to fix the original complaint
(having to remember to open the Active app). It does not; it is one-tap.
Renamed accordingly rather than leaving the doc overclaiming.

Corrections propagated everywhere the premise had spread: PLAN.md's opening
sections, Phase 1, top risks (the chain is now longer and has a human link
that fails silently — earns a "nothing ingested in N days" nudge), and the
verification checklist; DECISIONS.md D3's justification; README.md, which
was additionally stale on nearly every other point (claimed no code written,
Postgres, compose, four containers); and RESEARCH.md, where the claim
originated under a "verified" header it had not earned. RESEARCH.md is
annotated rather than rewritten — it is a record of what was found, and the
correction is part of that record. USB path facts are marked unverified too,
since they came from the same unverified batch.

D20 records the process lesson: the plan contained the right check ("first
action before writing any code"), it was never run, and nothing downstream
required it to have been. Device capabilities get confirmed on the device
before being written as fact.

Also adds the two phases requested before this came up, both grounded in
feasibility research rather than assumption:
- Phase 1A, an open-ended UI pass done together, including the verbose field
  surface driven off activity_field_inventory.
- Phase 1B, live tracking. Constrained hard by reality: iOS suspends
  backgrounded PWAs and implements no Web Bluetooth, and Bryton's own Live
  Track needs the phone relaying over BLE, so the tracking client cannot be
  our PWA. Shape that works is OwnTracks POSTing to our API for position,
  with the server deriving distance/pace/elevation; HR and power need BLE and
  are explicitly a second-class opt-in, not a blocker. Two rules written in:
  live positions must never become activities (invariant #6), and "no privacy
  zones, ever" does not extend to a public live link.

Inserted as 1A/1B rather than renumbering Phases 2-5, whose numbers are
referenced from DECISIONS.md, deploy/README.md and code comments.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R2ZKeWkZV7ehf7fivrAkkG
This commit is contained in:
2026-09-21 23:20:37 -04:00
co-authored by Claude Opus 5
parent 244fe525dd
commit 7f33cb1593
4 changed files with 345 additions and 78 deletions
+51 -5
View File
@@ -26,18 +26,31 @@ Bryton BLE is a dead end regardless, and the *server* does all syncing.
**Kept as insurance:** the backend stays strictly API-first with a CI-enforced OpenAPI contract, so
if Apple ever makes the PWA route untenable, a native client is a code-generation exercise.
### D3 — Bryton cloud poller is the primary ingestion path
### D3 — Bryton cloud poller is the primary ingestion path — **premise corrected, decision survives**
**Chosen:** server-side poller against the reverse-engineered Bryton Active API, every 15-20 min.
**Rejected:** Strava as a source (no `export_original` — decoded smoothed streams only; plus the
June 2026 tier restructure caps new apps at 10 users and requires a paid dev subscription);
BLE/ANT-FS direct (nobody has reverse-engineered Bryton's BLE — weeks of work, breaks on firmware
updates); depending on the Bryton Active phone app (the original complaint).
**Why:** the Rider 650 has on-device Wi-Fi (`Main Menu -> Data Sync`) and uploads to Bryton's cloud
with no phone involved, and the cloud API returns the **original unmodified FIT bytes**. That's both
zero-touch *and* higher fidelity than the current Strava route.
updates, and separately impossible from an iOS PWA, which has no Web Bluetooth at all).
**Why:** the cloud API returns the **original unmodified FIT bytes** — higher fidelity than the
Strava route, and everything downstream of the cloud is ours.
**Fallbacks, both built:** USB watch folder (also the historical-backfill mechanism, so it stays
exercised rather than bit-rotting) and manual upload.
> **Corrected 2026-09-22.** This entry originally justified itself with "the Rider 650 has on-device
> Wi-Fi (`Main Menu -> Data Sync`) and uploads to Bryton's cloud with no phone involved… That's both
> zero-touch *and* higher fidelity," and listed "depending on the Bryton Active phone app (the
> original complaint)" as *rejected*. **The Wi-Fi premise was false** — the Rider 650 has ANT+ and
> Bluetooth only, and its only sync route is BLE to the Active app (confirmed on the physical
> device; see `docs/PLAN.md`, "How rides actually reach the app"). So the rejected option is in fact
> the only one available, and the chain is
> `head unit → BLE → Active app → Bryton cloud → poller`.
>
> **The decision itself still stands** — polling Bryton's cloud for original FIT bytes remains the
> best available primary path, and nothing downstream of the cloud depended on how rides got into
> it. What changes is the *claim*: this is one-tap, not zero-touch, and it does not fix the original
> complaint. See D20 for the process lesson.
### D4 — Python / FastAPI / Postgres+PostGIS — **database choice superseded by D15**
**Chosen:** Python 3.12, FastAPI, Pydantic v2, SQLAlchemy 2.0 async, Alembic, PostgreSQL 16 + PostGIS 3.4.
**Rejected:** TypeScript full-stack, Go.
@@ -400,6 +413,39 @@ manual deploy — `deploy/README.md`'s "Publishing the image" section.
---
### D20 — The Rider 650 has no Wi-Fi; verify device capabilities on the device
**What happened:** the plan's headline section, "The sync breakthrough," asserted that the Rider 650
has on-device Wi-Fi and a `Main Menu → Data Sync` entry that uploads rides to Bryton's cloud with no
phone involved. It does not. The Rider 650 has ANT+ and Bluetooth only; its sole sync route is BLE to
the Bryton Active app. Confirmed on the physical device, and corroborated by BikeRadar's hands-on
("ANT+ and Bluetooth connectivity", syncing via "Bryton's Active App"). The most likely origin of
the error is conflation with the Rider 750 / S800, which do have Wi-Fi.
**Why it survived so long:** the plan *did* contain the right check — "First action before writing
any code: on the Rider 650, go to `Main Menu → Data Sync`… and confirm a test ride uploads without
the phone." It was never run, and nothing downstream required it to have been. An entire phase was
planned, and Phase 0 fully built and deployed, on top of an unverified device capability that was
written down in the declarative voice of a finding rather than the provisional voice of an
assumption.
**What it cost, and didn't:** less than it first appeared. Everything downstream of Bryton's cloud —
ingestion, dedupe, schema, wear engine, garage, notifications, all of Phase 0 — never depended on
how a ride reached that cloud, so no built code was invalidated. What was invalidated was the
*product promise*: Phase 1 was called "Zero-touch ride history" and claimed to fix the original
complaint (having to remember to open the Active app). It does not. It is one-tap, and the
complaint stands. That renaming, not a refactor, was the actual repair.
**The rule going forward:** a physical-device capability that a phase depends on is confirmed **on
the device** before it is written down as fact. Model-adjacent sources (a spec page for a different
unit in the same family, a review of a sibling model) do not count. Until confirmed, such a claim is
written as an open question in `docs/RESEARCH.md`, not as a premise in `docs/PLAN.md` — and any
phase resting on it carries the verification as its first task, not as a footnote. The same applies
to the remaining unverified device claims: the USB `.fit` path layout, and whether the
`intervalssync` protocol still retrieves activities from a current Bryton account.
---
## Deliberately deferred
- **Finish the Watchtower auto-updater** (D19) — retry with a maintained image; `velodrome` is