Skip to content

fix(web): keep disabled-model editing equally available in both editor surfaces - #718

Merged
Menci merged 1 commit into
mainfrom
fix/disabled-model-toggle-parity
Nov 13, 2026
Merged

Menci merged 1 commit into
mainfrom
fix/disabled-model-toggle-parity

Conversation

@Menci

@Menci Menci commented Nov 13, 2026

Copy link
Copy Markdown
Owner

Problem

In the upstream detail editor, an administrator could not enable or disable a model from the model list on the right, while the disabled-models combobox in the left sidebar edited the same field fine. The two surfaces of one setting had drifted apart.

Cause

Both controls were added their read-only flags by #626, each against a different restriction:

  • The model-list switch used permissionReadOnly || readOnly. Here readOnly is !manualModelsSupported(record), i.e. "the provider owns this catalog" — always true for Copilot, Codex and Claude Code. So the switch was read-only for exactly the kinds whose catalog can only be toggled, never restructured.
  • The sidebar combobox used immutable (editLevel !== 'full'), which is about editing the connection. That made the opposite case wrong too: a models-level editor could flip the switch but not the combobox.

Fix

modelsEditable(editLevel) in data.ts is now the single statement of "may this caller set which listed models are enabled": anyone who may edit the catalog, i.e. editLevel !== 'off'. Both surfaces read it.

The structural controls (add / edit-as-YAML / delete / model detail) still take the shape-based readOnly alongside it, so catalog-owned rows remain unaddable and uneditable. permissionReadOnly is gone — it was the second name for the same capability, which is what let the two derivations diverge.

Tests

New disabled-model-parity_test.tsx mounts the sidebar and the workspace in one form and asserts both surfaces agree — same aria-readonly, switch actually toggles when editable — for Full (provider-owned catalog), models-level and audit-only editors.

Reverting the fix makes two of its cases fail, so it observes the property the defect lived in: the old suite only asserted disabled, never readOnly, which is why this shipped. The two superseded workspace-interactions cases are removed rather than kept as duplicates.

Verification

  • pnpm exec vitest run over all 19 apps/web/__tests__/components/upstream-editor/* files — 117 passed
  • pnpm --filter @floway-dev/web run typecheck
  • pnpm exec eslint over the changed sources and tests

…r surfaces

The provider-owned-catalog kinds (Copilot, Codex, Claude Code) could not
toggle a listed model from the model-list switch: the switch folded in
`readOnly`, which for those kinds is always true because the upstream owns
the catalog and the rows cannot be added, removed or restructured. The
disabled-models combobox in the sidebar moved the same field for the same
editor, so the two disagreed.

The mirror-image case was broken too: the combobox was gated on Full
connection editability, so a models-level editor could flip the switch but
not the combobox.

Both controls now read one predicate, `modelsEditable`, which expresses the
actual requirement -- an editor who may change the catalog may set which
listed models are enabled. Structural controls keep the shape-based
restriction, so catalog-owned rows stay unaddable and uneditable.

The regression test mounts both surfaces in one form and asserts they agree
for Full, models-level and audit-only editors. It replaces the two
workspace-interactions cases, whose assertions only covered `disabled` and
missed the `readOnly` state this defect lived in.
@Menci
Menci merged commit ae258cd into main Nov 13, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant