Release v0.51.295 — stage-3739/3742 (model-pick revert fix #3739 + session-status revert #3742) (#3743)
* fix: honor explicit model pick, suppress silent revert on cross-family selection (#3737) When a user changes the model in the composer dropdown and sends, _resolve_compatible_session_model_state previously had no way to distinguish an explicit user pick from stale session state. The profile-aware branch (v0.51.290, PR #3448) and the legacy block both rewrote bare cross-family models to the profile default, and the client unconditionally applied effective_model — silently discarding the user's choice. Backend: accept explicit_model_pick flag (default False) on _resolve_compatible_session_model_state. Guard both the profile-aware branch (routes.py:2024) and the legacy block (routes.py:2124) to skip cross-provider normalization when set. _handle_chat_start extracts the flag and passes it through. Frontend: consult _readPendingSessionModel (sessionStorage, 10-min window) to detect explicit picks and include the flag. Add a toast as defense-in-depth when the server still returns effective_model. Closes #3737 * fix: tighten explicit-pick detection and add regression tests (#3737) Greptile P2-1: compare model_provider in pending pick detection, not just model name, to avoid false-positive flag when the session provider changes between pick and send. Greptile P2-2: only show the defense-in-depth toast when an explicit pick was actually overridden — stale-session normalizations are expected behavior and should be silent. Add two regression tests for the profile-branch guard: - explicit_model_pick=True → cross-family model survives - explicit_model_pick=False → existing normalization preserved * revert(sidebar): remove manual session status labels (#3570) The manual per-session status labels (Todo / In Progress / Done) added in v0.51.284 (#3570) stored state only in browser localStorage keyed by session id, with no server-side backing — so labels did not persist across browsers or devices (a user who labeled sessions on one machine saw none after moving to a laptop). They also rendered as three flat top-level entries in the session context menu, crowding the root menu. Per maintainer decision, remove the feature entirely for now. It can be reintroduced later with proper server-side persistence and a less intrusive menu treatment. Removes: - JS state/cycle helpers + SESSION_MANUAL_STATUS_KEY (static/sessions.js) - context-menu status entries + sidebar status badge render - .session-manual-status* CSS (static/style.css) - session_status_* locale strings across all locales (static/i18n.js) Full suite: 8084 passed, 0 failed. ESLint runtime gate: clean. reverts #3570 * fix(#3737): keep explicit-pick marker until send consumes it (Codex catch) Codex found the explicit_model_pick flag never engaged in the normal flow: boot.js modelSelect.onchange cleared the pending-pick marker right after /api/session/update, so by the time send() ran _readPendingSessionModel returned null, _explicitPick was false, and the server's profile-provider branch still reverted the cross-family pick (the exact #3737 bug). The flag only worked in the rare race where send beat the session-update round-trip. Fix (Codex prescription): do NOT clear the marker in onchange; clear it in send() immediately after reading a matching pending pick, so it's consumed for that send only. onchange still RECORDS the pick (_rememberPendingSessionModel) — only the premature clear is removed. * test(#3737): lock client clear-timing wiring (onchange records, send consumes) Static source guards for the Codex clear-timing fix: onchange must record the pending pick and NOT clear it post-session-update; send() must consume (clear) it only after reading a matching _explicitPick, and send the flag only when truthy. Complements the author's resolver-level tests in test_provider_mismatch.py. * test(#3737): realign refresh-persistence test to the moved pending-pick clear The Codex clear-timing fix moved the pending-pick clear out of modelSelect.onchange into send() (consume-on-send). test_model_selection_records_pending_state_before_async_session_update asserted the OLD onchange-clears behavior (assert _clearPendingSessionModel in body). Updated to assert the NEW correct behavior (onchange must NOT clear it — it survives to send). The test's core refresh-survives invariant (marker recorded before the async session-update; reapplied on load) is unchanged and still passes; only the stale clear-location assertion is flipped. Not a regression-blessing: the refresh-survives feature is intact, the marker lifecycle is more correct. --------- Co-authored-by: John Doe <johndoe@example.com> Co-authored-by: nesquena-hermes <[email protected]>
This commit is contained in:
10 files changed
+155
-125
No files matched your search
@@ -1438,6 +1438,43 @@ def test_custom_namespace_model_always_preserved_on_custom_provider(monkeypatch)
|
||||
assert effective == "custom/my-local-llm"
|
||||
|
||||
|
||||
def test_explicit_pick_survives_profile_family_mismatch():
|
||||
"""When explicit_model_pick=True, a cross-family bare model survives
|
||||
the profile-aware normalization instead of being rewritten to the
|
||||
profile default (#3737)."""
|
||||
import api.routes as routes
|
||||
|
||||
effective, provider, changed = routes._resolve_compatible_session_model_state(
|
||||
"gpt-5.4-mini",
|
||||
None,
|
||||
profile_provider="anthropic",
|
||||
profile_default_model="claude-sonnet-4",
|
||||
explicit_model_pick=True,
|
||||
)
|
||||
|
||||
assert changed is False, "explicit pick must not be normalized"
|
||||
assert effective == "gpt-5.4-mini", "user's model must survive"
|
||||
assert provider == "anthropic", "profile provider context preserved"
|
||||
|
||||
|
||||
def test_explicit_pick_false_allows_profile_family_normalization():
|
||||
"""Without explicit_model_pick, the same cross-family model IS rewritten
|
||||
to the profile default (existing behavior, must not regress)."""
|
||||
import api.routes as routes
|
||||
|
||||
effective, provider, changed = routes._resolve_compatible_session_model_state(
|
||||
"gpt-5.4-mini",
|
||||
None,
|
||||
profile_provider="anthropic",
|
||||
profile_default_model="claude-sonnet-4",
|
||||
explicit_model_pick=False,
|
||||
)
|
||||
|
||||
assert changed is True, "stale model must be normalized"
|
||||
assert effective == "claude-sonnet-4", "rewritten to profile default"
|
||||
assert provider == "anthropic", "profile provider context preserved"
|
||||
|
||||
|
||||
def test_stale_ui_js_does_not_inject_unavailable_option():
|
||||
"""renderSession() must no longer inject a bare (unavailable) option into
|
||||
modelSelect when the session model is not in the provider list (#829).
|
||||
|
||||
Reference in new issue
Block a user