Skip to content

fix(camera): harden + correct the AR/depth real-world-scale module#226

Open
Eliaaazzz wants to merge 5 commits into
mainfrom
fix/ar-scale-review-followups
Open

fix(camera): harden + correct the AR/depth real-world-scale module#226
Eliaaazzz wants to merge 5 commits into
mainfrom
fix/ar-scale-review-followups

Conversation

@Eliaaazzz

Copy link
Copy Markdown
Owner

What this is

Follow-up fixes after pulling the freshly-merged ARKit / depth real-world scale module
(a0d1283, the feat(camera): ARKit plane-raycast scale module work) and reviewing it. The
native iOS/Android files are reference templates that can't be compiled outside Xcode/Android
Studio — so this PR verifies and hardens everything testable (TypeScript bridge, device tiering,
test infra) and corrects the native templates + docs to be honest, correct starting points.

Verified locally: tsc --noEmit clean (only 4 pre-existing, unrelated errors) and 110/111
jest tests pass
(the 1 failure is the pre-existing useAuthStore dynamic-import issue).

Fixes

Test infrastructurenpm test was broken for every RN-importing test (no jest config wired
jest-expo; react-test-renderer floated off the pinned react). Added jest.config.js
(jest-expo preset) + jest.setup.js (AsyncStorage mock), pinned react-test-renderer@19.1.0.

JS bridge bugs

  • useDeviceCapabilities: removed iPhone 13 / 13 mini from the LiDAR list (they have none),
    replaced the dead/over-broad 'pro' substring with exact model-id matching, added the iPad Pro
    12.9" 5th-gen LiDAR IDs, and relabeled the depth tier so Android ToF devices aren't called "LiDAR".
  • useARScale: cleanup now resets the start-guard so it can restart after an enabled toggle;
    added a 12–90cm plausibility gate so an off-table raycast degrades to the plate fallback instead
    of sending a fabricated scale.
  • Added contract tests pinning the "never fabricate scale / degrade to plate fallback" guarantee
    and the device-tier classification.

Native templates (still device-only, now correct)

  • iOS GetDepthAtCenterPlugin: removed the CMGetAttachment(sampleBuffer, "AVDepthData") path —
    AVFoundation never attaches depth to a video sample buffer, so as written it could never return a
    distance. Replaced with a timestamp-matched DepthStore fed by the AVCaptureDataOutputSynchronizer
    delegate (the iOS analogue of the Android DepthBridge). Never emit fx:0 with hasDepth:true.
  • Android GetDepthAtCenterPlugin: decode the real DEPTH16 confidence bits instead of hardcoding
    0.9 (so the JS minConfidence filter is meaningful); same fx<=0 guard.

Docs — corrected native-depth-plugin/README.md: removed references to a non-existent
src/utils/scale.ts / estimateImageWidthCm(), fixed the DepthResult contract block and the
verify steps, and replaced the "TS side is fully wired" claim with an accurate "what's wired (the
route.params.imgWcm sink) vs what's still a template / unwritten glue" section.

Honest residual

Building the native modules and writing the capture-screen JS glue (calling useARScale().measure()
and pushing imgWcm into route.params) remain device-side work — now documented accurately rather
than overstated as done.

🤖 Generated with Claude Code

Eliaaazzz and others added 5 commits June 28, 2026 22:47
The repo shipped jest-expo + @testing-library/react-native as devDeps and
several RN-importing test files, but no jest config to tie them together, so
`npm test` failed for every test that imported react-native
("Cannot use import statement outside a module"). It also pinned
react@19.1.0 while react-test-renderer floated to ^19.2.3 (no exact match),
which @testing-library/react-native rejects.

- Add frontend/jest.config.js (preset: jest-expo) + jest.setup.js (official
  AsyncStorage mock).
- Pin react-test-renderer to 19.1.0 (frontend devDep + root overrides) to match
  the pinned react.

Result: npm test runs — 102/103 tests pass (the 1 failure is a pre-existing
auth-store issue: a dynamic import needing --experimental-vm-modules, unrelated).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses confirmed findings from the AR-module review:

useDeviceCapabilities:
- Remove iPhone 13 / 13 mini (iPhone14,4/14,5) from the LiDAR list — they have
  no LiDAR, so these very common phones were mis-tiered as 'lidar' (<1% accuracy).
- Replace the `modelId.includes('pro')` heuristic with exact model-id membership:
  it never matched a real Device.modelId ('iPhone15,2'...) and would have falsely
  flagged the LiDAR-less iPhone 11 Pro / 2018 iPad Pro if fed a model name.
- Relabel the depth tier "Pro Accuracy (LiDAR)" -> "Pro Accuracy (Depth Sensor)"
  since it also covers Android ToF devices (a Galaxy S23 Ultra is not LiDAR).

useARScale:
- Reset started.current in the effect cleanup so a later start() can actually
  restart after an `enabled` toggle (previously the session stopped but the guard
  stayed set, so start() early-returned forever).
- Add a 12-90cm plausibility gate on measured img_w_cm: an off-table raycast
  (e.g. a far wall) now degrades to the plate fallback instead of sending a
  fabricated scale to the backend.

Adds contract tests pinning the "never fabricate scale / degrade to plate
fallback" guarantee (native present, native absent, plausibility gate, clean
restart) and the standard_ar tier label.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
iOS GetDepthAtCenterPlugin (reference template):
- Remove the CMGetAttachment(sampleBuffer, "AVDepthData") path — AVFoundation
  never attaches AVDepthData to a video CMSampleBuffer, so as written the plugin
  could never return a distance on any device. Replace with a timestamp-keyed
  DepthStore that the app's AVCaptureDataOutputSynchronizer delegate feeds (the
  iOS analogue of the Android DepthBridge), with a staleness guard so a stale
  depth map is never paired with a newer video frame.
- Never emit fx:0 with hasDepth:true: when cameraCalibrationData is absent the
  pinhole conversion would divide by zero, so report hasDepth:false instead.

Android GetDepthAtCenterPlugin (reference template):
- Decode the real DEPTH16 confidence bits (high 3 bits, 0 => 100%, else
  (code-1)/7) and report the mean confidence instead of a hardcoded 0.9 — so the
  JS minConfidence filter is meaningful. Also guard fx<=0 => hasDepth:false.

These remain device-only templates (cannot be compiled here), but are now correct
starting points rather than dead/divide-by-zero paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The README described a scale pipeline that does not exist. Corrected to reality:

- Remove all references to src/utils/scale.ts / estimateImageWidthCm() (no such
  file or function exists). The pinhole helper is now shown as code-to-add under
  "Completing the LiDAR path", not as existing/wired.
- Stop claiming "the TypeScript side is fully wired". State clearly what IS wired
  (the standard_ar AR path: useARScale -> route.params.imgWcm -> backend) vs what
  is NOT (the LiDAR depth -> img_w_cm conversion: DepthResult has no fx, there is
  no conversion, CaptureMetadata has no imgWcm).
- Fix the DepthResult contract block to match the real interface (drop the
  fx/horizontalFovDeg fields it never declared; note fx is returned by native but
  not yet consumed; the FOV fallback is not implemented).
- Fix the verify step: CaptureMetadata carries { distanceCm, confidence, accuracy,
  deviceTier } — there is no metadata.imgWcm. The value that reaches the backend
  is route.params.imgWcm.
- Document the new iOS DepthStore / synchronizer wiring; note img_w_cm is gated
  (not clamped) to 12-90cm in useARScale.
- Drop the stale estimateImageWidthCm reference in useDistanceEstimation's docstring.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
From an adversarial verification of the previous commits:

- README (the big one): stop claiming the standard_ar path is "wired in JS, one
  device-side step remaining". Nothing calls useARScale and nothing sets
  route.params.imgWcm — only the SINK (ReviewMealScreen -> img_w_cm, 35cm default)
  is wired. Reword "what is wired today" + the diagram to say the producer-side JS
  glue is also unwritten for both paths.
- README: the verify step claimed GeminiMealAnalysisService logs img_w_cm; it does
  not log the request body. Point to the outbound nutritionApi request / gateway
  logs instead.
- iOS Swift: header comment said DepthStore is "keyed by presentation timestamp"
  (implying a map); it keeps only the latest sample matched within a staleness
  window. Clarified.
- useDeviceCapabilities: add the iPad Pro 12.9" 5th-gen (2021, M1) LiDAR ids
  (iPad13,8-13,11), which exact-match detection would otherwise miss.
- Add useDeviceCapabilities.test.tsx pinning the tier fixes (iPhone 13 -> standard_ar,
  15 Pro -> lidar, iPad 5th-gen -> lidar, ToF -> not labeled LiDAR) — the one
  coverage gap the verification flagged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cloudflare-workers-and-pages

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 287b233 Jun 28 2026, 01:14 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 28, 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 287b233 Jun 28 2026, 01:15 PM

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying aurafitness-2 with  Cloudflare Pages  Cloudflare Pages

Latest commit: 287b233
Status:🚫  Build failed.

View logs

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying aurafitness with  Cloudflare Pages  Cloudflare Pages

Latest commit: 287b233
Status:🚫  Build failed.

View logs

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying aurafitness-1 with  Cloudflare Pages  Cloudflare Pages

Latest commit: 287b233
Status:🚫  Build failed.

View logs

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