fix(agents): explain why Create/Save is disabled and fix Codex/Claude gate#2050
Conversation
f9d7521 to
08fdefa
Compare
wesbillman
left a comment
There was a problem hiding this comment.
The Codex/Claude correction is sound, but the new gate regresses the existing runtime-less legacy-definition path. Please use the already-derived provider-field capability (runtimeCanChooseLlmProvider, which includes blankRuntimeModelProviderEditable) rather than raw runtime support, and add coverage for a runtime-less edit before merge. The branch also needs rebasing onto current main and fresh CI after the fix.
| const customAiPairSatisfied = agentAiConfigurationModeSatisfied( | ||
| aiConfigurationMode, | ||
| { provider, model }, | ||
| runtimeSupportsLlmProviderSelection(runtime), |
There was a problem hiding this comment.
Blocking: this conflates "runtime drives its own provider" with "runtime is blank." Existing legacy/builtin definitions can intentionally edit model/provider with no runtime (blankRuntimeModelProviderEditable), and this dialog already exposes the provider field for that case via runtimeCanChooseLlmProvider. Passing raw runtimeSupportsLlmProviderSelection("") === false now makes Customize require only a model, so after choosing No preference (or editing a runtime-less definition) Save can persist provider: undefined despite the visible provider picker. Pass the actual field capability (runtimeCanChooseLlmProvider) here and to the reason input, and add a runtime-less edit regression test.
There was a problem hiding this comment.
🤖 (posted by Atish's AI agent) Fixed in 0b30d07. The three "provider is required" expressions — providerIsRequired, customAiPairSatisfied, and the submit-block reason input — now key off runtimeCanChooseLlmProvider (the same capability that governs whether the picker is shown) instead of the raw runtimeSupportsLlmProviderSelection(runtime). So a runtime-less legacy/builtin definition (blank runtime + saved model/provider, exposed via blankRuntimeModelProviderEditable) keeps requiring the visible provider, while Codex/Claude stay exempt (picker hidden). Added a runtime-less edit regression e2e (case 11 in global-agent-config-screenshots.spec.ts) that seeds no available runtime so the dialog does not auto-seed a runtime — verified it fails (Save enabled) without the change and passes with it — plus a policy unit test.
|
Code blocker resolved; merge gate still blocked on freshness. The new head correctly uses |
… gate The agent definition dialog only exposed a greyed-out Create/Save button with no indication of what was incomplete. Two related problems fed this: 1. `agentAiConfigurationModeSatisfied` always required a provider for a Customize pair, but Codex/Claude intentionally hide the provider picker (they drive their own provider). Editing such an agent left Save permanently disabled on a value the user could never set — Ian's "Save button stays disabled" regression. The check is now provider-aware: Buzz Agent/Goose still require provider+model, while Codex/Claude need only a model. 2. Nothing told the user why the button was disabled. A new pure module, `personaSubmitBlock`, maps the existing `canSubmit` gate OUTPUTS to a single, deterministic reason rendered next to the action. Precedence mirrors the `canSubmit` term order, so the reason is `null` exactly when the form can submit (transient Saving/Uploading states aside) and advances/disappears as the user fixes each input. Incomplete AI defaults name the actual missing pieces (provider/model/credential keys) and point to Settings → AI defaults; it is never reduced to a generic message. No gate/policy logic is duplicated in presentation code — the dialog passes the same gate results it already computes for `canSubmit`. Tests: focused policy coverage for the provider-aware seam, and full reason-mapping coverage for the pure module (including the assertion that the reason disappears once the blocking input is corrected). Playwright coverage in global-agent-config-screenshots.spec.ts now drives the real dialog footer: the blocked-create case asserts the visible provider/Settings reason, the enabled-create case asserts no reason, a CLI-login create case asserts the hidden provider does not block or explain, and a persona-linked Codex agent is opened in the actual Edit/Save path to prove the original defect is fixed (provider picker hidden, Save enabled, no reason). A narrow, test-only mock bridge extension lets a persona seed pin its runtime/model/provider. Co-authored-by: Atish Patel <atish@squareup.com> Signed-off-by: Atish Patel <atish@squareup.com> Co-authored-by: WorkerBeeGPT <c5c455215c2506cb8ba776518cec804af62d3a0526e32d496a22072e395042b9@buzz> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address wesbillman's blocking review: the Customize provider gate keyed off runtimeSupportsLlmProviderSelection(runtime), which is false for a blank runtime. But a runtime-less legacy/builtin definition still exposes the provider picker via blankRuntimeModelProviderEditable, so Save could persist `provider: undefined` despite the visible picker (also reachable after picking "No preference"). Gate the provider requirement on the field-visibility capability runtimeCanChooseLlmProvider instead — the same predicate that drives whether the picker is shown — for providerIsRequired, customAiPairSatisfied, and the submit-block reason. Codex/Claude stay unaffected (picker hidden -> provider not required), preserving Ian's original fix. Adds a runtime-less edit regression test (e2e case 11, seeded with no available runtime so the dialog does not auto-seed a runtime) plus a policy unit test; verified the e2e fails without the gate change. Co-authored-by: Atish Patel <atish@squareup.com> Signed-off-by: Atish Patel <atish@squareup.com> Co-authored-by: WorkerBeeGPT <c5c455215c2506cb8ba776518cec804af62d3a0526e32d496a22072e395042b9@buzz> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0b30d07 to
8a5ad09
Compare
Problem
Two related defects surfaced in the New/Edit Agent dialog:
agentAiConfigurationModeSatisfiedalways required a provider for a Customize pair, but Codex/Claude intentionally hide the provider picker (they drive their own provider). Save was gated on a value the user could never set.Both share a root UX gap: the dialog exposed only a grey button, never the reason.
Fix
agentAiConfigurationModeSatisfied(agentAiConfigurationPolicy.ts): newneedsProviderSelectionparam (defaultstrue, so existing callers are unchanged). The call site passesruntimeSupportsLlmProviderSelection(runtime)— Buzz Agent/Goose still require provider+model; Codex/Claude need only a model.personaSubmitBlock.ts, new pure module): maps the existingcanSubmitgate outputs to one deterministic, human-readable reason rendered next to Create/Save. Precedence mirrors thecanSubmitterm order, so the reason isnullexactly when the form can submit (transient Saving/Uploading aside) and advances/disappears as each input is fixed. Incomplete AI defaults name the actual missing pieces (provider/model/credential env keys) and point to Settings → AI defaults — never a generic "configuration incomplete".No policy logic is duplicated in presentation code: the dialog passes the same gate results it already computes for
canSubmit.Validation
desktopunit tests (node --test): 18 pass, 0 fail acrosspersonaSubmitBlock.test.mjs(new) andagentAiConfigurationPolicy.test.mjs(extended). Coverage includes: Buzz Agent + defaults missing provider/model shows the reason; Codex custom with model + empty provider is submittable; a valid form has no reason; the reason disappears once the blocking input is corrected; and reason precedence.tsc --noEmit: clean.biome check: clean.Rust pre-push hooks were skipped on push (change is JS-only); CI runs the full gate.
Remaining UX note
The reason is a single line derived by strict
canSubmit-order precedence — it names the first blocking input, not all of them at once. This is intentional (actionable + deterministic) and matches the acceptance criteria, but is worth a look during review.