Skip to content

fix(macapp): setCost failure status erased by its own reload + deferred manual smokes from #991 #1020

Description

@dennisonbertram

Observed behavior

In macapp/Sources/GoCodeUI/ModelSettingsView.swift, ModelSettingsModel.setCost (lines 132–140) was not brought in line with the fix applied elsewhere in this same file for fetch/setExposed/setAllVisible/saveProvider/delete (epic #991, slice U8, PR TBD):

func setCost(_ provider: String, _ model: String, input: Double, output: Double) async {
    do {
        try await client.setModelCost(
            provider: provider, model: model, input: input, output: output)
        await load()
    } catch {
        status = "Could not save cost: \(error.localizedDescription)"
    }
}

The five siblings all call load(clearingStatus: false) on both branches so the message they just set survives the reload (see the load() doc comment at line 40). setCost does not: its success path calls plain load() (clearingStatus: true, the default), and its catch block sets status but never reloads at all. This is the same bug shape the U8 fix addressed for the other five actions — setCost just wasn't included, deliberately, to keep that slice's diff scoped to the findings it was written against.

Expected behavior

setCost should follow the same load(clearingStatus: false) pattern as its siblings on both the success and failure branches, so a status message it sets is not vulnerable to being cleared by a later default-clearing load() and its failure path still refreshes provider/model state.

Reproduction

Preconditions: Settings → Models, a provider with at least one model.
Exact action: Click a model's price, enter a value, Save; observe setCost's catch branch never calls load(), so on failure the provider list is not refreshed to pick up any server-side state change.
Reproduction rate: Deterministic by code inspection (ModelSettingsView.swift:132-140); not exercised against a live daemon in this pass.

Impact

Low severity, UI-consistency only — no data loss or security implication. Confusing feedback if a save fails silently-stale, and this is the last inconsistency in the load(clearingStatus:) pattern introduced for #999/U8.

Suspected seam

Owning file/type: ModelSettingsModel.setCost, macapp/Sources/GoCodeUI/ModelSettingsView.swift:132-140.
Sibling pattern to match: fetch/setExposed/setAllVisible/saveProvider/delete in the same file (all call load(clearingStatus: false)).

Regression test

Add a ModelSettingsModelTests case that sets a fake client to fail setModelCost, asserts model.status still reads the failure message after setCost returns, and — the part not yet covered — asserts providers was refreshed from the fake client's post-failure state (currently it is not, since no reload happens on the catch branch).

Deferred manual smokes from #991 (require a live app/daemon; not run in this pass)

  • Transcript autoscroll: scroll-up behavior during live streaming.
  • Failed-load retry with the daemon killed mid-session.
  • Delete/undo confirmation cancel paths (Sessions).
  • Force-rewind "Restore Anyway" path.
  • Prompt-history Up/Down navigation with a half-typed draft in the composer.
  • VoiceOver and keyboard-only navigation on the Sessions and Models panes.

Latent deferral: caret-aware prompt history (D1 in the plan)

macapp/Package.swift pins .macOS(.v14). Literal caret-position-aware history recall (the #998 wording) needs a SwiftUI TextSelection/caret binding, which is a macOS 15 API, or an NSTextView bridge (NSViewRepresentable). The shipped approximation (recall only when the draft is empty/unchanged) is documented as KTD-7 in the plan; true caret-awareness is out of scope until the platform floor moves or the composer is rebuilt on an NSTextView bridge.

Fix boundaries

In scope (for a follow-up fix): setCost's two branches, matching the sibling pattern; the regression test above.
Out of scope: the deferred manual smokes and the caret-awareness deferral are tracked here for visibility, not for this fix — they need either live-app access or a platform-floor decision, not a code change alone.

Related: #991

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions