Skip to content

fix(macos): require explicit Provider selection in connection editor#30425

Closed
vellum-apollo-bot[bot] wants to merge 1 commit into
mainfrom
apollo/marina-provider-default-anthropic
Closed

fix(macos): require explicit Provider selection in connection editor#30425
vellum-apollo-bot[bot] wants to merge 1 commit into
mainfrom
apollo/marina-provider-default-anthropic

Conversation

@vellum-apollo-bot
Copy link
Copy Markdown
Contributor

@vellum-apollo-bot vellum-apollo-bot Bot commented May 12, 2026

What

Stops the macOS provider-connection editor from silently pre-selecting Anthropic when the user opens "New Connection." Users now have to make an explicit Provider pick before Save activates.

Why — Marina QA, 0.8.1 Desktop Cloud-hosted

Marina reported that an OpenRouter connection she created was labeled with the Anthropic tag. Her theory: the system was auto-detecting provider from the sk- key prefix. There is no such heuristic anywhere in the codebase.

Actual root cause sits in ProvidersSheet.beginCreate():

let provider = store.providerCatalog.first?.id ?? ""
editorDraft = ConnectionDraft(provider: provider)

providerCatalog.first is Anthropic (first entry in PROVIDER_CATALOG). So opening the editor silently seeded the dropdown to Anthropic. If a user pasted an OpenRouter / Fireworks / etc. key and hit Save without scanning the dropdown, the connection persisted as provider=anthropic with the foreign key stored at credential/anthropic/api_key. Downstream:

  • The row rendered with the Anthropic display tag (correct given the bad data)
  • The Profiles picker filtered the connection out when binding an OpenRouter profile (c.provider !== "openrouter")
  • Any inference call attempted against Anthropic's API would 401

How

Same shape as the isProviderMissing pattern on the Inference Profile editor:

  1. beginCreate() starts with an empty ConnectionDraft. Dropdown placeholder ("Select a provider…") makes the unfilled state visible. The dropdown's onChange already populates the credential ref + auth-type override on pick, so removing the pre-fill doesn't drop functionality.
  2. isProviderMissing + canSave computed properties expose the validation state.
  3. "Pick a provider" warning badge renders inline with the Provider label while empty. Same pattern as the "Pick a model" badge on InferenceProfileEditor.modelField.
  4. Primary footer button gets isDisabled: !canSave. Save can't fire until the dropdown is satisfied.
  5. commitEditor() belt-and-suspenders guard surfaces "Select a provider." inline if a future code path bypasses the disabled button (programmatic submit, keyboard shortcut). Daemon zod would reject provider="" anyway; this keeps the error message in the editor.

Edit-mode safety

editorProviderField only renders for .create state. The dropdown is hidden in .edit / .managedEdit. Both beginEdit and saveAsNewFromManagedEdit seed editorDraft.provider from the source row before flipping state, so canSave returns true on first frame for those paths. Existing connections render their saved provider tag unchanged.

Test

  • New connection: dropdown empty, Save disabled, warning badge visible → pick provider → Save activates → row persists with correct provider.
  • Edit existing: dropdown not rendered, Save active immediately (no regression).
  • Save-as-New from managed: provider pre-seeded from the source row, Save active.

Cherry-pick

cherry-pick-to-release label set for release/v0.8.1.

Related: companion bug #2 from same QA pass landed as #30413 (Provider picker on Language Model card).


Open in Devin Review

Marina QA on 0.8.1 Desktop Cloud-hosted: an OpenRouter connection was
labeled with the Anthropic tag. The reporter theorized that the system
auto-detected provider from the "sk-" key prefix — there is no such
heuristic anywhere in the codebase. The actual root cause sits in the
macOS provider-connection editor.

`ProvidersSheet.beginCreate()` pre-filled
`editorDraft.provider = providerCatalog.first?.id`. Anthropic is the
first entry in PROVIDER_CATALOG, so opening "New Connection" silently
selected Anthropic. If a user pasted an OpenRouter key (or any provider
other than Anthropic) into the API Key field and hit Save without
scanning the Provider dropdown, the row persisted as
`provider=anthropic` with the OpenRouter key stored at
`credential/anthropic/api_key`. Downstream, the connection rendered
the Anthropic display tag (correct given the bad row) and the Profiles
picker filtered it out when the user wanted to bind an OpenRouter
profile to it (since `c.provider !== "openrouter"`).

Fix — mirrors the `isProviderMissing` pattern that #30313 introduced
on the Inference Profile editor:

  1. `beginCreate()` starts with an empty `ConnectionDraft`. No
     pre-filled provider, no pre-filled credential ref. The dropdown
     placeholder ("Select a provider…") makes the unfilled state
     visible. Provider's `onChange` already populates the credential
     ref (`credential/<provider>/api_key`) and the auth-type override
     (Ollama → none) when the user picks a value, so removing the
     pre-fill doesn't drop functionality.

  2. New `isProviderMissing` + `canSave` computed properties expose
     the validation state to the view layer. `isProviderMissing` only
     trips in create mode — `beginEdit` pre-fills from
     `conn.provider`, and `saveAsNewFromManagedEdit` carries the
     managed row's provider into the clone, so neither path produces an
     empty draft.

  3. Provider field renders a "Pick a provider" warning badge inline
     with the label while `isProviderMissing` is true. Same affordance
     pattern as the "Pick a model" badge in
     `InferenceProfileEditor.modelField`.

  4. Primary footer button gets `isDisabled: !canSave` so the user
     can't fire Save until the dropdown is satisfied. The button stays
     enabled for legitimate edit paths (`beginEdit`,
     `saveAsNewFromManagedEdit`) because both seed a non-empty
     provider.

  5. `commitEditor()` gains a belt-and-suspenders guard that surfaces
     "Select a provider." inline if a future code path bypasses the
     disabled button (keyboard submit, programmatic Save, etc.). The
     daemon's zod schema would reject `provider=""` either way, but
     this keeps the error message in the editor where the user is.

Edit mode is unaffected: `editorProviderField` is only rendered for
`.create` state and the dropdown is hidden in `.edit` /
`.managedEdit`, so existing connections render their saved provider
tag as before. The Save-as-New flow seeds `editorDraft.provider` from
the source managed row before flipping to `.create`, so `canSave`
returns true on first frame.

Cherry-pick candidate for release/v0.8.1.
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

View 3 additional findings in Devin Review.

Open in Devin Review

}
// Provider intentionally left empty — the old pre-fill to
// `providerCatalog.first?.id` quietly defaulted to Anthropic and led
// users (e.g. Marina QA on 0.8.1) to paste an OpenRouter key and
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.

🔴 Comment includes real person's name "Marina QA", violating AGENTS.md generic-examples rule

The newly added comment at line 829 references Marina QA on 0.8.1, identifying a specific person. AGENTS.md's Generic Examples section explicitly prohibits this: "Never include personal user data — real names, emails, phone numbers, account IDs, or other identifying details of specific people — anywhere in the codebase. This covers code, tests, fixtures, documentation, comments, commit messages, and AGENTS.md files. Always use generic placeholders." The comment should use a generic placeholder like "a QA tester" instead of naming a real person.

Suggested change
// users (e.g. Marina QA on 0.8.1) to paste an OpenRouter key and
// users (e.g. a QA tester on 0.8.1) to paste an OpenRouter key and
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@vellum-apollo-bot
Copy link
Copy Markdown
Contributor Author

Closing as duplicate of #30423 — same branch (apollo/marina-provider-default-anthropic), same code at HEAD d3213c5ff9 (clean of PR-ref comments). Reopened #30423 since the branch force-push moved its HEAD onto the cleaned commit. Apologies for the noise.

@vellum-apollo-bot vellum-apollo-bot Bot reopened this May 12, 2026
@vellum-apollo-bot
Copy link
Copy Markdown
Contributor Author

Reopened. Tried to flip the canonical PR back to #30423 per Vargas, but GitHub rejected the reopen — once a force-push leaves a PR's recorded head_sha orphaned (no longer on the branch), the PR cannot be reopened. So this PR (d3213c5ff9, same branch, same code as #30423 at close-time + the PR-ref comment strip) stays as the live one. Sorry for the churn.

@vellum-apollo-bot
Copy link
Copy Markdown
Contributor Author

Closing — true dupe now. #30423 was merged at 4f64e9d (39a6da9 on main) at 19:44 UTC, landing the same fix. Opening a tiny followup to strip the two (PR #30313) source-comment refs that came along with the older SHA.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants