fix(canvas#2594): surface env-derived model/provider hint + block Save until picked #2887
Reference in New Issue
Block a user
Delete Branch "fix/2594-canvas-config-effective-values"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes #2594
Env-resolved workspaces (MODEL secret empty, config.yaml blank) run fine because the runtime derives model/provider from persona env baked at provision. The Config tab used to show empty required PROVIDER/MODEL dropdowns, making the workspace look broken and allowing a confusing Save-with-blanks action.
Changes
sourcefield from GET /workspaces/:id/model and trackmodelSourcein ConfigTab state.unresolvedand no model is selected, show an info hint explaining that provider/model are derived from the runtime environment.Test plan
npx vitest run src/components/tabs/__tests__/ConfigTab.env-resolved.test.tsx src/components/tabs/__tests__/ConfigTab.*.test.tsx→ 28 passednpx tsc --noEmit→ no new errors in changed filesnpx eslinton changed files → cleanNote
The canvas cannot read the live container env directly, so this surfaces the derivation clearly and forces an explicit model selection before persisting. A future backend endpoint that exposes effective env-resolved values can replace the generic hint with concrete values.
Pure canvas/TypeScript change; no Go changes.
SOP checklist
npx tsc --noEmitandnpx eslintclean./modelsource field), security (no new auth/data exposure), performance (no extra fetches).source: unresolvedsignal to GET /model.5-axis review — APPROVE. head
d2a4e3a(#2594)sourcefield fromGET /workspaces/:id/model, with a sensible backward-compat fallback (source ?? (model ? "workspace_secrets" : "unresolved")) so an older backend that omitssourcestill behaves. The hint + Save-block both gate onmodelSource === "unresolved" && !currentModelId, which matches the ticket: env-resolved workspaces stop showing empty required dropdowns as if broken.canSave = isDirty && !modelUnresolved; both Save buttons switch to!canSave || saving. Once a model is picked,currentModelIdclears the block. Tests cover hint-render, Save-disabled, and Save-enabled-after-pick.sourcefield; no auth/secret/input surface.Model IDs in the test fixtures (
claude-sonnet-4-6,claude-opus-4-7) are mock data, not a production catalog — correctly not a concern.Non-blocking notes:
canSaveANDs!modelUnresolvedinto the global dirty check, so on an env-resolved workspace a user who edits an unrelated field (e.g. aconfig.yamlsetting) is blocked from saving until they pick a model — and picking one persists it, converting routing from env-derived to stored. The PR's own comment confirms an empty-model save is safe (handleSaveskips the empty/modelPUT), so the hard block is broader than the documented risk. Consider scoping the block to the model action only (or downgrading to a one-time confirm / inline warning) so unrelated config edits stay saveable without forcing a routing conversion. This is a UX/design call, not a bug — flagging for the author/PM.CI is green on the required contexts (
CI / all-required,CI / Canvas); the red is the qa/security/sop approval ceremony. Approving on merits — does what #2594 asks; note 1 is worth a quick product decision.