fix(control-ui): persist Set Default agent through config save#91457
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 23, 2026, 6:48 PM ET / 22:48 UTC. Summary PR surface: Source +15, Tests +181. Total +196 across 4 files. Reproducibility: yes. Current main wires Set Default to a draft-only helper without Review metrics: 1 noteworthy metric.
Stored data model Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Next step before merge
Security Review detailsBest possible solution: Land this focused Control UI persistence fix after normal maintainer gates, while keeping the visible-list UX and dedicated RPC direction in their separate items. Do we have a high-confidence way to reproduce the issue? Yes. Current main wires Set Default to a draft-only helper without Is this the best way to solve the issue? Yes. Composing the existing default-staging helper with AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against b08d901dd2fc. Label changesLabel justifications:
Evidence reviewedPR surface: Source +15, Tests +181. Total +196 across 4 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
The Agents page Set Default button only staged a config form draft via stageDefaultAgentConfigEntry and never called config.set, so the chosen default was silently dropped on refresh (impact:data-loss). Route the one-click action through a new setDefaultAgent controller helper that stages the canonical agents.list[].default flag and then persists via the shared saveAgentsConfig path. AI-assisted (Claude). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
595d54a to
f188d20
Compare
Summary
The Agents page Set Default button did not persist the chosen default agent: clicking it only staged a config form draft and never wrote it back, so the selection was silently discarded on refresh. This PR routes the one-click action through the canonical config-save path so the default actually sticks.
This addresses the default-persistence half of #57067 (labeled
impact:data-loss). The issue's other half — replacing the agent dropdown with a visible list to reduce navigation friction — is a separate UI change and will follow in its own PR, so this PR intentionally does notClosesthe issue.What did NOT change: the dropdown selector, the Agents page layout, the canonical default representation (
agents.list[].default), the config save/reload RPCs, or any other handler. No new config keys, no new source of truth, noagents.defaultIdreintroduced.Root Cause
onSetDefaultinui/src/ui/app-render.tscalled only:stageDefaultAgentConfigEntry(ui/src/ui/controllers/config.ts) mutates the in-memory config form draft (settingagents.list[].defaulton the target, clearing it elsewhere) but never sendsconfig.set. Every sibling Agents-page mutation that must persist goes throughsaveAgentsConfig(e.g.onConfigSaveatapp-render.ts:3098), which runssaveConfig(config.set) →loadAgents→ restores selection. The Set Default handler was the only one that staged without saving, so on the nextconfig.get/refresh the staged draft was dropped.The fix belongs at the controller boundary, not inline in the renderer: a new
setDefaultAgenthelper composes the existing canonical pieces (stage the canonical flag, then persist via the shared save path). The renderer handler becomes thin wiring.Verification (supplemental automated checks)
pnpm test ui/src/ui/controllers/agents.test.ts— 15 passed (incl. 2 newsetDefaultAgentcases).pnpm test ui/src/ui/controllers/config.test.ts ui/src/ui/views/agents.test.ts— 51 passed.ui/src/ui/e2e/agents-set-default-persistence.e2e.test.ts.pnpm tsgo:core/pnpm tsgo:test:ui,oxlint,oxfmt --check— all clean.Regression Test Plan
controllers/agents.test.ts):setDefaultAgentstages the canonicalagents.list[].defaultflag and persists it throughconfig.setwith the expected raw; and it issues no request when the target agent is absent from the config list.e2e/agents-set-default-persistence.e2e.test.ts): loads the Agents page with two agents, selects the non-default one, clicks Set Default, and asserts aconfig.setrequest is emitted carryingkimi'sdefault: true. This guard fails on the pre-fix code (noconfig.setis ever emitted).Real behavior proof
mainat9d9389b; the after environment used this PR branch at5d064f3. Both used isolated proof state dirs,gateway.auth.mode: "none"for local loopback screenshot access only, andOPENCLAW_SKIP_CHANNELS=1so chat channels did not start.main, the browser showedopenclaw_test_agentselected whilemainstill appeared as(default), and the page displayedYou have unsaved config changes.After the same UI action and refresh, reading the real Gateway config still showed no persisted default flag:[ { "id": "main" }, { "id": "openclaw_test_agent" } ]config.set, followed by reload/readback:Reading the real Gateway config after the browser refresh showed the chosen agent persisted as canonical
agents.list[].default:[ { "id": "main" }, { "id": "openclaw_test_agent", "default": true } ]openclaw_test_agent (default)and the Default button was disabled for that selected agent. The persisted config also survived the UI refresh because the real Gateway wroteopenclaw_test_agent.default: trueto the active config.openclaw_test_agentselected whilemain (default)remains the default and the UI reports unsaved config changes; The after-fix screenshot showsopenclaw_test_agent (default)still selected after refresh, with the Default button disabled for that selected agent.Merge risk
agents.list[].defaultrepresentation and the existingconfig.setpath. No migration, no new key, no removed fallback.impact:data-loss(this fixes the reported loss), Control UI. No product-decision or security implications for this half.Refs #57067