fix(model-overrides): set liveModelSwitchPending when switching to default model with runtime fields mismatch#96318
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 24, 2026, 7:50 AM ET / 11:50 UTC. Summary PR surface: Source +8, Tests +33. Total +41 across 2 files. Reproducibility: yes. at source level: current main clears mismatched runtime fields but only sets Review metrics: none identified. 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. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Merge this or an equivalent helper-level fix, then verify the Do we have a high-confidence way to reproduce the issue? Yes at source level: current main clears mismatched runtime fields but only sets Is this the best way to solve the issue? Yes for the persisted runtime-field mismatch subcase: the helper already detects stale runtime identity, so feeding that fact into the existing pending-switch gate is the narrowest maintainable fix. It is not enough by itself to close the broader linked issue without gateway/live validation. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 1069c60e1e25. Label changesLabel justifications:
Evidence reviewedPR surface: Source +8, Tests +33. Total +41 across 2 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
|
…ult with runtime-only fields When a session's model comes from steering/fallback runtime fields (entry.modelProvider/entry.model) rather than explicit override fields, switching back to the default model via /model default would not set liveModelSwitchPending. The isDefault branch in applyModelOverrideToSessionEntry only sets selectionUpdated when it deletes override fields — but when no override fields exist, selectionUpdated stays false, preventing the liveModelSwitchPending flag from being set at the gate condition. Fix: after the runtime alignment check, set selectionUpdated when selection.isDefault and runtime fields are misaligned, so that liveModelSwitchPending is properly set for the pending live switch. Adds test coverage for this previously untested scenario. Related to openclaw#96269 Co-Authored-By: Claude <[email protected]>
fe76f64 to
3e5ae8a
Compare
|
Merged via squash.
|
…ult with runtime-only fields (openclaw#96318) When a session's model comes from steering/fallback runtime fields (entry.modelProvider/entry.model) rather than explicit override fields, switching back to the default model via /model default would not set liveModelSwitchPending. The isDefault branch in applyModelOverrideToSessionEntry only sets selectionUpdated when it deletes override fields — but when no override fields exist, selectionUpdated stays false, preventing the liveModelSwitchPending flag from being set at the gate condition. Fix: after the runtime alignment check, set selectionUpdated when selection.isDefault and runtime fields are misaligned, so that liveModelSwitchPending is properly set for the pending live switch. Adds test coverage for this previously untested scenario. Related to openclaw#96269 Co-authored-by: Claude <[email protected]>
…ult with runtime-only fields (openclaw#96318) When a session's model comes from steering/fallback runtime fields (entry.modelProvider/entry.model) rather than explicit override fields, switching back to the default model via /model default would not set liveModelSwitchPending. The isDefault branch in applyModelOverrideToSessionEntry only sets selectionUpdated when it deletes override fields — but when no override fields exist, selectionUpdated stays false, preventing the liveModelSwitchPending flag from being set at the gate condition. Fix: after the runtime alignment check, set selectionUpdated when selection.isDefault and runtime fields are misaligned, so that liveModelSwitchPending is properly set for the pending live switch. Adds test coverage for this previously untested scenario. Related to openclaw#96269 Co-authored-by: Claude <[email protected]>
Summary
/model defaultsilently fails to trigger a live model switch when the session is running a model obtained via steering or fallback (runtime-only fields). A missingselectionUpdatedmarker inapplyModelOverrideToSessionEntrypreventsliveModelSwitchPendingfrom being set in the isDefault path when no override fields exist to delete.entry.modelProvider/entry.model) rather than explicit override fields (providerOverride/modelOverride), executing/model defaultclears runtime fields but does not setliveModelSwitchPending. The gateway never initiates the pending model switch.selectionUpdated = truewhenselection.isDefaultand runtime fields are misaligned, so thatliveModelSwitchPendingis properly set.src/sessions/model-overrides.ts(5 lines of fix logic after runtime alignment block),src/sessions/model-overrides.test.ts(new test case covering the untested scenario)Change Type (select all)
Scope (select all)
Linked Issue/PR
Motivation
When a user switches to a different model via
/model <model>and then runs/model defaultto switch back, the model switch appears to silently fail —/statusstill shows the old model. This happens becauseliveModelSwitchPendingis not set on the session entry, so the gateway never performs the pending live model switch. The user is left with no feedback and no way to recover except using a third model as a workaround.Real behavior proof (required for external PRs)
Behavior addressed:
/model defaultdoes not trigger a live model switch when the current session's model is stored in runtime fields (steering/fallback path), causing a silent failure.Real environment tested: Linux 4.19.112, Node.js v22.11.0, branch
fix/live-switch-pending-default-96269, OpenClaw 2026.6.24Exact steps or command run after this patch:
Observed result after fix:
modelProvider,model) are correctly clearedcontextTokens(derived from stale runtime model) is clearedliveModelSwitchPendingis now correctly set totrue, enabling the gateway to perform the pending live model switchWhat was not tested: Full gateway E2E flow with real model provider (requires live model credentials); browser UI / mobile app behavior
Root Cause (if applicable)
In
src/sessions/model-overrides.ts, theisDefaultbranch (lines 42-57) only setsselectionUpdated = truewhen it deletes existingproviderOverride/modelOverridefields. When the session's model comes from steering or fallback runtime fields (entry.modelProvider/entry.model) — i.e., no override fields exist — the deletions are no-ops andselectionUpdatedstaysfalse. Later, theliveModelSwitchPendinggate at line 148 requiresselectionUpdated || profileUpdated, so the flag is never set.Provenance:
liveModelSwitchPending— theisDefaultbranch was designed around the assumption that override fields always existRegression Test Plan (if applicable)
src/sessions/model-overrides.test.ts— new test casesets liveModelSwitchPending when switching to default with runtime-only fieldsdirectly covers the bug scenarioUser-visible / Behavior Changes
After this fix, users who have switched away from their default model (via steering, fallback, or
/model) and then execute/model defaultwill see the model correctly switch. The silent failure no longer occurs.Security Impact (required)
Human Verification (required)
Compatibility / Migration
Best-fix Verdict
selectionUpdatedto the downstreamliveModelSwitchPendinggate. No architectural change is needed — just one conditional assignment in the correct location.selectionUpdated = trueunconditionally in theisDefaultbranch — rejected because this would setliveModelSwitchPendingeven when runtime is already aligned with the default, which is unnecessary and could cause spurious switches.AI Assistance 🤖
applyModelOverrideToSessionEntry; Provenance tracing through git blame; test gap analysis of existing test coverageRisks and Mitigations
Highest risk area: Minimal. The change is scoped to the specific condition
selection.isDefault && runtimePresent && !runtimeAligned— it only activates when the pre-existing code already determined that runtime fields are stale and cleared them. TheselectionUpdatedflag is an internal tracking variable scoped to this function invocation.Mitigation: The new test case directly exercises the activation path and the gate output. All existing tests continue to pass.
Related to #96269