Skip to content

feat(browser): show a refused Builderlab app as a page in the panel - #2680

Merged
nathan-thillairajah merged 4 commits into
mainfrom
dylan/browser-denied-page
Nov 6, 2026
Merged

nathan-thillairajah merged 4 commits into
mainfrom
dylan/browser-denied-page

Conversation

@nathan-thillairajah

@nathan-thillairajah nathan-thillairajah commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

If Builderlab refuses to let your account open an app (a 403 from the viewer exchange), the Buzz side panel shows a line of red text above an empty page. It looks broken rather than like an answer.

Now a 403 shows a proper page instead: a lock icon, You can’t open this app, "The link may be wrong, or the app may have been removed. Ask whoever shared it." and a Try again button. It doesn't say why access was refused. It reuses the shared EmptyState, the same as the app's own "You don’t have access to this" page.

To support this, native browser_attach now returns { message, page? } instead of a plain string, and only a 403 sets page: "denied". Every other failure keeps the existing red error bar. #2681 builds on this.

Testing

Rust and Vitest tests cover the status mapping, the new error shape, and the page replacing the error bar.

Manually tested on macOS by nthillairajah in a local dev build that combined this PR with #2678 and #2681. The build also had a local-only switch, not part of any PR, that fakes the exchange's answer. With a faked 403, the panel showed the "You can’t open this app" page with no red bar, and Try again showed it again. A normal app link loaded fine. We didn't test with a real app that refuses the account.

After that test, a review found that Try again lost keyboard focus while retrying. The fix (keep the page up during the retry, with the button in its loading state) is covered by a unit test and was rechecked by nthillairajah in a local build at this head: tabbing to Try again and pressing Enter repeatedly keeps focus on it. If a retry ends in some other error instead, focus moves to the error message. A unit test covers that part, but it hasn't been checked in the app.

🤖 Generated with Claude Code

When the native viewer exchange refuses the account (403), the app
panel shows "You can't open this app" with Try again in the website's
place, instead of a line of red text in the error bar. It gives no
reason for the refusal. Native attach now rejects with a message and an
optional page name; other failures keep the error bar.

Co-authored-by: Dylan G <69953f73c9f5149d56f2155107338a5435823b938a6c430c63e779f9a8a446f9@remote-agent-pilot.communities.buzz.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Nathan Thillairajah <[email protected]>

@kalvinnchau kalvinnchau 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.

🤖 One keyboard-focus regression reproduced in Chromium and WebKit: retrying a refused app removes the focused recovery control.

attachFailed = false;
attachStarted = false;
setLocalError(null);
setPage(null);

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.

🤖 [P2] Preserve focus during refused-app retry

Focus Try again and press Enter: clearing page unmounts the focused button. In both Chromium and WebKit, document.activeElement becomes BODY while attachment is pending and remains BODY after another refusal restores the button; immediate repeated refusal also loses focus. This conflicts with the focused-retry-control requirement in src/shared/design-system/DESIGN.md:356. Keep the recovery control mounted during retry with repeat activation guarded, or provide intentional focus handoff/restoration that respects subsequent user focus. Extend BrowserHostView.test.tsx's refusal/retry test with a deferred attachment and focus assertions after repeated refusal.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🤖 Good catch, fixed in cf3f753. Try again no longer clears the page. The page stays mounted while the retry runs, with the button in the shared Button's loading state, which keeps it focusable (focusableWhenDisabled) and ignores repeat presses. The page only clears once the website attaches. A repeated refusal just turns off loading, so focus stays on the same button.

The new test in BrowserHostView.test.tsx uses a deferred attachment and checks focus while the retry is pending, that a repeat press does nothing, and focus after a refusal, after an immediate second refusal, and through to success. It fails on the old view code (focus goes to BODY). #2681's "couldn't load" Try again had the same unmount. It picks this fix up through a merge, plus its own loading state and test in 695ee3c.

@dr0uleau
dr0uleau requested a review from a team November 6, 2026 00:44
Retrying from the "You can't open this app" page cleared the page, which
unmounted the focused button and dropped focus to the document body. The
page now stays while the retry runs, with Try again in its loading state
(still focusable, repeat presses ignored), and clears only once the
website attaches.

Co-authored-by: Dylan G <69953f73c9f5149d56f2155107338a5435823b938a6c430c63e779f9a8a446f9@remote-agent-pilot.communities.buzz.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Nathan Thillairajah <[email protected]>
nathan-thillairajah added a commit that referenced this pull request Nov 6, 2026
…retries

The "Buzz couldn't load this app" page now stays mounted during a retry
(from #2680's fix), so its focused Try again keeps focus. Put that button
in its loading state too, so the retry shows as pending and repeat
presses are ignored, as on the refused-app page.

Co-authored-by: Dylan G <69953f73c9f5149d56f2155107338a5435823b938a6c430c63e779f9a8a446f9@remote-agent-pilot.communities.buzz.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Nathan Thillairajah <[email protected]>
nathan-thillairajah added a commit that referenced this pull request Nov 6, 2026
…urns 401 (#2678)

When Buzz opens a Builderlab app in its side panel, it exchanges your
organization sign-in for a viewer cookie. If that exchange comes back
401, the panel asks you to sign in again, but Settings → Identity still
says you're signed in. The two disagree until something else notices.

Now a 401 from the exchange triggers one background check with the
sign-in adapter, using the existing `FederatedIdentity::recheck` that
agent decisions and community API calls already use. If the adapter says
the session ended, it moves to "reauthentication required" and Settings
follows. If the session is still good, nothing changes, so a stray 401
can't sign anyone out. Other failures (403, 5xx, network) and dev broker
sessions don't trigger the check, and the panel's error text is
unchanged.

## Testing

Unit tests cover both outcomes of the 401 check and the cases that skip
it.

Manually tested on macOS by nthillairajah in a local dev build that
combined this PR with #2680 and #2681. The build also had a local-only
switch, not part of any PR, that fakes the exchange's answer. With a
faked 401, the panel showed the sign-in-again message and Settings still
showed the organization sign-in as active. A normal app link loaded
fine. We couldn't fake Builderlab actually revoking a session, so the
"session really ended" path is only covered by the unit tests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: Nathan Thillairajah <[email protected]>
Co-authored-by: Dylan G <69953f73c9f5149d56f2155107338a5435823b938a6c430c63e779f9a8a446f9@remote-agent-pilot.communities.buzz.xyz>
Co-authored-by: Claude Opus 5.5 <[email protected]>
…page

* origin/main: (49 commits)
  fix(browser): recheck the FI session when the app viewer exchange returns 401 (#2678)
  fix(members): find verified people who have no profile (#2554)
  fix(dev): clear React's dev render measures so long sessions do not grow (#2495)
  Move app navigation to a dock and unify conversation headers (#2626)
  perf(mentions): rank only the shown rows and skip empty-query Space work (#2298)
  fix(messages): stop bare links at fullwidth and CJK punctuation (#2404)
  fix(agents): treat what an agent reads as information, and give answering rules only to mentions (#2456)
  feat: add up arrow message editing preference (#2659)
  fix(channels): move focus to channel settings opened from the header menu (#2526)
  fix(window): restore position before size and never fall back to a sub-minimum window (#2405)
  fix(messages): pick the latest message with the shared ordering (#2403)
  fix(channels): let shift+escape through the channel settings panel (#2402)
  fix(builderlab): clarify signed-out agent settings (#2602)
  feat(builderlab): preselect the catalog's recommended model (#2552)
  feat(host-process): set BUZZ_VERSION for every spawned process (#2674)
  fix(agents): finish Delete for a synced agent with no config (#2692)
  ci(flake-check): select related tests within a 9-minute lane budget (#2607)
  feat(macos): show the closed window again when Buzz becomes active (#2658)
  fix(channels): show channel actions as soon as the menu opens (#2657)
  fix(huddles): confirm before starting a huddle from the header (#2687)
  ...

Signed-off-by: Nathan Thillairajah <[email protected]>

# Conflicts:
#	docs/browser.md
#	src-tauri/src/browser/builderlab.rs
@nathan-thillairajah nathan-thillairajah removed the buzz-review-completed Agent review and human UI testing recorded label Nov 6, 2026
@nathan-thillairajah
nathan-thillairajah marked this pull request as draft November 6, 2026 00:59

@kalvinnchau kalvinnchau 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.

🤖 The retry-focus regression is fixed and verified in Chromium and WebKit. One nonblocking P3 remains: a retry ending in a generic error drops focus when the recovery button disappears. CI is still pending; this approval does not attest to a completed CI run.

@nathan-thillairajah nathan-thillairajah added the buzz-review-completed Agent review and human UI testing recorded label Nov 6, 2026
@nathan-thillairajah
nathan-thillairajah marked this pull request as ready for review November 6, 2026 01:05
@nathan-thillairajah nathan-thillairajah removed the buzz-review-completed Agent review and human UI testing recorded label Nov 6, 2026
@nathan-thillairajah
nathan-thillairajah marked this pull request as draft November 6, 2026 01:07
If Try again on the "You can't open this app" page ends in an error that
has no page of its own, the page and its focused button go away and the
error bar shows. Hand focus to that message so keyboard users aren't left
on the document body. Focus moves only if it was still on the page when
the retry failed.

Co-authored-by: Dylan G <69953f73c9f5149d56f2155107338a5435823b938a6c430c63e779f9a8a446f9@remote-agent-pilot.communities.buzz.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Nathan Thillairajah <[email protected]>
@nathan-thillairajah

Copy link
Copy Markdown
Contributor Author

🤖 Thanks. I fixed the P3 in 6c52d8c. If a retry from the page ends in an error that has no page of its own, focus now moves to the error message. That message is focusable through tabIndex={-1} and is still role="alert". Focus only moves if it was still on the page when the retry failed, so focus the user has moved elsewhere stays put.

The new test in BrowserHostView.test.tsx covers both cases: focus lands on the alert, and focus on the address field is left alone. It fails on the previous head. #2681 picks this up through a merge (3795bb8), because its "couldn't load" page uses the same code path.

@kalvinnchau kalvinnchau 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.

🤖 Re-reviewed the focus-handoff change. Chromium and WebKit confirm generic retry failures focus the alert while preserving intentional focus movement; pending/repeated refusal and panel-close recovery also pass. No blocking findings remain. Native IPC/upstream behavior is modeled in these browser checks. Current-head CI is still pending; this approval does not attest to a completed CI run.

@nathan-thillairajah nathan-thillairajah added the buzz-review-completed Agent review and human UI testing recorded label Nov 6, 2026
@nathan-thillairajah
nathan-thillairajah marked this pull request as ready for review November 6, 2026 01:57
@nathan-thillairajah
nathan-thillairajah merged commit 7d06475 into main Nov 6, 2026
46 checks passed
@nathan-thillairajah
nathan-thillairajah deleted the dylan/browser-denied-page branch November 6, 2026 01:58
nathan-thillairajah added a commit that referenced this pull request Nov 6, 2026
…able-page

* origin/main:
  feat(browser): show a refused Builderlab app as a page in the panel (#2680)
  fix(profiles): cache profiles on disk and keep dm partners through eviction (#2178)
  feat(profiles): let each reader show verified people by username or full name (#2356)
  fix(media): show transparent avatars without a black background (#2477)
  fix(channels): keep the reader's place when Back returns to a link-opened channel (#2500)
  BUZZ-518: Explain required community sign-in (#2710)
  feat(desktop): serve credentials to agents and your CLI, with pairing (#2260)
  test(solo): wait for search and access before Share focus reveal (#2573)
  fix(relay): answer bytes a failed spool wrote; deflake the memory read (#1970)
  ci(release): publish a signed Intel macOS DMG (#1825)

Signed-off-by: Nathan Thillairajah <[email protected]>

# Conflicts:
#	docs/browser.md
#	src-tauri/src/browser/builderlab.rs
#	src/features/browser/BrowserHostView.test.tsx
#	src/features/browser/BrowserHostView.tsx
#	src/features/browser/platform.test.ts
nathan-thillairajah added a commit that referenced this pull request Nov 6, 2026
…n and Open in browser (#2681)

When Buzz can't reach Builderlab to open an app in the side panel (a
network failure, a 5xx, or a reply it can't read), the panel shows
"Builderlab sign-in request failed" in a red bar above an empty page,
with no way to retry.

Now those failures show a page: a cloud icon, **Buzz couldn’t load this
app**, and two buttons. **Try again** retries. **Open in browser** opens
the app in your default browser, where the app's own login takes over.
If the browser can't be opened, the page says so. 401, 403 and 404
behave as before, and bad URLs or credentials still use the red bar.

## Testing

Rust and Vitest tests cover which failures get the page, both buttons,
and the case where the browser can't open.

Manually tested on macOS by nthillairajah in a local dev build that
combined this PR with #2678 and #2680. The build also had a local-only
switch, not part of any PR, that fakes the exchange's answer. A faked
network failure and a faked 503 both showed the page with no red bar.
Open in browser opened the app in the default browser, and Try again
failed the same way, as expected while the fake was still on. A normal
app link loaded fine. We didn't test with a real network outage.

After that test, a review on #2680 found that Try again lost keyboard
focus while retrying. This page gets the same fix (keep the page up
during the retry, with the button in its loading state). That's covered
by a unit test and was rechecked by nthillairajah in a local build
before main was merged in (the merge changed no code in this PR):
tabbing to Try again and pressing Enter repeatedly keeps focus on it. If
a retry ends in some other error instead, focus moves to the error
message. A unit test covers that part, but it hasn't been checked in the
app.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Nathan Thillairajah <[email protected]>
Co-authored-by: Dylan G <69953f73c9f5149d56f2155107338a5435823b938a6c430c63e779f9a8a446f9@remote-agent-pilot.communities.buzz.xyz>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

buzz-review-completed Agent review and human UI testing recorded

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants