Repository navigation
fix(mobile): offer a new identity beside saved keys on unclaimed accounts - #9107
Merged
Merged
Conversation
…unts A newly created Builderlab account with no binding and no backups showed only the saved identities on this device, with no way to start a new one. Any eligible saved key forced selection, and generation required the device inventory to be empty. Selection for an unclaimed account now also offers "Create a new identity". Creation still writes only this account's empty slot, so every saved and device key is kept, and it is authorized only when every current device key was among the choices shown. Bound accounts, accounts with backups, pairing and pending import never offer it. Co-authored-by: Xander <ff30c859b96bd5233ffaf5d071200458f1274bd0dab03589214cf1055198491d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@squareup.com>
After binding a chosen saved key was definitively rejected, that key stayed in the account's slot, so every launch skipped selection and retried the same rejected binding. A 400/404/409/422 on identity verify, recorded for the current attempt, account and pubkey, now offers "Choose a different identity". Before removing anything it rechecks that the account is still unbound with no backups, that the slot has no backup receipt, and that another saved slot or device record holds the same key. It then deletes only this account's slot, verifies the deletion and returns to identity choice, where a new identity can be created. Ambiguous failures, stale evidence, a sole key copy, an existing binding or backup and another account keep the slot. An already-empty slot counts as released, so retries after a partial failure or restart converge. Co-authored-by: Xander <ff30c859b96bd5233ffaf5d071200458f1274bd0dab03589214cf1055198491d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@squareup.com>
hosted_flow.dart is at the mobile file-size limit, so the rejected binding recovery moves into a part of the same library, unchanged. Co-authored-by: Xander <ff30c859b96bd5233ffaf5d071200458f1274bd0dab03589214cf1055198491d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@squareup.com>
Releasing a rejected saved choice must keep every key when the account changes, the flow is cancelled or the device inventory cannot be read partway through, and a failed delete must survive a restart. These cases already hold; the tests pin them. Co-authored-by: Xander <ff30c859b96bd5233ffaf5d071200458f1274bd0dab03589214cf1055198491d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@squareup.com>
Co-authored-by: Xander <ff30c859b96bd5233ffaf5d071200458f1274bd0dab03589214cf1055198491d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@squareup.com>
Co-authored-by: Xander <ff30c859b96bd5233ffaf5d071200458f1274bd0dab03589214cf1055198491d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@squareup.com>
Co-authored-by: Xander <ff30c859b96bd5233ffaf5d071200458f1274bd0dab03589214cf1055198491d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@squareup.com>
A release refused without an error (for example, when the keychain inventory cannot confirm the copy kept elsewhere) left the rejection tied to an older attempt, hiding Choose a different identity until setup restarted. Record the rejection for the current attempt on any outcome other than release. Co-authored-by: Xander <ff30c859b96bd5233ffaf5d071200458f1274bd0dab03589214cf1055198491d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@squareup.com>
johnmatthewtennant
marked this pull request as ready for review
November 5, 2026 19:13
johnmatthewtennant
temporarily deployed
to
codex-review
November 5, 2026 19:14 — with
GitHub Actions
Inactive
brow
approved these changes
Nov 5, 2026
bradseiler
added a commit
that referenced
this pull request
Nov 5, 2026
…-media * origin/main: fix(mobile): offer a new identity beside saved keys on unclaimed accounts (#9107) fix(desktop): show upgrade guidance (BUZZ-517) (#9033) fix(mobile): skip the read-state snapshot when nothing needs publishing (#9093) fix(mobile): keep verified names on screen in channels with over 1,024 members (#9111) mobile: reuse channel message rows that did not change (#9115) fix(mobile): back off push bootstrap retries (#9092) fix(mobile): keep reply counts on screen while a reconnect catches up (#9100) fix(mobile): inflate gzip read-state snapshot responses (#9089) perf(relay): cache the community-active check on socket admission (#9109) fix(db): auto-migrate prebuild-guarded indexes on small databases (#8802) docs: mark desktop/ as the deprecated Buzz 0.X app (#8840) Signed-off-by: Brad Seiler <seiler@squareup.com>
wpfleger96
added a commit
that referenced
this pull request
Nov 5, 2026
…dary * origin/main: fix(mobile): focus the search field when search opens (#9120) perf(mobile): validate each verified profile once, not on every publish (#9101) fix(mobile): offer a new identity beside saved keys on unclaimed accounts (#9107) fix(desktop): show upgrade guidance (BUZZ-517) (#9033) fix(mobile): skip the read-state snapshot when nothing needs publishing (#9093) fix(mobile): keep verified names on screen in channels with over 1,024 members (#9111) mobile: reuse channel message rows that did not change (#9115) fix(mobile): back off push bootstrap retries (#9092) fix(mobile): keep reply counts on screen while a reconnect catches up (#9100) fix(mobile): inflate gzip read-state snapshot responses (#9089) perf(relay): cache the community-active check on socket admission (#9109) fix(db): auto-migrate prebuild-guarded indexes on small databases (#8802) Signed-off-by: Will Pfleger <wpfleger@block.xyz>
brow
added a commit
that referenced
this pull request
Nov 6, 2026
…(backport #9089, #9093, #9107 to 0.27) (#9175) ## Summary Backports three fixes from `main` to `mobile-v0.27` for the Nov 10 App Store release (0.27.1): - [#9089 fix(mobile): inflate gzip read-state snapshot responses](#9089). The Builderlab relay gzips responses, so on 0.27 every read-state snapshot fetch fails and read marks made on the phone never reach other devices. - [#9093 fix(mobile): skip the read-state snapshot when nothing needs publishing](#9093). Once the snapshot works, this avoids an extra full snapshot download on each idle resume. - [#9107 fix(mobile): offer a new identity beside saved keys on unclaimed accounts](#9107). This fixes a Builderlab sign-in dead end: a new account on a device that already holds saved keys could only reuse one of those keys, and a rejected bind left it stuck on "Let's try that again". ## Provenance `git cherry-pick -x --signoff` of the squash merges `5e36d65c9e`, `dce37f74d7` and `c848b6dcad`, in that order, onto `mobile-v0.27` at `54d57a5430`. All three applied cleanly, and their patch IDs match the originals. Nothing else from `main` is included. ## Validation Independent stable-fitness reviews by two agents. Both said to pick all three, with no blockers and no missing `main`-only prerequisites. Results on this branch's tree (`2e9b94cd9bf9363eb8fadd772764b0ecc4b8c091`): - Mobile analysis and format: clean. - Full mobile suite with CI's auth-domain and push-gateway defines: 4,427 passed, 7 skipped. The new hosted identity journeys ran, covering rejection and restart, failed release, account switching and key preservation. - iOS simulator debug build with auth domains enabled: passed (Xcode 27.1). Limitation: no live Builderlab sign-in or device read-mark sync was run on this stable build. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: Tom Brow <tomb@squareup.com> Signed-off-by: John Tennant <jtennant@squareup.com> Co-authored-by: Tom Brow <tomb@squareup.com> Co-authored-by: studio <dc2fc481343672df31a689840c09299972e704dac592e0da6f18486646a7ee3e@buzz.block.builderlab.xyz> Co-authored-by: John Matthew Tennant <jtennant@squareup.com> Co-authored-by: Xander <ff30c859b96bd5233ffaf5d071200458f1274bd0dab03589214cf1055198491d@buzz.block.builderlab.xyz> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This branch was previously 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.
Summary
This fixes a dead end in mobile Builderlab sign-in. A new account (unbound, with no backups) on a device that already had saved identities could only pick one of those identities. Any saved key forced the picker, and creating a key required the device to have none. If the service then definitively rejected binding the chosen key, the app showed "Let's try that again" with no way forward: the chosen key stayed in the account's slot, so every retry and every launch re-sent the same rejected bind.
For an unclaimed account, the picker now also offers Create a new identity:
When binding a chosen key is definitively rejected, the blocked screen offers Choose a different identity:
/verifyin the current attempt, for the current account and pubkey, and never during pairing, whose transferred key is the only choice. Evidence from an earlier attempt is cleared before each bind. A challenge rejection, a 403, a 503 or any other ambiguous failure offers nothing./currentand status), no backups, the same subject, and a slot that still holds that pubkey without a receipt.The recovery code lives in
hosted_flow_identity_recovery.dart, a part ofhosted_flow.dart, to stay within the mobile file-size limit.Related issue
None found.
Testing
Physical iPhone, a signed release build of
1fb633d55installed over the existing app (keychain and saved identities kept): a saved identity was rejected, then Choose a different identity, the picker, Create a new identity and Join your community. Joining then completed. This head differs from1fb633d55only in the guards for leaving, pairing and a failed or refused release listed below, which this journey does not reach, and in no UI code. The status of that phone's original rejection was not captured, so it is not confirmed to be one of the rejections above. The 409 case is covered by tests.Tests (red on
main, green here):/current, at status, or between the recheck and the delete; cancelling between the recheck and the delete; leaving the picker before a late creation; an unreadable device inventory; leaving while a release is in flight; a failed delete retried in place and after a restart; a release refused because the inventory is unreadable, then retried in place; a rejected paired key. Each keeps every key, leaves the new account's slot untouched and leaves a cancelled flow idle, and a retry converges.Screenshots
Physical iPhone at
1fb633d55(frames from a screen recording; saved public keys redacted):Widget-test renders of the same screens (the real
HostedFlowagainst a mocked account service, with a fixture key; the square at the top left is the test renderer's placeholder for an asset):🤖 Generated with Claude Code