fix(web): make WSL settings searchable - #8881
UI Consistency: All clear
All clear
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.
Reviewed the WSL settings-search change against the rendered row.
isWslSettingsRowVisible(ConnectionsSettings.logic.ts) is now the single owner of the row-visibility rule and is consumed by bothrenderWslRow()andSettingsSidebarNav, so the search catalog and the anchor can no longer drift. The state branch (available || enabled || wslOnly) is exactly the previous panel logic, and thestate === nullbranch reduces to the olddesktopWslErrorretry-row condition, so panel behavior is unchanged.- The prior concern about
dataanderrorbeing non-null simultaneously (SWRpreviousSuccess) is resolved: the predicate only consultserrorwhenstateis null. - Gating parity checked: the panel row requires
canManageLocalBackend && desktopBridge; in ElectroncurrentSessionScopesisAuthAdministrativeScopes(includesaccess:write) andisElectron === (window.desktopBridge !== undefined), so the sidebar'sisElectrongate matches. Both sides read the samekeepAliveatom, so no split state. - The memo now depends on both
desktopWsl.dataanddesktopWsl.error, so the load-error case is reachable; the new clamp effect keepsactiveResultIndex,aria-activedescendant, and Enter handling valid when the result list shrinks after the WSL state resolves. {...searchableSetting("wsl-backend")}matches the established pattern inSettingsPanels.tsx,IntegrationsSettings.tsx, andProviderSettingsPanel.tsx, and the three rows that spread it are mutually exclusive, so the anchor id stays unique.isWindowsPlatform(navigator.platform)follows existing repository precedent and is SSR-guarded.
🔍 Agent activity — 16 tool calls
💭 I'm checking whether the extracted predicate in ConnectionsSettings.logic.ts properly resolves the prior concern about data and error being non-null simultaneously — it now prioritizes state when present, falling back to error only otherwise, which looks correct. I'm now looking at whether the memo's dependency on data from the earlier review comment has been addressed. Now I need to verify several remaining details: whether
searchableSetting("wsl-backend")supplies a title prop for SettingRow, whetherisWslSettingsRowVisiblein SettingsSidebarNav correctly typesdesktopWsl.data(which could be undefined) against the expectedDesktopWslState | nullparameter, whether the hardcodedstate: nullin the first branch matches the actual desktopWslState at that point, and whetheruseEffectis properly imported in SettingsSidebarNav. I also want to check thewindowsOnlygate usingnavigator.platform— since the sidebar now filters by state, this check might be redundant, so I should look at howisWindowsPlatformis implemented. Additionally, I need to confirm thatsearchSettingsactually accepts the secondsearchableItemsargument being passed to it. Let me pull up these files to check.
1. browse_code — apps/web/src/components/settings/settingsSearch.ts
2. browse_code — apps/web/src/components/settings/SettingsSidebarNav.tsx
💭 I want to check the useEnvironmentQuery signature and desktopWslStateAtom, plus how ConnectionsSettings renders the WSL row and what type wslState actually is for isWslSettingsRowVisible.
3. browse_code — apps/web/src/components/settings/ConnectionsSettings.tsx
4. browse_code — apps/web/src/components/settings/ConnectionsSettings.logic.ts
💭 I notice a mismatch: the error/retry row in ConnectionsSettings also gates on
canManageLocalBackend, but the sidebar's search predicate doesn't check that flag—so if the WSL row fails to load whilecanManageLocalBackendis false, the row won't render even though the search item still points to it, creating a dead anchor. I need to trace where renderWslRow gets called to see how canManageLocalBackend affects the whole section.
5. git_tools — canManageLocalBackend|renderWslRow|desktopWslError|desktopWslState =
6. browse_code — apps/web/src/state/desktopWslState.ts
💭 I found it: ConnectionsSettings only renders the WSL row when canManageLocalBackend is true, but the sidebar's query gates only on isElectron, ignoring canManageLocalBackend. That means on Windows desktop without the required access:write scope, the actual row never shows, yet the search result still surfaces it—a mismatch between the two gating conditions. But the sidebar's row still renders because its own query fetches state independently, creating a dead anchor when Electron's canManageLocalBackend is false. I need to verify whether that false case is actually plausible by checking currentSessionScopes, and also check the return type of useEnvironmentQuery to see if the data field is nullable or undefined, since that affects the DesktopWslState typing. The windowsOnly platform gate on the search item now looks redundant given the state-based filter, but it's probably harmless.
7. git_tools — export function useEnvironmentQuery
8. git_tools — currentSessionScopes
💭 I'm checking whether canManageLocalBackend depends solely on desktopBridge existing versus the isElectron flag, since these two checks might not always align if isElectron is determined by user agent rather than the actual bridge object being present. If isElectron is true but desktopBridge is missing, the sidebar's WSL state atom would throw a DesktopWslStateUnavailableError, producing an error message that makes the search predicate return true and show the item—while ConnectionsSettings, which gates strictly on desktopBridge, would return null and hide the row, creating a dead anchor. I need to check env.ts to see how realistic this isElectron-without-bridge scenario actually is.
9. browse_code — apps/web/src/env.ts
10. git_tools — isWindowsPlatform
💭 I'm confirming that the desktopBridge existing lines up with isElectron being true, and that the sidebar's visibility gate matches the same conditions ConnectionsSettings checks for managing local backends. The windowsOnly filter using navigator.platform works fine on Electron/Windows since it reports "Win32". I'm also checking a minor divergence around mutation errors versus query errors when the state is null, but this seems unlikely to cause real issues since state usually isn't null after a mutation error. I'm now comparing the old and new logic for rendering the WSL row, tracing through the previous conditional structure that handled null state with error and permission checks. The new predicate-based check placed before the availability check produces equivalent behavior to the original branching logic. One nuance I'm tracking is whether the retry row's visibility properly requires canManageLocalBackend permission, and whether that permission scope is correctly derived from the administrative auth scopes in Electron — I want to verify this includes the write access scope. I'm also concerned the sidebar predicate might miss a rendering gate around lines 3020-3090, where renderWslRow is conditionally called only when the primary environment is local or connected — if so, the anchor could be dead when the desktop's primary environment is remote instead.
11. browse_code — apps/web/src/components/settings/ConnectionsSettings.tsx
12. git_tools — AuthAdministrativeScopes =
💭 The gating chain confirms consistency between the WSL row rendering and sidebar logic, both checking Electron state properly. I'm now checking whether the
windowsOnlyfield in settingsSearch is redundant given the sidebar's real-state filtering—it seems like a harmless defensive secondary gate rather than a violation, though it does rely on the deprecatednavigator.platformAPI. I'm also noting that mounting the sidebar triggers a WSL state fetch on every desktop platform, including macOS, which is a minor perf consideration but not really a UI consistency issue since it just returns unavailable quickly there. Checking the recovery row case where the backend is available:false but enabled:true still renders correctly with the right id, matching sidebar behavior. Looking at the visibility predicate for the WSL row, I see it passes state as null inside a branch, which reduces down to just checking whether there's an error — a bit convoluted but functionally fine, and canManageLocalBackend is always true in electron so that gate isn't a concern. The clamping effect on activeResultIndex when results shrink seems reasonable and necessary — without it, if a user's query narrows the result list while their selection points past the end, the active descendant would become undefined and pressing Enter would silently do nothing. The imports look clean, importing only types where needed, and the added environment query subscription in the sidebar nav for WSL state shouldn't affect the search results memo's identity or cause unnecessary recomputation. I'm now checking whether there's a genuine divergence between what the sidebar predicts versus actual behavior worth flagging. The retry row for the "wsl-backend" error path relies on the same shareddesktopWslStateAtom, so both the sidebar and the ConnectionsSettings panel read from the identical global atom instance in the registry -- meaning they can't get out of sync since it's one shared state, not split copies. With keepAlive, prior data shows immediately so timing isn't a real issue there. But I do notice a transient false negative case: if the sidebar mounts on a non-Connections page before the WSL query resolves, the item briefly hides even though WSL exists — opposite of the earlier false-positive concern, but minor since it's just a few ms during initial load. Let me check git log on this branch to see what else changed beyond these files that I should review. The diff scope covers apps/web/src ts/tsx and is small. I want to check whether SettingsRow accepts an id prop that gets applied to the row element, since{...searchableSetting("wsl-backend")}now spreads id+title in place of a plain title. Since the three WSL rows are mutually exclusive branches (retry, recovery, main), having the same id on all three isn't actually a conflict. I'll verify SettingsRow's id handling and check how other panels use searchableSetting to confirm the spread pattern is consistent.
13. git_tools — searchableSetting(
14. git_tools — 9b2d043..33b8cbd
💭 All the earlier findings look resolved and the pattern usage matches the rest of the codebase. Checking the clamping logic added in the last commit—it safely handles an empty results array by falling back to zero, and I'm now looking at whether the effect re-running on every render could cause any issue. The scroll-into-view effect below looks fine, and I'm now weighing whether the new sidebar subscription belongs with the nav or the search catalog—there's some duplication between the settings config flags and a runtime state filter, but it's acceptable and not worth flagging. I'm also verifying that the conditional hook usage in SettingsSidebarNav stays stable since it depends on a module-level constant rather than something that changes at runtime, so hook ordering isn't a problem. Now I want to check the test file more closely, specifically around the assertion for the "wsl" search settings case, to confirm it's properly scoped to the desktopOnly/browser test context.
15. browse_code — apps/web/src/components/settings/settingsSearch.test.ts
💭 Word wrap mapping to appearance settings is correct and pre-existing, so no issue there. I check for sidebar filter tests and confirm there isn't one, though the predicate itself is tested, which seems fine. Now I'm turning to a potential issue with the new test file for ConnectionsSettings logic. I also verify that error mismatches between the sidebar and ConnectionsSettings don't cause real divergence, since a mutation error can only exist alongside non-null state, and state takes priority when non-null. The code looks solid overall, so I'm doing a final pass on the diff, particularly the windowsOnly filter that checks navigator.platform, confirming it works for Win32 but wondering about behavior on Windows ARM64.
16. git_tools — navigator.platform
💭 I'm double-checking that the WSL row's dependency on the desktop bridge atom is separate from environment-scoped state, and confirming both stay properly gated within the primary environment section without conflict. I don't see any new concrete violations. Checking the sidebar's readonly
SETTINGS_SEARCH_ITEMStuple, the types remain assignable toSettingsSearchItemwhether the memo returns the tuple or a filtered array, sosearchSettingsstill typechecks fine either way. The remaining subtlety is thatresultsidentity shifts wheneversearchableItemsidentity changes, and that's only recomputed when the underlying data or error state changes. I also notice that each 30-second revalidation creates a new data object, which recomputessearchableItemsandresults, retriggering the scroll effect on the active result — but sincescrollIntoView({block: "nearest"})is a no-op when already visible, this is harmless. The clamp effect only depends onresults.length, so that's fine too. Overall I'm landing on no findings here. Given the tension between the instruction to respond with just "All clear" versus the required JSON schema, I'll follow the schema but set the summary and title to reflect an all-clear state, using something like "UI Consistency: All clear" with a success state.
Agent Credits: 107 credits
Token usage: 20 uncached input · 470,951 cache-read input · 79,487 cache-write input · 11,387 output
Agent Credits may also include non-token charges from external tools such as web research.