Repository navigation
Conversation
… class, database recovery) Steps 9, 10, 11, 12, 14 and 16 of the iOS readiness study, in pure TypeScript with tests: the accuracy of a permission on either platform (iOS full/reduced), a negative accuracy is not a position and a negative speed is no speed, fixes closer than 2 s and 50 m are thinned in the store, every Keychain write uses AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY, and the database open (core/open-outbox.ts) replaces a stale file after a backup restore and drops the session of a reinstall on iOS only. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…s, device name on iOS - session.restore() asks the address rule again (acceptAddress) and drops a stored http session that a release build must not use; wired in native/runtime.ts. - @obop/client post(): a deadline that fires after the app was suspended gets one grace period, so a refresh answered by the server is not cut off after waking (refresh tokens rotate with no grace window). - core/device.ts: an iPhone is listed as iPhone, not as an Android phone. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
- app.config.ts: location background mode on, When In Use text, no Always, motion, temporary full-accuracy or Face ID keys, NSLocalNetworkUsageDescription, fetch background mode removed, iOS bundle id from OBOP_IOS_BUNDLE_ID (sign-in scheme and Android package unchanged). - config/: pure helpers and tests that run the real Expo CLI on app.config.ts. - scripts/ios: release gate (fails a release build with cleartextAllowed or an ATS exception) and the pod install and Simulator build helper, with tests. - package.json: export:ios, ios:prebuild, ios:pods, ios:build:simulator, ios:gate, ios:run:device. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
- tOn/tOs: a sentence has an .ios version where the Android one names a notification, the quick settings, Google Play services or a force-stop; six sentences, tests that every iOS sentence is read by a screen and has the placeholders of the usual one. - recorder.ts: the comment names both systems and the options of one system only. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…he boundaries found by mutation - Home shows a warning (until dismissed) when the app replaced the database file of a restored backup (iOS), read through staleFileRecoveredAt / dismissStaleFileNote. - more tests: Infinity speed, a fix exactly at the far limit, a stored value that is not a key. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
- scripts/simulator/route.mjs: simctl location start along the 41-waypoint reference route (prints the command, --run, --clear, --set-start), argument list built and checked in route-lib.mjs. - scripts/lib/is-main.mjs replaces the file:/// comparison of route.mjs, which printed nothing on macOS and Linux (and for a path with a space). Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
- Screen pads itself for the keyboard on iOS only (KeyboardAvoidingView); Android keeps the plain View it had. - Banner announces a warning or a problem on iOS, which has no live region; the choice is a tested function in platform.ts. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
An app built with CODE_SIGNING_ALLOWED=NO has no entitlements, and the Keychain of
the iOS Simulator refuses every expo-secure-store call ("A required entitlement is
not present"): the app started and could not open its database. The identity "-"
signs ad hoc, with no account, team or profile.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
One xcodebuild session signs in through the system browser sheet, starts a trip, presses Home, waits for the route and stops and submits the trip. The route is started by the host when the test prints RECORDING-SHOWN, because the Simulator clears a simulated route when a UI test session ends or starts. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…tor route, Q37 iOS bundle id Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…dress Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…s Clear storage Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…ntences too Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…ad hoc Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…te, largest gap) Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
… the risks and gaps of 12 to 15 Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…n ran in the iOS Simulator Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…m 3) iOS reports the app permission as denied while the system switch is off, so the Permissions screen named the wrong cause. On iOS the switch is asked first. The step choice moves into a pure module with tests. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A generated database key is stored as pending:<key> on iOS and becomes plain only after the database was opened with it, so a crash between the two steps cannot lose the recovery note. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
… the rules (review item A) OBOP_RECORD_THINNING=off is honoured by the app only in a test build (plain http allowed). scripts/emulator/thinning-replay.mjs runs the 2 s / 50 m rule, 1.5 s / 50 m and off over a recorded track; check-trip.mjs shares the server helpers in server-lib.mjs. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…ev client entries (review items A, 4, 16, 27, 2) app.config.ts carries extra.thinningOff; the gate fails a release build with it, and fails a release build that has the Bonjour service or the exp+ URL scheme of the development client. Those entries are removed in the outer Info.plist mod unless OBOP_DEV_CLIENT=1. The config tests get a 30 s timeout. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…eck (review items 1, 24) Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…or drive (review item 25) Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…lder private scripts/ios/env.sh replaces the session-private ~/build/mileage-ios-env.sh in the owner steps: it sets the tool paths, the language CocoaPods needs and the caches, unsets CI (Expo refuses its device question under CI) and makes ~/.obop-mileage-build with mode 700. It sets none of the OBOP_ variables that change the app. drive.mjs also makes <root>/uitest mode 700, since xcodebuild keeps the test password in its build data (review items 14 and 18). Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
… the grace The deadline test of the OAuth refresh had cases far on either side of the slack; a delay of 6 s was not covered (found by breaking the slack on purpose in the fix round of PR 34). Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…test covers The class decides whether a process that starts while the phone is locked can read the database key. A recording that was started unlocked never reads it again, so the locked-phone test (T1 of docs/spec/mobile.md 9.2) does not exercise the class. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…ks and the cleanup steps 8.8 and ISSUES L44 now state the 1 Hz runs as they were (the first run's -1.39% is a phase of the 2 s rule, not its price; 0 to about 1.1% by start offset; thinning off 248 points, -0.26%), the 100 m recount (42 again), what was run on Android, five Release builds checked with plutil on the Mac, the dev-client entries removed by the outer Info.plist mod, the Save Password trap, the password that xcodebuild keeps in its build data, and the cap arithmetic. 9.2 gets the removal of the Apple ID, certificate, profiles and DerivedData, T1 reworded (it does not test the locked Keychain read), and the steps that 9.2 does not cover named. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
… its exported session log Checked once on the Mac with xcrun xcresulttool export diagnostics (review item 23). Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…round 2, R2.4) If dropping the sign-in of a fresh database, writing the note of a replaced file or making the pending key plain throws, the file was left open: the caller forgets a failed open, so a retry opened it a second time. openWithKey now returns the handle and openOutbox closes it before the error goes on. Two tests (the promotion fails once; the sign-in drop fails). The header and the test title no longer say that a key that was read is never followed by a deletion: a pending key is, and has not opened a file. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
… about a test id without cleartext (round 2, R2.1, R2.2) thin.ts honours thinningOff only with cleartextAllowed, so in test mode thinningOff without it now fails with the reason, and the release failure says what the app does with the variable. A test bundle id with cleartextAllowed=false warns that the build may have been made without the OBOP_ variables exported (an incremental build evaluates app.config.ts again with its own environment). Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…y; env test checks every export (round 2, R2.6, R2.7, R1.7) --skip N drops the first N fixes before the replay, so the start phase can be varied and the series of 8.8 reproduced (reproduced on the 248 points of the unthinned Simulator run: 0, 0, 1.14, 0, 1.15, 0 %). The reference is not used with --skip. The header, the table and check-trip.mjs say that the distance is the server's haversine without its accuracy and speed filters and that the density line shows the cadence of the phone only for a build made with OBOP_RECORD_THINNING=off. -0.00 % prints as 0.00 %. env.test.ts now fails on any exported OBOP_ variable but OBOP_BUILD_ROOT. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…ORD_THINNING=off does not store every fix (round 2) Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
8.8: the app has English only (item 3); the strip phase of the dev launcher and why a release build can ship _expo._tcp (incremental builds only), with B1 to B5 stated as clean builds; the reviewer's groups run that 9.1 and 9.2 cite; the Android figures that have no log say so; 0.26% and four later runs; the start-phase series with the command that reproduces it (--skip); the one-sentence placeholder note; the loss of the recovery note across a kill; the store.ts link that no unit test guards. 9.1 and 9.2: the branch of the checkout, no second --device, seven items not covered, the smallest-gap check of step 10, exact wording of what the test build stores and what the density line shows, the rebuild trap, the Sign in screen in T2, and no Devices page for a reviewer. ISSUES L44, L48 follow. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…s or an http server (round 3, R1.2)
The ordinary test mode only warned about a test bundle id with cleartext off, and a
missing OBOP_RECORD_THINNING or OBOP_DEFAULT_SERVER passed with no line at all, so the
build that a new Terminal made without the variables passed the gate. With --drive
(this build is for the first drive on a phone) the gate fails unless the embedded config
has cleartextAllowed=true, thinningOff=true and an http:// default server, names the
variable that fixes each, and says what to do. Without the flag nothing changes except
the wording of the warning ("OBOP_ALLOW_CLEARTEXT was not set for this build"). --drive
without --test-build is a usage error.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
… (round 3, R1.1, R2.2, R2.6, R2.7) --save-points FILE writes the time and position of every point the replay fetched as the JSON that --points reads back, so the track of the first drive survives the deletion of the test instance (it refuses to overwrite a file and writes mode 600). route-feed.mjs writes the synthetic 1 Hz feed along the reference route, and a test pins that it replays to 0.00, 1.15, 0.00, 1.16, 0.00, 1.17 % short for --skip 0..5 with 121, 120, 120, 119, 119, 118 points kept. The header says "every fix after the first that is later than the one before it", the printed table says that the row "off" need not equal the distance_m of the trip, and a test pins "0.00 %" in the reference column. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…ding (round 3, R2.4, R2.5, R2.11)
One helper closes a file on a path where another error is on its way and absorbs a close
that rejects as well as one that throws before it returns a promise (the driver's close is
a plain method); both catch sites use it. FakeDatabaseFile gets failNextCloseWith(error,
{ sync }) and closeCalls, and the tests show that the received error is the promotion's
or the open's in both cases, that nothing closes a file twice on a failure, and that a
failing write of the note of the replaced file closes the handle. The header, the test
title and the comment say what is true: a plain key is never followed by a deletion; a
pending key may be, whether or not an earlier start opened a file with it. thin.ts and
app.config.ts: one comment line rewrapped, one dangling parenthesis removed.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
… series, the gate rules (round 3) 9.2: a list of what the owner provides, decides and does alone; the Apple and Xcode steps in the order phone, Trust, Xcode, Developer Mode, Apple ID, signing; when signing out is safe and when to tell the lead session; the build check is `ios:gate -- APP --test-build --drive` with the pass criterion; step 10 runs `emulator:thinning` first with --save-points and reads the cadence line, then `emulator:check`; a standalone iPhone report list with who receives it; the removal steps with the PC side written out. 9.1: the checkout may be a worktree, `cd <checkout>`, the second window's folder, and the Devices page is each person's own (Customize.tsx:81-84, Security.tsx:82, devices.rs). 8.8 and ISSUES L44/L48: the corrected clean-feed series (0.00, 1.15, 0.00, 1.16, 0.00, 1.17 %; the earlier "1.15 throughout" was a summary error), the closure check's own Simulator series, what is and is not established about the first test build's key, the gate rules, the pending-key wording, the full Simulator identifiers. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…" line (round 3) 8.8: the gate bullet now says that the --drive gate was run on Release builds for the Simulator on the Mac (a build with the variables passes, an incremental build without them passes the ordinary test gate with one warning and fails --drive). 9.2 step 10: the sentence about what --save-points prints. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…pm store variable, --save-points tests (round 4) gate: with --drive, --expect-server URL (an http address, compared as scheme, host, port and path with a trailing slash ignored) and --expect-bundle-id ID fail a build whose default server or bundle id is another one, and name the variable that fixes it; the remedy names both rebuild commands (ios:run:device and ios:build:simulator Release); the failure for thinning without plain http carries the "drive build:" prefix when --drive is set. Without the options nothing changes. env.sh: pnpm 12.6.0 ignores npm_config_store_dir; the setting it reads is pnpm_config_store_dir (checked with pnpm store path and vp install on the Mac). thinning-replay: tests for --save-points with --skip (the saved file keeps every fix) and for a relative name (the announcement prints the absolute path); comments that the wx flag and the 0600 mode cannot be asserted here, and that the saved file belongs outside the repository. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…e, one numbering, runnable blocks, the signing step 9.2: keep a copy of the built app in ~/build/obop-keep (A8) and put it back on the phone with xcrun devicectl, so that T5, T9 and T10 no longer contradict "sign out as soon as the app is installed"; "When it is safe" rewritten; the instance is not deleted until the lead session has written that it has what it needs. Every step is numbered once (A1 to A8, B1 to B4, C1 to C3, D1 to D3.5, T1 to T10, R1 to R9, P1 to P4) and the cross-references follow. The signing step says what a working Signing & Capabilities looks like, to choose the phone as destination and build once in Xcode, and that expo run:ios does not repair a missing profile once a team is set. D3.2 saves the route to $env:USERPROFILE\iphone-trip-points.json (the owner's file, outside any repo) and replays it with --points FILE --skip N. The gate line carries --expect-server and --expect-bundle-id. 9.1: placeholders explained, OBOP_CHECK_PASSWORD on its own line, cd for the second window, restart recipe, the long first cargo build, rev-parse in the Mac block. 8.8: "from 0 up to about 1.2 %", which tracks the 1.5 s = 2 s sentence covers, the source of the second measurement, the device identifiers, what the Mac mini said without a phone. ISSUES L44 follows. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…n macOS, where /var is /private/var) The test of a relative file name expected the printed path to equal resolve(dir, name). On the Mac the temporary folder is reached through the link /var, and the script, started in it, prints the folder it is really in (/private/var/...). The script was right and the test was too strict: it now checks that the printed path is absolute and names the file that was written. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
They ran from hand-made files on the PC and, as vp run ios:gate over a hand-made folder shaped like an app, on the Mac mini; not on a real Release build and not on an app built for an iPhone. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…ne fails the gate (round 5) --expect-bundle-id is an exact match. Nothing pinned that: replacing the comparison by startsWith left every test green. Adds a unit case (org.obop.mileage.test2, org.obop.mileage.test.extra and com.org.obop.mileage.test against org.obop.mileage.test) and one command case (exit 1, the drive build: line). The startsWith mutation survives the tests of 6583184 and is killed by these, as are includes and endsWith. Renames the test of the thinning flag to say what it checks. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…th closure check 9.2: the final Mac clean-up names both profile folders (MobileDevice and Xcode/UserData) and says to look in both; the kept app copy (~/build/obop-keep) has its own paragraph and timing (after the last use, T10) and the Apple ID block says it is not part of it and what an early clean-up costs; one sentence before the Mac block on replacing <LAN> and the other angle-bracket words; "Untrusted Developer" after a reinstall is the install and not a failed test (A8); the PC stays on at the same address through T7 to T10 until the lead session has what it needs; D3 is done before any extra test's trip is submitted, else --trip ID; the gate facts in the order it prints them; "can no longer be installed or no longer starts" after seven days, counted from the profile. 8.8 and 9.2: the --expect-* options were run on a real Release Simulator build by the fourth closure check (the device path was not); the "Incompatible device" clause belongs to the first closure check and the full identifiers worked in every run that used them. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
… id of a trip in the address bar (round 5) Before any signing neither ~/Library/MobileDevice/Provisioning Profiles nor ~/Library/Developer/Xcode/UserData/Provisioning Profiles exists on the Mac mini; only ~/Library/Developer/Xcode/UserData itself does (ls, 2026-10-30). The first version of this text said that the second folder exists. A missing or empty folder now means "nothing left" only when looked at after the signing, and both folders are looked at again after the sign-out. D3: the id of the trip is the text after id= up to the next & (or the end of the address). Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This branch has not been deployed
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.
What this is
The Mileage app (
web/apps/mileage-app) on iOS, as the owner ruled on 2026-10-29 (relayed by the lead session; not yet indocs/DECISIONS.md): every native app works on Android and on iOS. Draft. The base isfeat/mileage-app(PR #27, the Android app), which this branch does not change; merge this one after #27, or retarget it.Everything below is marked in
docs/spec/mobile.md: the port and its evidence are 8.8, the steps for the owner's iPhone are 9.2. Nothing here was run on an iPhone, no agent signed in to an Apple account, and nothing was built for a device.What changed
app.config.ts,config/ios.ts): thelocationbackground mode and nothing else (nofetch, no Always, motion or full-accuracy keys), the app's own "While Using" text,NSLocalNetworkUsageDescriptionin every build, the bundle id fromOBOP_IOS_BUNDLE_ID(test buildsorg.obop.mileage.test; the sign-in scheme staysorg.obop.mileage). The development client's Bonjour service and URL scheme are taken out of the generatedInfo.plistunlessOBOP_DEV_CLIENT=1.vp run ios:gate): fails a build that can speak plainhttp, carriesthinningOff, has an ATS exception of its own, declares another background mode, or carries the development client's_expo._tcporexp+scheme. In a test build it failsthinningOffwithoutcleartextAllowed(the app ignores the variable then) and warns about the test bundle id withcleartextAllowed=false. Round 3:--drive(vp run ios:gate -- APP --test-build --drive, for the build of the first iPhone drive) also fails unlesscleartextAllowedis true,thinningOffis true and the default server is anhttp://address, and names the variable that fixes each. New in round 4:--expect-server URLand--expect-bundle-id ID(only with--drive; the server must be anhttp://address and is compared as scheme, host, port and path, a trailing slash and the case of the host ignored; the bundle id is an exact match). A build that points at another address, or has another bundle id, fails with adrive build:line that names the variable (OBOP_DEFAULT_SERVER,OBOP_IOS_BUNDLE_ID), and the remedy line names both rebuild commands (vp run ios:run:devicefor the phone,vp run ios:build:simulator Releasefor a Simulator build). Used without--drive, with no value, or with anhttps://server, the command stops with the usage text (exit 2). The failure "the build ignores OBOP_RECORD_THINNING because plain http is not allowed" now begins withdrive build:when--driveis set, like the other three.AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLYon every write; "Precise Location off" is refused like Android's coarse; negative accuracy and speed are no position; fixes closer than 2 s and 50 m are dropped (thinning, on every platform); a restored backup with no key replaces the unreadable file and says so (and a failure half way no longer leaves the app stuck: the new key is stored as pending first, the opened file is closed if anything after the open throws, and a close that itself rejects or throws cannot replace the error that is on its way,closeQuietly; a plain key is never followed by a deletion, a pending key may be, when the file it meets is not a database); a reinstall starts signed out; a stored address is checked again at start; a call whose deadline passed during a suspension gets one grace period; 7 sentences have an iOS wording (including the Failed screen, which named Android's "Clear storage"); on iOS the Location Services switch is asked before the app's permission, which the system reports as denied while the switch is off.OBOP_RECORD_THINNING=off: does not thin the fixes (the store still drops implausible, not-later and stale fixes and stops at 20,000 points), so that the first iPhone drive shows what the phone delivers. Honoured only in a build that also allows plainhttp; the gate fails a release build that has it.vp run emulator:thinningreplays the rules 2 s / 50 m, 1.5 s / 50 m and none over a trip from the server (--skip Ndrops the first N fixes, to vary the phase of a 1 Hz feed). Round 3:--save-points FILEwrites the time and position of the fetched points as the JSON that--pointsreads back (it refuses to overwrite a file), andscripts/emulator/route-feed.mjswrites a clean synthetic feed. Round 4: the header of the script says the file is the owner's and gives an absolute path outside any repository (9.2 uses$env:USERPROFILE\iphone-trip-points.json), and that other start phases are run as--points FILE --skip N, not as the first line again (it refuses to overwrite); the line it prints names the absolute path; the reasons for thewxflag and the mode0600are in comments.export:ios,ios:prebuild,ios:pods,ios:build:simulator(ad hoc signed),ios:gate,ios:run:device(not run),simulator:route,simulator:drive,scripts/ios/env.sh(the build environment, withCIunset). Round 4:env.shsetspnpm_config_store_dir; pnpm 12.6.0 ignores thenpm_config_store_dirthat the file set before, so the pnpm store went to~/Library/pnpm/storeand not to~/build/tools(checked on the Mac, below).route.mjsprinted nothing on macOS and Linux; fixed.emulator:checkprints the points per minute and the largest gap.~/build/obop-keep/obopMileage.app(ditto), outside everything the clean-up removes, and gives the reinstall asxcrun devicectl device install app --device <name or identifier> <path>("general knowledge, not tried with a phone"; the syntax is from--helpon the Mac). T5, T9 and T10 now use that copy, so they no longer contradict "sign out as soon as the app is installed"; "When it is safe" is rewritten, the clean-up removes~/build/obop-keep, and the instance on the PC is not deleted until the lead session has written that it has what it needs.expo run:iosdoes not repair a missing profile (@expo/cli58.1.13,XcodeBuild.jslines 302 to 304 andconfigureCodeSigning.jslines 68 to 72).--points FILE --skip 1and--skip 2; report item R4 asks for those two outputs.OBOP_CHECK_PASSWORDis on its own line, the second window starts withcd, a restart recipe, a note that the firstcargo buildis long, the Mac block pulls and printsrev-parse; the gate line carries both--expect-*options. The sections and rows the text refers to are named (8.8, 4.2, 9.1, 9.2, L39 to L48), and Q37, D21 and M3 are marked as needing no action.Round 5 (the last one; the fourth closure check found only hand-over defects and one test gap):
~/Library/MobileDevice/Provisioning Profilesand~/Library/Developer/Xcode/UserData/Provisioning Profiles; look in both, delete what is there; on the Mac mini, before any signing, neither folder exists, only~/Library/Developer/Xcode/UserDataitself does, checked withlson 2026-10-30, so the folders are looked at again after the sign-out); the kept copy of the app (~/build/obop-keep) has its own paragraph and timing (after the last use, T10), the Apple ID block says it is not part of it and what an early clean-up costs (T5, T9 and T10 need a rebuild with a new sign-in); one sentence before the Mac block says to replace<LAN>and the other words in angle brackets together with the brackets; "Untrusted Developer" or "Unable to Verify App" after a reinstall is the install and not a failed test (A8); the PC stays on, awake, at the same address through T7 to T10 until the lead session has what it needs; D3 is done before the trip of any extra test is submitted, else--trip ID(an option of both scripts, read fromserver-lib.mjs); the gate facts are listed in the order it prints them; after seven days the app "can no longer be installed or no longer starts", counted from the profile that Xcode makes in A5.--expect-*options were run on a real Release Simulator build by the fourth closure check (the device path was not); the "Incompatible device" clause belongs to the first closure check, and the full simulator identifiers worked in every run that used them (the second, third and fourth checks).gate.test.ts: a bundle id that only starts with, or ends with, the expected one (org.obop.mileage.test2,org.obop.mileage.test.extra,com.org.obop.mileage.testagainstorg.obop.mileage.test) fails, in a unit case and a command case; one test is renamed to say what it checks. Nothing undersrc/,web/packagesorcrates/changed in round 5.What was run
PC, on the head of this branch (
8f1a3e0; the last change to code iscf0b72a, a test ingate.test.ts; round 4 changed code in11f958dand1727c0d; the other commits are documents). The counts below were taken onba27130;8f1a3e0changesdocs/spec/mobile.mdonly, so the tests are unchanged and were not re-run for it:vp testinweb/apps/mileage-app: 30 files, 424 passed, 1 skipped (a symlink case that Windows cannot make; on the Mac it ran).web/packages/client: 156 passed, 10 skipped (not mine).ui-native: 15 passed.trip-core: 26 passed (these three at451f2b1; nothing in those packages changed after it, round 5 included).vp checkin the app: 114 files formatted, no lint errors in 112 files. At the web rootvp checkreported 3 unformatted generated files that this branch does not touch (as in round 2; not re-run in round 4).vp run export:ios,vp run export:android,vp run check:sdk: pass (at451f2b1).--info-plist-json,--app-config): a match exits 0 ("gate passed (test build)"); another server exits 1 and another bundle id exits 1, each with aFAIL drive build:line naming the variable and the remedy line;--expect-serverwithout--driveexits 2;--expect-bundle-idwith no value exits 2. The unit tests (54 ingate.test.ts) run the same cases and more.vp run emulator:thinning -- --save-points FILEagainst a stand-in HTTP server (round 3) and in unit tests: it saved 241 points, printed the table, and a second run refused to overwrite the file. Against a real instance it was run by the third closure check ofa3ad91d(reported to me, not run by me): it saved all 248 points, and the replay worked from the file after the instance's data was removed.--driveor with anhttps://server, the command not passing the options on, a flag taken as a value), 2 to--save-points(the skipped points saved, the relative name announced) and 1 toenv.sh(the variable that pnpm ignores): all 19 failed the test file. After the test fix below, the relative-name change was run again against the new test and fails it. The scripts are in the session's scratch folder, not in the repository. Earlier rounds: 19 changes in round 3 (all failed the test files), four changes to the close paths ofopen-outbox.ts(a fifth, a plainawait file.close()insidecloseQuietly, is not caught and is equivalent underasync), and the lead's closure check ran 24 of 25 mutations caught (the one that survived is the line innative/store.tsthat joinsthinningFor(extra)tocreateSqlStore, which no unit test can reach without expo modules).gate-lib.mjs, andgate.test.tswas run:startsWith,includesandendsWithinstead of!==. Before the new cases thestartsWithversion survived the tests of6583184(every test stayed green); with the new cases all three fail the file (2, 2 and 1 tests). The script is in the session's scratch folder, not in the repository.Mac mini (macOS 27.0, Xcode 27.1), synced with
git pull --ff-onlyeach time:vp testandvp checkonba27130(same code as8f1a3e0): 30 files, 425 passed, no lint errors;gate.test.tsalone 54 passed. The first Mac run, on451f2b1, failed one test (422 passed): the new test of a relative--save-pointsname compared the printed path with the path of the temporary folder, and on macOS that folder is reached through the link/varwhile the script prints/private/var. The script was right and the test too strict;1727c0dcompares the printed path (absolute, and the same file byrealpath), and the Mac run on1727c0dpassed all 423. The PC cannot show this: Windows has no such link.vp run ios:gate -- APP ..., over a hand-made folder shaped like an app (anInfo.plistconverted withplutilandEXConstants.bundle/app.config): a match 0 (also with a trailing slash on the address), another server 1, another bundle id 1, without--drive2, a missing value 2 (also when another option follows), anhttps://address after--expect-server2,--drivewithout the options 0. Not run on a real Release build.xcrun devicectl list devicesprinted one device,iPhone 18 Pro, stateshutdown, Realitysimulated;xcrun devicectl device install app --helpprinted the usagedevicectl device install app --device <uuid|ecid|serial_number|udid|name|dns_name> <path> ...;xcrun devicectl --versionprinted 651.13.5.pnpm store path(pnpm 12.6.0):~/Library/pnpm/store/v11withnpm_config_store_dir,PNPM_STORE_DIR,NPM_CONFIG_STORE_DIR,npm_config_store-dirorpnpm_config_store-dirset, and~/build/tools/pnpm-store/v11withpnpm_config_store_diror--store-dir;vp installwithpnpm_config_store_dirreported "Content-addressable store is at: /Users/rorymac/build/tools/pnpm-store/v11". A straytail -fof an earlier round was stopped;~/build/mileage-ios/out/was kept.Info.plistread byplutiland the gate run on each (a test build with the thinning off, the same again, a plain build, a build withOBOP_DEV_CLIENT=1that the gate fails on theexp+obop-mileagescheme, and a test build after that); the strip phase ofexpo-dev-launcherdeletes only_expo._tcp, never theexp+scheme, and in an incremental build it runs before the plist is processed. Round 3, onc405891: a test build made with the four variables exported passed--test-build --drive(exit 0); an incrementalvp run ios:build:simulator Releaseof the same app in a shell without them wrotecleartextAllowed false, no default server and the thinning on, passed the ordinary--test-buildgate with the one warning (exit 0) and failed--drive(exit 1) with the three facts and the remedy. The Simulator drive (simulator:drive) was run after the code of round 3 by the third closure check, ona3ad91d: 6 tests,** TEST SUCCEEDED **, 248 points, -0.26%. Nothing was driven on8f1a3e0, because rounds 4 and 5 changed no code that runs in the app or in the drive (round 4: the gate and its option handling,env.sh, the header and comments of the replay script, and tests; round 5: documents and one test; nothing undersrc/,web/packagesorcrates/). The fourth closure check ran the gate with--test-build --drive --expect-server ... --expect-bundle-id ...on a real Release Simulator test build (head6583184): exit 0 with the two "as expected" lines for the right server and bundle id; exit 1 with thedrive build:lines and the remedy for a wrong host, port, path and bundle id; exit 2 for a missing value and for the options without--drive; thedittocopy of the app was identical (diff -r), passedcodesign --verify --deep --strictand the gate. The same options on a build for a device are not run. Details in 8.8.iOS Simulator against a throwaway instance (
ASWebAuthenticationSessionsign-in, trips with the Home button, the server's check of the points, reinstall, restored backup,simctl terminateandsimctl launchmid-trip (not a force-quit by a finger: that stays for the iPhone, T2), plainhttpover the LAN, a plain build that refuseshttp). Reference 4,000 m; the length by the server's formula is 3,989.44 m:seq1 to 42The first 1 Hz run's -1.39% was first explained as the chords across the route's corners and as the price of the 2 s rule. That explanation was wrong and is withdrawn: the route has 4 corners, not 40, and the later runs did not repeat the figure. Replaying the rule over the 248 unthinned fixes after dropping the first K of them (
vp run emulator:thinning -- ... --skip K, K = 0 to 5; reproduced from a file of the trip's points, the instance being gone) costs 0, 0, 1.14%, 0, 1.15% and 0% of the length of the fixes it was given: the cost depends on a phase of the 1 Hz feed that nobody chose, and the first run's figure is read as the bad phase (an inference; its phase was not recorded).The clean-feed series. A clean synthetic 1 Hz feed along the route (
node scripts/emulator/route-feed.mjs feed.json, 241 fixes, 100 m legs at 16.667 m/s, thenvp run emulator:thinning -- --points feed.json --skip K; a unit test pins the figures) falls short of the fixes it was given by 0.00, 1.15, 0.00, 1.16, 0.00 and 1.17% for K = 0 to 5, with 121, 120, 120, 119, 119 and 118 points kept (the same for 1.5 s and 2 s on this feed): in the even phases the rule keeps the three corners and the last fix, and in the odd ones it loses three corner chords of 9.7 m each and the last step of 16.6 m. A second real measurement, by the second closure check (head1c52c90, 2026-10-30, a fresh device, a test build with the thinning off; 248 points over 241 s, 3,989.44 m; the trip was not kept): it replays for 2 s / 50 m to 121, 120, 120, 120, 119 and 119 points kept and a shortfall of 0.00, 1.13, 1.13, 0.00, 1.14 and 0.00%, and for 1.5 s / 50 m to 121, 121, 120, 120, 119 and 119 points and 0.00, 0.00, 1.13, 0.00, 1.14 and 0.00% (so on that trip 1.5 s and 2 s differ for K = 1; on the clean feed and on this session's 248-point trip they give the same distance). The real series and the clean one differ from each other and nothing shows why: their track files were not kept, and the clean feed reproduces the mechanism and not these exact numbers. What these runs support: on this route at 1 Hz the rule costs from 0 up to about 1.2%, depending on a phase that nobody chose. What they do not support: a price per corner, a price of 2 s, or a reason to prefer 1.5 s. Nothing was changed to make any figure pass, and none of these runs is evidence for or against 2 s: that is left to the first iPhone drive (L44).Android regression:
expo prebuild --platform androidat the base and at the head gives byte-identical project files (the review), and the release APKs differ only in the JavaScript bundle and the embedded config. Two runs of the 41-waypoint route on the emulator: this session's build from the range up to6a1dafbgave 56 points, 3,989.35 m, -0.27% (as recorded in the first text of this pull request; no log was kept); the review's build of head1e29576gave 70 points, 3,981.64 m (-0.20% against 3,989.44 m), 11.5 points a minute, smallest gap 3.003 s, and the base 82 points, 3,985.53 m. The thinning acts on Android too, but the 5 s interval ofexpo-locationmeans the 2 s rule does not act in the normal case. Details in 8.8, "What was run on Android".Checked only by unit tests, or only by reading
src/core/open-outbox.ts(a close that rejects, a close that throws synchronously, a double close, the close after a failed note write) and the deletion of a pending key: a fake database file only. The Simulator drive of the closure check passed through the success path of this file on a first start; no failure path was ever run on a Simulator or a phone.src/native/store.tsthat givesthinningFor(extra)tocreateSqlStore: no unit test reaches it. Only the Simulator drive (248 points stored with the thinning off) and the smallest-gap check of 9.2 step D3.2 guard it.--drivegate with--expect-serverand--expect-bundle-idon an app built for an iPhone: not run. With the options it ran on hand-made files, on a hand-made app folder and, in the fourth closure check, on a real Release build for the Simulator; without them it ran on Release builds for the Simulator.find ~/Library/Developer/Xcode/DerivedData -maxdepth 6 -path '*Release-iphoneos/obopMileage.app') andecho $?after the gate: no build for a device was ever made, so the line never found anything; that Expo puts the app in the standard DerivedData folder is read from the CLI's source.D:\git\SpaceCorps\obop-wt\iosin the owner's copy,New-Item,target\debug\obop.exe, the restart recipe, the second window starting withcd): not run in that form on the owner's PC.xcrun devicectl device install app(A8) anddittoof the app: the syntax is from--helpon the Mac mini; it was never run with a phone, anddittowas checked on a signed macOS app, not on the iPhone app.--save-pointsand the replay from the saved file were run against a real instance by the third closure check; the exact 9.2 line ($env:USERPROFILE\iphone-trip-points.jsonon Windows, then--points ... --skip 1and--skip 2) was not run in that form.Customize.tsx,Security.tsx,devices.rs), not seen in a browser.Not verified (the iPhone test, 9.2)
Recording with the phone locked for 10 and 30 minutes under "While Using" (the decisive test; if it fails the fallbacks of 4.2 need an owner ruling), the local network prompt and a refusal of it, Low Power Mode, Allow Once, a force-quit by a finger, Precise Location off and Location Services off on a phone (unit-tested only), a real GPS, the battery, a device build and the free team's signing,
vp run ios:run:deviceand the phone-name form of its argument. Not covered by any step of 9.2: the Keychain read while the phone is locked (a recording never reads the Keychain again; it needs a process that starts while locked),fetchwithredirect: "manual"on the 400 path, the stored-address recheck of a release build, the deadline of a call after the app was suspended (unit-tested only), VoiceOver, dark mode and text sizes beyond Home, iPad.Decisions of the lead session (the owner can override)
"While Using" only;
AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY; no ATS exception; test bundle idorg.obop.mileage.test(Q37 has the point); reinstall signs out, a restored backup is recovered with a notice; store items out of scope; the server needs no change for iOS. Open: the thinning gap, chosen after the first iPhone drive.🤖 Generated with Claude Code