Repository navigation
feat(builderlab): offer restoring the account's backed-up key on mismatch - #2458
Draft
kalvinnchau wants to merge 3 commits into
Draft
kalvinnchau wants to merge 3 commits into
kalvinnchau wants to merge 3 commits into
Conversation
…atch when the builderlab account is linked to a different key than this device and that key is backed up, the identity mismatch section offers restore backed-up key. it opens settings -> identity; signing out returns to first run, where welcome back restores the linked key. adds a read-only nsec-backups/status route to the native backend; a failed read hides the option. Co-authored-by: peon <9ac6794b000690b7e814eb1805ad32405d0bec7d52838de3a86cf967565dacc0@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Kalvin Chau <kalvin@block.xyz>
gate the mismatch restore on the native uploader's builderlab backup flag, as backupOfferedFor does for first run, and share first run's all-or-nothing backup key list parsing (backedUpKeyList). rename the action to open identity to sign out and describe the automatic restore after builderlab sign-in. Co-authored-by: peon <9ac6794b000690b7e814eb1805ad32405d0bec7d52838de3a86cf967565dacc0@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Kalvin Chau <kalvin@block.xyz>
capture the session credential before awaiting nsec_backup_state and check it afterwards, as bind does, so a sign-in that replaced it is never read. Co-authored-by: peon <9ac6794b000690b7e814eb1805ad32405d0bec7d52838de3a86cf967565dacc0@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Kalvin Chau <kalvin@block.xyz>
matt2e
added a commit
that referenced
this pull request
Nov 9, 2026
…s the next step (#3082) ## Why Identity sync names Builderlab in two of its sentences: when this key isn't linked to the account (waiting `not_linked`) and when the account already holds a different key (failed `other_key_linked`). The next step for both, **Connect Buzz identity** or **Switch to this device's identity**, is on Settings → Plugins → Builderlab, but the row had nothing to click. People had to find that page themselves. ## What In those two sentences the first "Builderlab" becomes a `SettingsSectionLink` to `block.builderlab/login`, the shared inline link from #2122. Settings passes the opener only while the Builderlab card is registered and reuses `openSection`, so focus moves into the opened section as it does from Decisions API. Without an opener, or in any other state, the sentence stays plain text. The status text itself is unchanged; only the name becomes a link. Deliberately unlinked: - `no_session`: Builderlab signs in with the build's NIP-FI session, and that Organization sign-in is on this same Identity page. - `no_consent`: its next step is the row's own **Sync with Builderlab** button; Builderlab's page has nothing for it. - Retryable failures and `signed_out` clear on a new sign-in. The remaining failures have no fix on Builderlab's page. The linked states arise only after the user opted into automatic backup: `nsec_backup.rs` admits consent (`NoConsent`) before it reads the account's key (`NotLinked`, `OtherKeyLinked`). The link only navigates. Connecting on Builderlab's page starts a backup the user already agreed to. ## How `BackupRow` takes an optional `openBuilderlab` and splits the existing feedback sentence around the first "Builderlab" for the two states. `Settings` derives the opener from `openSection` and the contributed card list. Two files of production change, about 40 lines, plus tests and the help guide and identity doc. ## Risk Low. No native, protocol, or persistence changes. The shared link style makes the Identity sync row about 2 px taller in the linked states. Known gap, shared with the existing Sync button: a status change while the link has focus drops focus to the page. ## Testing - `pnpm typecheck` and biome pass on `01c105cd5`. - `IdentitySettings.test.tsx` and `Settings.test.tsx`: 124 tests pass. With main's `IdentitySettings.tsx` and `Settings.tsx` swapped in, three fail: the two "links Builderlab from the … sentence to its Settings page" cases and "opens Builderlab from Identity sync only while its card is registered". The negative cases (`no_session`, `no_consent`, idle, running, complete, every other failure kind, and no opener) pass on both, as intended. - The Settings test removes the Builderlab card while Identity is open and asserts the link goes in place with no section switch, as well as the mount-time gate. A `BackupRow` that reads its opener once (`useRef`) passes the old test and fails this assertion. - The before/after recording below exercises the real `Settings` component in headless Chromium. Not verified: a native FI build, both themes, narrow widths, enlarged text, and keyboard navigation in the real app. ## Before and after The same pointer script runs on origin/main (left) and this branch (right): click "Builderlab" in the Identity sync sentence, go back to Identity, switch the backup status from waiting `not_linked` to failed `other_key_linked`, click "Builderlab" again. https://github.com/user-attachments/assets/04a67ba8-6374-4226-ae24-d8222287f861   **Real:** the `Settings` component, the side menu, the link, the section switch and the focus move. **Faked:** the backup status, and a fixture Builderlab card that renders the real `Login` and `HostedCommunities` on fake sign-in and account data, since the plugin registers its card only with a native session or the live development server. Recorded in headless Chromium with Playwright, not a native build. ## Bigger picture Overlaps with #3069, which deletes `BackupRow` and moves backup state into a key-card notice, and rewrites the same paragraphs in `docs/buzz-help.md` and `docs/identity.md`. Whichever lands second reconciles. If this lands first, #3069 can either carry `openBuilderlab` and the card gate into the notice, splitting the period-less `not_linked` and `other_key_linked` strings, or drop the prop and its tests on the grounds that its plugin-contributed `BuilderlabIdentityRow` already links Builderlab from Identity. If #3069 lands first, this becomes a re-implementation in the notice, and the "only button in the row is the link" assertion has to allow for **Retry backup**. #2458 links the other direction: Builderlab's Hosted communities card opens Settings → Identity when the account's backed-up key differs from this device's. It is complementary. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: mtoohey <mtoohey@squareup.com> Co-authored-by: Claude Opus 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.
Problem
If a Builderlab account is linked to key Y (which is backed up) and this device holds a different key X, there is no way to switch the device to Y. Native recovery refuses while a key is present (
identity.require_missing(), called fromnsec_restore_begin), and Settings → Identity has no Builderlab session to detect the mismatch.Change
The Builderlab Hosted communities card already detects the mismatch, with or without backup consent. When Y is in the account's backup list, its mismatch section now offers Open Identity to sign out:
communities/native.ts: read-onlybackupsaction →/v1/buzz/nsec-backups/status, sent only whennsec_backup_state.builderlabis true (the same gate as first run'sbackupOfferedFor). Elsewhere it lists no keys.communities/api.ts:backedUpKeyList, first run's all-or-nothing key list parsing, now shared by first run (builderlab-account.ts) and the card.HostedCommunities.tsx: reads the list while mismatched. If it contains the bound key, the section shows copy and a button that callsonRestore. A failed read or malformed list hides the option.index.tsx: on desktop,onRestoreopens Settings → Identity. The existing Sign out dialog makes the user reveal or copy X and confirm "I have my key" before signing out.docs/buzz-help.md: one sentence describing the option.Non-goals: live key swap without sign-out, auto-opening the Sign out dialog from the plugin, and a browser-host route (restore is desktop-native only).
Validation
builderlabis true, and POSTs{}when it is.vitest run src/bundled/builderlab/communities src/bundled/builderlab/index.test.tsx src/features/identity: 24 files, 640 tests pass.tsc --noEmitandbiome checkare clean.Not yet done: the full sign-out → restart → Welcome back → Restore flow in the running app, and a visual check in both themes and at narrow and wide widths.