Skip to content

Keep in-session /model picks from leaking into the global default#12

Open
payne0420 wants to merge 1 commit into
OnlyTerp:mainfrom
payne0420:fix/preserve-global-model
Open

Keep in-session /model picks from leaking into the global default#12
payne0420 wants to merge 1 commit into
OnlyTerp:mainfrom
payne0420:fix/preserve-global-model

Conversation

@payne0420
Copy link
Copy Markdown
Contributor

Problem

Claude Code v2.1.153+ persists an in-session /model pick (pressing Enter in the picker) to the user-global settings file (~/.claude/settings.json, the model key). Under UltraCode the picker lists proxy-only ids (e.g. claude-composer, claude-gpt-5.5-codex), so picking one silently makes it your global default — and a plain claude run outside the proxy then fails, because the real Anthropic API doesn't recognize that id.

The launchers set the session model via --settings + --model (both session-scoped, never persisted), so the launcher itself never pollutes globals — but an interactive /model Enter-pick does, and there's no Claude Code flag/env to disable that persistence.

Fix

Both launchers now snapshot the global model key before launch and restore it on exit, so /model picks stay session-scoped:

  • bin/ultracodesnapshot_global_model before start_proxy; restore_global_model inside the existing stop_proxy EXIT trap.
  • windows/Start-UltraCode.ps1Save-GlobalModel / Restore-GlobalModel at the equivalent points.

Both do the JSON edit by shelling out to Python (already required by the launchers) rather than re-serializing the whole file, so only the model key is touched and the rest of settings.json is left intact. (On Windows this also avoids ConvertTo-Json collapsing single-element arrays in nested blocks like hooks.)

Behavior

  • Ref-count-safe across concurrent sessions: a clean start snapshots, a session that joins a running proxy keeps the existing snapshot (so a mid-session pick isn't captured as the "original"), and the last session out restores, then removes the saved-state file.
  • Restores the prior value, or removes the model key entirely if there wasn't one before.
  • Picking with /model still applies for the current session; it's just reverted globally on exit.
  • Opt-out: UC_PRESERVE_GLOBAL_MODEL=0 restores Claude Code's normal persist-globally behavior.
  • Native alternative for a one-off: press s in the /model picker (session-only, never saves) — documented too.

Docs

docs/TROUBLESHOOTING.md gains a section describing the symptom, the guard, and the escape hatch.

Testing

  • Round-trip verified for both cases: restores a prior model, and removes a model the session added when none existed before — permissions/hooks/other keys preserved.
  • bash -n bin/ultracode clean.
  • python3 test_proxy.py → ALL TESTS PASSED; python3 scripts/doctor.py → 17 ok, 0 failures.

No changes to proxy.py; this is launcher-only and independent of other open PRs.

🤖 Generated with Claude Code

Claude Code v2.1.153+ persists an in-session `/model` pick (Enter in the
picker) to the user-global settings file (`~/.claude/settings.json`, the
`model` key). Under UltraCode that means picking a proxy-only id (e.g.
`claude-composer`, `claude-gpt-5.5-codex`) silently becomes your global
default — and a plain `claude` run *outside* the proxy then fails, because
the real Anthropic API doesn't recognize that id.

Both launchers now snapshot the global `model` key before launch and restore
it on exit, so `/model` picks stay session-scoped:

- bin/ultracode: snapshot_global_model before start_proxy, restore_global_model
  inside the stop_proxy EXIT trap. The JSON edit shells out to Python so only
  the `model` key changes; the rest of settings.json is untouched.
- windows/Start-UltraCode.ps1: Save-GlobalModel / Restore-GlobalModel doing the
  same via Python (avoids ConvertTo-Json mangling single-element arrays in
  nested blocks like hooks).

Ref-count-safe across concurrent sessions: a clean start snapshots, a running
session keeps the existing snapshot, and the last session out restores (then
removes the saved-state file). Opt out with UC_PRESERVE_GLOBAL_MODEL=0, which
restores Claude Code's normal persist-globally behavior. To keep a pick for the
current session only without saving, press `s` in the picker instead of Enter.

docs/TROUBLESHOOTING.md documents the symptom, the guard, and the escape hatch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant