Skip to content

fix(desktop): surface model requests the harness rejected - #4519

Open
mikececco wants to merge 1 commit into
block:mainfrom
mikececco:fix/unapplied-model-request
Open

fix(desktop): surface model requests the harness rejected#4519
mikececco wants to merge 1 commit into
block:mainfrom
mikececco:fix/unapplied-model-request

Conversation

@mikececco

Copy link
Copy Markdown

Problem

When a requested model has no byte-exact match in the harness catalog,
buzz-acp warns and starts the session on the agent's own default:

// crates/buzz-acp/src/pool.rs
"desired model {desired} not found in agent's available models — proceeding with agent default"

The only signal that reached Desktop was modelOverridden. That flag is false
in two different situations:

  1. no model was requested at all, and
  2. a model was requested and the harness rejected it.

reader.rs collapses both (if !model_overridden { … }), so the ModelPicker kept
showing the rejected pick as though it had taken effect. A wrong model was
indistinguishable from a right one, with nothing surfaced anywhere.

That matches the reports in #2692 ("picker persists but nothing applies it at
spawn", 3 independent repros), #4004 (composer-2.5[fast=false] runs as
fast=true), and #2265 (silent fallback when the default model needs credits).

Change

The harness now reports two additional fields in session_config_captured:

Field Meaning
requestedModel what was asked for, null when nothing was
modelApplied whether the harness accepted it

The config surface derives unappliedModelRequest from the pair, and the
ModelPicker renders <model> not applied (with a tooltip naming the fallback)
when the live session is not honouring the pick.

Both fields are purely additivemodel_overridden and all its existing
consumers are untouched. Harnesses predating the pair omit both, and a missing
requestedModel keeps those sessions out of the warning rather than
mislabelling them.

Why this PR also moves two blocks of code

desktop/scripts/check-file-sizes.mjs forbids any growth in files already
past the 1000-line cap, and both files this fix has to touch were over it. Per
the repo's own guidance ("split the file — never bump the limit"), each got one
mechanical extraction:

File Before Extracted to After
commands/agent_config.rs 1112 config_bridge/session_payload.rs (ACP payload parsing, beside the types it produces) 989
shared/api/types.ts 1032 shared/api/configBridge.ts (config-bridge types, re-exported) 956

Both moves are code-identical — no behaviour change. Reviewing them as pure
moves should be quick; happy to split them into a separate prep PR if you'd
rather land them independently.

Tests

Three regression tests in config_bridge/reader_tests.rs pin the distinction
that was missing — including the case that makes this non-trivial: a session
running the harness default because nothing was requested must stay silent,
even though model_applied is false there too.

unapplied_model_request_is_surfaced_when_the_harness_rejects_it
unapplied_model_request_is_none_when_the_request_was_applied
unapplied_model_request_is_none_when_no_model_was_requested

Green locally:

  • just desktop-check — pass (size ratchet included)
  • just desktop-test — 3923 pass, 0 fail
  • just desktop-tauri-test — 2109 pass, 0 fail
  • just desktop-tauri-clippy — clean
  • cargo test -p buzz-acp — 668 pass, 0 fail

UI note

The visible change is a single conditional label in ModelPicker.tsx, using the
same text-2xs text-warning treatment as the existing "restart to apply"
indicator next to it. I haven't attached a screenshot because the e2e mock
bridge doesn't populate unappliedModelRequest — I'd need to seed
buildMockConfigSurface to stage the state. Glad to add that plus a captured
screenshot if you'd like it before review.

Refs #2692, #4004, #2265

When a requested model has no byte-exact match in the harness catalog,
buzz-acp warns and starts the session on the agent's own default instead.
The only signal reaching desktop was `modelOverridden`, which is false
both when a request was rejected AND when no model was requested at all,
so the ModelPicker kept displaying the rejected pick as though it had
taken effect. A wrong model was indistinguishable from a right one, with
nothing surfaced anywhere.

The harness now reports `requestedModel` and `modelApplied` alongside
`modelOverridden` in `session_config_captured`. The config surface derives
`unappliedModelRequest` from that pair, and the ModelPicker renders
"<model> not applied" when the live session is not honouring the pick.

Both fields are purely additive; existing `model_overridden` consumers are
untouched. Harnesses that predate the pair omit both, and a missing
`requestedModel` keeps those sessions out of the warning rather than
mislabelling them.

Two mechanical extractions are included because
`desktop/scripts/check-file-sizes.mjs` forbids *any* growth in files
already past the 1000-line cap, and both files this fix must touch were
over it:

  - `commands/agent_config.rs` (1112) — ACP session-payload parsing moved
    verbatim to `config_bridge/session_payload.rs`, beside the types it
    produces. Now 989.
  - `shared/api/types.ts` (1032) — config-bridge types moved verbatim to
    `shared/api/configBridge.ts` and re-exported. Now 956.

Both moves are code-identical with no behaviour change.

Refs block#2692, block#4004, block#2265

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Mike Cecconello <mikececco2000@gmail.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