qa(device-proof): wire apps/mobile harness for PR #8 checklist reproducibility - #29
Open
obvious-autobuild[bot] wants to merge 4 commits into
Open
obvious-autobuild[bot] wants to merge 4 commits into
obvious-autobuild[bot] wants to merge 4 commits into
Conversation
apps/mobile becomes the reproducible vehicle for the 10-item device-proof checklist (spikes/voice-photo/README.md): spike deps at the README pins, Expo config wiring for microphone/photo/speech permissions, the spike's transcription + photo-storage ports imported via the pnpm workspace, and a DeviceProof debug screen that logs timestamped, class-labeled evidence rows for every checklist item (TurboModule probe, locale availability matrix, prepare taxonomy, ~5s recording + transcribeToText, photo pick, and the 3-request Convex upload with a deterministic fake transport fallback). Type-environment notes: React Native's global AbortSignal class satisfies @types/node's onmessage check, dropping `reason` from the merged global — restored via a local interface augmentation; the upload body is sent as ArrayBuffer, accepted by RN networking, Expo winter fetch, and Convex alike. Co-authored-by: Gilbert Polanco <gilbertpolanco42@gmail.com>
…n scripts) `npx expo prebuild -p ios --no-install` syncs app config in bare workflow: adds the explicit ios.bundleIdentifier default and switches the android/ios scripts to `expo run:*`. Generated ios/ output stays untracked per the gitignore added in the parent commit. Co-authored-by: Gilbert Polanco <gilbertpolanco42@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Human author: Gilbert Polanco (gilbertpolanco42@gmail.com)
Why
The PR #8 spike (voice dictation + photo capture for iOS 26) ends with a 10-item device-proof checklist that can only be closed on Apple hardware — but this workspace cannot run that verification itself: macOS sandboxes are not enabled (
obvious computers provisionSandbox --platform=macos→FORBIDDEN: macos sandboxes are not enabled for this workspace), and no Mac is paired or registered. Until now,apps/mobilewas a bare Expo 57 shell with no capture dependencies, so the eventual simulator/device run would have required rebuilding the wiring from the spike's notes by hand. This PR makes that run mechanical: correct pins, correct native config, and a debug screen that exercises every checklist item and logs timestamped, class-labeled evidence rows on-screen.What
1. Spike dependency pins in
apps/mobile(exact versions from the spike README's pinned set):expo-audio 57.0.5,expo-image-picker 57.0.18,expo-file-system 57.0.7,@react-native-ai/apple 0.12.0.newArchEnabled: trueretained (TurboModule requirement).2. Expo config wiring per the README's "Expo config wiring":
expo-audioplugin with itsmicrophonePermissionstring,expo-image-pickerplugin with itsphotosPermissionstring,ios.infoPlist.NSSpeechRecognitionUsageDescriptionwith the README string.3. Spike ports importable from the app — workspace import (preferred path).
spikes/voice-photois added topnpm-workspace.yamlandapps/mobiledepends on@journal/spike-voice-photo: workspace:*(metro.config.jsalready watches the workspace root). Chosen over copyingsrc/transcription/*into the app because it keeps a single source of truth for the transcriber port and its error taxonomy — a copy would fork the tested port and silently drift. The spike's own npm lockfile/vitest setup is untouched: its standalone gates still run insidespikes/voice-photounchanged (38/38 below).4. DeviceProof debug screen (reachable via a visible "Device proof checklist" button on the home screen; the app now also boots when
EXPO_PUBLIC_CONVEX_URLis unset, running the harness on the fake transport). It runs and logs timestamped on-screen evidence rows for:__apple__llm__transcribe__global present at runtimeisAvailable(locale)matrix over en-US, en-GB, es-MX, es-US, fr-FR, de-DE + one deliberately bogus locale to capture the failure pathprepare('en-US')with errors mapped to the spike's taxonomy codesHIGH_QUALITYrecording (file URI, byte size, mimeType) thentranscribeToTextvia the spike adapter — unavailable/null adapter andisAvailable=falsestates degrade to evidence rows, no crashesexpo-image-pickerphoto pick with size and typeEXPO_PUBLIC_CONVEX_URLis set, else against a deterministic in-memory fake — every row labeled with which transport it usedEvery evidence row carries a class label:
simulator/fake/hardware-only-blocked. Synthetic family data only ("Milo ate oatmeal at 8am", invented author). No credentials anywhere: no URLs or keys committed,.envstays gitignored (.env.examplecomment extensions only).5. Type-environment fix (app-local, no spike changes): React Native's global
AbortSignalclass (src/types/globals.d.ts) satisfies@types/node'stypeof globalThis extends { onmessage: any }conditional, which empties node'sAbortSignalaugmentation and dropsreasonfrom the merged global type — the spike's port readssignal.reasonfor cancellation causes.apps/mobile/src/types/abort-signal.d.tsrestores it via interface merging.How to Review
apps/mobile/src/device-proof/—DeviceProofScreen.tsx(UI + run controls),checks.ts(the six checks),evidence.ts(row model + classes),convexTransport.ts/fakeTransport.ts(the port's two transports).convexTransport.tsspeaks the documented Convex HTTP API over plainfetch(the photoStorage module is not deployed to the dev deployment yet, so step 1/3 failures are an expected, honestly-reported outcome until that module is ported intobackend/convex).ArrayBuffer(photo.bytes.slice().buffer) — accepted by RN networking, Expo's winter fetch, and Convex's upload endpoint alike;new Blob([bytes])does not typecheck under RN's global types.expo prebuild -p iosoutput (ios/) is gitignored per Expo's continuous-native-generation convention (apps/mobile/ios/,apps/mobile/android/in.gitignore); prebuild also syncedapp.json(explicitbundleIdentifier) and switched the run scripts toexpo run:*— both accepted as-is in the second commit.photoStorageintobackend/convex.Gate outputs (Node v20.20.2, pnpm 10.34.5 via Corepack, Bun 1.3.14)
Tested head SHA:
837f132b372063994386e67f9336bec44e80e81e(all outputs below ran against this exact commit after merging current master).pnpm installclean;pnpm install --frozen-lockfile(CI parity):Done in 1.8s, no errors.pnpm typecheck(turbo, all packages incl. spike + mobile):Tasks: 10 successful, 10 total.Spike vitest:
Test Files 3 passed (3),Tests 38 passed (38).pnpm test:6 successful, 6 total;pnpm build:5 successful, 5 total;bun test ./security:17 pass, 0 fail; evaluation corpus:Summary: 6/6 fixtures passed; negative control (--adapter=./src/example/broken-adapter.ts --expect-failure):Summary: 1/6 fixtures passed, exit 0 (expected failure confirmed).npx expo prebuild -p ios --no-installsucceeds on Linux. Generatedios/SharedChildJournal/Info.plistcontains (quoted):<key>NSMicrophoneUsageDescription</key>/<string>Allow Shared Child Journal to access your microphone so you can dictate journal entries.</string><key>NSPhotoLibraryUsageDescription</key>/<string>Allow Shared Child Journal to attach pictures to journal entries.</string><key>NSSpeechRecognitionUsageDescription</key>/<string>Shared Child Journal transcribes your dictated entries on-device.</string>Expo's Podfile does not name pods literally — it delegates to autolinking at
pod installtime (use_native_modules!(config_command)). Running that exact command (node --eval "require('expo/bin/autolinking')" expo-modules-autolinking react-native-config --json --platform ios) resolves@react-native-ai/applev0.12.0 withpodspecPath: node_modules/@react-native-ai/apple/AppleLLM.podspec— the pod links whenpod installruns on a Mac. Git status clean at the end;ios/output untracked.Checklist mapping (PR #8 items 1–10 → what this harness exercises)
__apple__llm__transcribe__presentNSMicrophoneUsageDescriptionverifiedNSPhotoLibraryUsageDescriptionverifiedNSSpeechRecognitionUsageDescriptiongeneratedprepare(locale)asset downloadPer this task's brief: do not merge — this PR is the wiring that makes the hardware run mechanical; the run happens on a Mac outside this workspace and its evidence should land against this branch.
🔗 Obvious Project · 🧵 Obvious Thread