Everything that changed in Garage Buddy, newest first.
1.2.3 2026-08-05
Fixed
Documents are named for when they were issued, not when they expire. 1.2.2 led a registration's filename with its expiration, filing a registration issued this year under next year. The document scan now reads the issue/effective date as well — it previously extracted only the expiration, which is why the name reached for the wrong one — and that is what the filename leads with: "2026-03-14 — Registration.pdf". A document showing only an expiration gets no date rather than a misleading one. The expiration is still read and still drives the renewal reminder; it just doesn't name the file.
1.2.2 2026-08-05
Changed
Document names lead with the document's own date, in YYYY-MM-DD. A receipt is now "2026-07-16 — Les Schwab.pdf", so documents sort chronologically in the app, in Drive, and in Files. The date is read off the document, never today's; a document with no date of its own gets no date rather than a filing date, which would say when you scanned it instead of what it is.
1.2.1 2026-08-05
Fixed
"Suggest a Name" reported readable receipts as unreadable. The document read only knows vehicle paperwork — title, registration, insurance — so a receipt came back classified as "other", and the app turned "I have no name for this kind of document" into "Couldn't read that document", sending you looking for a problem that wasn't there. Receipts are now read as receipts and named for the vendor and date ("Les Schwab — 2026-07-16.pdf"), and the two failures are told apart: one message for a file that wouldn't open, a different one for a document that was read but couldn't be named.
1.2.0 2026-08-05
Added
Scanned documents name themselves. With an AI key configured, a document you photograph or scan is read before it's saved and filed under a name worth reading — "Registration — expires 2027-03.pdf", "Insurance — State Farm.jpg" — instead of "Document 2026-08-05.pdf". The name comes from the structured fields the app already extracts, not from asking a model to invent a filename. Characters a filename can't hold (slashes, colons, newlines and the rest) are replaced, names are length-capped on a word boundary, and anything unusable falls back to the old dated name. No key, no signal, or an unrecognised document all fall back silently — the document still saves. Files you import keep the name you already gave them.
Rename a document. Long-press any car document → Rename. The extension is preserved for you, so a file can't stop opening as a PDF because the name was cleared. With AI configured, "Suggest a Name" reads the document and fills the field for you to accept or edit — it never applies a name behind your back, and it says so rather than replacing a good name with a generic one when it can't tell what the document is. Renaming also renames the file in your Google Drive, so the two don't drift apart.
Full vehicle history — the packet you hand a buyer. A car page now has two export buttons. The existing share button still produces the quick report; the new one builds a complete history document: a cover page, an AI-written history of the car (how long you've had it, how it's been used, what's been repaired and when), charts for mileage, spending, where the money went, and fuel economy per fill, then the service and repair record with your notes in full, the complete log, a page or two of photo thumbnails, and the scanned receipts themselves.
It deliberately withholds what a buyer shouldn't get. No purchase price, sale price, or profit anywhere in it. No title, registration, or insurance documents — those carry VIN provenance, policy numbers, and your address — and no receipts filed under Insurance or Registration. Those entries still appear as lines in the log; only their scanned images are held back. Nothing is removed from the app or the quick report.
It degrades instead of failing. No AI key or no signal means the history page is simply left out, and any attachment that can't be downloaded is counted in the document ("2 attachments couldn't be downloaded") rather than quietly missing. Photos beyond the two-page cap are reported the same way.
Receipts are downscaled before embedding, so a packet with dozens of them stays a sane file size. Multi-page PDF receipts are included in full, each page captioned "page 2 of 3" — a shop invoice often runs its line items and totals onto later pages.
The progress panel says what it's actually doing instead of one unchanging line: which step is running, what it's working on right now (the model being asked, or the file being fetched and how far through), and a per-step seconds counter so a slow AI call is visibly alive rather than apparently hung. Past eight seconds it also notes that the writing step usually takes 10–30 seconds.
1.1.0 2026-08-01
Added
Garage Buddy on the Mac. The app now runs natively on macOS (Mac Catalyst). Sign in with the same Google account and it reads and writes the same spreadsheet as your iPhone — edit on either, and the change shows up on the other. On-device mode stays per-device, since there's no shared storage to sync.
iPad support. Required for the Mac build, and the layout adapts.
Fixed
PDF reports couldn't be saved on the Mac. Exporting a report opened the macOS Share menu, which offers share destinations only — AirDrop, Mail, Messages — with no way to save the file. "Save to Files" is an iOS-only share action; on macOS saving belongs in a save panel. The Mac build now opens a proper save panel, and iPhone/iPad keep the share sheet (where Save to Files already appears alongside AirDrop and Mail). This also clears the duplicate "Copy" entry the Mac menu was showing.
Buttons were nearly invisible in dark mode. The accent colour had no dark-appearance variant, so blue-on-dark text sat at a 1.21:1 contrast ratio against a 4.5:1 accessibility minimum — worst on Settings, where "Connect Google Sheets" and "Keep Everything on This Device" were barely legible. Now 5.11:1. This affected iPhone dark mode too, not just the Mac.
The app called itself "CarLog" on the Mac — in the menu bar, in Finder, and in the macOS "…wants to use accounts.google.com to sign in" prompt. It now says Garage Buddy everywhere.
1.0.0 2026-07-24
Changed
First public release. No functional change from 0.38.4 — this version exists so the build matches the 1.0 record in App Store Connect. Every feature below shipped and was field-tested through the 0.x TestFlight series: local-first or Google Sheets storage with two-way migration, AI/on-device odometer scanning, per-fill MPG and MPGe, reminders and service intervals, photo albums and document scans, PDF reports, and home-screen widgets.
Fuel economy no longer reads in the thousands of MPG. An entry with no odometer reading was being used as the starting point for the tank-method average, so the car's whole odometer counted as one tank's worth of miles — a car doing a real 30 MPG could show as 2,515 MPG on the fleet economy chart, in the PDF report, and in the AI fleet summary. The same applied to EV efficiency (MPGe). This completes the 0.38.2 fix, which covered the mileage charts but missed the economy calculations.
No more reminders for cars you've sold. Date reminders on a retired car kept their scheduled notifications, so a repeating one (registration, insurance) would nudge you every year about a car you no longer own. Retiring or un-retiring a car now updates its notifications immediately.
A bad AI Base URL no longer crashes the app. Typing or pasting an invalid address into the custom AI endpoint (a stray space, a trailing newline) crashed on the next scan — and because the value was saved, it crashed again on every retry. It now reports a clear error instead.
The AI provider picker stops erasing itself. When entering a custom model id, choosing a provider reset the model back to a suggested one, which made the Provider section — and the Base URL field with it — disappear mid-selection. Choosing a provider now keeps your custom model and just moves the endpoint.
0.38.3 2026-07-20
Fixed
A dropped connection during a data move can no longer lose your photos. While moving data between Google and this device, any problem reading a file was treated as "you must have deleted this one" — so the move quietly unlinked it and carried on. A Wi-Fi drop, a timeout, or Google rate-limiting a burst of downloads partway through could therefore detach every remaining photo and document, report the move as successful, and (moving back the other way) overwrite the originals. Only a file the source genuinely no longer has is skipped now; anything else stops the move, which was always safe — nothing is deleted and you can simply retry.
0.38.2 2026-07-20
Fixed
Car prices no longer collapse to a few dollars. Typing a purchase or sale price went wrong the moment it reached four digits: the field re-formatted itself mid-typing (2500 → 2,500), and the next digit made it read the separator as a decimal point — so a $25,000 car saved as $2.50. Prices are now parsed once when you tap Save, matching how the rest of the app handles money entry.
Entries without an odometer reading no longer distort everything. A blank odometer (an insurance or registration entry, or an imported row with an empty cell) was being read as a genuine reading of zero, which:
charted the car's entire odometer as miles driven, smeared back across every month before the first real reading;
reported that whole figure as "driven with you" on the car and in the PDF report;
raised a false "possible odometer typo" warning on clean data, and put the orange check-this-entry triangle next to correct entries in the report. A reading of zero now consistently means "no reading" everywhere, as it already did on the activity heatmap.
Miles land in the right months. An entry that recorded no new miles (a blank or rolled-back odometer) reset the measuring point, pushing the surrounding interval's miles into later months on the chart.
Finishing an overdue reminder actually clears it. Completing a repeating reminder rolled it forward by exactly one interval — so an oil change 12,000 miles overdue, or a registration two years past, came back still overdue and needed tapping again for every interval you'd missed. It now rolls forward to the next date or mileage genuinely ahead of you (keeping any custom offset), and overdue annual reminders can be notified again.
Report typo-flagging catches both sides. When several entries share the most recent day, all of them are now checked against a later-dated higher reading instead of only the first.
Changed
The unit-test bundle can be code signed again (it had no generated Info.plist, so xcodebuild test failed to build). Tests are part of the release pipeline, so this had to work before anything could ship on it.
0.38.1 2026-07-20
Fixed
Data moves are safer (local-first review). A batch of fixes to the Google↔on-device migrations:
Adding or editing anything during a move is now held with a "finishing the data move" note and the + button hides, instead of the change landing in the store about to be swapped out and being lost.
Moving back to Google no longer resurrects deleted cars/entries. The move now clears the destination sheet before rewriting it, so it mirrors what's on your device exactly (previously it merged into the reused spreadsheet, so anything deleted while on-device reappeared).
A single file the app can't read (a Drive file you deleted, a missing local blob) is skipped and reported instead of aborting the whole move.
Tapping "Move Data to Google Sheets" twice can't start two moves; a failed move cleans up the files it uploaded so a retry doesn't pile up duplicates in Drive.
Charts tell same-named cars apart. Two cars with no nickname and the same year/make/model (or two blank "Untitled Car"s) no longer share a color, collapse into one legend entry, or get isolated together — charts now key on each car's identity, not its display name.
Smoother photo album. Adding several library photos at once, and opening a photo full-screen, no longer briefly freezes the screen (image encoding moved off the main thread); album memory use is capped.
0.38.0 2026-07-18
Added
Local-first mode — no Google account needed. On first run the app asks where your data should live: a Google Sheet in your own Drive (as before), or entirely on this iPhone. Local mode stores everything on-device (included in your device backup), works with zero network, and only the optional AI features ever leave the phone. You can switch later and take your data with you, in both directions, from Settings → Data: "Move Data to This Device" copies every entry, photo, and document out of Google (leaving the spreadsheet behind as a frozen snapshot), and "Move Data to Google Sheets" moves a local garage back into Google Sheets + Drive. Both moves show progress, survive being retried, and never delete the source until the copy has fully succeeded.
0.37.1 2026-07-18
Fixed
Copying the PDF report finally pastes. The share sheet's Copy now puts a named PDF *file* on the clipboard — the same thing the Files app's Copy produces — so pasting into Messages, Mail, or Files attaches the report. (The previous fix copied typed PDF bytes, which most apps ignore on paste; that's why Copy still appeared to do nothing.)
0.37.0 2026-07-18
Added
Add several album photos at once. "Add Photos" on a car's page now opens a multi-select library picker — pick any number and they all join the album in selection order (the first becomes the cover on an empty album). Taking a photo with the camera remains one at a time.
Paste photos into the album. Copy a photo anywhere (e.g. from a text message) and "Add Photos" offers Paste Photo whenever the clipboard holds an image. iOS asks its usual allow-paste question on first use.
Fixed
Chart legend wraps. The tappable car legend under the charts scrolled horizontally, which clipped cars off the end and dragged the chart when swiped. It now wraps onto as many lines as needed, every car visible.
Garage rows no longer wrap. With the to-do counter and badges in the row there wasn't width for longer names, which wrapped onto extra lines. The name and odometer use a slightly smaller font and names stay on one line (truncating if very long).
Car page toolbar overlap. The car's PDF-export button overlapped the Edit button; it now sits on the left edge of the navigation bar, in the same spot as the whole-garage export button.
Dashboard summary cards. Long amounts (e.g. $19,930) in the Spent card no longer wrap — the value shrinks to fit on one line. The Cars card now opens the Garage tab when tapped, and the Due soon card jumps to the reminders list further down the dashboard.
Fuel $ chart crash. The fuel-price chart colored each dot by the car name *stored on the transaction*, while its color scale was pinned to *current* car names — so any fill logged before a car was renamed (or a hand-edited name) crashed the chart the moment it appeared, every time. Dots now resolve the car by its stable id and always use the live name; fills whose car no longer exists are dropped. (Reproduced against the chart framework and covered by regression tests; the other charts already derived names from live cars and were never affected.)
0.36.0 2026-07-18
Added
Car photo albums. Each car has a Photos section on its page: add whole-car shots from the camera or library, tap to view full-screen (pinch-zoom), long-press to delete or pick the cover — the photo that headlines the car's page and appears as its thumbnail in the garage list (first photo added becomes the cover automatically). Pictures are stored in a "Photo Album" folder inside the car's own Google Drive folder, ride the same offline pipeline as receipts (photos added with no signal appear immediately and upload when a connection returns), and render from an on-device cache that decodes thumbnails at thumbnail size — no Drive round-trip, no scroll stutter. The album is separate from the AI "reference photo" (still a dashboard shot for scan recognition) and from Documents (paperwork).
Per-car to-do list. A lightweight checklist on each car's page for maintenance things you notice and don't want to forget — type an item, tap to check it off, swipe to delete. No due dates or notifications (that's what Reminders are for). The garage list shows a badge with each car's open-item count. Stored in a new Todos tab (created automatically) and fully offline-capable like every other write.
Fixed
Offline cache survives updates. Adding a new field to the on-device cache (the todos list) would have silently discarded every existing cache on first launch after updating — blanking the app for anyone who updated while offline. The cache now decodes old files, treating missing sections as empty (with a regression test).
Sheet booleans (is_done) are now parsed by one shared reader for reminders and to-dos, so the accepted truthy spellings can't drift apart.
Removed
Driving-activity heatmap dropped from the PDF report — the GitHub-style day squares spread miles evenly between odometer readings, which didn't reflect how entries are actually logged.
0.35.0 2026-07-17
Added
Concurrent-edit protection. Rows now carry an updated_at stamp (column added automatically). A change queued offline no longer clobbers an edit made later from another device or the sheet — the newer edit wins and the stale queued write is skipped. Rows without a stamp (hand edits, older data) always accept writes, preserving hand-edit tolerance.
Attachments work offline. Receipts and documents captured with no signal are staged on-device, appear (and preview) immediately, and upload to Drive automatically when a connection returns — completing the offline story. Deleting a still-queued attachment cancels the upload instead of round-tripping it.
Fleet report. A new report button on the Dashboard opens a this-month / this-year fleet summary: total miles and spend, a utilization ranking (workhorses at the top, idle cars flagged), and an AI-written narrative covering standouts, upcoming maintenance, and anomalies. The year view doubles as a year-in-review.
Scans learn from corrections. When a photo scan guesses the wrong car (or can't guess) and you pick the right one before saving, that photo automatically becomes the car's reference photo if it didn't have one — so the next scan recognizes that dashboard.
AI document reading. Long-press any car document (new or previously imported, photo or PDF) → "Extract Details" has the AI read it: VIN, license plate, policy number, carrier, and expiration date, presented in a review sheet where you choose what to apply — set the car's VIN/plate, and create a renewal reminder from the expiration date. Nothing is applied without review.
EV support. New "Charging" entry category with a kWh field (a kwh column is added to existing sheets automatically). EVs get a per-charge efficiency chart (mi/kWh with moving average) on their car page, and they join the fleet economy chart as MPGe (33.7 kWh ≈ 1 gal), marked with an "e" — the Model 3 and the F-150 finally share a scale.
Single-car report. A share button on the car screen exports that car's full history as its own PDF — the "hand it to the buyer or mechanic" document.
Report: documents + driving heatmap. Each car's report section now ends with its car-level documents (title, registration, …) and includes a compact GitHub-style driving-activity strip (one square per day for the last 26 weeks; miles spread between odometer readings).
PDF report navigation. The garage overview rows on the report's first page are now clickable — tapping a car jumps to its section. Cars are ordered deliberately (active first, then most recently driven) instead of whatever order the sheet had.
Smarter anomaly alerts. Two new data-quality warnings join the rollback/stale badges: an entry costing several times the car's norm for that category ("check for a typo"), and a fill-up whose fuel economy is well below the car's recent average ("service may be due"). Shown on the car page and as garage badges.
Fuel price trends. A new "Fuel $" dashboard chart shows what you actually paid per gallon (or liter) at every fill — one dot per fill-up, colored by car, with a 5-fill rolling average line. Swipe/range and the tap-to-isolate legend work like the other charts.
Service templates. New reminders offer one-tap presets (oil every 5k, tire rotation every 7.5k, registration yearly, insurance every 6 months, …) that fill the form with the repeat interval and the next due point anchored to owner's-manual mileage multiples. Everything stays editable before saving.
Retire = sell in one step. "Retire (Sold) Car" now opens a small sheet asking for the sale price (optional, with live profit preview) and sale date, instead of retiring and then editing the car to record the price.
Report entries sort by mileage (highest first), and any entry whose calendar day contradicts the mileage order gets an orange "date out of order" flag — likely an entry error. Several same-day entries with shuffled miles are normal and never flagged. Both sides of a date/mileage disagreement are flagged, since either could be the typo.
Fixed (from code review)
Locale-safe number entry. Costs, gallons, and car prices are parsed with a locale-tolerant parser: "1,250.50", "1.250,50", and "1250,50" all read correctly. Previously a US-style thousands separator silently dropped the cost, and comma-decimal input could inflate a price 100×.
Revoked Google access recovers gracefully. If you remove Garage Buddy's access in your Google Account, the app now signs out locally, keeps your cached data and queued changes, shows one clear banner, and Settings' "Try Again" runs the full sign-in. Previously it looped error banners while still looking connected.
Car document PDF import reports read failures instead of silently doing nothing.
AI scan replies parse more robustly — JSON extraction now survives models that wrap the answer in prose containing braces.
Odometer photos decode once, not twice, for the upright + rotated OCR passes (faster scans, less memory).
Settings footer no longer references the old "Car Log Data" sheet name.
0.34.1 2026-07-16
Fixed
"Copy" in the report share sheet now copies the PDF itself (pasteable into Mail as an attachment) instead of a useless file path. Sharing directly to Mail also gets a sensible subject line.
0.34.0 2026-07-16
Added
Car documents. A car's Documents section now has an "Add Document" button for files that belong to the car itself — title, registration, insurance card — with the same scan/photo/PDF options as entry attachments. They upload to the car's Drive folder and no longer require inventing an entry to hang them on. (The Attachments tab gains a car_id column, added to existing sheets automatically.)
0.33.3 2026-07-12
Fixed
The Dashboard's chart tab now follows the fuel-economy unit setting — "MPG" becomes "km/L", "L/100 km", or "gal/100 mi" — and "Mileage" reads "Distance" for metric users.
0.33.2 2026-07-12
Fixed
No more red "Cancelled" banner. Interrupted requests (backgrounding the app mid-sync, abandoning a pull-to-refresh, dismissing the Google sign-in sheet) are cancellations, not failures — they're now silenced everywhere errors surface, and a cancelled sync retries automatically on the next foreground/reconnect.
0.33.1 2026-07-12
Changed
The spreadsheet is now named "Garage Buddy" (was "Car Log Data"). The app renames an existing sheet in place on next connect — the Drive id is unchanged, so nothing about your data or links breaks. Fresh installs also find the sheet under either name.
0.33.0 2026-07-12
Added
Units settings (#10): Settings → Units picks distance (miles/km), volume (gallons/liters), and fuel economy (MPG, km/L, L/100 km, gal/100 mi). Applies across entry forms (type in your unit — the sheet keeps storing miles/gallons unchanged), charts, car details, reminders, widgets, the PDF report, and scan plausibility checks. Defaults are mi/gal/MPG, so nothing changes until you switch.
All app data now lives in a "Garage Buddy" Drive folder (#15): new spreadsheets are created there, and attachments/reference photos go into per-car folders directly beneath it. Existing files keep working wherever they are (Drive tracks them by id); a short manual migration moves old files into the new folder.
0.32.1 2026-07-12
Changed
The exported PDF is named with the date ("Garage Buddy Report 2026-07-12.pdf"), so saved reports don't overwrite each other and sort chronologically.
0.32.0 2026-07-12
Changed
PDF report (#14): notes now render as grey text underneath their entry; the main table columns got the freed width and never wrap.
Dashboard (#16, #17, #18): the reminders card only shows IMMINENT reminders (overdue, or due within 30 days / 500 miles) and sits below the graph; the "Entries" tile is now "Due soon" (imminent-reminder count); Recent Activity shows the last 5 entries. The Reminders widget follows the same imminent-only rule.
Garage (#19): a car's impending-reminder bell can no longer be hidden by a data-quality warning — reminder and odometer/stale badges now show independently side by side.
Fixed
Demo/seeded mode showed "Not Connected" since 0.29.0 (it never set the data-loaded flag the views now gate on). DEBUG-only.
0.31.0 2026-07-12
Added
Unit tests (#9): a CarLogTests target with 48 tests across six suites covering the app's pure logic — mileage distribution (incl. the 0=unknown rule), per-fill MPG + moving average, tank-method MPG, service-interval anchoring (the 0.27.1 bug is now a permanent regression test), car matching, lenient sheet-date parsing, transaction dedup, OCR number extraction, the offline journal/cache round-trips, offline-error classification, ISO dates, and AI JSON extraction. The test suite runs in Xcode Cloud on every push — a failing test now blocks the TestFlight deploy.
Changed
Service-interval due-point math moved to Analytics (nextServiceDue); a few internal helpers exposed for testing. No behavior changes.
0.30.2 2026-07-12
Fixed
Attachments with slashes in their names open again (#12). Files named before the ISO-date switch (0.14.3) — e.g. "Photo 7/4/2026.jpg" — failed to preview with a "file doesn't exist" error, because the slashes read as folders in the local temp path. The name is now sanitized for the filesystem only; display names and Drive files are unchanged.
0.30.1 2026-07-12
Added
API keys are verified when you save them — a tiny live call runs against the provider and reports immediately: verified, rejected (bad paste/revoked), over quota (with an explanation of free-tier resets vs billing), or model-access problems. A "Verify Key" button re-checks a saved key any time. No more discovering key problems as cryptic scan failures.
0.30.0 2026-07-12
Fixed
API keys no longer "disappear." Two hardening fixes: key state is now read live from the Keychain (a launch-time cache could go stale when iOS prewarmed the app before first unlock, making every key look lost at once), and keys are stored with AfterFirstUnlock protection so prewarmed reads succeed. If your keys were showing as missing, they may reappear after this update; if the app was reinstalled, they need one re-paste (iOS wipes an app's Keychain on uninstall).
Changed
AI settings reorganized. The section is now just "AI" (it powers scanning AND text features like service intervals, not only photos):
The model list is the page's focus; the Provider picker only appears when a custom model id is selected.
The API Key section names the provider it edits and lists which providers have saved keys.
The test bench moved to its own "Test Models" screen.
0.29.2 2026-07-12
Added
Sync status chip on the Dashboard. An orange "Offline" capsule (with the queued-change count, e.g. "Offline · 2") appears in the nav bar when disconnected — tap it to retry — and a brief "Syncing" chip shows while queued changes drain after reconnecting. Nothing shows when everything is normal.
0.29.1 2026-07-12
Fixed
Offline launch actually shows your data now. GoogleSignIn's silent sign-in restore needs the network to refresh an expired token, so it fails in airplane mode — and 0.29.0 gated the offline cache behind that check, showing "Not Connected" instead of your garage. The cache now loads regardless (its presence proves ownership — signing out deletes it); auth is retried automatically when connectivity returns, then queued changes sync. Offline sessions also keep the cache updated and show the account name in Settings.
0.29.0 2026-07-12
Added
Offline support (ADR-016). The app now works without a connection:
Instant launch / offline viewing — the last-loaded data is cached on device and shown immediately at launch (even in airplane mode); the live Google sync refreshes in the background.
Offline logging — every change (entries, reminders, cars, deletes) goes into a persistent write-ahead journal and syncs automatically when connectivity returns (on reconnect, foreground, or launch). Queued changes survive app restarts and are always visible in the app.
Offline status — Settings shows an "Offline" state with the number of changes waiting to sync and a Try Again button; the rest of the app stays fully usable, including the quick-add button.
Attachments and reference photos still need a connection (they upload to Drive) and say so if it fails.
0.28.3 2026-07-12
Changed
Errors now show as a banner instead of an alert — a red banner slides in at the top, auto-dismisses after 5s (tap to dismiss sooner), and renders in its own overlay window so it appears above sheets too. This closes the last silent-failure hole: an alert can't present while a sheet is open, so a failed save or attachment upload during entry showed nothing.
0.28.2 2026-07-12
Fixed
MPG tab no longer goes blank on short ranges. The tank method needs two fills inside the window, so the 6-month default could empty the chart for seldom-driven cars — it now falls back to all-time averages (and says so), and an isolated car with no fills in range shows its full history instead of bouncing back to the fleet view.
Gemini 3.1 Pro scans no longer risk empty responses: the output-token cap was raised so reasoning models can think before emitting the JSON (unused headroom costs nothing).
A custom OpenAI-compatible base URL now survives switching providers — it's stored separately and restored when you come back.
AI calls retry on rate limits/transient errors (1s/2s backoff) — Test All's burst of requests was prone to spurious 429 failures.
Signing out clears cached reference photos, so a different account's scans can't match against the previous account's dashboards.
0.28.1 2026-07-12
Changed
One model list across all providers. The scanning list now shows every provider's models together (Claude, Gemini, GPT — plus On-device and a custom id); picking a model automatically selects its provider and uses that provider's saved key. Models whose provider has no key yet show an orange key badge. The Provider picker remains only for custom model ids and the generic endpoint's base URL.
0.28.0 2026-07-12
Added
Per-provider API keys. Each provider (Anthropic, Gemini, OpenAI-compatible) keeps its own key in the Keychain — switching providers never loses a key. Your existing key migrates automatically to the provider it was saved under.
Cross-provider Test All. The scan test bench now sweeps every model of every provider you've saved a key for (plus on-device) in one tap — e.g. Opus, Sonnet, Haiku, Gemini Pro, Gemini Flash, and on-device, all on the same photo.
0.27.2 2026-07-12
Changed
Faster scans. Reference photos are only uploaded when the car is still unknown (a scan with the car already picked skips them entirely); the main scan image is capped at 1600px (~40%% fewer upload bytes and vision tokens, digits still crisp); receipt pages are downsampled to 1800px for the upload (the stored PDF keeps full quality); the on-device OCR's two orientation passes now run in parallel. JPEG quality is unchanged — compression artifacts hurt digit reading.
0.27.1 2026-07-11
Fixed
AI service intervals now anchor to absolute odometer multiples (the way the owner's-manual table works: 30k/60k/90k…), not to current mileage + interval — which wrongly assumed every service had just been performed and set everything one full interval late. A 10k oil interval on a car at 80,282 mi now comes due at 90,000 (was 90,282); 60k plugs at 120,000 (was 140,282). The review sheet shows each item's computed due point, and the footer explains how to adjust when you know a service was done recently.
0.27.0 2026-07-11
Added
AI-suggested service intervals. On a car's page (Reminders section, when an AI key is set): "Suggest Service Intervals (AI)" asks your model for the manufacturer's routine schedule — oil, rotation, filters, fluids, plugs — and presents it for review with checkboxes. Selected items become repeating reminders: mileage intervals anchor to the current odometer, time intervals to today. Items whose name matches an existing reminder default to off, and every created reminder is noted "AI-suggested — verify against the owner's manual." EVs get EV-appropriate schedules (no oil changes).
0.26.2 2026-07-11
Fixed
On-device OCR no longer reads upside-down odometers backwards (80282 → 28208). Dashboard photos taken pointing down often carry a wrong gravity-based orientation from iOS, and rotationally-symmetric seven-segment digits (0/2/8) still "read" flipped. The on-device reader now OCRs the photo both ways and lets mileage plausibility pick the orientation (the rotated reading must win by a clear margin). The AI prompt also warns the model the photo may be inverted.
0.26.1 2026-07-11
Added
Test All in the scan test bench: one tap runs every model of the current provider plus on-device OCR sequentially on the same photo, with progress shown per engine. (Cross-provider sweeps would need a second API key — each provider's models share the one saved key.)
0.26.0 2026-07-11
Added
Scan test bench in Settings → AI. Choose a test photo once, then run the complete odometer pipeline — reference photos, seven-segment prompt, on-device cross-check — against the currently selected model. Results accumulate (model, reading, the digits it saw, car match, confidence, cross-check agreement, elapsed time), so you can switch models and re-run on the same photo to compare. Nothing is added to your log.
0.25.0 2026-07-11
Added
Google (Gemini) provider (#8). Settings → AI now offers Gemini via Google's OpenAI-compatible endpoint — Gemini 3.1 Pro (class-leading vision) and Gemini 3.5 Flash (fast/cheap). Paste a Gemini API key and pick a model.
On-device cross-check for AI odometer scans. Every AI scan also runs the free on-device OCR in parallel: if both read the same number the scan message says so; if they disagree it shows both readings and asks you to double-check — catching the single-flipped-digit errors segmented displays cause.
Changed
Scan prompts rebuilt for seven-segment displays (#8): the model is told the display type and its classic confusions (0/8, 5/6, 6/8), to transcribe digit by digit, to exclude boxed/decimal trailing tenths digits and trip meters, and to sanity-check digit count against the car's last known miles. Gas-pump scans get the same treatment plus explicit decimal-place guidance.
Requests now follow the documented best layout for vision models: images come before the instructions and every image is individually labeled (odometer photo, each reference photo, each receipt page).
0.24.1 2026-07-11
Changed
Dashboard charts now default to the 6-month range (was All). Swipe up on the chart or tap the range chip for longer windows.
0.24.0 2026-07-11
Added
Swipe the Dashboard chart (#6): left/right switches between Mileage / Expenses / MPG; up/down widens/narrows the time range (6M · 1Y · 2Y · All). A tappable range chip next to the subtitle shows the active window and cycles it as an alternative to swiping. The range also scopes the MPG views (fleet averages and per-fill charts).
Changed
All dates now display in ISO-8601 (#7): entry rows, car details, reminders (in-app and widget), notifications, and the PDF report show YYYY-MM-DD. Attachment filenames already did.
0.23.0 2026-07-11
Added
Per-fill-up MPG charts (#5). A car's fuel economy now shows every individual fill-up as a bar (miles since the previous fill ÷ gallons added) with a 5-fill moving-average trend line:
Dashboard → MPG: tap a car in the legend to drill from the fleet averages into its per-fill history; tap again to go back.
Car detail: a new "Fuel Economy" section shows the same chart.
Implausible readings (missed fills, typos over 150 MPG) are dropped from the chart rather than distorting it.
0.22.2 2026-07-08
Fixed
PDF report page breaks are clean. Sections were rendered as one tall image and sliced every page-height, cutting rows mid-text. The report is now paginated block-by-block (headers, charts, individual table rows), so a row that doesn't fit moves whole to the next page. Tables use fixed-width columns so rows stay aligned across pages, and table headers travel with their first row. Verified against a 21-page report with 47-row tables.
0.22.1 2026-07-08
Fixed
The Scan Odometer widget opens the camera again on a cold launch. The entry sheet appears before the Google sync delivers the cars; the auto-scan ran once, found no cars, and gave up silently. It now fires as soon as the fleet loads (and shows "Loading your garage…" instead of "Add a Car First" while syncing). Warm launches were unaffected — which is why the widget seemed to work until an app update forced a cold start.
Notes
Adds DEBUG-only launch args (-seedSlow, -quickActionScan) used to reproduce and verify the race in the simulator.
0.22.0 2026-07-08
Added
Fleet-wide Reminders on the Dashboard. Every open reminder across active cars, most urgent first — tap to open the car, long-press to mark done. The in-app counterpart of the Reminders widget.
Reference photos are now wired up. Add a dashboard/interior photo when editing a car (the reference_photo_id field finally has UI); AI odometer scans attach the fleet's reference photos so the model identifies the car visually, not just by mileage. Photos are cached locally for fast scans.
Fixed
Picking GPT-4o mini no longer silently reverts to GPT-4o on next launch (the legacy weak-model upgrade now runs exactly once).
Disconnecting clears attachments, reminders, and the home-screen widgets — no more previous-account data lingering after sign-out.
"Driven with you" and the PDF report now honour the "0 = unknown starting mileage" rule, matching the charts (a used car with unknown start no longer shows its whole odometer as driven).
Deleting an entry now also deletes its attachments (sheet rows + Drive files); cancelling a new entry cleans up attachments already uploaded.
Errors now surface on every tab, not just Settings — a failed save can no longer look like a success.
Reminders due today now fire their 9am notification (they were skipped).
Receipt OCR no longer overwrites a date you picked yourself.
Google API calls retry with backoff on rate limits/server errors.
Changed
Internal streamlining: one shared miles-driven/odometer implementation, one currency-code helper, vehicleDescription on Car, dead code removed.
0.21.1 2026-07-08
Changed
Compacted the small Fleet Snapshot widget — each stat is now a single line with a short label and auto-shrinking text, so all three fit without clipping. Medium keeps the larger stacked layout beside the sparkline.
0.21.0 2026-07-08
Added
Two data home-screen widgets (small + medium):
Upcoming Reminders — your next maintenance/renewal reminders across active cars, most urgent first, colour-coded by status.
Fleet Snapshot — active-car count, miles and spend this month, plus a six-month miles-per-month sparkline.
The app publishes a shared snapshot to an App Group container after every reload and data change, and refreshes the widgets automatically.
Notes
Requires the group.com.shuffman.carlog App Group on both the app and widget App IDs (one-time provisioning).
0.20.1 2026-07-08
Changed
Simplified AI settings. The separate on-device/AI engine toggle is gone — "On-device" is now just a Free option in the same list as the AI models. Pick a row and it sets everything; the Provider and API Key fields only appear when an AI model is selected.
0.20.0 2026-07-08
Added
AI model picker. Settings → AI now offers a menu of vision models with a rough per-scan cost, instead of typing a model identifier. For Anthropic: Opus 4.8 (~2–3¢, most accurate), Sonnet 5 (~1¢), Haiku 4.5 (~0.3¢). A "Custom model…" option still accepts any identifier.
Fixed
The Scan widget now actually deploys. 0.19.0's Cloud build failed because the widget extension's CFBundleVersion (1) didn't match the app's — the extension now tracks the app's MARKETING_VERSION/CURRENT_PROJECT_VERSION, so it passes App Store export and reaches TestFlight.
0.19.0 2026-07-08
Added
First Home Screen widget: "Scan Odometer" (small) — tap it to jump straight into mileage entry, via a garagebuddy://scan deep link. This is Slice 1 of the widget work: it stands up the widget extension target and proves the build/sign/ deploy path with minimal risk (no shared data yet).
Notes
Adds a WidgetKit app-extension (com.shuffman.carlog.widgets) embedded in the app, and a garagebuddy:// URL scheme. Data-backed widgets (reminders, fleet snapshot, miles sparkline) need a shared App Group and come in Slice 2.
0.18.0 2026-07-08
Fixed
Much better odometer/gas-pump/receipt OCR. Two root causes addressed:
Model: the default AI model was Haiku 4.5 — the weakest vision tier. Default is now Claude Opus 4.8 (high-resolution vision, far stronger OCR). Anyone still on the old Haiku default is auto-upgraded; you can still choose a cheaper model in Settings → AI.
Image handling: scan photos are now sent upright (baking in camera orientation so the model never gets a sideways image) and at higher JPEG quality, instead of a heavily-compressed frame.
Notes
Opus costs more per scan than Haiku, but a scan is one image + a tiny response (well under a cent). For best results, frame the odometer so it fills the shot.
0.17.0 2026-07-07
Added
New "Odometer Check" category for plain mileage readings — and it's now the default for new entries (the most common action).
Changed
Existing "Other" entries can be reclassified to Odometer Check in one tap: Settings → Data shows "Reclassify N ‘Other’ as Odometer Check" whenever any "Other" entries exist. (Opt-in, so it never silently changes data; the row disappears once none remain.)
0.16.0 2026-07-07
Changed
App name is now "Garage Buddy" (was briefly "Garage Manager"). Updated the Home Screen name and all in-app product references. App Store Connect listing is "Garage Buddy: Car Tracker" (the bare names were taken).
0.15.0 2026-07-07
Changed
Renamed the app to "Garage Manager." The Home Screen name (CFBundleDisplayName) and in-app product references (PDF report title, Settings privacy note, Siri/Shortcut descriptions, permission messages) now say Garage Manager. Siri phrases follow the display name automatically.
Notes
Bundle id, internal module name, and the Google Sheet/Drive names (Car Log Data, Car Log Attachments) are unchanged, so existing data and the TestFlight app lineage are preserved. The App Store Connect app name is managed separately in App Store Connect.
0.14.3 2026-07-05
Changed
Scanned/attached files are now named with an ISO-8601 date (Receipt 2026-06-21.pdf), using the date the work was performed — for receipts the OCR now runs before the file is named/uploaded, so the receipt's own date is used rather than today's.
0.14.2 2026-07-05
Changed
When several cars share the same canonical year/make/model, their attachment folders are disambiguated with a .1/.2/.3 suffix (stable by creation order), e.g. 2001 Porsche Carrera.1. A car with a unique name stays unsuffixed.
0.14.1 2026-07-05
Changed
Per-car attachment folders now use the canonical year/make/model name (e.g. 2001 Porsche Carrera) rather than the car's custom display name.
0.14.0 2026-07-05
Changed
Attachments are now organized per car in Drive. New uploads go into a subfolder named for the car, inside "Car Log Attachments" (e.g. Car Log Attachments/996 Carrera Cab/). Files are still located by id in the sheet, so this is purely for tidier browsing in Drive.
Notes
Existing attachments already uploaded stay in the top-level folder; only new uploads are foldered. (A one-time migration of existing files can be added if wanted.)
0.13.0 2026-07-05
Added
Long-press an attachment for a menu: Delete, and (on a transaction) Extract Details (OCR) to re-read an already-saved attachment — including PDFs added earlier — and pre-fill the entry. Attachments on the car's Documents list can be deleted the same way.
Changed
Attachment delete moved from a swipe action to the long-press menu.
0.12.1 2026-07-05
Fixed
Scanned PDFs were corrupt / wouldn't open. The PDF context was finalized in a defer that ran after the bytes were already read, so the file was missing its trailer. It's now finalized before the data is returned.
0.12.0 2026-07-05
Added
Scan multi-page receipts. "Scan Document" in a transaction's attachments uses the system document scanner to capture several pages, combined into a single PDF attachment (instead of loose images).
AI receipt reading. If an AI key is configured, a scanned receipt is read to pre-fill the entry's total, date, and category, with a brief summary written into the notes. Best-effort — you verify before saving; the PDF is attached either way.
Changed
AIVisionService now supports multi-image prompts (used for multi-page receipts) via a new scanReceipt.
0.11.0 2026-07-04
Added
Reminders per car. Each car has a Reminders section: add date-based renewals (registration, insurance, inspection) or mileage-based service (oil every N miles). Reminders can repeat, and "Done" rolls a repeating one forward to its next due date/mileage.
Local notifications for date reminders (morning-of). Permission is only requested once you actually have a date reminder to schedule. Mileage reminders are tracked in-app against the current odometer (mileage can't be time-scheduled).
Garage badges now include reminders: a red bell for an overdue reminder, orange for due-soon — prioritised alongside the existing odometer/stale flags.
New spreadsheet tab Reminders, auto-created on connect for existing spreadsheets.
0.10.0 2026-07-04
Added
Photo & PDF attachments on transactions. Add receipts, titles, and service records to an entry (take a photo, choose from library, or pick a PDF). Files upload to a "Car Log Attachments" folder in your Drive; tap any attachment to preview it (QuickLook).
Documents on the car screen. A car aggregates all attachments across its transactions into one Documents section.
New spreadsheet tab Attachments (attachment_id, txn_id, kind, file_id, name, created_at), created automatically on connect for existing spreadsheets.
Notes
Uses the existing drive.file scope (no new sign-in consent). Deleting an attachment also removes the underlying Drive file.
0.9.0 2026-07-04
Added
Cost per mile & spend breakdown on each car — operating cost per mile (excludes purchase price), total spent, and spend by category.
Data-quality guardrails. The Garage flags cars that need attention: an odometer rollback (a later reading lower than an earlier one) and a stale car (active but not logged in 30+ days), shown as a badge in the list and a banner on the car's screen.
Search & filter a car's history by text and by category.
Notes
Cost-per-mile and the guardrails honour the "0 = unknown starting mileage" rule when measuring miles driven.
0.8.1 2026-07-04
Added
Each car's page in the PDF report now includes its miles-per-month chart.
Removed
The redundant "Log Entry for This Car" button on a car's screen — the floating + button already defaults to the car you're viewing.
0.8.0 2026-07-04
Added
VIN, purchase price, and sale price on each car. Purchase price is entered in the car's Ownership section; VIN in the Vehicle section; sale price appears when a car is retired (sold). The car's detail screen and the PDF report show these, plus profit (sale − purchase) for sold cars.
New spreadsheet columns vin, purchase_price, sale_price. Existing spreadsheets are migrated automatically on connect: missing columns are appended without disturbing existing columns, order, or data.
Changed
Header handling is now non-destructive (ensureColumns appends only missing headers), replacing the previous full-row rewrite — safe to run on reordered or hand-edited sheets.
0.7.0 2026-07-04
Added
Current-month estimate on the mileage chart. Since miles only appear once a reading brackets them, the newest month always looked empty. The chart now projects each active car's likely miles since its last reading (at its average measured daily rate) so the current period isn't blank. These bars are rendered faded and labelled "estimates projected from recent use" so they're clearly not measured data. Applies to the dashboard and per-car charts; the PDF report shows measured data only.
Changed
A starting mileage of 0 is treated as "unknown." Previously a car with no entered starting mileage had its entire odometer counted as miles driven and smeared back from its acquired date (e.g. a used car showing tens of thousands of fabricated miles). Now mileage for such a car is anchored from its first real reading instead.
0.6.0 2026-07-04
Added
PDF export of the whole garage. An Export button on the Garage screen produces a multi-page report: a title page with the fleet overview and the miles-per-month chart, then one page per car listing its transactions. Long histories paginate automatically.
Per-car mileage chart. Each car's Garage detail now shows a miles-per-month chart for just that vehicle.
Quick-add remembers the car you're viewing. Opening the quick-add (+) while looking at a car in the Garage pre-selects that car, and treats it as your explicit choice so a photo scan won't reassign it.
Fixed
Duplicate transactions from a double-tap. Saving a new entry now reuses one stable id and ignores repeat taps while the save is in flight, so a fast double-tap updates one record instead of writing two rows.
Defensive de-duplication on load. Rows identical in every meaningful field (car, date, odometer, category, amounts, notes) are collapsed to one when reading the sheet, cleaning up any duplicates already written. Genuinely distinct entries (e.g. two fills the same day) are preserved.
0.5.0 2026-07-04
Added
Tappable chart legend. The mileage and expenses charts now have a custom interactive legend — tap a car to isolate it in the chart, tap again to clear the filter and show all cars. Car colors stay stable, and the legend scrolls horizontally to fit many cars.
Fixed
Chart bars no longer collapse into a single month. Transaction dates that Google Sheets stored as date-time cells came back space-separated (e.g. 2026-01-15 8:00:00, 1/15/2026 8:00:00) and weren't recognized, so each fell back to its row's creation time and piled onto one recent bar. The lenient date reader now understands these space-separated date-time formats, so each entry keeps its real date. (Fixes #1.)
0.4.1 2026-06-28
Fixed
Hand-edited dates no longer jump to "today." Reading a date from the sheet was strict RFC3339 only, so any other format silently fell back to the current date — quietly relocating a transaction (and its mileage/expense bars) onto the current month. Date cells are now parsed leniently: RFC3339 (with or without fractional seconds), plain calendar dates (2026-06-28, 2026/06/28, 6/28/2026), and Google Sheets' own serial date numbers are all accepted. A transaction whose date still can't be read falls back to when its row was created rather than today. (ARCHITECTURE.md ADR-009.)
0.4.0 2026-06-28
Changed
Spreadsheet columns are now matched by header name, not position. You can reorder the columns in the Cars/Transactions tabs, and any extra columns you add are ignored on read and preserved when the app updates a row. The contract is now "don't rename/delete the known headers" instead of "don't reorder." (ARCHITECTURE.md ADR-009.)
0.3.1 2026-06-28
Fixed
On-device odometer OCR accuracy — the biggest fix: camera photos were handed to Vision without their rotation, so portrait shots were read sideways. Vision now receives the correct image orientation.
Thousands separators no longer fragment a reading (e.g. 128,456 was split into 128 and 456); grouped digits are kept intact.
Changed
When several numbers are equally plausible, the larger (more prominent) digits on screen now win — usually the odometer rather than the clock or trip meter.
0.3.0 2026-06-28
Added
Landscape chart mode — rotate the phone on the Dashboard for a focused, full-width chart with room for many months.
Fixed
Chart x-axis crowding — month labels now thin out as the range grows (every 2nd/3rd/6th month) instead of overlapping, and show the year when the range spans more than one calendar year.
0.2.0 2026-06-28
Added
Fast entry: Home Screen Quick Actions (long-press the app icon) plus App Shortcuts surfaced in Siri, Spotlight, the Shortcuts app, and the Action Button — "Scan Odometer" (opens straight to the camera) and "Log Entry".
On-device odometer OCR using Apple's Vision framework — free, private, offline, and works with no API key. A Settings toggle chooses on-device vs AI.
Closest-mileage car guessing: a scanned reading is matched to the car whose last mileage is nearest (and the right number is picked from the photo).
Changed
Mileage is distributed over time: miles between two readings spread across the intervening months (proportional to time), and the first reading spreads from the car's acquired date — so usage is never lumped into a single month.
Charts: abbreviated month labels (e.g. "Jun") and a rolling ~6-month x-axis window so sparse data still reads clearly.
0.1.0 2026-06-28
Added
Car management (the Garage): add/edit, retire/reactivate, starting mileage for used cars; scales to 10+ cars.