Implements the architecture 4.8 resolution ladder: local SQLCipher cache ->
Open Food Facts (4s timeout) -> USDA FoodData Central fallback, with every
remote hit written through to the new FoodProductsTable (schema v5).
- domain: FoodProduct entity + IFoodSearchRepository contract (pure Dart)
- data: UsdaFdcClient (FDC nutrient-ID mapper 1008/1003/1004/1005/1079/1093,
api_key sent as query parameter - FDC ignores body keys and 403s otherwise),
OpenFoodFactsClient (userAgent with app identity, SearchTerms search,
getProductV3 barcode lookup), shared RemoteFoodDataSource interface
- presentation: FoodSearchController (400ms debounce, request-token
supersession), FoodSearchScreen (MobileScanner sheet on mobile, manual UPC
dialog + notification on desktop/web, macro action sheet with serving
multipliers, Log to Diary / Add to Pantry actions)
- integration: /food-search route with ?preset=pantry, Nutrition screen entry
button, Pantry screen Scan Item action
- hardening: FoodSearchUnavailableException distinguishes total source outage
from a healthy empty result; GoogleHealthConnector dotenv reads now guarded
with isInitialized (fixes NotInitializedError crashing app_navigation_test)
- tests: +16 (fallback sequence cache->OFF->USDA for text and barcode,
USDA request shape via MockClient); full suite 170/170, analyze clean