Skip to content

feat: phase-2 + AI calorie pipeline (LiDAR depth, USDA cache, geometric portion refinement) - #227

Merged
Eliaaazzz merged 13 commits into
mainfrom
feat/phase-2-discovery-social-tracking
Jul 1, 2026
Merged

feat: phase-2 + AI calorie pipeline (LiDAR depth, USDA cache, geometric portion refinement)#227
Eliaaazzz merged 13 commits into
mainfrom
feat/phase-2-discovery-social-tracking

Conversation

@Eliaaazzz

Copy link
Copy Markdown
Owner

Summary

Brings the accumulated feat/phase-2-discovery-social-tracking branch to main. Headline new work is the AI calorie pipeline, capped by a geometric LiDAR-volume portion refinement that attacks the dominant calorie error (portion/grams) while keeping the vision model's strengths (food identity + energy density).

What's in this branch (main..HEAD, 10 commits)

  • feat(nutrition): geometric LiDAR-volume portion refinement (new, this session) — physics decomposition: kcal = flashKcal^w × physicsKcal^(1-w) (geometric-mean blend), where physicsKcal = (volume/volumeBias) × massWeightedDensity × sceneEnergyDensity. Mass from geometry + a food-intrinsic density table; energy density stays the model's. Generalizes with no in-domain calibration — see docs/calorie-accuracy-roadmap-25-to-15.md §10 (SimpleFood45 real phone photos: ~6% median APE pure physics vs ~27% raw).
  • feat(camera): on-demand LiDAR depth native module + capture wiring; ARKit plane-raycast scale for non-LiDAR AR devices.
  • feat(nutrition): default to gemini-2.5-flash (thinking disabled); USDA SR Legacy seed (~7.8k foods) + trigram indexes; USDA FoodData Central two-level cache (Caffeine L1 + Redis L2) as lookup fallback.
  • feat(phase-2): discovery rows, social feed, achievements, workout/strength tracking.
  • Cloud Run scaling fix; docs (calorie-accuracy investigation + interview prep).

The calorie-physics feature (detail)

  • FoodDensityCategory: per-category bulk density (g/cm³) LUT added.
  • FoodCategoryClassifier: deterministic food-name → density category (short cues match whole tokens so "tea" can't hide inside "steak").
  • CaloriePhysicsRefinementService: scene-level geomean portion correction; clamped to [min,max]-scale; strict no-op when disabled or no LiDAR volume is present (non-depth devices, web, gallery photos) — behaviour is never worse than the model alone.
  • FoodRecognitionRequestMetadata: parse volume_cm3 / area_cm2 / mean_h_cm; wired provider-agnostically in FoodRecognitionService.
  • Config app.nutrition.physics-refine.* (enabled, blend-weight, volume-bias, min/max-scale), all env-overridable; all values generalize (no per-domain fit).
  • Frontend: threads on-device volumeCm3 through ReviewMealScreen into the analyze request as volume_cm3.

Test plan

  • Backend unit suite green offline: ./gradlew test -PskipContainerTests --offline (BUILD SUCCESSFUL).
  • New tests: CaloriePhysicsRefinementServiceTest (geomean math, no-op safety, clamp, per-food density weighting, multi-item ratio preservation, partial/null items), FoodCategoryClassifierTest, FoodDensityCategory density assertions.
  • Frontend: tsc --noEmit clean; jest --ci 100/100 (10 suites).
  • Manual device test (LiDAR iPhone): capture a meal, confirm volume_cm3 reaches /analyze and calories shift toward the geometric estimate. (owner: manual)

Risk / rollback

  • The refinement is gated on a client-sent positive volume_cm3; without it the pipeline is unchanged. Flip NUTRITION_PHYSICS_REFINE_ENABLED=false to disable instantly, or NUTRITION_PHYSICS_BLEND_WEIGHT=1.0 to fall back to pure model output.
  • No DB migration in the physics change; correction is clamped so a bad volume can't produce absurd calories.

Eliaaazzz added 10 commits May 19, 2026 18:49
…ength tracking

Phase 2 mobile-first features inspired by Uber Eats (discovery) and Strava
(achievement + social). Plus mobile YouTube playback fix and image perf work
already discussed.

Mobile perf & playback (carried from earlier in branch):
- Rewire YouTubePlayerModal to react-native-youtube-iframe with ID validation,
  watchdog timeout, error UI, and Linking fallback to the YouTube app.
- Swap RN Image for expo-image (memory+disk cache, transition) in WorkoutCard
  and RecipeImage (native branch). Web paths untouched.
- Downgrade YouTube thumbs to mqdefault on native; prefetch first 6 thumbs on
  Workouts/Recipes screens. keepPreviousData on recommended hooks.

F1 — Recipe Discovery Rows: HorizontalRecipeRow component + useRecipesByTag
hook; five themed carousels (Recommended, Quick, High protein, Trending,
Plant-forward) replace the flat grid on Recipes.

F2 — Smart Filter Chips: FilterChipBar with six multi-select chips (time, kcal,
protein, diet, budget, saved) filters every row client-side.

F3 — Log Again: LogAgainCard on Home dashboard surfaces last 7 days' meals,
deduped to 3 most-recent unique names; useReLogMeal mutation re-logs at today's
timestamp.

F4 — Bottom-sheet detail pattern: reusable DetailBottomSheet primitive (slide-up
modal w/ backdrop + drag handle); RecipeCard exposes onOpenDetail prop.

F5 — Adaptive Home Hero: AdaptiveHomeHero morphs by time-of-day (morning /
afternoon / evening / night) with personalized copy and CTAs.

F6 — Achievements V2: AchievementsCard badge wall + useAchievements hook
deriving 8 badges from existing data (streak, protein, veggies, cardio,
hydration, macro mastery). Tap-to-detail sheet.

F7 — Personal Records: PersonalRecordsCard + usePersonalRecords hook on Profile
(longest streak, protein PR, most meals, on-target days, cleanest sodium).

F8 — Weekly Summary: WeeklySummaryCard with totals, on-target days, PRs;
native Share with formatted week recap.

F9 — Friends & Activity Feed: FriendsFeedCard + useSocialStore (persistent,
Zustand) with mock seed, kudos toggle, privacy flags per activity type. Ready
to swap to a real backend feed endpoint when it lands.

F10 — Challenges: ChallengesCard + useChallengeStore (persistent) with 4
templates (protein 5/7, breakfast 7, 4-in-14, calories in-range 10/30), progress
bars, XP rewards.

F11 — In-Workout Timer + Live Status Strip: useWorkoutSessionStore + floating
WorkoutSessionStrip mounted at navigator root (persists across tabs).
WorkoutCard gets a 'Start' button.

F12 — Strength Sessions: StrengthLogModal w/ sets/reps/weight steppers +
useStrengthLogStore with auto-PR detection via Epley 1RM. New 'Log strength'
FAB on Workouts.

F13 — Consistency Heatmap: ConsistencyHeatmap (GitHub-style 12-week grid) +
useConsistencyCells hook on Profile.

F14 — Day History: DayHistorySheet opens from any heatmap cell, shows that
day's meals, strength sessions, and day score.

Notes:
- All UI follows the project's glass + orange/violet design system.
- No backend schema changes; everything piggybacks on existing endpoints
  (meal-history, weekly-insights, recipes search, recommendations).
- Local-only stores (social, challenges, strength, workout session) use Zustand
  + AsyncStorage. They expose stable APIs so a real backend can replace them
  later without touching components.
… Redis L2) as lookup fallback

When the local food_nutrition table misses, fall back to the USDA FoodData Central API,
cached two-level: in-process Caffeine (hot, recurring food vocab) + optional shared Redis
(cross-instance, survives Cloud Run churn). Redis is optional/null-safe. Honest scope: density/
nutrition lookups, not the vision call. Tests: cache L1/L2/miss/redis-absent, client JSON parse,
local-hit-skips-USDA.
…DAR) devices

Adds ARScaleModule (native iOS) + useARScale hook: on ARKit devices without a depth sensor,
raycast frame-center to a horizontal plane for camera-to-table distance -> img_w_cm, the same
scale signal the LiDAR frame-processor emits (tier between LiDAR +-1cm and plate fallback +-5cm).
Degrades to plate fallback when the native module is absent (never fabricates scale). Reference
template -- ARKit raycasting needs a real device; capture-screen AR view wiring documented in README.
…) + trigram indexes

Bulk-loads ~7,756 authoritative USDA FoodData Central SR Legacy foods (per 100g) into the local
food_nutrition table, restoring the USDA grounding dropped in V44 but as LOCAL data (no external
API on the hot path). ON CONFLICT (food_key) DO NOTHING preserves the existing hand-curated common
foods (incl. Chinese names). Adds pg_trgm GIN indexes for fast fuzzy lookup. Flyway disabled in
tests so this is prod-only; validated via sqlite (7.8k rows, ON CONFLICT preserves curated rows).
Roughly halves calorie error vs 2.0-flash at the same ~4s latency (Nutrition5k benchmark, docs/calorie-accuracy-investigation.md). Adds thinkingConfig (budget 0) guarded to 2.5-family models; configurable via GEMINI_MODEL / GEMINI_THINKING_BUDGET. Tests updated + thinkingConfig coverage.
Local Expo module (MetrifulLidar): brief AVCaptureDepthDataOutput session on builtInLiDARDepthCamera, computes center distance + on-device volume/area/height geometry. useLidarScale feeds img_w_cm + geometry into capture metadata. Replaces the non-functional VisionCamera frame-processor depth path; graceful fallback on non-LiDAR/web.
Cross-dataset depth corrector (N5k/MetaFood3D/SimpleFood45 ~25% zero-shot), LiDAR/ARKit integration, DB design, latency, limitations.
…onfig

Batches pre-existing working-tree changes: deploy-backend.yml/Dockerfile (Cloud Run 2Gi/2cpu scaling fix), scale.ts ARKit scale module + tests, jest config, RedisConfig, UI components and screens.
…e accuracy

Add a physics decomposition that corrects portion (the dominant calorie
error) from an absolute LiDAR-measured food volume, keeping the vision
model's food identity and energy density.

kcal = flashKcal^w x physicsKcal^(1-w)  (geometric-mean blend), where
physicsKcal = (volume/volumeBias) x massWeightedDensity x sceneEnergyDensity.
Mass comes from geometry + a food-intrinsic density table; energy density
(kcal/g) stays the model's. This generalizes with no in-domain calibration:
on SimpleFood45 (real phone photos, measured volume) pure physics measures
~6% median calorie APE vs ~27% raw, per docs/calorie-accuracy-roadmap-25-to-15.md.

Backend:
- FoodDensityCategory: add per-category bulk density (g/cm3) LUT.
- FoodCategoryClassifier: deterministic food-name -> density category
  (short cues match whole tokens so "tea" doesn't hide in "steak").
- CaloriePhysicsRefinementService: scene-level geomean portion correction,
  clamped and a strict no-op without volume / when disabled.
- FoodRecognitionRequestMetadata: parse volume_cm3 / area_cm2 / mean_h_cm.
- Wire refinement into FoodRecognitionService (provider-agnostic).
- application.yml: app.nutrition.physics-refine.* (enabled, blend-weight,
  volume-bias, min/max-scale), all env-overridable.

Frontend:
- Thread the on-device LiDAR volumeCm3 through ReviewMealScreen into the
  analyze request as volume_cm3; type it in nutritionApi.

Tests: CaloriePhysicsRefinementServiceTest (geomean math, no-op safety,
clamp, density weighting, multi-item ratios, partial items),
FoodCategoryClassifierTest, FoodDensityCategory density assertions.
Backend suite green (-PskipContainerTests, offline); frontend tsc + 100 jest green.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 1, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
❌ Deployment failed
View logs
aurafit b075147 Jul 01 2026, 07:05 AM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 1, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
❌ Deployment failed
View logs
aurafitness b075147 Jul 01 2026, 07:06 AM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 1, 2026

Copy link
Copy Markdown

Deploying aurafitness-2 with  Cloudflare Pages  Cloudflare Pages

Latest commit: b075147
Status:🚫  Build failed.

View logs

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 1, 2026

Copy link
Copy Markdown

Deploying aurafitness with  Cloudflare Pages  Cloudflare Pages

Latest commit: b075147
Status:🚫  Build failed.

View logs

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 1, 2026

Copy link
Copy Markdown

Deploying aurafitness-1 with  Cloudflare Pages  Cloudflare Pages

Latest commit: b075147
Status:🚫  Build failed.

View logs

- Frontend (HIGH): clear stale LiDAR volumeCm3 on gallery/retake/basic-camera
  paths so a non-depth image is never refined with a prior capture's volume.
- Classifier (MEDIUM): whole-word (token) matching for all single-word cues so
  "butter" no longer fires on "butterfly" (etc.); regular-plural handling keeps
  "chip"->"chips". Prefix cues replaced with singular forms; add grapefruit.
- Service (LOW): skip refinement below a plausible minimum scene volume (2 cm3)
  so a near-zero noise reading no-ops instead of clamping to min-scale.
- Service (LOW): harden config validation — reject non-finite values and reset
  an inconsistent min/max-scale pair to safe defaults.

Tests added: butterfly/plurals/steak substring-FP guards; tiny-volume no-op;
inconsistent-clamp-config reset. Backend suite green (offline); frontend tsc + 100 jest green.

Note (pre-existing, out of scope): countable-unit items save grams from the
count field, so scaled estimated_grams isn't persisted for pieces/servings —
calorie correction still propagates via nutrition. Tracked as follow-up.
@Eliaaazzz

Copy link
Copy Markdown
Owner Author

C:/Program Files/Git/gemini review

Eliaaazzz added 2 commits July 1, 2026 16:58
Reconcile with #225 (phase-2 discovery/social/tracking, already on main).
Conflicts (all phase-2, taken from main as canonical):
- DashboardScreen: username -> profile.displayName
- DetailBottomSheet: onClose optional -> required
- application.yml: keep the new gemini thinking-budget + nutrition.physics-refine block
- DashboardScreen: currentUser.data.username (UserProfileResponse has no
  displayName field, so #225's profile.displayName does not type-check).
- DetailBottomSheet: onClose optional so the (title || onClose) guard compiles.

Frontend tsc + 100 jest green; backend suite green.
@Eliaaazzz

Copy link
Copy Markdown
Owner Author

C:/Program Files/Git/gemini review

@Eliaaazzz
Eliaaazzz merged commit 6c98802 into main Jul 1, 2026
1 of 6 checks passed
@Eliaaazzz
Eliaaazzz deleted the feat/phase-2-discovery-social-tracking branch July 1, 2026 07:37
Eliaaazzz added a commit that referenced this pull request Jul 12, 2026
- Add react-native-web-webview: react-native-youtube-iframe's web entry
  requires it, and the missing package has broken every "Deploy Frontend
  to Firebase Hosting" run since #227 (Metro: "Unable to resolve module
  react-native-web-webview"), leaving aurafitness.org on a stale build.
- postbuild now runs only the node prerender script: the previous
  "cp -r public/* dist/" chain was Unix-only (cmd.exe: "system cannot
  find the path"), silently skipping the prerender on Windows. Expo
  already copies public/ into the export, and CI keeps its own copy step.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant