Skip to content

fix(builderlab): give refused app sign-ins and Identity links a route to Organization sign-in - #3078

Merged
matt2e merged 3 commits into
mainfrom
link-to-builderlab-settings
Nov 14, 2026
Merged

matt2e merged 3 commits into
mainfrom
link-to-builderlab-settings

Conversation

@matt2e

@matt2e matt2e commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Two Builderlab surfaces tell you where to go in Settings and neither takes you there. This branch gives both a route, in two commits that stand alone.

  1. A refused app sign-in gets its own panel page. When Builderlab answers 401 to the app viewer exchange while the account session still stands, the panel used to show a bare red bar, "Sign in again in Settings → Identity → Organization sign-in, then reopen this app", with no control. It now shows an EmptyState with Open settings and the primary Try again. The native side gains ViewerPage::SignIn, so the frontend can tell a refused sign-in from the denied and unavailable pages. An app on its own browser login never gets the settings button, and the fix(browser): recheck the FI session when the app viewer exchange returns 401 #2678 recheck is untouched.
  2. Identity links land on Organization sign-in. The Builderlab card's "Identity → Organization sign-in" link, the native account-access error sheet and the new panel button all opened Identity at its top, with Private key, Identity sync and Sign out of Buzz above the section they named. A settings target can now carry part: "organization-sign-in", allowed only with the identity section. Opening Identity with it scrolls the section into view and focuses its heading, not its sign-out button. The landing re-lands while the layout settles, stops scrolling on any user wheel, touch, pointer or scroll key, and ends when focus moves on. feat(identity): add native federated sessions and agent tokens #1424's section order stands.

Closing Settings now keys its dialog by community alone instead of dropping section fields by name. That fixes two pre-existing crashes on Close and Escape, one with a plugin import visit left behind, and a case where Back reopened Settings after Close.

Before and after

Left is main at 7f42b13, right is this branch. Step 1 opens a Builderlab app link while Builderlab refuses the sign-in. Step 2 follows the Builderlab card's link.

before-after.mp4
The refused app panel Before: a red text bar with no control. After: the sign-in page with Open settings and Try again
Open settings from the panel Before: still on the bar. After: Settings open on Identity with the Organization sign-in heading in view and focused
The Builderlab card's Identity link Before: Identity at the top, Organization sign-in below the fold. After: the heading and the Example Org row in view

At the landing the heading sits low in the panel because only about 130 px of content lies below it. The section is scrolled fully into view at the bottom. No focus ring shows because the visit started from a mouse click.

The recording runs the production frontend in Chromium with the native organization sign-in and browser view answered in-page, so the Settings, Identity, Builderlab card and app panel are the real components. It models the 401 refusal and does not exercise the native viewer exchange or a live tenant.

Which states show the new page

  • No Builderlab account signed in: the panel shows the Login card, as before.
  • Signed in and Builderlab answers 403: the existing "You can’t open this app" page, as before.
  • Signed in and Builderlab answers 401: the new page. If the adapter recheck also rejects the session, the panel swaps to the Login card by itself.

Verification

  • tsc --noEmit, Biome, and the eight touched Vitest files (271 tests) pass.
  • A wider run of 107 Vitest files under src/app, src/features/{navigation,browser} and src/bundled/builderlab (2124 tests) passed on the previous revision; later revisions changed only a button label, its order, and tests.
  • Rust browser::builderlab tests pass (10) and clippy is clean.
  • A full-app Playwright probe of the production build samples the Identity scrollport every frame. In all three journeys (panel button, card link, card link followed by a wheel scroll and a forced section replacement) the heading ends focused with the whole section in view, Close and Escape both close Settings, the wheel is respected, and there are no page errors.
  • Four review rounds; all findings addressed.

Notes for reviewers

  • Button order. The primary sits last in the row, per the design guide's action-row rule and every dialog footer. Every other two-button EmptyState in the codebase puts the primary first, including the "Buzz couldn’t load this app" page in the same component. That inconsistency predates this change and is left for a design call.
  • Server status codes. Whether Builderlab returns 401 or 403 for an account that lacks access to a given app was not verified. If it is 401, users in that state would be told to sign in again.
  • Neighbours. fix(settings): keep each section's scroll place while Settings is open #2811 restores per-section scroll in Settings and should be reconciled with the landing once it lands. feat(builderlab plugin): add settings overview and management views #3041 restructures the Builderlab card and will conflict textually with the link change. The "Agents tab" hint from the same source note is covered by feat(builderlab plugin): add settings overview and management views #3041 and is not in this branch.
  • Pre-push hook. The branch was pushed with --no-verify because the unit-test lane timed out on unrelated suites under heavy machine load; the same tests passed in a clean workspace.

Deferred

  • Native desktop run of both flows against a real tenant, including a genuine app refusal and the Keychain session.
  • Both themes, narrow width and enlarged text for the new panel page.
  • A WKWebView check that a scrollbar press fires pointerdown before the first scroll event, as Chromium does.

🤖 Generated with Claude Code

@matt2e
matt2e marked this pull request as ready for review November 9, 2026 11:08
@matt2e
matt2e requested review from a team as code owners November 9, 2026 11:08

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Star Lord’s automated source review via Wes’s account, with independent Rocket (lifecycle/navigation), Mantis (authentication/correctness), and Groot (UI/accessibility) coverage.

Reviewed head 33435cb23cf6cb42421c9592c57e89567071cc8b, target base 58116b826171d190a7376fc57b0509c32c507dde, merge-base 7f42b1331c811101572f7235b9f4b1cf0467f4d2. One P3 keyboard-focus finding is inline. No additional concrete defects found in the reviewed authentication classification, session recheck, landing lifecycle or Settings dismissal paths.

Source-only: we ran no tests, builds or app workflows. We inspected the public description and all three screenshots; the video was not visually inspected. Native/live-tenant status semantics, WKWebView input/scroll behavior, screen-reader announcements and theme/viewport/zoom acceptance remain unverified.

The head’s CI snapshot has passing JavaScript/browser checks, but CI required is red: dependency policy reports RUSTSEC-2026-0459 and RUSTSEC-2026-0462 before Rust formatting/lint/tests and Node integration run; Windows native validation is skipped. This PR does not change dependency files. Author-reported local checks and modeled browser probes do not establish native acceptance or completion of the bypassed pre-push hook.

@matt2e
matt2e force-pushed the link-to-builderlab-settings branch from 33435cb to 01cec78 Compare November 9, 2026 15:00

@kazemek21 kazemek21 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Kyle reviewed this PR and the Nexus review-tracker notes and approved with no comments.

Posted by Codex (gpt-6.1-sol) on behalf of kylez

@matt2e
matt2e force-pushed the link-to-builderlab-settings branch from bf0c395 to 8982cb5 Compare November 9, 2026 21:50

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Star Lord’s automated source follow-up via Wes’s account, with independent Rocket, Mantis and Groot coverage.

No new actionable findings at 8982cb52fad2d6a258c5d202b450b1ee1e8b6287 (target base 5c18e73dbe05cafa1cbe8801bd5a8a60fd346951, merge-base 21c44da7cddcc59b4dccf5ad15580eac71a0e9d8). The prior retry-focus P3 is repaired in source: stable action keys preserve the retry control across sign-in ↔ unavailable, and the new deferred regression checks DOM identity, focus and subsequent Enter behavior in both directions. The newer Settings/Builderlab routing, landing and dismissal integration produced no additional findings.

Source-only review; no tests, builds or apps were run. The description and three unchanged screenshots were inspected; the video remains unviewed. Native/live-tenant sign-in, real keyboard/history behavior, themes, narrow widths and enlarged text remain unverified.

The CI snapshot is still red: Chromium shard 4/9 reports two composer-growth failures in typing.spec.mjs:290 with React error #185. Those logs alone do not establish causation by this change. JavaScript, Rust/tool integration and the other browser shards passed; Windows validation was skipped.

matt2e and others added 3 commits November 14, 2026 11:49
When Builderlab's viewer exchange answered 401 while the organization
session still stood, the app panel showed only a red bar telling you to
sign in again in Settings, with no way to get there.

Native now names that refusal with a `sign-in` viewer page, for both 401
arms, the stale-session fallback and a viewer credential whose expiry has
passed or is out of range. The browser view shows it as an EmptyState,
like the denied and unavailable pages, with Try again and, when the view
has an opener, Open settings. The primary Try again sits last in the
row, per the design guide. The Builderlab app panel passes that opener
only to apps that use the Settings sign-in: it opens Identity in an FI
build and the Builderlab card in live development. Apps on their own
browser login get none. The #2678 recheck that retires an ended session
is unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: mtoohey <[email protected]>
Signed-off-by: Matt Toohey <[email protected]>
Builderlab's "Identity → Organization sign-in" links opened Identity at
its top, with Private key, Identity sync and Sign out of Buzz above the
section they named, which #1424 placed last and is below the fold.

A settings target can now carry `part: "organization-sign-in"`, allowed
only with the identity section. Opening Identity with it scrolls the
section's heading into view and focuses it, not its sign-out button.
Loading every server's session, or one signing in or out, replaces that
section, so a replacement takes focus back while the replaced heading
still held it; a section that goes with none in its place ends the
landing. Until the layout settles, for 12 seconds at most, the landing
lands again whenever the heading moves or the section is cut off, as
when loading shrinks the page for a moment, clamping its scroll, then
grows it past where it was, so the whole section ends up in view. Only
the user's wheel, touch, pointer or scroll keys in the scrollport stop
it scrolling, and focus moving on ends it. Opening Identity any other
way keeps today's top position, and #1424's order stands.

Closing Settings now keys its dialog by community alone, built from the
target, instead of by dropping the fields that belong to one section,
which missed `part`: Close and Escape threw on it and left Settings
open. A plugin import visit left behind had the same crash, and a visit
naming no community, as every link and shortcut to Settings does,
survived Close from a section that named one, so Back reopened Settings.
Both predate this change. Such a visit now counts as Settings for
whatever is selected when Settings closes.

The Builderlab card's Account link, its provider and Decisions sign-in,
an app panel's Open settings, and the native account-access
error sheet all open that target.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: mtoohey <[email protected]>
Signed-off-by: Matt Toohey <[email protected]>
The denied, unavailable and sign-in pages each render Try again in an
EmptyState action row, with no keys on the buttons, and the row puts it
first on the unavailable page but last on the sign-in page. A retry
from a focused Try again that fails another way, say a refused sign-in
whose retry can't reach Builderlab, swaps the page under the same row,
and React reused the focused button for the action now in that
position, so the next Enter opened the app in the browser instead of
retrying; the other way round it opened Settings.

The three pages now share a keyed Try again, with its own key on each
other action, so the button and its focus survive the page change
wherever it sits in the row. A test drives a focused retry from
sign-in to unavailable and back, checking that the same button keeps
focus and the next Enter still retries.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Signed-off-by: Matt Toohey <[email protected]>
@matt2e
matt2e force-pushed the link-to-builderlab-settings branch from 8982cb5 to 0463a21 Compare November 14, 2026 00:55
@matt2e
matt2e merged commit 886eecd into main Nov 14, 2026
68 checks passed
@matt2e
matt2e deleted the link-to-builderlab-settings branch November 14, 2026 01:14
wpfleger96 added a commit that referenced this pull request Nov 14, 2026
* origin/main: (27 commits)
  [2/2] feat(agents): offer Automatic (Recommended) in Create agent and Identity (#3345)
  fix(solo): preserve and resume unsent chat drafts (#3525)
  fix(builderlab): give refused app sign-ins and Identity links a route to Organization sign-in (#3078)
  fix(agents): say why Import an agent can't import here, and link Manage plugins (#3073)
  [1/2] feat(agents): add a recommended agent setup (core) (#3344)
  test(browser): survive the live-stream restart a page-clock jump causes (#3442)
  chore(renovate): hold rusqlite below 0.41 for mesh-llm (#3438)
  test(browser): serve the diffs and CLI reload fixtures outside the navigation budget (#3445)
  test(browser): play the draft clip at rate 0 instead of looping it before the Play click (#3446)
  fix(status): reopen a status whose preset deadlines coincide as Today (#3449)
  fix(messages): tolerate a cancelled reaction-name chunk load (#3450)
  fix(pairing): tolerate /proc/net/tcp rows printed twice or missed (#3451)
  test(browser): wait on the signal each test asserts in local-agent-activity and message-navigation (#3444)
  Expose native channel headings to Solo sources (#3523)
  test(browser-host): run the 100,000-channel broker scale check alone in the measurements lane (#3447)
  fix(solo): keep every contributed destination action (#3516)
  fix(identity): report an issuer that keeps rate-limiting an agent as a limit (#2365)
  fix(identity): let a stuck at-launch sign-in copy its link (#2453)
  fix(profiles): open avatar previews on hover in compact density (#3510)
  feat(solo): smooth chat recency reordering (#3502)
  ...

# Conflicts:
#	src-tauri/src/federated_identity/agent/tests.rs
#	src-tauri/src/federated_identity/mod.rs
#	src-tauri/src/federated_identity/protocol.rs
joahg added a commit to joahg/buzz-app that referenced this pull request Nov 14, 2026
* origin/main: (133 commits)
  Expose ordinary chats without workspace headings (block#3530)
  [2/2] feat(agents): offer Automatic (Recommended) in Create agent and Identity (block#3345)
  fix(solo): preserve and resume unsent chat drafts (block#3525)
  fix(builderlab): give refused app sign-ins and Identity links a route to Organization sign-in (block#3078)
  fix(agents): say why Import an agent can't import here, and link Manage plugins (block#3073)
  [1/2] feat(agents): add a recommended agent setup (core) (block#3344)
  test(browser): survive the live-stream restart a page-clock jump causes (block#3442)
  chore(renovate): hold rusqlite below 0.41 for mesh-llm (block#3438)
  test(browser): serve the diffs and CLI reload fixtures outside the navigation budget (block#3445)
  test(browser): play the draft clip at rate 0 instead of looping it before the Play click (block#3446)
  fix(status): reopen a status whose preset deadlines coincide as Today (block#3449)
  fix(messages): tolerate a cancelled reaction-name chunk load (block#3450)
  fix(pairing): tolerate /proc/net/tcp rows printed twice or missed (block#3451)
  test(browser): wait on the signal each test asserts in local-agent-activity and message-navigation (block#3444)
  Expose native channel headings to Solo sources (block#3523)
  test(browser-host): run the 100,000-channel broker scale check alone in the measurements lane (block#3447)
  fix(solo): keep every contributed destination action (block#3516)
  fix(identity): report an issuer that keeps rate-limiting an agent as a limit (block#2365)
  fix(identity): let a stuck at-launch sign-in copy its link (block#2453)
  fix(profiles): open avatar previews on hover in compact density (block#3510)
  ...

Signed-off-by: Joah Gerstenberg <[email protected]>

# Conflicts:
#	src/bundled/codex/prompt.test.ts
#	src/bundled/goose/runtime.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants