SPRINT-7 T5-D Step 1 — RadioCheck TestFlight pointer proof¶
Date: 2026-08-30
Status: POINTER PROOF PASS. Mac archive + TestFlight upload pending. STOP for Ryan journey.
Prerequisite: T5-C PASS (Option B confirmed), api.last1.app live.
Executive summary¶
| Workstream | Result |
|---|---|
| Env injection mechanism | ios/.xcode.env.local (not EAS — bare React Native) |
| Sovereign pair source | BWS LAST1APP_DB_PROD_SUPABASE_ANON_KEY + https://api.last1.app |
| Auth pointer trap fix | lib/supabase.ts — direct process.env.* + auth reads env (was hardcoded hosted) |
| T5-B iOS RPC patches | Present in audit clone (joinUnitByInvite, useUnit, auth) |
| Production bundle verify | PASS — api.last1.app ×2, hosted ref 0, RPC strings present |
| Server preflight | PASS — health 200, Apple/Google enabled on sovereign GoTrue |
| TestFlight IPA upload | Pending Ryan Mac (agent-01 / Xcode) |
1. How env is injected (NOT eas.json)¶
RadioCheck is bare React Native (react-native run-ios, Xcode archive). There is no eas.json in the repo.
| Layer | Mechanism |
|---|---|
| Build-time env file | ios/.xcode.env.local (gitignored) — generated by script |
| Xcode bundle phase | Sources .xcode.env + .xcode.env.local before Metro |
| Babel | babel-plugin-transform-inline-environment-variables inlines listed EXPO_PUBLIC_* keys at bundle time |
| BWS | LAST1APP_DB_PROD_SUPABASE_ANON_KEY from project last1-radiocheck |
Script: scripts/last1app/t5-d-ios-render-xcode-env.sh /path/to/RadioCheck
Writes (all four required for auth + data):
export EXPO_PUBLIC_SUPABASE_URL=https://api.last1.app
export EXPO_PUBLIC_SUPABASE_ANON_KEY=<BWS sovereign anon>
export EXPO_PUBLIC_LAST1_SUPABASE_URL=https://api.last1.app
export EXPO_PUBLIC_LAST1_SUPABASE_ANON_KEY=<same sovereign anon>
export NODE_ENV=production
There is no separate “staging scheme” (unlike Onset). Step 1 uses Release archive with sovereign env in .xcode.env.local only. App Store users still on hosted until Step 2 production ship.
2. Hosted-fallback trap (found and fixed)¶
Trap A — data client only via env¶
EXPO_PUBLIC_SUPABASE_URL overrides data client, but without injection defaults to tcthzakppndkykatoefx.
Trap B — auth hardcoded (critical)¶
Before fix, supabaseLast1 (phone OTP / Apple SIWA) used DEFAULT_SUPABASE_URL only, ignoring env. A build with only EXPO_PUBLIC_SUPABASE_* set would still authenticate against hosted.
Trap C — babel did not inline via ENV alias¶
Code used const ENV = process.env then ENV.EXPO_PUBLIC_*. Babel only inlines direct process.env.EXPO_PUBLIC_* references. Runtime fell back to hosted defaults in the shipped bundle.
Fix (audit clone /tmp/sprint7-audit/RadioCheck)¶
lib/supabase.ts: auth usesprocess.env.EXPO_PUBLIC_LAST1_*||EXPO_PUBLIC_SUPABASE_*|| defaults- All env reads use
process.envdirectly for babel inline
Must land in RadioCheck repo before Mac archive.
3. T5-B coupled iOS patches (in build)¶
| File | Change |
|---|---|
lib/joinUnitByInvite.ts |
join_unit_by_invite_code RPC |
hooks/useUnit.ts |
join_unit_by_invite_code RPC |
lib/auth.ts |
rc_user_exists_by_email, rc_call_sign_taken RPCs |
Bundle verify: join_unit_by_invite_code ×2, rc_user_exists_by_email ×1.
4. Bundle pointer verification (Onset xcconfig equivalent)¶
Script: scripts/last1app/t5-d-ios-verify-bundle.sh
Log: /tmp/last1app-gate-b/t5d-bundle-verify-20260830T233332Z.log
Artifact: /tmp/radiocheck-t5d-main.jsbundle
| Check | Result |
|---|---|
api.last1.app occurrences |
2 (auth + data createClient) |
tcthzakppndkykatoefx occurrences |
0 |
| Sovereign anon JWT in bundle | iss=supabase (not hosted ref) |
| RPC patches | Present |
Extract from minified supabase.ts module:
u="https://api.last1.app".trim()
...
I="https://api.last1.app".trim()
createClient(u,o,...) // supabaseLast1
createClient(I,f,...) // supabaseRadioCheck
Verdict: PASS — pointer took; do not assume, proved in bundle.
5. Server preflight¶
Script: scripts/last1app/t5-d-ios-step1-preflight.sh
Log: /tmp/last1app-gate-b/t5d-ios-preflight-20260830T233332Z.log
| Check | Result |
|---|---|
GET /auth/v1/health via CF |
200 + cf-ray |
| GoTrue Apple | enabled |
| GoTrue Google | enabled |
| Realtime websocket probe | 400 (expected without WS upgrade) |
6. Mac: archive + TestFlight (Ryan)¶
Agent cannot run Xcode on Linux. On Ryan Mac with RadioCheck repo (include Step 1 commits):
cd /path/to/RadioCheck
bash /path/to/lastvet-gates/scripts/last1app/t5-d-ios-render-xcode-env.sh "$(pwd)"
bash /path/to/lastvet-gates/scripts/last1app/t5-d-ios-verify-bundle.sh "$(pwd)"
# Must print PASS before archive
cd ios && pod install && cd ..
open ios/RadioCheck.xcworkspace
# Scheme: RadioCheck | Configuration: Release
# Product → Archive → Distribute → TestFlight
Or: bash scripts/last1app/t5-d-step1-archive-upload.sh /path/to/RadioCheck (requires export options plist / Apple credentials on Mac).
Version today: 1.1.14 (build 6 in repo). Bump build number if App Store Connect rejects duplicate upload.
7. Ryan journey test (after TestFlight install)¶
Network: cellular or Wi‑Fi, Tailscale OFF.
| # | Step | Pass criteria |
|---|---|---|
| 1 | Phone OTP login | OTP via sovereign Twilio/GoTrue; session established |
| 2 | Own check-ins load | User sees their rows (RLS), not empty/error |
| 3 | Apple SIWA | Return URL hits sovereign callback (Ryan added Return URL) |
| 4 | Realtime | Unit thread / check-in updates live without refresh |
| 5 | Sign out | Clean logout; re-login works |
Failure signals: requests to *.supabase.co, auth loop, empty data with valid user, SIWA 400 on wrong Return URL.
Report template:
T5-D Step 1 journey: PASS | FAIL
Device:
Network: cellular | wifi
Phone OTP: ok | fail
Apple SIWA: ok | fail
Check-ins load: ok | fail
Realtime: ok | fail
Sign out: ok | fail
Notes:
8. Constraints held¶
| Constraint | Status |
|---|---|
| Hosted Supabase rollback net | Untouched |
| Sovereign SMS crons | OFF (Option B until T-6) |
| Both Apple Return URLs | Hosted + sovereign (until T-6) |
| Step 2 production archive | Blocked until journey PASS |
STOP¶
Pointer proof PASS. Run Mac archive → TestFlight → Ryan journey. Reply T5-D Step 1 journey PASS before Step 2 production archive.
Repo action: Commit/push audit-clone changes (lib/supabase.ts + RPC patches) to Last-1-Enterprises/RadioCheck before archiving.