Files
fuelboard/BACKLOG.md
T

8.4 KiB
Raw Blame History

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. DONE — radius-scoped lookup 037c241 (real-data regression: GULF HISTON 100.9p 132 mi away), per-fuel literal-word shortcuts e55078c, names = app words a3b6f37, plus Directions-to-cheapest caadf18 + spoken hand-off aed99e3.
  • 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 lookupStationEntity + EntityQuery so Shortcuts/Siri pick a station by name; ReturnsValue<Double> enables user automations ("get cheapest diesel → notify").

  • Siri: favourite station price — "How much is my favourite unleaded?" FavouriteFuelPriceIntent: TOP favourite per fuel (first in the manual Favourites order — SiriCheapestLookup.topFavourite, refresh-vs-dump with offline cached-snapshot fallback); no location required (distance shown only when the saved fix exists). DirectionsToFavouriteFuelIntent (Maps hand-off, station announced first) stays in code but carries no voice phrases — every favourite price card has the interactive Directions button (6d7254e), and the voice slots went to Directions-to-Cheapest (c6bd634). DONE — favourites already carried station snapshots, so no StationEntity dependency.

  • 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)

  • 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
  • Widget configuration — per-widget fuel/sort/distance via WidgetConfigurationIntent (AppIntentConfiguration), distance options as WidgetDistance AppEntity (26e724f); small widget gained Favourites (d0bd4e7) + .systemMedium (b94f769)
  • Unit test target for the app — FuelBoardShared XCTest target, 37 tests (title sanitizer, decode price guard, RAG thresholds, cheapest/closest baseline, miles↔km)
  • Backlog hygiene — implemented items marked DONE, obsolete pruned
  • Sync retry with exponential backoff — relay-api retries failed syncs 15s→15min cap; /health exposes retry state (6a58275)
  • England-wide full-UK dump, on-device filtering (relay 142061b, app cf9765b)
  • Cache-first, 12 h gate, pull-to-refresh only
  • Distance filter 5/10/15 miles, local-only changes (no re-fetch)
  • Closest mode = whole country, distance picker disabled
  • Delta/TOP baseline = cheapest-within-radius in both modes
  • Three-layer price sanitisation (relay band 50500p / app decode guard / title-case)
  • Title-case station names (chunk-aware sanitizer)
  • Native large-title navigation (Mail-style collapse) — 313fe84
  • E10/E5 bracket labels in picker + descriptions — 05f3ef0
  • Gitea remotes created + pushed (apt/fuelboard, apt/fuelboard-relay)
  • Brand logos: proper 2x/3x assets, 75% smaller (315 KB → 79 KB)
  • Security audit (see SECURITY.md)
  • App Store approval gotcha review (see APPSTORE.md)