Commit Graph
265 Commits
Author SHA1 Message Date
FuelBoard Contributor 494412ba99 Debug: live alert trace — surface every gate decision from the alert chain
ProximityMonitor now records each stage the live chain reaches (Settings →
Debug → Alert trace, in-memory, newest-first, capped at 8):
- didEnterRegion arrival (regionEventCount + entry line) — distinguishes
  'geofence never fires' from 'gate blocked the alert'
- gate misses: station not in loaded list, hourly dedup (with minutes ago),
  no fuel sellers within radius after fresh fetch, entered-not-cheapest
  (with the cheaper station + price)
- the previously-silent fresh-fetch catch now logs the error
- 'alert scheduled' once UNUserNotificationCenter.add is called — if that
  line appears but no banner shows, the failure is delivery/permission,
  not alert logic

SettingsView shows the trace (symbol per kind) + region-event count; the
existing Test-alert button already probes notification permission and
reports denial inline. 83 tests, Release build green.
2026-08-15 11:10:10 +01:00
FuelBoard Contributor d0d25811c7 chore: untrack build-sim artifacts (ignored now; were committed before the rule) 2026-08-15 10:12:46 +01:00
FuelBoard Contributor aab2d02ef5 P1: Favourites Trends graph — FuelHistoryStore + TrendsView (Swift Charts)
- Shared/FuelHistory.swift: GitHub mirror read path (latest.json pointer +
  history/YYYY-MM-DD.json day files); day math (UTC noon keys), slim day decode
  with the shared price band, series + deltaSeries (vs-cheapest rebase),
  favourites-only app-group cache (90-day prune), parallel per-day fetches,
  404/network = graph gap never error.
- FuelPriceProvider: shared priceBand + mapGrades (single source for live +
  history decoding).
- FuelBoard/TrendsView.swift: line chart in the Favourites tab via toolbar
  button + sheet; fuel capsule (fuels with favourites only), 7/30/90 range,
  Price/vs-cheapest toggle, per-station legend, price-display style on y-axis,
  empty/loading/retry states with honest copy; no widget in v1.
- Localizable.strings: Trends keys.
- 83 tests (13 history + URL regression): day math, band guard, series gaps,
  delta rebasing, prune, and the appendingPathComponent regression (relative
  URL resolution dropped /main — all fetches 404'd).
- Sim-verified: favourites rows + TOP/deltas; sheet controls + building-up
  state with live first-snapshot date (archive has 1 day; lines render once
  >=2 snapshots).
- Backlog: P1 Trends DONE (unmerged); P0 REMAINING = live provider chain +
  telemetry beacon.
2026-08-15 10:12:23 +01:00
FuelBoard Contributor 5b83d9f510 Backlog: P0 mirror push side DONE (repo live, deploy key, launchd daily job); size math corrected to full-dump 2.58MB/day @180d retention (Option 2) 2026-08-15 09:24:33 +01:00
FuelBoard Contributor 0545659c1b Backlog: P0 GitHub mirror — capture telemetry implications (widget beacon survives, opportunistic app beacon, /stats canary + fallback-outage signal) 2026-08-15 08:43:59 +01:00
FuelBoard Contributor 817a829107 Backlog: P0 archive reshaped to GitHub price-history mirror (relay pushes daily latest.json + history/YYYY-MM-DD.json to public fuelboard-data repo; app reads raw.githubusercontent); P3 production-mirror item absorbed into P0 2026-08-15 05:49:18 +01:00
FuelBoard Contributor 16bf9ba9dd Backlog: price-history direction — P0 relay archive (start now) → P1 Favourites Trends graph → P2 avg/min-max overlay → P3 prediction arrow + widget sparkline 2026-08-15 05:39:51 +01:00
FuelBoard Contributor bb6ee33254 Stations: move price-rating Key (legend) into a modal sheet opened by an info button in the nav header; list stays focused on stations. Sheet = medium detent, inline nav title 'Key', Done button; legend rows unchanged. Adds -showKeySheet launch arg (QA hook for sim). 2026-08-14 23:53:02 +01:00
FuelBoard Contributor 75506b48b7 Telemetry: tag relay fetches with X-Client (app/widget) — app via RelayFuelProvider, widget focused/fuel-only fetches + diag beacon; Siri untouched (cached-only). Purely observational for relay admin /stats. 2026-08-14 23:23:06 +01:00
FuelBoard Contributor d91652aedc Onboarding: space the welcome feature rows out vertically (per-row padding 5pt → 10pt) 2026-08-14 22:47:48 +01:00
FuelBoard Contributor 9571e0c876 Onboarding: enlarge welcome-page feature icons 16pt → 20pt (semibold, wider alignment frame) for readability 2026-08-14 22:43:10 +01:00
FuelBoard Contributor bfb59d498a Live Activity: price now respects the Price display style — style carried in ContentState (captured at push time, extension renders deterministically instead of re-reading storage), setting change pushes an in-place content update via explicit override (no save-ordering race) 2026-08-14 21:06:04 +01:00
FuelBoard Contributor 8a589a35c1 Settings: drop parenthesized example figures from Price display segmented buttons — 'Station sign' / 'Pounds & pence' (cleaner, roomier labels) 2026-08-14 20:56:01 +01:00
FuelBoard Contributor e317a1e451 Settings: rename 'Show introduction' row to 'Replay onboarding' with round-arrow icon (arrow.clockwise.circle); key stays the address, value changed in Localizable.strings 2026-08-14 19:34:11 +01:00
FuelBoard Contributor 80113456ae Price display: station sign drops the £ (134.9) — matches roadside signs; pounds & pence uses forecourt superscript style £1.29⁹/L (small superscript third digit, small /L); attributed Text on list/widgets/Live Activity, Unicode superscript in Siri cards 2026-08-14 18:14:10 +01:00
FuelBoard Contributor fd7fa5f9c9 Settings: price display toggle — station sign (£129.9) vs pounds & pence (£1.299); display-only, all surfaces (list, widgets, Live Activity, Siri cards); Siri dialog stays speech-safe pounds 2026-08-14 15:33:26 +01:00
FuelBoard Contributor 819d2e6152 BACKLOG: favourite-directions phrase drop + Directions-to-Cheapest allocation 2026-08-14 14:50:53 +01:00
FuelBoard Contributor 8a4c472ab3 Siri: allocation swap B — Directions-to-Cheapest voice phrases restored (per-fuel), favourite-directions phrases dropped (card button covers it) 2026-08-14 14:50:31 +01:00
FuelBoard Contributor b46c09109e Siri: full-width Directions row on price card — fixes squashed 2-line button in narrow snippet 2026-08-14 14:45:51 +01:00
FuelBoard Contributor 6d7254ebba Siri: interactive price-card snippet (iOS 26 SnippetIntent) with Directions button → Apple Maps; zero AppShortcut slots 2026-08-14 14:38:14 +01:00
FuelBoard Contributor b458d92972 Siri: favourite price + favourite directions (top favourite per fuel); trim to 10 AppShortcuts (Apple cap) 2026-08-14 14:24:20 +01:00
FuelBoard Contributor aed99e3f32 Siri directions: speak the station before Maps hand-off (.result(opensIntent:dialog:)) 2026-08-14 14:05:42 +01:00
FuelBoard Contributor caadf184f2 Siri: Directions to Cheapest [fuel] AppShortcuts — OpenURLIntent hands off to Apple Maps (universal link) 2026-08-14 13:55:03 +01:00
FuelBoard Contributor a3b6f37843 Siri: shortcut names match app (Unleaded/Premium/Diesel); phrases cover UK speech variants 2026-08-14 13:23:52 +01:00
FuelBoard Contributor e55078c5e2 Siri: per-fuel App Shortcuts with literal fuel words (unleaded/petrol/premium/diesel) + UK case labels 2026-08-14 13:06:26 +01:00
FuelBoard Contributor 037c2418af Siri: scope cheapest-near-me to the saved search radius (5/10/15 mi) 2026-08-14 12:49:30 +01:00
FuelBoard Contributor be781d80f5 Backlog: Siri on-screen awareness (View Annotations + StationEntity) P2 2026-08-14 12:42:27 +01:00
FuelBoard Contributor 29d4a0472b Siri: cheapest [fuel] near me intent + App Shortcuts (cached-first, freshness label) 2026-08-14 12:32:31 +01:00
FuelBoard Contributor b5fe21c44b Backlog: Siri cheapest entry notes Tailscale interim + IN PROGRESS status 2026-08-14 12:29:09 +01:00
FuelBoard Contributor f85c74a7a8 Backlog: Localizable.strings key-normalization idea (P3, no action) 2026-08-14 12:02:26 +01:00
FuelBoard Contributor b66a9161a3 Copy sync: Localizable.strings matches current UI (114 keys; favourites reorder note, geofence debug section, alerts footer; dead keys pruned) 2026-08-14 11:07:15 +01:00
FuelBoard Contributor fbf8c79feb Favourites: drag-and-drop reorder per fuel; stored order drives single-widget favourite 2026-08-14 10:48:53 +01:00
FuelBoard Contributor aa95d214e8 Alerts: move geofence monitoring status to Settings debug; plain-language footer 2026-08-14 10:34:45 +01:00
FuelBoard Contributor edf376d378 Settings: move Data source section behind debug flag (debug-only) 2026-08-14 10:01:36 +01:00
FuelBoard Contributor 3dd567a5cc Widgets: merge small+medium into one kind (both sizes, one gallery entry) 2026-08-14 09:52:50 +01:00
FuelBoard Contributor e62177ae2e Backlog: add lock screen widgets (P2, Option B) 2026-08-14 05:02:43 +01:00
FuelBoard Contributor e044a1552a Backlog: add Siri/Shortcuts features (P1-P3); mark widget config done 2026-08-14 04:59:26 +01:00
FuelBoard Contributor f5a5d3325f Swap app icon to water-drop pound design (drip export) 2026-08-14 04:53:31 +01:00
FuelBoard Contributor b94f769750 Small widget: support .systemMedium so the icon menu offers both sizes
The iOS 18+ long-press app-icon menu (turn icon into a widget) shows
the supported SIZES of the FIRST-registered widget kind only — it
does not list widget kinds and is not keyed by intent type (two
distinct intents in 6375af4 changed nothing on-device). The bundle's
first kind (FuelPriceSmallWidget, small-first since 38b47ee) therefore
decided the menu offered only the small tile.

The early build worked because FuelPriceWidget was registered first
back then. Reordering to medium-first would fix the menu but flip the
gallery order (38b47ee). Instead: this first kind now supports BOTH
.systemSmall and .systemMedium, so the icon menu offers both size
tiles (ChatGPT/Safari behave the same way — their first kind supports
several families). The gallery still lists both widget kinds in bundle
order; the Small entry just gains a medium variant. The shared view
already branches on family, so medium renders the station list.
2026-08-14 02:53:52 +01:00
FuelBoard Contributor 6375af4c05 Small widget: restore its own intent TYPE (minimal params) for the icon menu
iOS keys the long-press app-icon contextual menu by configuration-intent
TYPE, so sharing one intent across kinds collapsed the menu to the
first-registered kind (small) — the medium 'FuelBoard Prices' vanished
from the icon menu while the + gallery (kind-keyed) still listed both.
dafc96f's skeleton fix was never the problem; the extra favourite
AppEntity params were.

FIX: FuelBoardSmallWidgetConfigurationIntent — a distinct intent TYPE
with the IDENTICAL minimal parameter set (fuel/sort/distance only, no
AppEntity params). Fresh-widget default-config resolution stays intact;
the appex metadata now registers two WidgetConfiguration actions.

Verified: build green, 45/45 tests, extract.actionsdata lists both
intents, fresh IPA packaged + signed per component.
2026-08-14 02:28:43 +01:00
FuelBoard Contributor 050c9f13a3 Onboarding: show the app icon with iOS rounded corners
The welcome page's green-gradient circle + SF symbol is replaced by the
app icon itself (AppIconArtwork imageset mirroring AppIcon.png), clipped
to the standard iOS rounded-rect corner radius (21pt at 96pt — ~22%),
so onboarding matches the Home Screen icon.
2026-08-13 23:41:16 +01:00
FuelBoard Contributor fec139cf5a Icon: replace with Fuelboard-copy composer render (Vecteezy gas-station art)
New Icon Composer project artwork (gas-station vector, layered + background)
rendered via ictool at 1024pt: Default / Dark / TintedLight renditions
replacing all three asset-catalog slots.
2026-08-13 23:36:57 +01:00
FuelBoard Contributor 018e6eec2f Icon: use the Icon Composer render instead of the pre-baked appiconset PNG
The export zip contained both a pre-rendered appiconset AND the
Fuelboard.icon composer project; the pre-baked PNGs were a different
(flatter) render. Re-rendered the .icon bundle with ictool (Xcode 26
Icon Composer executable) — Default/Dark/TintedLight renditions at
1024pt — and replaced the three asset-catalog slots.
2026-08-13 22:00:14 +01:00
FuelBoard Contributor 3545239ad6 New app icon: green pump showing £1.00, with dark + tinted variants
Replaces the single 1024px icon with the three-variant set (universal,
dark-luminosity, tinted) generated for FuelBoard; Contents.json updated to
the iOS-only slots.
2026-08-13 21:37:36 +01:00
FuelBoard Contributor cd88e23601 Widget pump icon flexes to the chosen fuel's colour
The pump glyph in the widget header (both the small single-station face and
the medium list face) was hardcoded green; it now uses the app's fuel
colour wheel — green #30D158 unleaded, yellow #FFD60A premium, cyan
#64D2FF diesel — mirroring the per-fuel pumps in the app's segmented
picker. Palette duplicated in the widget target (Shared is Foundation-only
by design); cross-referenced to StationsView.swift's FuelType.tintColor.
2026-08-13 20:53:20 +01:00
FuelBoard Contributor dafc96f845 Fix small widget skeleton: use the proven medium intent; delete failing small intent
ROOT CAUSE (proven by A/B test on-device): the small widget's custom
intent (FuelBoardSmallWidgetConfigurationIntent with favouriteFuel +
favourite AppEntity params) fails fresh-widget default-config resolution
at the system level — the whole default config fails, no timeline is
ever requested, makeEntry never runs, widget stays on the skeleton.
A minimal small widget sharing the MEDIUM intent (fuel/sort/distance
only) populated immediately in the same extension on the same device.
Neither removing defaultResult() (4d35b08) nor the
@IntentParameterDependency (8a9e775) helped — the extra entity params
themselves break resolution.

FIX: FuelPriceSmallWidget now uses FuelBoardWidgetConfigurationIntent
exactly like the working test widget. FuelBoardSmallWidgetConfiguration
Intent, FavouriteFuel(+Query), WidgetFavourite(+Query) deleted; the
pinned-favourite picker is gone (approved tradeoff). Favourites sort
still works on both faces via the list branch — the small face renders
the cheapest favourite of the chosen fuel. Beacon tag distinguishes the
small widget in diagnostics; test widget removed, gallery order
restored (small, medium, live activity).
2026-08-13 20:43:15 +01:00
FuelBoard Contributor 553730681f A/B test: from-scratch minimal small widget (proven medium intent)
Temporary FuelBoardTestSmallWidget kind: a small-face widget driven by
the PROVEN medium intent (fuel/sort/distance only — no favourite params,
no dependency, no custom entity query), registered first in the gallery.
Isolates small SIZE vs small INTENT: if it populates while
FuelPriceSmallWidget skeletons, the small intent's extra machinery is the
culprit; if it also skeletons, the failure is small-family/system-level.
Provider gains a beaconTag so the test widget's beacons are
distinguishable in the relay log and keychain slots. Removed once the
root cause is confirmed.
2026-08-13 20:27:03 +01:00
FuelBoard Contributor 8a9e77546f Small widget: remove @IntentParameterDependency from WidgetFavouriteQuery
Final structural difference from the working medium widget. The
dependent-picker machinery resolves the favourite parameter through the
hidden favouriteFuel parameter during fresh-widget default-config
resolution (default Sort = Cheapest hides favouriteFuel), and that
resolution fails on-device — the whole default config fails and the
small widget strands on the skeleton. Favourite picker now lists ALL
favourites, each row labelled with its fuel (the fuel-scoped filter was
the dependency's only purpose). Configured Favourites-mode widgets are
unaffected. Per-intent beacons remain in place to verify the outcome.
2026-08-13 20:23:12 +01:00
FuelBoard Contributor 3cf1d5c9af Widget diagnostics: per-intent beacons (keychain slots + relay query)
The single-slot beacon couldn't distinguish small vs medium (last writer
wins). Beacon keychain service now embeds the intent type name so small
and medium never overwrite each other; the relay GET now carries the
intent type + entry state as query params so the access log shows exactly
which widget kind ran makeEntry, when, and with what. Diagnostics screen
shows SMALL and MEDIUM beacons side by side — a missing SMALL beacon
after adding the small widget proves its timeline never reaches the
provider (system-level config resolution failure).
2026-08-13 20:13:24 +01:00
FuelBoard Contributor 335a9751dc Widget diagnostics: beacon from makeEntry (keychain + relay) + app diagnostics section
The small-widget skeleton survived four resolution fixes; instrument the
widget instead of guessing. Every makeEntry now writes its entry state
(intent type, location source, station count, fuel, sort, favourites flag,
first station) to a keychain beacon (app-readable) and fire-and-forgets a
GET to the relay's /api/v1/widget-diag so its access log records whether a
widget timeline actually runs in the extension and what it produced.
Settings → Debug gains a Widget Diagnostics section: the keychain beacon +
the installed-widget inventory from WidgetCenter. Relay gains the
/api/v1/widget-diag route (204, log-only).
2026-08-13 19:52:12 +01:00