- Made `/api/auth/me` public and return explicit allowlist (name, email,
two_factor_confirmed_at, tier, subscription fields) instead of spreading
`$user->toArray()` which leaked is_admin, stripe_id, pm_type, pm_last_four,
postcode. Returns `null` when unauthenticated rather than 401.
- Moved `/auth/logout` to remain behind auth:sanctum gate.
- Added 3×200ms retry with exponential backoff to EiaBrentPriceSource and
FredBrentPriceSource on ConnectionException or 5xx responses. Timeout
raised from 10s to 30s.
- Both sources now throw typed BrentPriceFetchException on exhausted retries
instead of silently returning null + logging. Updated tests to assert
exception message includes HTTP status or "connection failed".
- BrentPriceFetcher owns ingestion (fetchFromEia / fetchFromFred, each throws on failure)
- BrentPricePredictor owns prediction and marks latest brent_prices row as generated
- oil:fetch command tries EIA, falls back to FRED, fails loudly if both fail
- oil:predict command prompts if latest price already has a prediction; --force bypasses
- add prediction_generated_at column to brent_prices
- delete OilPriceService (replaced by the two focused services)