Files
fuelboard/BACKLOG.md
T

129 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# FuelBoard Backlog
FuelBoard — England-wide petrol-station price widget + app.
Relay mirrors the UK gov data (hourly CSV); app fetches the full-UK dump
(~8,038 stations, all prices) once per 12 h and filters/sorts on-device.
Priority guide: P0 = next build, P1 = soon, P2 = later, P3 = maybe/idea.
Status: TODO / IN PROGRESS / DONE / BLOCKED.
---
## P1 — Soon
- [ ] **Siri: "Cheapest [fuel] near me"**`AppShortcutsProvider` + `CheapestFuelIntent`
(App Intents, iOS 16+), dialog + price-card snippet, cached full-UK dump first
with freshness label ("as of 6am"), last-known-location fallback. Constraint:
fresh-data path needs a reachable source off-LAN — cached answers work anywhere
the app has previously fetched (see P3 data mirror). Interim: the relay on
Tailscale (100.120.217.98:8789) is reachable off-LAN for testing; relay will
move to a permanent box or the GitHub mirror long-term. *IN PROGRESS — MVP
intent shipped (dialog + snippet + freshness label), closest phrase still
pending.*
- [ ] **Siri: "Closest [fuel] station"** — same plumbing as cheapest, sort by
distance instead of price. Free second phrase in the same `AppShortcutsProvider`.
## P2 — Later
- [ ] **Shortcuts: station price lookup**`StationEntity` + `EntityQuery` so
Shortcuts/Siri pick a station by name; `ReturnsValue<Double>` enables user
automations ("get cheapest diesel → notify").
- [ ] **Siri: favourite station price** — "What's diesel at my favourite station?"
(cheap once `StationEntity` exists).
- [ ] **Siri: on-screen awareness (View Annotations + StationEntity)** — iOS
26/27 Siri prefers the visible screen over the App Shortcut when the app is
frontmost; without view annotations it answers from the rendered screen
(unstructured "screenshot" parse) — visible tab/sort/radius, not the full
dataset or the intent's logic. Fix: `StationEntity` (shared with the station
lookup P2 above) + `.appEntityIdentifier` on station rows (collection
annotation on the List) + `NSUserActivity` on the station detail/map +
App Schema conformance (optional `IndexedEntity` for Spotlight semantic
search). Verify annotations with AppIntentsTesting (P3). Off-screen/locked
requests already route to `CheapestFuelIntent` correctly; this closes the
frontmost gap. WWDC26: "Build intelligent Siri experiences with App Schemas"
+ "Explore advanced App Intents features".
- [ ] **Lock screen widgets** — new `FuelPriceLockScreen` kind (fuel-only config
intent, reuses `FuelPriceTimelineProvider`): `accessoryRectangular` (cheapest
station + price + distance), `accessoryCircular` (price ring), `accessoryInline`
("⛽ 139.9p"). Provider + `containerBackground` already exist; must branch the
view for accessory families (current `else` renders `stationList`). Bonus:
StandBy mode. ~1 session.
- [ ] **CarPlay** — widgets render but can't launch Maps; consider a proper CarPlay
scene (CPListTemplate) for navigation deep-links.
- [ ] **Alert region tuning** — make alert trigger radius + dedup window
user-configurable (currently fixed 300 m trigger / 1 per station per hour).
- [ ] **Pull-to-refresh spinner state** — surface refresh in-flight state
(currently `refreshable` fires but no visible progress in the row list).
- [ ] **Offline first-run** — first launch without network currently shows empty
states; bundle a sample dump so the app demos offline.
- [ ] **Localization** — UK English only today; audit string catalog for l10n.
- [ ] **Dark mode polish** — verify brand logos (white-circle clips) and RAG dot
contrast under dark appearance on-device.
## P3 — Ideas / Research
- [ ] **Siri: create fuel alert** — voice-create an alert for a station. Deferred:
alert semantics (cheapest-within-radius + hourly dedup) are hard to express in
a dialog; needs the alert engine surfaced as an intent.
- [ ] **App Intents testing** — AppIntentsTesting (iOS 27) for intent coverage
instead of XCUITest when the Siri features land.
- [ ] **Production data mirror (GitHub)** — relay pushes the synced dump
(`stations.json` + `latest.json` version pointer) to a PUBLIC GitHub repo; the
app fetches `raw.githubusercontent.com` with the LAN relay as fallback source.
Public repo required for App Review (reviewer must fetch the data themselves);
HTTPS/ATS fine; plain JSON data is not "remote code" (2.5.2-safe); bundle a small
fallback dump so the app never demos empty. Needs GitHub PAT (aptonline account).
- [ ] **Fuel-price prediction** — trend from historical dumps (relay could keep a
rolling archive) to show "price tends up/down".
- [ ] **Price alerts for a specific station** — notify when a starred station's
price drops below a user threshold.
- [ ] **Share sheet** — share "Cheapest Unleaded near me" as text/image card.
- [ ] **Watch app** — glanceable cheapest-price complication (WatchKit + shared
app-group store; watch target would need to be added to the project).
- [ ] **App Clip** — 10 MB budget for "find cheapest fuel nearby" without install.
- [ ] **Widget picker: dual-unit distance labels** — the Edit-Widget Distance row
label is cached by the system at sheet-render time and only re-resolves on
picker interaction, so "mirror the app unit" goes stale. Fix: label options
with both units ("5 miles (8 km)") so nothing can go stale; widget rows keep
mirroring the live unit.
- [ ] **Localizable.strings: normalize keys to match copy** — today 8 onboarding
keys diverge from their values (key = old source literal, value = tightened
copy, e.g. "…updated twice a day" key vs "…updated daily" value). Option to
rename keys so key == value everywhere. Requires sweeping every Swift
occurrence of the old literal (Text/NSLocalizedString) AND updating key +
value together in one commit + rebuild — renaming the file entry alone
silently reverts copy to the Swift literal at next build. Not recommended:
keys are invisible anchors; value edits already change copy without code
changes, and normalization reintroduces divergence on the next value edit.
## Done (recent)
- [x] Switch dataset to govuk API data — official Fuel Finder OAuth API via
relay-api :8789 (source: api, ~8,012 stations); app baseURL → :8789; credentials
live in relay `.env` only, never in the IPA
- [x] Widget configuration — per-widget fuel/sort/distance via
`WidgetConfigurationIntent` (`AppIntentConfiguration`), distance options as
`WidgetDistance` AppEntity (`26e724f`); small widget gained Favourites
(`d0bd4e7`) + `.systemMedium` (`b94f769`)
- [x] Unit test target for the app — FuelBoardShared XCTest target, 37 tests
(title sanitizer, decode price guard, RAG thresholds, cheapest/closest baseline,
miles↔km)
- [x] Backlog hygiene — implemented items marked DONE, obsolete pruned
- [x] Sync retry with exponential backoff — relay-api retries failed syncs
15s→15min cap; `/health` exposes retry state (`6a58275`)
- [x] England-wide full-UK dump, on-device filtering (relay `142061b`, app `cf9765b`)
- [x] Cache-first, 12 h gate, pull-to-refresh only
- [x] Distance filter 5/10/15 miles, local-only changes (no re-fetch)
- [x] Closest mode = whole country, distance picker disabled
- [x] Delta/TOP baseline = cheapest-within-radius in both modes
- [x] Three-layer price sanitisation (relay band 50500p / app decode guard / title-case)
- [x] Title-case station names (chunk-aware sanitizer)
- [x] Native large-title navigation (Mail-style collapse) — `313fe84`
- [x] E10/E5 bracket labels in picker + descriptions — `05f3ef0`
- [x] Gitea remotes created + pushed (`apt/fuelboard`, `apt/fuelboard-relay`)
- [x] Brand logos: proper 2x/3x assets, 75% smaller (315 KB → 79 KB)
- [x] Security audit (see SECURITY.md)
- [x] App Store approval gotcha review (see APPSTORE.md)