Skip to content

Qt-removal R7.5d follow-up: the composer must DISPLAY what it writes#153

Merged
dovvnloading merged 1 commit into
mainfrom
qt-removal/r7-5d-2-composer-display-parity
Jul 26, 2026
Merged

Qt-removal R7.5d follow-up: the composer must DISPLAY what it writes#153
dovvnloading merged 1 commit into
mainfrom
qt-removal/r7-5d-2-composer-display-parity

Conversation

@dovvnloading

@dovvnloading dovvnloading commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Completes #152, which wired the composer's Thinking Mode toggle to persist and re-apply to api_provider but left the display side reading a separate, unsynchronized field.

Problem

ComposerDocument kept its own reasoning_level (default "quick") while SettingsManager.get_ollama_reasoning_mode() defaults to "Thinking". The composer therefore showed the wrong mode until the user changed it:

value
persisted ollama_reasoning_mode Thinking
live api_provider.OLLAMA_REASONING_MODE Thinking
composer display Quick Mode (No CoT)

The user sees "Quick" while the model runs chain-of-thought, and selecting the already-active "Quick" is the only way to reach the displayed state.

The two surfaces also never notified each other: a composer change published only app-composer, leaving an open Settings dialog stale; a Settings change did not update the composer at all.

Change

  • ComposerDocument takes an optional reasoning_level_reader, wired by register_composer to the active provider's persisted getter (branching on is_local_llama_cpp_mode()). This matches legacy _reasoning() (graphlink_composer_bridge.py:464-474), which derived the level rather than storing one — the reason the Qt composer and Settings dialog could not diverge. The local field remains as the fallback when no SettingsManager is supplied, keeping register_composer(bus, counter) valid in tests.
  • Both reasoning intents cross-publish the other surface's topic, guarded by a new SessionBus.has_topic(). Cross-topic publishing is already used here (composer → token-counter, canvas → notification), but publish() raises UnknownTopicError for unregistered topics, which single-module test buses would hit.

Test plan

  • 880 backend tests (6 new: derivation from the persisted setting, llama.cpp-active branch, bare-document fallback, both cross-publish directions, has_topic guard)
  • 1204 frontend tests, 0 lint errors, typecheck and build clean
  • Burn-down gate unchanged at 152/84/68
  • Verified against a real ~/.graphlink/session.dat: startup label matches the persisted value; composer → settings publishes ['app-settings','app-composer']; settings → composer updates the composer's level
  • Browser check: composer shows "Thinking Mode (Enable CoT)" with Thinking persisted

An adversarial re-audit of the shipped R7.5d diff found the fix was half
a fix. The first pass wired the write path (composer toggle ->
SettingsManager -> api_provider) but left the composer's own private
reasoning_level field as the display source, so the two halves
disagreed.

That divergence was arguably worse than the original bug.
SettingsManager.get_ollama_reasoning_mode() defaults to "Thinking" while
DEFAULT_REASONING_LEVEL is "quick", so any user who had never touched
the setting - the default state, not an edge case - saw the composer
confidently report "Quick Mode (No CoT)" while every chat call really
ran with think=True. Reproduced against the real session.dat: persisted
Thinking, live OLLAMA_REASONING_MODE Thinking, composer displaying
quick. The first pass's own live browser check looked directly at that
wrong label and recorded it as evidence the increment worked.

Both sync directions were broken too, measured rather than assumed. A
composer change persisted correctly but published only app-composer, so
an open Settings dialog stayed stale. A Settings change did not move the
composer's label at all, and no amount of republishing could have fixed
it, because the composer read a private field rather than the shared
setting.

The fix removes the second source of truth instead of trying to keep two
in sync. ComposerDocument gains an optional reasoning_level_reader that
register_composer wires to the active provider's persisted getter,
branching on is_local_llama_cpp_mode() exactly as legacy's _reasoning()
did - legacy never stored a composer-local reasoning level either, which
is precisely why the Qt composer and Settings dialog could never
diverge. The private field survives only as the fallback for a bare
ComposerDocument() with no manager, keeping register_composer(bus,
counter) valid in unit tests.

Both reasoning intents now cross-publish the other surface's topic,
guarded by a new SessionBus.has_topic(). Cross-topic publishing is
already an established pattern here - the composer publishes
token-counter, the canvas publishes notification - but publish() raises
UnknownTopicError on an unregistered topic, which a focused
single-module test bus would hit.

Re-verified: all three reproductions now pass. Startup label matches
disk, composer -> settings publishes both topics, settings -> composer
moves the composer's level. Confirmed live in the browser as well - the
very reading that was wrong before now shows "Thinking Mode (Enable
CoT)" against a persisted Thinking.

6 new regression tests: derivation from the persisted setting, the
llama.cpp-active branch, the bare-document fallback, both cross-publish
directions, and the has_topic guard. 880 backend / 1204 frontend,
lint/typecheck/build clean, burn-down gate unchanged at 152/84/68.

Process note worth keeping: the first pass had a design-synthesis agent,
an adversarial reviewer, and a live browser drive, and all three missed
this. Each was scoped to "is the write path correct?" - the question the
increment posed - rather than "is the feature correct?". A bidirectional
binding needs both directions named in the review scope explicitly, or
the unexamined direction survives every check.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dovvnloading
dovvnloading merged commit d7751fe into main Jul 26, 2026
2 checks passed
@dovvnloading
dovvnloading deleted the qt-removal/r7-5d-2-composer-display-parity branch July 26, 2026 22:12
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