Dashboard: pick a per-task agent CLI in the n new-task flow - #399
Draft
tildesrc wants to merge 3 commits into
Draft
Dashboard: pick a per-task agent CLI in the n new-task flow#399tildesrc wants to merge 3 commits into
n new-task flow#399tildesrc wants to merge 3 commits into
Conversation
The per-task `agent_cli` override has had a full data path since #384/#385 (model, store, service, REST, client, MCP) but no way to set it interactively — the dashboard's `n` flow went repo -> workflow -> memo and always inherited the repo default. Insert a CLI step between workflow and memo, pre-selected to the repo's own default so the common path is still Enter/Enter/Enter-and-type. Picking the default sends `agent_cli=None` rather than a redundant explicit value, matching `resolve_agent_cli`'s contract. - `KNOWN_AGENT_CLIS` in `core/models.py`. The CLI name set is a control-plane concept (it drives the base-image variant + config mount), and `terminal`/`taskservice` can't import `container/cli` at runtime without breaking the determinism invariant, so the list is restated in the LLM-free core and guarded by a test. - `registered_agent_clis()` on the adapter registry — the registry is populated lazily, so a drift test comparing against `_REGISTRY` directly would pass vacuously. The new guard also pins the two long-duplicated `DEFAULT_AGENT_CLI` constants together. - `_OptionListModal` grows an `initial=` param; it had no way to set the starting highlight, which the pre-select needs. The repo default is named in the picker title rather than baked into an option label, since `ChoiceScreen` dismisses the option text. - Read the repo default from the `list_repos()` payload `action_new_task` already fetches, not `self._repo_clis` — that cache is only filled on a refresh pass and can be empty or stale at this point. - Swap four hardcoded `"claude"` literals in `dashboard.py` for `DEFAULT_AGENT_CLI`. The fake-adapter registration test now cleans up after itself; it was leaking `fake-cli` into the module-global registry, which would have made the drift guard order-dependent. Co-Authored-By: Claude <noreply@anthropic.com>
The separate ChoiceScreen step made task creation four screens deep and cost a keystroke even when the operator didn't care which CLI ran. Fold it into the memo modal as a labelled Select that starts on the repo's default, so the common path is back to repo -> workflow -> type -> enter and the CLI is right there when wanted. - MemoScreen takes `agent_clis` + `repo_default` and dismisses a 4-tuple, the added member being the picked CLI. Both default to empty, so the screen still stands alone with no dropdown. - Tab reaches the dropdown (TextArea's default tab_behavior is "focus"); picking hands focus back to the memo, so the next Enter submits rather than reopening the overlay. The refocus is deliberately unconditional — while the overlay is open, focus is on the overlay rather than the Select, so a has_focus guard never fires on the path that matters. - `_selected_cli` falls back to the first known CLI when a repo names one this build doesn't offer, since allow_blank=False needs a value that's really in the options. - Reverts the `initial=` param added to _OptionListModal last commit; with no CLI ChoiceScreen there is nothing left that pre-selects. The 12 flow tests lose the extra `enter` they grew last commit. New coverage for the dropdown: repo default shown and sent as no-override, an explicit override, every known CLI on offer, an unknown one rejected, an unofferable repo default, and the full tab -> pick -> focus-returns interaction (which caught the focus bug above). Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The per-task
agent_clioverride has had a full data path since #384/#385 (model, store, service, REST, client, MCP) but no way to set it interactively — the dashboard'snflow went repo → workflow → memo and always inherited the repo default.The memo modal now carries an agent CLI dropdown that starts on the repo's own default. Leaving it alone sends
agent_cli=Nonerather than a redundant explicit value, matchingresolve_agent_cli's contract, so task creation stays repo → workflow → type → Enter and the CLI is there when you want it.What's here
KNOWN_AGENT_CLISincore/models.py. The CLI name set is a control-plane concept (it drives the base-image variant + config mount), andterminal/taskservicecan't importcontainer/cliat runtime without breaking the determinism invariant — so the list is restated in the LLM-free core and guarded by a test.registered_agent_clis()on the adapter registry._REGISTRYis populated lazily by_load_builtin_adapters(), so a drift test comparing against it directly would have passed vacuously or depended on import order. The new guard also pins the two long-duplicatedDEFAULT_AGENT_CLIconstants (core/models.pyandcontainer/cli/base.py) together — a pre-existing hole.MemoScreentakesagent_clis+repo_defaultand dismisses a 4-tuple whose added member is the picked CLI. Both params default to empty, so the screen still stands alone with no dropdown.TextArea's defaulttab_behavioris"focus"); picking hands focus back to the memo so the next Enter submits rather than reopening the overlay. That hand-back is deliberately unconditional — while the overlay is open, focus sits on the overlay rather than theSelect, so ahas_focusguard never fires on the path that matters._selected_cli()falls back to the first known CLI when a repo names one this build doesn't offer, sinceallow_blank=Falseneeds a value that's genuinely in the options.action_new_taskalready fetches the repo dicts and threw everything but the id away; it now keeps theagent_clitoo.self._repo_cliswas the tempting source but it's only filled on a refresh pass, so it can be empty or stale at this point."claude"literals indashboard.pyswapped forDEFAULT_AGENT_CLI.The fake-adapter registration test now cleans up after itself — it was leaking
fake-cliinto the module-global registry, which would have made the drift guard order-dependent.Dropdown coverage: the repo default is shown and sent as no-override, an explicit override, every known CLI is on offer, an unknown one is rejected, an unofferable repo default degrades sanely, and the full tab → pick → focus-returns interaction.
Out of scope: a post-creation
set_agent_clioperation, and validatingRepo.agent_cliagainstKNOWN_AGENT_CLISin the repo form.Plan: the task's
plan.mdartifact.🤖 Generated with Claude Code