Skip to content

Standalone override commands (/planmode, /model, /reasoning) discard unrelated chat prefs fields #124

@nathanschram

Description

Description

The standalone commands /planmode, /model, and /reasoning create EngineOverrides objects with only the fields they care about, discarding all other existing override fields. This means toggling one setting silently clears unrelated settings.

Reproduction

  1. Open /config → toggle diff preview to ON → state shows diff_preview: true
  2. Run /planmode auto (5 seconds later)
  3. Check chat prefs state → diff_preview is now null

Confirmed via journalctl logs:

15:51:33 config.diff_preview.set chat_id=-5284581592 value=True
15:51:38 prefs.override.set chat_id=-5284581592 engine=claude model=None permission_mode=auto reasoning=None

The /planmode command at commands/planmode.py:68-72 created:

updated = EngineOverrides(
    model=current.model if current else None,
    reasoning=current.reasoning if current else None,
    permission_mode=mode,
)

This only preserved model and reasoning, discarding ask_questions, diff_preview, show_api_cost, and show_subscription_usage.

Affected commands (6 code paths)

Command File Lines Fields preserved Fields discarded
/planmode set commands/planmode.py 68-72 model, reasoning ask_questions, diff_preview, show_api_cost, show_subscription_usage
/planmode clear commands/planmode.py 87-91 model, reasoning ask_questions, diff_preview, show_api_cost, show_subscription_usage
/model set commands/model.py 138-141 reasoning permission_mode, ask_questions, diff_preview, show_api_cost, show_subscription_usage
/model clear commands/model.py 213-216 reasoning permission_mode, ask_questions, diff_preview, show_api_cost, show_subscription_usage
/reasoning set commands/reasoning.py 156-158 model permission_mode, ask_questions, diff_preview, show_api_cost, show_subscription_usage
/reasoning clear commands/reasoning.py 236-238 model permission_mode, ask_questions, diff_preview, show_api_cost, show_subscription_usage

Note: The /config sub-page handlers in config.py correctly preserve all fields — this bug was only in the standalone commands.

Impact

  • Any setting toggled via /config (diff_preview, ask_questions, show_api_cost, show_subscription_usage) gets silently cleared when the user runs /planmode, /model, or /reasoning
  • /model and /reasoning additionally discard permission_mode, so running /model set sonnet after /planmode on would clear plan mode
  • Users have no indication their settings were lost
  • This also caused the diff_preview gate in claude.py to appear broken — the gate code was correct but never saw diff_preview=True because /planmode had silently cleared it

Fix (applied on feature/github-hardening)

All 6 code paths now preserve all existing override fields when updating their own field. Example for /planmode:

updated = EngineOverrides(
    model=current.model if current else None,
    reasoning=current.reasoning if current else None,
    permission_mode=mode,
    ask_questions=current.ask_questions if current else None,
    diff_preview=current.diff_preview if current else None,
    show_api_cost=current.show_api_cost if current else None,
    show_subscription_usage=current.show_subscription_usage if current else None,
)

Also reverted temporary info-level debug logging in claude.py (diff_preview gate) back to debug level.

Tests

All 1563 tests pass (81% coverage). Existing tests for /config toggles, planmode, model, and reasoning commands all pass. Will verify end-to-end during staging dogfooding.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions