Skip to content

feat(web): settings only show up where they can actually be saved - #5832

Closed
inayayousfi wants to merge 5 commits into
pingdotgg:mainfrom
inayayousfi:web/per-device-text-gen-model-hide-empty-source-control
Closed

inayayousfi wants to merge 5 commits into
pingdotgg:mainfrom
inayayousfi:web/per-device-text-gen-model-hide-empty-source-control

Conversation

@inayayousfi

@inayayousfi inayayousfi commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Problem

Global settings persist to the primary environment's settings.json. The hosted static web app never owns a PrimaryConnectionTarget, so useUpdateSettingsTarget (apps/web/src/hooks/useSettings.ts) silently drops every server-key patch there. No log, no error, no toast. Two surfaces were affected badly enough to be worth fixing on their own.

Changes

Text generation model is now per device. It moves out of GeneralSettingsPanel into EnvironmentProviderSettings on the Providers page, writing through useUpdateEnvironmentSettings instead of the primary-only updater.

This is not just a relocation. The setting stores a provider instance id such as codex_personal, and that id only exists on the device where the instance is configured. Every consumer runs server-side and reads its own settings.json — thread titles at apps/server/src/orchestration/Layers/ProviderCommandReactor.ts:866 and :929, commit and PR text at apps/server/src/git/GitManager.ts:2074. Under the old General page you could only write the value to the primary, so a remote server kept the decoded default and generated titles with plain codex regardless of the pick. The row now writes to the device that actually runs the generation.

It also picks up two things for free by living inside the existing panel: the inert read-only wrapper, and useEnvironmentSettings' loading fallback, so the reset button no longer flashes.

Source Control is no longer listed in the hosted static app. Every control on that page is primary-scoped (SourceControlSettings.tsx:511), so with no primary it renders only its empty state and already hides its own writable section. It now drops out of the sidebar nav and the settings search index together, via one predicate isSettingsPathListed.

Notes for review

The gate keys off isHostedStaticApp(), not off the environment catalog. An earlier pass used the catalog and was wrong: the catalog starts { isReady: false, entries: new Map() } and hydrates over an HTTP round trip, so a catalog-derived answer reads "hidden" on the first frame on every platform. The second commit fixes that and explains it.

There is deliberately no route guard. The panel's own empty state is a better destination than a redirect, particularly for the two existing "Setup Required" buttons in GitActionsControl.tsx and CommandPalette.tsx that link here.

Out of scope, worth flagging: /settings/general still carries several ServerSettings-backed rows that hit the same silent-drop path in the hosted web app, and useUpdateSettingsTarget still discards those writes without a log.

Verification

  • Repo typecheck clean across all 15 packages
  • @t3tools/web: 220 files, 1998 tests pass
  • Lint and format clean
  • 7 failures in apps/server are pre-existing; confirmed identical with this branch stashed
  • Ran the web app in static mode (VITE_HOSTED_APP_CHANNEL=latest) to check the settings pages by hand

🤖 Generated with Claude Code


Note

Medium Risk
Changes where text-generation settings are saved and how model selection resolves during stale provider probes; hosted users lose nav/search entry to Source Control (direct URLs still work).

Overview
Moves text generation model configuration from General to the per-environment Providers panel so picks persist to each device's settings.json via useUpdateEnvironmentSettings (thread titles and source-control text read that file on the server that runs them). The row reuses the providers panel's read-only inert wrapper and environment-scoped settings loading; search now lands on Providers instead of General.

In the hosted static web app, Source Control is removed from the sidebar and settings search through isSettingsPathListed, keyed on isHostedStaticApp() so sections do not flicker while the environment catalog loads.

resolveAppModelSelectionState overlays applyProviderInstanceSettings before honoring enabled, matching model pickers when provider probes lag behind a settings write.

Reviewed by Cursor Bugbot for commit 0933644. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Move text generation model settings to the Providers panel and hide primary-only settings in hosted mode

  • Moves the "Text generation model" picker from the General settings panel to the per-environment Providers panel (ProviderSettingsPanel.tsx), where changes can actually be saved per environment.
  • Removes the model picker and all related state from SettingsPanels.tsx (General panel).
  • Adds isSettingsPathListed in settingsSearch.ts to hide primary-only settings paths (e.g. /settings/source-control) from the sidebar nav and search results when running as a hosted static app.
  • Fixes resolveAppModelSelectionState in modelSelection.ts to apply environment settings before checking probe state, so a newly enabled instance is not incorrectly rejected due to a stale disabled probe snapshot.

Macroscope summarized 0933644.

Global settings persist to the primary environment's settings.json, and
the hosted web app never owns a primary connection target. Writes to
those settings were silently discarded there, with no log and no error.

Text generation model moves from General to the Providers page. It now
writes per device through useUpdateEnvironmentSettings, so it works in
the hosted web app once a device is connected.

Source Control drops out of the sidebar, the settings search index and
its own route whenever no primary environment exists. Every control on
that page writes to the primary, so the page had nothing to offer.
… catalog

The first pass gated on "no primary environment exists", derived from the
connection catalog. That catalog starts empty and hydrates over an HTTP
round trip, so the guard read false on the first frame everywhere. Desktop
and CLI users opening Source Control directly got redirected away before
the catalog landed, and the sidebar entry flickered in after load.

Gate on isHostedStaticApp() instead. It is derived from the build channel
and the page origin, so it is correct synchronously on the first render.

Drop the route redirect. The panel already renders an honest empty state
with no primary environment, and it hides its own writable section there,
so a deep link or a stale "Setup Required" button now lands on a page that
explains itself instead of silently bouncing to General.

Replace the exported path set with isSettingsPathListed, so the nav, the
search index and the tests all exercise one predicate.
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: de388f90-b15a-4370-838e-a7c4845bdda9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 9, 2026
triggerClassName="min-w-0 max-w-none shrink-0 text-foreground/90 hover:text-foreground"
onInstanceModelChange={(instanceId, model) => {
updateSettings({
textGenerationModelSelection: resolveAppModelSelectionState(

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.

🟡 Medium settings/ProviderSettingsPanel.tsx:786

The onInstanceModelChange handler for the text-generation model picker calls resolveAppModelSelectionState with the raw serverProviders array, but the picker's own instanceEntries are built from applyProviderInstanceSettings(...) to overlay settings-enabling onto streamed probe state. When a just-enabled instance's streamed snapshot still reports enabled: false, the picker shows it as selectable, but resolveAppModelSelectionState rejects it and silently persists a fallback instance/model instead of the user's selection. The resolver should receive the settings-overlaid entries or the call should avoid revalidating the already-selectable choice against stale probe state.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/components/settings/ProviderSettingsPanel.tsx around line 786:

The `onInstanceModelChange` handler for the text-generation model picker calls `resolveAppModelSelectionState` with the raw `serverProviders` array, but the picker's own `instanceEntries` are built from `applyProviderInstanceSettings(...)` to overlay settings-enabling onto streamed probe state. When a just-enabled instance's streamed snapshot still reports `enabled: false`, the picker shows it as selectable, but `resolveAppModelSelectionState` rejects it and silently persists a fallback instance/model instead of the user's selection. The resolver should receive the settings-overlaid entries or the call should avoid revalidating the already-selectable choice against stale probe state.

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.

fixed at the resolver level

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.

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

@macroscopeapp

macroscopeapp Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

1 blocking correctness issue found. This PR adds conditional settings visibility based on hosted app mode and relocates text-generation model settings. The medium-severity finding appears to be a false positive as the resolver overlays settings internally at line 297. Author is new to this codebase.

You can customize Macroscope's approvability policy. Learn more.

Every model picker builds its selectable instance list from
applyProviderInstanceSettings, which overlays the user's enable/disable
choices onto the streamed probe snapshot. resolveAppModelSelectionState
tested `enabled` on the raw snapshot instead.

Provider probes keep their previous `enabled` value for a moment after a
settings write, so during that window the picker offers a just-enabled
instance, the resolver rejects it, and a fallback instance and model get
persisted in place of the pick. Nothing surfaces the substitution.

Overlay settings in the resolver too, so it and the pickers agree on what
is selectable. Fixes both call sites, the text generation model on the
Providers page and the writer model in Source Control settings.
@inayayousfi

Copy link
Copy Markdown
Contributor Author

Worth noting this works toward #4564.

That issue asks for environment-owned settings to be editable by any authorized client, with client-owned settings staying with the client. The text generation model is environment-owned — every consumer reads it from its own server's settings.json — but it could only ever be written to the primary. Moving it onto the Providers page alongside the other per-device provider config lines it up with that split.

It does not close #4564. /settings/general still has several server-backed rows that only reach the primary.

@juliusmarminge

Copy link
Copy Markdown
Member

Closing as part of the open-PR backlog sweep (wave 1).

Reason: Settings visibility superseded by the settings reorg
Related: #9354

Reopen if this is still wanted and you’re willing to rebase onto current main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants