Repository navigation
Conversation
…of 2026-10-28 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The owner's amendment of OR-34 e: SDK 58 if the packages pass the 7 day cooldown of D23 when step 5 starts. expo 58.0.16 (2026-10-19) and the packages of its bill of materials pass; all 778 locked entries are older than 7 days. react and react-dom stay 19.3.0 (the bill of materials and the renderer of react-native 0.88.0), the web uses 19.3.1. check:sdk compares with the bill of materials of the installed expo (offline); check:sdk:latest asks Expo's API, which wants patch releases younger than the cooldown. The dates are in docs/spec/mobile.md 8.7. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…eb Storybook The small themed React Native component set of the Mileage app (Screen, Button, Row, Field, Banner, plus Text), the native tokens generated from web/packages/ui/src/colors.ts and a small table with a parity test against tokens.css, and web/apps/storybook, which draws the components through react-native-web (Q42 as answered by the owner, OR-34 g). Storybook 10.6.3, react-native-web 0.21.4 and vite 8.3.4 pass the D23 cooldown. esbuild's install script stays off (allowBuilds: false). The Docker web stage leaves the two new members out of its install, as it does the app. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…oken exchange and refresh) to @obop/client Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The pure-TypeScript core of step 5: the encrypted-store port (SqlDb), the outbox store, the upload engine (batches of at most 1000 with fixed ranges and deterministic idempotency keys, 429 and 503 handling, incomplete retry, reconcile-first end/submit/discard), the typed calls, and 51 tests that run the real SQL on node:sqlite against a scripted server. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…r with tests Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ices Home, permissions with the in-app disclosure, Recording, Trips, Trip detail and Account over @obop/ui-native; system-browser sign-in with PKCE; the encrypted SQLite outbox behind the location task; English catalogue; the SQLCipher and cleartext-for-test config. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…the emulator route and the 8.7 notes The Gradle helper sets CMAKE_POLICY_VERSION_MINIMUM so expo-sqlite (SQLCipher) configures under CMake 4. The manifest blocks permissions that libraries add and the app never uses. route.mjs is the synthetic reference route of the emulator run; docs/spec/mobile.md 8.7 states the route and its tolerance before the run, and the decisions of step 5. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…on answers, stale first fix - RECEIVE_BOOT_COMPLETED is declared: expo-task-manager schedules a persisted job at the first location and Android refused it, so the release build crashed at the first fix. - end, submit and discard answer with the changed fields and the id, not the trip: the app now reads the trip after each action (the API test uses the real answer shape). - A cached fix from before the start is not stored (STALE_FIX_MS, 10 s): the first trip had a point 900 m and six minutes away as point 1 and a distance of 4,880 m for a 3,989 m route. - drive.mjs gets a third offline phase for the second full run. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…-phone steps - docs/spec/mobile.md 8.7: what was verified on the emulator (sign-in, permission steps, recording with the screen off, offline Stop and drain, distance within the stated tolerance, submit, reviewer confirm and the monthly total, discard, force-stop recovery, Account rename, remove and sign out), the findings (the failed first run and its stale first fix, the missing boot permission, the action answers), and what is not verified; corrects the "41 points" statement. - docs/spec/mobile.md 9.1: the real-phone test, step by step, with the tolerance proposed before the drive. - scripts/emulator/check-trip.mjs: a read-only check from the server's side. - ui-native Row: a long value no longer squeezes the title to nothing (found on the Account screen); a Storybook story with the long value. - docs/ISSUES.md: L20 to L28. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
ISSUES.md: K22 keeps the r29 multi_process flake from the docs branch, K23 is the closed row of main. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…dmap, the spec Corrects statements that still described the SDK 57 spike (PR 27 review, findings 12, 18, 19, 34, 42), the Q43 wording about expo-location/next (20), the setting.get paragraph and ISSUES L20 (21), the corner-cutting estimate of 9.1 (39) and the ISSUES section heading (41). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…efresh, OAuth timeouts - engine: a trip the server discarded or no longer has finishes on the phone or stops with a worded code, and can always be discarded from the phone (6, 7); the end and the purge of a trip are one transaction and a sweep removes routes a kill left behind (30); the wait after an incomplete end doubles (24). - session: the rotated refresh token is held in memory before the write (22, 27); a client the instance may have forgotten is registered again (2, pure rule with tests). - client oauth: a timeout on every call (5); the state is checked before an error (28). - tests: the session rig follows the real single-use rule of the token endpoint (22); the fake server answers a missing trip like the module (400, no code). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ess in core The outbox records whose trips it holds (server origin and the person the instance reports as owner of the device). A sign-in as someone else, or at another address, finds unsent trips foreign: nothing is uploaded, shown or deleted until the driver chooses. The address check moves to core with tests. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ording health, plurals Sign out and Remove this phone say how many trips and points exist only on the phone and keep them for the same account or delete them after a second confirmation. A foreign outbox gets its own screen. A trip that is open when the sign-in ends keeps a Stop control. Start refuses approximate location and switched-off location; Recording starts the service again when the app returns, warns when no position arrives, and scrolls. The catalogue has plural forms and Intl numbers and dates. Selected state for the purpose buttons. The native stories move to the Storybook app so ui-native needs no Storybook dependency. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…6; vp run tasks for the emulator scripts classify() has a table test; a portal answer (invalid_response) and a 408 are repeated later instead of being a standing refusal; the sync controller's timer is tested with a wait that differs from the idle interval, and over the real client and engine. Boundary tests: the stale fix, the 20,000 point cap, the backoff cap, the token skew. A gate decides before any route that an open trip is never hidden behind the sign-in screen. The app's minimum Android is 26, where expo-location starts its foreground service. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ends on Storybook Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ed state as aria-checked; record the review round in the docs The first build of the review round crashed at start on the emulator: 'undefined cannot be used as a constructor' from new Intl.PluralRules. The plural rules fall back to English and the number and date formatting to plain text. ISSUES L28 to L36, the spec (8.7 design of the outbox and of the recording checks, 9.1 steps, minimum Android 26). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…d say 'not completely on the server' The emulator run showed '1 trip ... exist only on this phone', and the count includes the points of a trip that was already uploaded in part while recording. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…a far-off first fix) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…rect the redirect comment A call that was answered with headers and then stalled in the body held the sign-in for as long as the connection lived; the whole call, body included, now has the deadline. OAuthError.timedOut tells a given-up call from a refusal, so that the session can treat a refresh that timed out as an unknown outcome. The comment on parseAuthorizationResponse no longer claims that a forged error redirect cannot end a pending sign-in: the state check only changes the word it ends with. PR 27 delta findings 2, 9, 13, 15, 22. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…is not "gone"; discard of a refused create
Session: a generation counter changes with every sign-in change (sign-in, sign-out, end, restore).
A refresh whose answer arrives after that is dropped and writes nothing, Keystore writes go one
after the other, and a refresh that times out ends the sign-in as "unsure" (the old token is not
presented again; the instance is told in the background). An invalid_client clears the stored
use so that the next sign-in registers again.
Engine: a pass remembers the sign-in it started under and stops before its next call when it
changed ("interrupted"); the sync controller then runs again at once under the new one. Only
the plain 400 "no mileage_trip X" means that the server has no trip; a 404 keeps every point and
the problem code is "unknown", which cannot be discarded. A discard of a trip whose create was
refused finishes on the phone. Comments and tests agree on the 409 delay (8 s in production).
PR 27 delta findings 4, 5, 7, 11, 13, 14, 16, 20, 21, 25, 27, 28.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ice, register again, bound the calls runtime.ts gives the engine the session's generation and refuses a client when the outbox is not the signed-in person's. The "instance was not told" flag is read by a hook that follows changes and is set before the screens are told. The sign-in screen says when a sign-in ended because a refresh timed out. App.tsx forgets the choice to sign in from the recording once a sign-in has finished. The Recording screen says nothing about missing positions before it has read them. A client the instance has forgotten for another reason than idleness is found with one read of the authorization page (400) and registered again once; the owner question and the device calls have a 30 second deadline. New texts: the unknown 404 of a trip, the unsure end, a truthful database hint. Comments on leaveSession and the owner key corrected. PR 27 delta findings 1, 6, 7, 8, 13, 17, 18, 22, 26, 27, 29, 33, 34, 38. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…md 8.7 and 9.1, screen lists ISSUES: L29 (the 60 s refresh timeout and the "unsure" end, every call has a deadline), L30 (only the plain 400 is "gone"; a 404 is "unknown"), L32 (a forged redirect still ends a pending sign-in), L37 (four points, about 8,583 km), L38 (more untested glue), new L39 (a new address of the same instance is foreign; backend change named), L40 (the client forgotten by a replaced database), L41 (no notice on the Recording screen for a trip the server lost). mobile.md: the 1.2 rows that 8.7 contradicted, the 8 seconds of the first 409 wait, the Foreign screen in the screen list (also CLAUDE.md and D87), 22 tests of oauth.ts, seven stories (the seventh not looked at), the real order and bounds of Sign out and Remove this phone, what is kept when trips are kept, the identity key and the address warning in 9.1, UTC time of the revocation, the rebuilt instance, the Hermes plural crash as finding 5, the APK of 38e04db (size and SHA-256), the third emulator run with what was and was not run. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…s no longer cite OR-34 (b) Not part of the APK (the comments of two test scripts and of router.ts; the build of 38e04db is the APK). OR-34 (b) names Metro, the Expo CLI, Gradle and Xcode; the emulator scripts are vp run tasks and claim no exception. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- refreshRemote and refreshTrip capture the sign-in before the call and drop the answer when it changed, so a list of the person who signed out never lands in the next person's outbox. - withDeadline races the call against the deadline as well as aborting its signal, so a call that ignores the signal (a token wait, a half-open connection) is bound too; it ends as a network error. - A sign-in the phone ended on its own after a refresh without an answer now tells the sign-in screen when the background revoke fails too (onEndUntold); the notice text covers both cases. - A refresh refused as invalid_client re-checks the sign-in after writing the config. - Tests that fail on revert for the serialised Keystore writes and the six guards of round 2 (refreshing cleared in signOut, the finally guard, both catch guards, the loop-top check), for both new guards and for the deadline race (15 new tests). - Comments: the backoff factors, Stop alone does not finish a trip the server discarded, a 404 at the create step keeps the server's code, the real order of Sign out. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…s a discarded trip flagged Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- L29: the notice when the background revoke of an unsure end fails too; the deadlines are raced. - L30: what each step does with a 404, and the create step as the exception. - L38: which guards now have tests that fail on revert, and what still has none. - L40: the per-address sign-in page slot of the look at the page; the invalid_client branch is defence in depth, the obop server never sends it to a refresh. - L41: Stop alone does not finish a trip the server discarded; Try again after Stop and Discard do. - 8.7: the real order of Sign out, the fourth emulator run, the mutation record, the Not verified list corrected; 9.1: the new APK (size, SHA-256, commit), steps 11 to 15 in an order that can be followed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
One conflict, docs/DECISIONS.md: this branch rewrote the rows OR-34 and D87 (SDK 58, the cooldown), main added OR-35 after OR-34 and D88, D89 and their paragraph after D87. Both sides are kept: the branch's OR-34 and D87, then main's added rows and paragraph, unchanged. ISSUES.md, mobile.md (main's M8 update of the batch-rate paragraph), DEPLOY.md, CLAUDE.md merged without conflicts; the L rows (L1 to L41) and the K rows (K1 to K41) do not clash. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…tch, honest tests; wording fixes - core/deadline.ts: the network error is made before the signal is aborted, so a call that rejects in its abort listener cannot win with its own error; the stalled.catch line and its wrong comment are gone (it was a no-op); the test that could not fail is replaced by one that checks that no timer is left after an answer or an error, and one for a call that ends after it was given up. - 'a newer sign-in has begun' says 'has finished' (the guard is on completeSignIn). - The create-step 404 comments no longer say that nothing of the trip is on the server. - The Failed screen hint says what to do when Try again keeps failing (the key is lost). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…, test count, hand-over steps - 8.7: the held list of the fourth run outlasted the 120 s trip deadline and shows nothing about the guard; the fifth run repeats it with a 57 s hold; the order of Sign out is read from the code, the proxy log shows the 5 s bound only; which tests are revert tests (17 of 18); which APK each run was on; the force-stops that 8.7 does record (7b). - ISSUES L29/L30/L34/L38: the trip calls rely on the abort alone, isGone tests the message only, the create-step 404 rationale, a lost Keystore key and the Failed screen, what has no test. - 9.1: adb path, throwaway passwords, plain http, rita is an administrator, firewall for a Public network or no firewall, same router and subnet, Battery Saver, clocks (phone local, server UTC), keep the app on screen until Ended, what to undo afterwards. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Conflicts: docs/DECISIONS.md (two hunks). OR-34 and D87 keep the branch's rows; OR-35, D88 and D89 take main's rows byte for byte (47005a6). docs/ISSUES.md, docs/ROADMAP.md and docs/MODULES.md merged without conflict. web/, crates/ and the lock files are unchanged by the merge. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The comment said that no change to the function was found that makes the test fail. It fails when the race is removed and when the late end of a call that was given up has no handler (fifth review round, findings 3, 5 and 8). Comment only: the test file is not in the APK. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… commands Fifth review round, docs only: say what the proxy log of the held revoke shows and what follows from the code (1, 9, 10), the real reason why the orphan trip case is narrow (2, 6, 7), one count and the named control (3, 5, 8), what step 7b's force-stop runs record (4), the timeline of the two held list runs (11), how to pick the PC's address (12), and single quotes for every command that carries a password, with the history file in the removal list (13). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
9.1 and 8.7 said the commits after 938f9eb change documents only; the fifth round also changed a comment in deadline.test.ts. No source file that goes into the APK changed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Two textual conflicts, both in documents, resolved keeping both sides: - CLAUDE.md: the "Native apps" paragraph keeps this branch's description of the built Android app and takes OR-36 (every native app works on both Android and iOS) from main. - docs/DECISIONS.md: OR-34 (this branch: SDK 58 pinned, Storybook check run) with OR-35 to OR-37 from main; D87 row, the "Ratified on" paragraph and the D87 status line merged word by word so that both the SDK 58 record and the OR-36 ratification stand; the D88/D89 paragraph and D88 row come from main. Ids checked for collisions (ISSUES L20 to L41 against main's D8 to D15 and K43 to K46, Q37 to Q43, D87 to D90): none. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
One conflict, in docs/DECISIONS.md (the "Ratified on 2026-10-01 ..." paragraph): the branch's "amended on 2026-10-28 to SDK 58, which the app pins" and main's "and on 2026-11-02 D91 as written ... (OR-38)" are both kept. No Rust, manifest or lockfile of main is changed by the merge. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
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.
Draft. Step 5 of the Mileage first slice (docs/spec/mobile.md 9): the Android app in
web/apps/mileage-app(Expo SDK 58, React Native 0.88.0). No file undercrates/is changed by this pull request.What is in it
bulk_createin fixed batches with deterministic idempotency keys (429, 503 and network loss handled), Stop =mileage_trip.endwith a retry on 409incomplete(first wait 8 s, then doubling), Submit, Discard with a reason, trips and trip detail, Account (own device rename and removal, sign out), and a screen for the trips of another sign-in. English strings in one catalogue with plural forms andIntlformatting.@obop/ui-native(components and generated native tokens with parity tests),web/apps/storybook(the components in the web Storybook through react-native-web, Q42; touch, safe areas, fonts and TalkBack are not shown there), an OAuth helper in@obop/client.scripts/emulator/(the route, the feeder, a read-only server-side check of a trip; run withvp run emulator:route|drive|check).First review round (45 confirmed findings)
minSdk26). Plural forms andIntlwith fallbacks (Hermes has noIntl.PluralRules). Declined findings are in docs/ISSUES.md with a reason each.Second review round (a delta check of a15cb10: 38 confirmed findings, 17 medium and 21 low; the code is in 38e04db)
client()also refuses while the outbox is foreign.unsentOfwording, the OR-34 citation of the emulator scripts, the hand-over file. New ISSUES rows: L39 (the same person at a new address of the same instance is foreign; needs a backend change, the instance id is not exposed; the test hand-over warns to keep the PC's address), L40, L41.canDiscardfor unknown, liveness,keepSigningIn,clientUnknown, the body-read bound, the deadline helper). Glue that imports Expo has no unit test (L38).Third review round (a delta check of 8ca73db: 18 confirmed findings, 3 medium (one defect) and 15 low; the code is in 9ec6d87 and b74a10d)
refreshRemoteandrefreshTripcapture the sign-in before the call and drop the answer when it changed; they were the one place where the server's rows are inserted into the outbox and the one that the guard of the second round did not cover. No other place incore/ornative/awaits the network and then writes to the store without the check.withDeadlinenow races the call against the deadline as well as aborting its signal, so a call that ignores the signal (a wait for a token, a half-open connection) is bound too; it ends as a network error (10). A sign-in the phone ended itself after a refresh without an answer now says so on the Sign in screen when the background revoke fails as well, with the notice of the offline sign-out (12). A refresh refused withinvalid_clientre-checks the sign-in after the config write; the obop server never sendsinvalid_clientto a refresh (exchange_refreshanswersinvalid_grant), so that branch is stated as defence in depth for other servers, and the probe of the authorization page is the real recovery (8, 17). The comment of the backoff factors, the 404 of the create step, "Stop alone does not finish a discarded trip" and the order of Sign out (the request to the instance is started first, the phone does not wait for it) are corrected in the code and in the documents (5, 7, 15, 16, 18). L40 now names the sign-in page slot of the probe (9).refreshing = nullinsignOut, thefinallyguard, the guard at the top of the catch ofrefresh(refusal and timeout), the guard before the repeat of a failed write, the loop-top check of a pass, the guards ofrefreshRemoteandrefreshTrip, the race ofwithDeadline, the notice after a failed revoke, the create-step 404, "Stop alone leaves the flag" (4, 11). The third round added 17 tests; 15 of them fail when their guard is taken out, one is a control that has no guard ("a list under an unchanged sign-in is merged") and one could not fail ("leaves nothing unhandled ..."; it was replaced in the fourth round). Still without a test (L38): the wiring of the notice innative/runtime.tsand the order of the steps ofsignOut.Fourth review round (a closure and delta check of f56489e: 18 confirmed findings, 2 medium (both about what the documents say a run showed) and 16 low; the code is in 938f9eb, the documents in ab7b960)
refreshRemote, and 8.7 and this description no longer offer it as a device check. It was repeated with a hold of 57 seconds (below). The order of the steps of Sign out is read fromsignOutandleaveSession; the proxy log shows only that a revoke request arrived at the test proxy and was held (the instance never saw it; the fifth round corrected this line), that the request carried the token follows fromrevokeSignIn, and the notice that appeared between two screen reads fits the 5 second bound of the Sign out path (REVOKE_TIMEOUT_MSinnative/runtime.ts; the background end usesEND_REVOKE_MSincore/session.ts); none of it shows the order. Step 7b says what 8.7 does record (two force-stops of a recording trip on the emulator) and what it does not (the swipe from recents, a locked minute afterwards, a maker's battery manager).withDeadline(3, 4, 8, 11): the network error is made before the signal is aborted, so a call that rejects in its abort listener cannot win with its own error (the older test now expects theObopNetworkError); thestalled.catchline, which did nothing, and its test, which could not fail, are removed; two tests were added (no timer is left after an answer or an error; a call that ends after it was given up leaves nothing unhandled). Every call site (ownerOf,probeAuthorize, the three device calls) goes throughclient.callor an async wrapper and is unchanged in behaviour.isGonetests the message and not the status; L29 says that the trip calls rely on the abort alone and are not raced (not changed: the installedexpo/fetchcancels the request on abort); the create-step 404 no longer says that "nothing of the trip is on the server" (the phone holds no server id there, and a trip made by a create whose answer was lost may still be on the server; the behaviour is unchanged and the case is written down in L30); "a newer sign-in has begun" says "has finished" in the five places.databaseKey()is not changed: refusing to replace a key whenmileage.dbexists needs a file-existence check (a new native module and build) and cannot be tested without importing Expo, so it is in L38.http,ritais a full administrator with the second factor off, the firewall step for a network that is Public or a firewall that is off (it is off on the build PC), the same router and subnet, the full path ofadb, Battery Saver, the clocks (the phone shows local time, the back office and the check script UTC), keep the app on screen until Ended, and everything the steps change with how to undo it.vp teston the test file, the file restored;git diff -- webwas empty before and after; the harness is a throwaway script and is not committed). All 19 failed as they should, with these tests:refreshing = nullinsignOutfinallyguard ofrefreshingrefreshinvalid_clientonEndUntoldnever calledonEndUntoldignores a finished newer sign-inrefreshRemoterefreshTripwithDeadlinewithout the racewithDeadlinenever aborts the signalwithDeadlineaborts before it rejectsclearTimeoutremovedclearTimeoutonly after an answer, not after an errorCount: of the 18 tests of the third and fourth rounds that are in the code now, 17 fail under at least one of these mutations; the control "a list under an unchanged sign-in is merged" cannot (it has no guard). Still without a test: the create-step 404 after a create whose answer was lost (L30) and the wiring in
native/(L38).Fifth review round (a closure check of 0b31461: no high or medium finding and no blocker for the real phone; 13 low findings, about 7 distinct; docs and one test comment only, no change to the app: the APK is the same 54,531,555 bytes, SHA-256
c00d1a4a..., of 938f9eb; the head is 6d10c6f)/oauth/revokearrived at the test proxy and was held, never forwarded, body not logged), what follows from the code (the token was in it:revokeSignInsends nothing without one) and what the session read on the screen (the notice between two reads, which fits the 5 second bound of the Sign out path). The constants are named:REVOKE_TIMEOUT_MS(native/runtime.ts) for Sign out,END_REVOKE_MS(core/session.ts) for the background end after an unsure refresh.Dispatcher::callanswers the 404 of a hidden command (a lost group, a removed module) beforebulk_createlooks up the idempotency key, so the replay does not help then. It is narrow because it needs a create that was applied with its answer lost and then the command hidden before any retry got through. Behaviour not changed.withDeadlinesaid that no change made it fail; it fails when the race is removed and when the late end of the call is left without a handler, and the comment now says so. 8.7, L38 and this description say the same: 18 tests of the third and fourth rounds are in the code, 17 fail under a mutation, and the control "a list under an unchanged sign-in is merged" cannot.seq(1 to 22); the review run records 15 points and 779.08 m, noseqcheck; 8.7 does not measure what a force-stop loses.<LAN>is the PC's address on the network that the phone is on, found by the first three numbers of the phone's own address; the PC that built the APK is on Ethernet and lists two more private addresses (vEthernet) that a phone cannot reach.$in double quotes:"Mil3age$test99"is 7 characters,'Mil3age$test99'is 14; theadmin createof the build checked on a scratch database: single quotes create the user, double quotes are refused as shorter than 12 characters); the removal list says that the passwords typed intoadmin createare in PowerShell's history file.vp checkclean andvp test209 tests in 13 files inweb/apps/mileage-app;git diff 938f9eb -- webis one comment indeadline.test.ts;git diff origin/main...HEAD -- cratesis empty; the APK is untouched. CI (run 40865428046, first attempt, no rerun): web, e2e, rust, lockfiles and all passed.Verified (emulator, Android 16 x86_64, a local instance with a throwaway database; written down in 8.7)
dave, Start, 10 positions, Stop: the trip uploaded and the server refused the end for lack of distance (4 of 13 points counted; it looks like L37), Discard finished it; a second trip of 12 positions fed (9 points stored): Ended, 1.2 km, Submit, In review, andemulator:checkok on every line (9 points,seq1 to 9 once each,point_count9 =max_seq9, the distance 1,208.22 m against 1,201 m = 0.60 %). The trip list held across a sign-out and a sign-in as someone else, with a hold of 57 seconds (inside the 120 seconds of a trip call): the release came afterritahad signed in, and Home and the Trips screen underritashowed none ofdave's trips. It does not show that the app received the answer (the proxy writes a held answer to the client's connection without checking whether it is still open; nothing on the phone says it arrived) or that the build without the guard would have shown them (the unit test does). Not run on this APK: the Confirm and the monthly total, the new Failed text on the screen,refreshTrip, the race ofwithDeadline. The instance binary was built beforemainwas merged in (crates/is unchanged by the PR).revokeSignIn, and the Sign in screen showed the notice between the two screen reads, 1 to 3 and 4 to 7 seconds after the arrival, which fits the 5 second boundREVOKE_TIMEOUT_MS; the log does not show the bound, no screenshot of the reads was saved, and the order of the steps was not seen, it is read from the code). The trip list held for 3 min 54 s across a sign-out and a sign-in asritashows nothing about the guard: it outlasted the 120 second deadline, so the phone had already given the call up (the fifth run repeats it). Not run on a device: the race ofwithDeadline,refreshTrip, an interrupted pass at the last call of a trip, a failed Keystore write.in_review); sign-in after the instance database was replaced (400, register again, 200), twice; the Failed screen with the new text (nothing deleted, the trips back after the key file was restored).seq1 to 58, no gap, 3,989.65 m against 4,000 m = -0.26%, tolerance 1.0%), delete at sign-out, location switched off while recording; Submit, a reviewer's Confirm and the monthly total, Discard, Account rename and remove.main(5f8c3a9, 01ec24f) was merged in at f56489e; the only conflict was docs/DECISIONS.md (the branch's OR-34 and D87 rows kept, main's OR-35, D88, D89 and paragraph added after them); no file undercrates/differs frommain, andweb/and the lock files are byte-identical to before the merge, so the APK below is still the build of that code. CI on f56489e: rust, lockfiles, web, e2e and all passed (run 40848210960). Local at the merged head:vp checkclean in apps/mileage-app, ui-native, trip-core, client, storybook and backoffice;vp testapps/mileage-app 208 tests in 13 files (191 before the third round), ui-native 11, trip-core 26, client 151 (10 integration tests skipped without a server binary), backoffice 280. At ab7b960 (fourth round):vp checkclean andvp testapps/mileage-app 209 tests in 13 files (the one that could not fail is gone, two forwithDeadlineare new); the other packages are untouched. CI on ab7b960 (fourth round): rust, lockfiles, web, e2e and all passed (run 40856861906).mainwas merged in again at 0b31461 (47005a6: PR 31, OR-35 k and K42). The only conflict was docs/DECISIONS.md, in two hunks: OR-34 and D87 keep the branch's rows, OR-35, D88 and D89 are main's rows byte for byte (checked by script); docs/ISSUES.md, docs/ROADMAP.md and docs/MODULES.md merged without conflict.web/,crates/and the lock files are unchanged by the merge, so the APK above is still the build of that code. CI on 0b31461: e2e, lockfiles, web, rust and all passed on the second attempt (run 40858426480). The first attempt failed inrustonan_exhausted_pool_answers_503_with_retry_after_never_a_401_and_the_server_recovers(crates/obop/tests/pool.rs:520:obop_db_pool_in_use{pool="main"}was 1, expected 0), a test in code this PR does not touch (crates/is identical tomain, whose own run at 47005a6 is green); the rerun of the failed jobs passed. It is not in the flake list of K22.D:\Android\phone-test\obop-mileage-test.apk, 54,531,555 bytes, SHA-256c00d1a4a1f2936209fd98550ac998bf5482a520de3e10d27aa4dde8af52dbb60, built from commit 938f9eb (vp run android:build:release,OBOP_ALLOW_CLEARTEXT=1,OBOP_DEFAULT_SERVER=http://192.168.1.132:18080; both ABIs; the Hermes bundle holds the new Failed text). The commits after it change documents and one comment in a test file (git diff --stat 938f9eb..HEAD -- web/apps/mileage-appis empty). The earlier figures (54,531,407 bytes with5181b20e..., built from 9ec6d87, and 54,530,159 bytes with8f515f85...) are no longer valid for that path. Which run was on which APK is in 8.7.Not verified: anything on a real phone (10-minute lock, GPS noise, a maker's battery manager, the battery figure), an arm64 device, a phone without Play services, TalkBack and the focus moves, the Settings screen for precise location, "No new position" notices and the false alert of finding 27 (unit-tested only), the
invalid_clientclearing (it cannot happen against this instance), offline sign-out with unsent trips or with Delete on the new APK, the 30 s bounds and the race ofwithDeadlineon a slow network, the Failed screen with a recording trip, a lost Keystore key on a phone (the next start replaces it and the trips cannot be read; L34), the upload after Stop with the phone locked or the app in the background (JavaScript timers pause; read from the library source),refreshTripon a device, a failed Keystore write, the 429/503 answers on the real instance, a release or store build. The list is in 8.7.Not merged, not final. The Gradle build is the mobile-toolchain exception of OR-34; the build of a test APK needs
OBOP_ALLOW_CLEARTEXT=1and is not for anyone else.Merge of main
2026-11-02, approved by the owner:
origin/main(33f48c6) is merged into this branch as a normal merge commit, head93a5379(parentse4c3aeaand33f48c6; no rebase, no force push). Two documents conflicted and nothing else:CLAUDE.md(the Native apps paragraph keeps this branch's description of the built Android app and takes OR-36 from main) anddocs/DECISIONS.md(OR-34 from this branch with OR-35 to OR-37 from main; the D87 row, the "Ratified on" paragraph and the D87 status line carry both the SDK 58 record and the OR-36 ratification). This PR changes no Rust, socrates/andCargo.lockare identical to main. No ids collide (L20 to L41 here, D8 to D15 and K43 to K46 on main). No phone and no emulator were used for the merge.Merge of main (2026-11-02)
Head
ae55abd= merge of93a5379and main36fa622(PR 37 Microsoft sign-in, PR 41, PR 42). One conflict, indocs/DECISIONS.md(the "Ratified on 2026-10-01 ..." paragraph): the branch's SDK 58 clause and main's OR-38 clause are both kept. No Rust, manifest or lockfile of main is changed by the merge; no duplicate ids or headings in the merged documents. CI green (all jobs). Still a draft; no phone or emulator was used for this merge.🤖 Generated with Claude Code