Repository navigation
UI Consistency: 1 issue found
apps/web/src/components/ProviderUpdatePrimaryNotification.tsx (lines 223-226) — the new gate if (!shouldShowPrimaryProviderUpdateToast(runningView)) is constant-false (getProviderUpdateRunningToastView always returns phase: "running"), so it only ever closes the prompt toast and has no branch that renders progress. If the predicate ever returns true for a running view, the prompt toast lingers with its stale Update CTA and no state update, reintroducing the behavior fixed in #6544. Suggested fix: close the prompt unconditionally and drop the now-unused getProviderUpdateRunningToastView import. The same comment notes that, with the running toast removed, the only remaining in-progress surface is SidebarProviderUpdatePill, which is off-screen when the sidebar is collapsed (collapsible="offcanvas") or when the mobile sheet is closed, leaving no feedback between pressing Update and the terminal toast.
Inline review comment posted.
Details
Note
Your check run agent prompt is: .macroscope/check-run-agents/ui-consistency.md
More information about how Check Run Agents work can be found in our Help Center.
Files examined: ProviderUpdatePrimaryNotification.tsx (head and merge base), ProviderUpdateLaunchNotification.logic.ts, ProviderUpdateLaunchNotification.logic.test.ts, ui/toast.tsx, ui/toastHelpers.ts, sidebar/SidebarProviderUpdatePill.tsx, sidebar/SidebarChrome.tsx, plus AppSidebarLayout.tsx / ui/sidebar.tsx for sidebar collapse behavior.
Checks performed and found clean:
- No styling, Tailwind, CSS-ownership, theme-variant, or shared-primitive changes in this diff; no raw controls reconstructing
Button/Input/Select. - Toast lifecycle transitions re-traced for the add-instead-of-update migration: prompt close on unmount,
openProviderSettingsnarrowing to thepromptvariant, ref-identity guard (activeUpdateToast !== activeUpdate) in the async completion path, and the progress effect. No duplicate or leaked toasts were identified: the terminal toast is added exactly once whether the live-provider effect or the dispatch loop reaches the terminal view first. data.onClose(which persists the dismissal key) is only invoked by the toast dismiss button (handleToastDismissClick), not bytoastManager.close, so closing the prompt programmatically does not silently dismiss the notification key.dismissAfterVisibleMsauto-dismiss works for freshly added toasts (ThreadToastVisibleAutoDismisskeys its remaining-time map by toast id), so the success toast still self-dismisses.- Removal of the Base UI merge comment is acceptable: it described
toastManager.update, which is no longer used. - Minor, not reported: the
input.view.type === "loading"branch inaddProviderUpdateToastis now unreachable, since every caller gates outrunningviews (the only source of theloadingtype).
🔍 Agent activity — 15 tool calls
💭 I should look at the actual files to understand this change. It seems the PR reverts a "toast update" approach back to an "add new toast" approach, and hides the "running" phase toast—let me dig into the code to confirm.
1. browse_code — apps/web/src/components/ProviderUpdatePrimaryNotification.tsx
2. browse_code — apps/web/src/components/ProviderUpdateLaunchNotification.logic.ts
💭 I'm examining how the PR changes toast behavior, specifically noting it removes the loading toast during updates and reverts to adding new toasts instead of updating existing ones. I'm checking whether the running-toast visibility logic in
runUpdatesstill behaves correctly given that the running view phase is always "running." Then I trace the progress effect for potential duplicate toasts: when the update state becomes terminal, both the effect and the async completion handler could try to add a toast, but the ref-based guard seems to prevent double-firing in one direction. I'm now checking the reverse case, where the async completes first and sets the ref before the effect runs. I also notice the effect's dependency array only includes providers and openProviderSettings, so in the running case it relies on provider state changes to re-trigger polling, which seems intentional. Looking at addProviderUpdateToast, the toastId assignment pattern with a definite assignment assertion is a bit unusual but matches existing conventions elsewhere in the file, so I'll treat it as fine rather than a bug. I'm also checking whether removing the comment about Base UI's toast merging behavior matters now that the update path is gone — since toastManager.update is no longer used, dropping that comment seems appropriate rather than a loss of an important constraint note. One thing worth flagging though: previously users saw a loading toast while the update ran, giving feedback that something was in progress, but now clicking "Update" closes the prompt toast immediately without that same visual continuity.
3. git_tools — getProviderUpdateSidebarPillView|ProviderUpdatePrimaryNotification|shouldShowPrimaryProviderUpdateToast
4. git_tools — 9d2cc19
💭 I should check the git history for this file since this PR seems to revert a previous fix — I want to see the merge-base version to understand if the "update in place" approach was deliberately introduced to fix a bug like duplicate or stacking toasts.
5. git_tools — apps/web/src/components/ProviderUpdatePrimaryNotification.tsx
6. browse_code — apps/web/src/components/sidebar/SidebarProviderUpdatePill.tsx
💭 The sidebar pill already shows loading progress, so removing the duplicate running toast makes sense, but the terminal toasts (succeeded/failed/unchanged) still overlap with the pill's terminal states—though that's not something I need to change. I'm now looking for actual regressions introduced by the change, starting with what the previous commit did. That earlier commit hid the Update toast action while an update runs, and now the prompt toast closes entirely when the update starts, so that intent is preserved. I'm checking for edge cases: whether the terminal toast could fire twice if the effect watching
providerstriggers on partial state changes—say one provider finishes while another hasn't started yet, which could mean the snapshot used to decide completeness is inconsistent. Tracing through, if provider1 fails first, the effect adds a failed toast and clears the ref before provider2 finishes updating, but the later completion check compares against the stale ref and bails out, so only one toast fires—matching prior behavior. I'm also checking theopenProviderSettingsclosure logic for closing toasts by id, and it seems to correctly skip clearing the active toast when the ids don't match, though I suspect there's still a subtle bug lurking here. I'm trying to recall whether ESLint's prefer-const rule actually flags aletvariable with a definite assignment assertion that's only assigned once — I believe there's some nuance where definite assignment assertions might be exempted from this rule, but I'm not fully certain. Since the existing codebase already uses this exact pattern elsewhere and passes lint, I conclude it's fine here too. Now I'm looking at therunUpdatesblock logic, checking how it determines the running toast view and whether to show the primary toast based on provider count. If the policy function ever returned true while updates were running, the prompt toast would stay open with a stale "Update Available" CTA instead of transitioning to the running state — but sinceshouldShowPrimaryProviderUpdateToastis currently a constant, this branch is effectively dead code, just a latent inconsistency worth flagging. A stronger issue: once the loading toast is removed, clicking "Update" closes the prompt toast and leaves nothing visible except the sidebar pill until the terminal state is reached. I need to check whether that pill is always rendered — it shares the same atom as the primary notification, but if the sidebar can be collapsed or hidden, the user would lose all progress feedback entirely. I'm also noting that theloadingbranch inaddProviderUpdateToastnow looks unreachable, since every caller gates on not-running, so that code path and its associated props are effectively dead. Another concern is behavior: previously the toast was updated in place (same id, type changed from warning to success), preserving position and screen reader focus. Now it seems a new toast gets added instead, which could cause stacking. I want to verify whetherdismissAfterVisibleMsstill works correctly when applied to a freshly added toast versus an updated one. Since Base UI merges top-level keys and data is one of them, it gets fully replaced either way, so the outcome is the same. I should look at the actual toast implementation to confirm details around dismissAfterVisibleMs and the add/close behavior.
7. git_tools — dismissAfterVisibleMs|hiddenToastActionProps|stackedThreadToast
8. git_tools — SidebarProviderUpdatePill
💭 I'm checking whether the success toast auto-dismisses correctly since it now gets a fresh toastId instead of being updated in place, which should be fine. I also want to confirm the error toast still keeps its "Settings" action in the stacked toast behavior. More importantly, I'm now tracing a potential regression: closing the prompt toast via
toastManager.close(toastId)triggers itsonClosehandler, which callsdismissNotificationKey, permanently persisting the dismissal. Previously the toast was updated in place rather than closed, soonClosenever fired during that transition — meaning if the update fails now, the prompt's dismissal could get persisted unintentionally. I need to verify whetherclose()actually invokesdata.onClose, or if that's only wired to the close button's click handler, so I'm going to check howonCloseis wired in toast.tsx. I also note that clicking Settings already closes the prompt toast as pre-existing behavior, which is a separate but related consideration.
9. git_tools — onClose
💭 Good, onClose only fires on explicit dismiss clicks, not programmatic closes, so closing the prompt toast programmatically won't dismiss the notification key—and since the advisory changes after a successful update, the key naturally changes too. Now I'm checking whether a duplicate prompt could re-show after runUpdates closes the toast and resets the active toast reference. Tracing through the effect dependencies, if providers update mid-flight but the notification key stays the same, the active toast is still "update" kind so nothing closes, and the ref being truthy causes an early return—that's correct behavior. Once the terminal toast is added and the ref is cleared, if the notification key is still non-null because the update failed, the seen-keys set already contains that key so the prompt effect bails out again, which is also correct. I'm now tracing whether the async success path and the providers-change effect could ever both fire and create a genuine duplicate toast, since they're only guarded by ref identity and effect timing. I also confirm the
openProviderSettingscallback signature lines up with whataddProviderUpdateToastexpects, and that closing the prompt toast on update start resolves the earlier bug where the Update action could linger during an active run. The failed/unchanged case correctly produces a separate stacked toast with its own Settings action. The remaining concern is that once the prompt toast is closed, the "update" entry in the active-toast ref no longer corresponds to any visible toast on screen, which matches the type definition since the toastId field was removed from that variant—so I'm checking whether unmount cleanup handles this dangling state properly. If that predicate were ever true, the prompt toast would stay open with a stale "Update" CTA, regressing the earlier fix — but since it's a constant false, there's no concrete current regression to flag. I'm also noticing the "loading" branch in addProviderUpdateToast looks unreachable since all callers gate on shouldShow, which excludes the running phase — making it dead code. Dead code isn't really a UI consistency violation per the guidelines, though I want to check if there's a genuine UX regression: does removing the loading toast eliminate the only progress indicator for users who don't see the sidebar pill? I need to verify whether that sidebar pill is always rendered.
10. browse_code — apps/web/src/components/sidebar/SidebarChrome.tsx
💭 I'm noticing the pill lives in the sidebar footer, which disappears when the sidebar is collapsed—so a user clicking "Update" with the sidebar closed gets zero feedback during what could be a lengthy npm install. That seems like a real regression worth flagging, since it removes loading-state feedback that should be preserved during migrations. But I should weigh this against the PR's stated intent of removing a duplicate progress indicator—the sidebar pill and toast both showed the same running state while the sidebar was visible, so the duplication claim holds in that case. The "no feedback when collapsed" issue is more debatable and product-decision-adjacent, so I'd rather prioritize more objective issues and move on to checking the test file for stale expectations around lines 300-350.
11. browse_code — apps/web/src/components/ProviderUpdateLaunchNotification.logic.test.ts
💭 I want to check the merge base version of ProviderUpdatePrimaryNotification.tsx to compare behavior, particularly the effect around line 159 in the old version and how terminal handling worked there.
12. browse_code — apps/web/src/components/ProviderUpdatePrimaryNotification.tsx
💭 I'm comparing the old and new effect logic for potential regressions, specifically checking whether a terminal toast could get duplicated if the effect fires and clears the ref before the async loop's final check runs against the stale reference. Tracing through the unmount cleanup, I confirm the ref being nulled prevents any toast from being added after unmount, matching the old behavior, and the reordering of that null assignment looks harmless. Checking the settings action on the terminal toast, clicking it correctly closes that toast via openProviderSettings, and the prompt toast closure logic still seems consistent with the seen-notification tracking. Now I'm considering whether duplicate toasts could stack when a terminal update finishes. Checking the toastId narrowing logic confirms it's fine. I'm also verifying there's no dead code left behind —
isTerminalProviderUpdateToastViewwas only local so its removal is safe, and the other helpers are still used. Now I'm double-checking thelet toastId!pattern in addProviderUpdateToast won't trip ESLint's prefer-const rule since it's assigned immediately after declaration. I'm wondering whether the definite assignment assertion case (let toastId!: T;) is specifically exempted from prefer-const, since that's TypeScript-specific syntax that ESLint's core parser wouldn't recognize, and I'm not sure typescript-eslint has a dedicated extension rule handling it. That explains why it passes. Looking ataddProviderUpdateToast, the read of toastId inside the onClick closure occurs textually within the same assignment statement as the write, just positioned after it — so the rule's check for reads-before-write doesn't trigger, since the read reference comes after the write's identifier position in source order. Since the closure only executes later, using const would work fine at runtime even though the read appears inside the initializer. But I can't actually run ESLint to confirm whether prefer-const fires here, and this is really a code-style nitpick rather than a UI consistency issue, so flagging it as a definitive finding feels risky and out of scope. Stepping back, I should focus on genuine UI-consistency findings instead. This diff is mostly toast lifecycle logic with no real styling changes, so the relevant checks are around preserving accessibility/interaction semantics and requiring tests when state transitions change. One real issue worth raising: inrunUpdates, since the running-toast view predicate always returns phase "running", the branch that closes the prompt toast is always taken, making the conditional check effectively dead logic. If the gating policy ever changes, the stale prompt toast could linger with its "Update" CTA re-enabled, reintroducing the bug fixed in #6544 — so I'd suggest closing the toast unconditionally rather than relying on the predicate. Similarly, the loading branch inaddProviderUpdateToastlooks unreachable now since "loading" type only comes from running views that are always filtered out, which is dead code worth flagging, though both of these feel like weaker, secondary findings. A stronger concern is the loss of in-progress feedback when the sidebar is collapsed or hidden: since the pill lives inSidebarFooter, clicking "Update" now closes the prompt toast and shows nothing until the update finishes if the sidebar isn't visible, whereas before the toast itself displayed "Updating provider…". I want to verify whether the sidebar uses icon-collapsible mode (footer stays visible) versus offcanvas (fully hidden) to confirm this is a real regression, so I'm checking theSidebarFootercomponent and its collapse behavior.
13. browse_code — In apps/web/src, how is the app sidebar configured (collapsible mode: icon vs offcanvas) and is SidebarChromeFooter/SidebarFooter (which renders SidebarProviderUpdatePill) still visible when the sidebar is collapsed or closed? Point to the Sidebar component props and any CSS that hides the footer when collapsed.
💭 I'm realizing that when the sidebar is collapsed or on mobile, the pill is invisible, so removing the running toast would eliminate all visible progress feedback in those states—the terminal toast stays as a safety net for completion, but the running state would have no fallback surface. This does seem like a concrete regression: clicking "Update" would close the toast immediately with no feedback if the sidebar is collapsed, even though the underlying npm-install update can take a while. That's a real loading-state/interaction-semantics issue, and likely my strongest finding here. Still, I want to weigh whether the PR's intent—removing the duplicate progress indicator—makes this an acceptable tradeoff, or whether a reviewer should push back and suggest gating the toast removal on sidebar visibility instead. I'm also checking the async completion logic comparing the active toast reference against the current update, which looks equivalent to the old check and seems fine. Now I'm tracing through what happens with multiple providers updating concurrently—after one provider's update resolves and the providers atom updates, the effect reruns and needs to correctly distinguish a succeeded provider from one still pending, which could reveal another subtle bug worth flagging. I should check whether the success toast's dismiss-after-visible timeout logic, keyed by toast id, still behaves correctly now that the id comes from a freshly added toast rather than a reused prompt toast id — worth looking at the toast manager's timeout handling around where it tracks remaining visible time per toast.
14. browse_code — apps/web/src/components/ui/toast.tsx
💭 I'm checking whether the success toast path and prompt-closing logic actually introduce a real bug, particularly looking into the runUpdates function for an issue. Tracing through the notificationKey effect, I confirm the prompt toast correctly stays suppressed while an update is active, matching the old behavior. The main concern I land on is that the gate checking
shouldShowPrimaryProviderUpdateToast(runningView)in runUpdates is effectively dead code since it's always false, which closes the prompt toast regardless and could cause a stale "Update" CTA to linger if that predicate's logic ever changes — echoing a past regression. I also notice the "loading" branch inaddProviderUpdateToastappears unreachable given how view types map to phases, though the "success" case it shares the condition with is still reachable, so it's only partially dead code. Beyond that, I'm weighing whether losing in-progress feedback when the sidebar is collapsed matters, and checking whether there's another surface — like inline status rows on the settings page — that already communicates provider update progress independently of this toast system. Given the review needs concrete findings, I think the strongest comment is on the runUpdates block: the code computes a running view and calls a predicate that's effectively constant-false, so the intent to close the prompt while updating is never actually triggered, leaving the "Update" CTA toast on screen and undoing the earlier fix. I'll suggest closing the toast unconditionally, or adding a running-state toast in the else branch if progress feedback is desired — and consider whether to fold in a note about lost feedback on mobile/collapsed sidebar as a separate comment. Let me double-check this claim before posting it: on mobile the sidebar is a Sheet that closes after navigation, and on desktop a collapsed sidebar pushes the pill off-screen, so in both cases clicking Update genuinely gives zero visual feedback — that confirms it's a valid regression, not a false alarm. Now I'm weighing whether this should register as a failing or neutral finding given it's a moderate regression risk rather than a pure aesthetic nitpick. Given the explicit guidance that the check defaults to failure whenever there are findings, and I do have two real findings, I'll mark this as failure rather than neutral. Now I need to write the inline comment targeting lines 223-226 on the right side of ProviderUpdatePrimaryNotification.tsx, making sure those lines actually appear in the diff. I'm noting that sincegetProviderUpdateRunningToastViewalways returns a running phase, the predicate check is effectively dead code that only ever closes the prompt — if it ever evaluated true there's no else branch to render it, risking a regression of the stale Update CTA issue from #6544. I also want to flag that the only remaining in-progress indicator,SidebarProviderUpdatePill, is invisible when the sidebar is collapsed or on mobile, so users pressing Update in those contexts would get no feedback at all. Now I'm checking whether my suggested fix of just closing the toast unconditionally is self-contained — but it would leavegetProviderUpdateRunningToastViewunused as an import, triggering a lint error, whileshouldShowPrimaryProviderUpdateToastis still referenced elsewhere so that one's fine. Since the suggestion isn't fully clean on its own, I need to consider a different way to present this feedback. I'll skip the inline code suggestion and instead describe the fix in words, noting that the now-unused import should be removed, then post this as a review comment with a brief body alongside the inline note.
15. modify_pr — One finding on the removed running-state toast — see the inline comment.
Agent Credits: 229 credits