Skip to content

fix: persist /model selection in topics whose session is missing or stale - #169

Merged
PleasePrompto merged 1 commit into
PleasePrompto:mainfrom
ryuhaneul:fix/topic-model-switch-session
Jul 12, 2026
Merged

fix: persist /model selection in topics whose session is missing or stale#169
PleasePrompto merged 1 commit into
PleasePrompto:mainfrom
ryuhaneul:fix/topic-model-switch-session

Conversation

@ryuhaneul

Copy link
Copy Markdown
Contributor

Problem

In a topic that has no usable session yet, completing the /model wizard
reports success but the selection is persisted nowhere. Sessions are only
created by the message flow; the wizard's switch path records the target
only on an already-existing session object; and topic switches never touch
the global config by design. So in a brand-new topic (no message sent yet)
— or one whose session has gone stale via idle timeout, daily reset, or
the target provider's bucket reaching max_session_messages — the next
message resolves a fresh session from the global defaults and the user's
choice is silently discarded. Re-running /model doesn't help.

When the stored session was stale, the switch summary could also
advertise a "Resuming …" state belonging to a session the next message
was about to discard.

Fix

  • New SessionManager.resolve_session_target(key, *, provider, model) -> (session, created): atomically (under the manager lock) record the
    target on the session the next message will actually use — fresh
    sessions are retargeted in place, stale or missing ones are replaced by
    a fresh shell carrying the target. Freshness is judged against the
    target provider's bucket count (plus the usual idle/daily checks),
    matching what the next message-flow resolve will decide.
  • switch_model uses it for topic switches and recomputes the resume
    summary from the resolved session, so newly created or replaced shells
    no longer advertise stale resume state. DM/main-chat behavior is
    unchanged (global-config path).

Deliberately out of scope (pre-existing behavior, separate issue
candidates): the remaining unlocked full-file session writers
(resolve_session, reset_*) and update_session's caller-identity
write-back.

Tests

  • E2E through the real message path: wizard ms:r callback → a normal
    turn with no override asserts the CLI AgentRequest carries the
    selected provider/model (fresh, empty-topic, and target-stale cases).
  • Manager unit matrix: fresh retarget / stale shell / missing shell;
    current-fresh–target-stale and the reverse; deterministic two-order
    concurrency contract for the new method.
  • Global config/file/CLI default unchanged on topic switches; DM
    no-session path creates nothing; provider bucket deep-snapshot
    preservation; stale-resume suppression; existing switch tests intact.

4 files changed, +438/−7. Rollback = revert (no config/schema impact).

Merge notes: #166 replaces the adjacent kill line in
switch_model (this PR's kill mocks adapt on merge). #164
(per-session effort) overlaps the same sync region — when it lands,
the creation path should pass reasoning_effort (preserving a fresh
session's effort when None). #156 moves bucket keys to
session_bucket_key(provider, interactive=…) — the target-count
lookup adapts accordingly.

…tale

Normal message handling creates the topic session, but the /model wizard does not enter that message flow and previously had nowhere to persist an early selection.

Introduce a locked target resolver for this path; existing unlocked session writers remain separate debt outside this change.

Evaluate max-session freshness against the selected provider bucket used by the next normal message.

Recompute resume metadata from the resolved session so newly created or replaced shells do not advertise stale resume state.
@PleasePrompto
PleasePrompto marked this pull request as ready for review July 12, 2026 06:44
@PleasePrompto
PleasePrompto merged commit 379f19c into PleasePrompto:main Jul 12, 2026
PleasePrompto added a commit that referenced this pull request Jul 12, 2026
…olve_session_target

Review fixes on top of the #169 merge:

1. Effort persistence (reconcile with #164): resolve_session_target now takes
   reasoning_effort and writes it onto the created/retargeted session, so a
   topic /model that also picks an effort survives to the next message. The
   topic branch of switch_model passes the resolved effort through.

2. Per-provider staleness: when a session is stale only because the TARGET
   provider bucket hit max_session_messages, reset ONLY that bucket and keep
   the other providers' history instead of wiping the whole shell. Session-wide
   staleness (idle timeout / daily reset) still replaces the full shell.

3. DRY: factor the freshness primitives (_within_message_limit, _time_fresh)
   shared by resolve_session and resolve_session_target; drop the provider-
   mutating _is_fresh_for_target hack.

4. switch_model: extract _persist_switch_target/_SwitchContext to stay within
   complexity limits (no lint suppressions); keep #166's topic-scoped kills.

Tests: update the bucket-wipe assertions to per-bucket reset, add a genuine
idle-stale whole-shell test, add the codex-fresh + claude-count-stale -> /model
claude keeps codex case, and switch the topic mocks to kill_by_chat_topic.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PleasePrompto added a commit that referenced this pull request Jul 12, 2026
…ics)

Integrates ryuhaneul's resolve_session_target with review fixes on top:
per-session reasoning effort is persisted on the switch target (#164
compatibility), message-count staleness now resets only the target
provider bucket instead of wiping all bucket history, and the freshness
logic is shared with resolve_session instead of duplicated.

Closes #169
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants