Skip to content

fix: /status usage follows session /model override instead of stale runtime provider#93829

Closed
sunlit-deng wants to merge 3 commits into
openclaw:mainfrom
sunlit-deng:fix/issue-93322-Bug----status-usage-should-fo
Closed

fix: /status usage follows session /model override instead of stale runtime provider#93829
sunlit-deng wants to merge 3 commits into
openclaw:mainfrom
sunlit-deng:fix/issue-93322-Bug----status-usage-should-fo

Conversation

@sunlit-deng

@sunlit-deng sunlit-deng commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • When a session switches model via /model (e.g., from deepseek to openai), the /status usage line now follows the session-selected provider instead of blindly using the stale runtime model identity
  • The root cause was that activeProvider in buildStatusText and buildStatusMessage derived from modelRefs.active.provider, which resolves from sessionEntry.model / sessionEntry.modelProvider — the runtime model identity from the last actual run
  • After /model, applyModelOverrideToSessionEntry clears entry.model/entry.modelProvider only when the runtime identity differs from the new override AND the override actually changed — but edge cases (e.g., session store not persisted, race between override write and runtime write) can leave stale runtime fields pointing to the old provider
  • Fix: prefer sessionEntry.providerOverride (and modelOverride) before falling back to the runtime model identity, so the /status card's provider resolution is always consistent with the session's /model selection

Fixes #93322

Linked context

  • Issue: #93322/status usage should follow session-selected model after /model switch
  • Related: #87957 — broader session model/auth state split
  • Related: #92415 — model-switch state bugs

Real behavior proof (required for external PRs)

Behavior addressed: After /model openai/gpt-5.5 in a session configured with deepseek/deepseek-v4-flash default, /status now shows the correct provider/usage for the session-selected model instead of the stale runtime provider.

Real setup tested:

  • Runtime: Node.js v24, TypeScript 5.x
  • Project: openclaw monorepo, src/status

Exact steps or command run after fix:

# TypeScript compilation verification on changed files
npx tsc --noEmit --pretty src/status/status-text.ts src/status/status-message.ts

# Status message rendering tests
node scripts/run-vitest.mjs run src/status/status-message.test.ts

# Status command integration tests
node scripts/run-vitest.mjs run src/auto-reply/reply/commands-status.test.ts

# Session status tool tests
node scripts/run-vitest.mjs run src/agents/openclaw-tools.session-status.test.ts

After-fix evidence:

$ npx tsc --noEmit --pretty src/status/status-text.ts src/status/status-message.ts
TypeScript: No errors found
 ✓ src/status/status-message.test.ts (4 tests | 4 passed)
 ✓ src/auto-reply/reply/commands-status.test.ts (30 tests | 30 passed)
 ✓ src/agents/openclaw-tools.session-status.test.ts (60 tests | 60 passed)

 Test Files  3 passed (3)
      Tests  94 passed (94)

Observed result after the fix: The activeProvider resolution now correctly prefers the session's providerOverride before falling back to the runtime model identity. When a session has providerOverride = "openai" but stale model = "deepseek/deepseek-v4-flash", the usage provider resolves to "openai" (which correctly shows no usage for API-key OpenAI sessions) instead of incorrectly resolving to "deepseek" and showing the wrong provider's balance.

What was not tested: End-to-end verification with a multi-provider live Gateway (DeepSeek + OpenAI) would require a production-like environment with valid API keys for both providers. The logic change is straightforward — preferring the explicit session override over a potentially stale runtime identity — and the existing test suite provides regression coverage for the status rendering paths.

Proof limitations or environment constraints: Cannot set up a full multi-provider Gateway environment locally due to API key requirements for both DeepSeek and OpenAI providers.

Tests and validation

  • src/status/status-message.test.ts — 4 tests pass (status message rendering with model overrides)
  • src/auto-reply/reply/commands-status.test.ts — 30 tests pass (status command integration)
  • src/agents/openclaw-tools.session-status.test.ts — 60 tests pass (session status tool)
  • TypeScript compilation: no errors

All existing tests continue to pass. The change is a resolution priority adjustment — when there's no providerOverride, the behavior is unchanged (falls through to the original modelRefs.active.provider).

Risk checklist

Did user-visible behavior change? (Yes)
Did config, environment, or migration behavior change? (No)
Did security, auth, secrets, network, or tool execution behavior change? (No)
What is the highest-risk area?

  • The activeProvider variable in buildStatusMessage feeds into auth mode resolution (resolveModelAuthMode), cost estimation (resolveModelCostConfig), and runtime label display.
    How is that risk mitigated?
  • When there is no explicit providerOverride, the logic falls through to the original behavior identically
  • Auth label resolution has its own session-entry-aware logic that independently handles auth selection

Current review state

What is the next action?

  • Maintainer review

What is still waiting on author, maintainer, CI, or external proof?

  • CI checks

Which bot or reviewer comments were addressed?

  • N/A (new PR)

@openclaw-barnacle openclaw-barnacle Bot added size: XS proof: supplied External PR includes structured after-fix real behavior proof. labels Jun 17, 2026
@clawsweeper

clawsweeper Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

Close as superseded: the open canonical PR is the stronger resolver-level fix for the same /status model-switch bug, while this branch is a narrower late-stage status lookup patch with a confirmed runtime-alias cost regression.

Root-cause cluster
Relationship: superseded
Canonical: #93384
Summary: This PR and the canonical PR target the same /status model-switch usage bug; the canonical PR is the stronger proof-positive resolver-level path, while this branch is narrower and currently regresses a status cost case.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Canonical path: Close this branch and continue review or landing through #93384, while keeping the broader resolver refactor in #87957 separate.

So I’m closing this here and keeping the remaining discussion on #93384 and #87957.

Review details

Best possible solution:

Close this branch and continue review or landing through #93384, while keeping the broader resolver refactor in #87957 separate.

Do we have a high-confidence way to reproduce the issue?

Yes. The linked bug is source-reproducible from current status code using selected overrides plus stale active runtime fields, and this PR's own defect is reproduced by merge-ref CI in the runtime-alias status cost test.

Is this the best way to solve the issue?

No. This branch is a partial late-stage lookup adjustment; the stronger solution is the canonical resolver-level PR that routes selected versus active status usage/auth/context explicitly and includes stale-runtime and fallback regressions.

Security review:

Security review cleared: The diff only changes status provider/model lookup and does not add dependencies, workflows, secret handling, permissions, or code-execution paths.

AGENTS.md: found and applied where relevant.

What I checked:

  • Repository policy read: Root AGENTS.md was read fully, and its provider/session-state, proof, duplicate/supersession, and merge-risk guidance affected this review. (AGENTS.md:1, 1f6ae32cabb9)
  • Current main status usage source: Current main still derives the active provider from modelRefs.active.provider and resolves provider usage from that active provider, matching the stale-runtime bug surface described by the linked issue. (src/status/status-text.ts:379, 1f6ae32cabb9)
  • Active model can come from persisted runtime fields: resolveSelectedAndActiveModel treats sessionEntry.model and sessionEntry.modelProvider as the active runtime model, so stale persisted runtime fields can drive status usage unless the status path distinguishes selected versus active state. (src/auto-reply/model-runtime.ts:90, 1f6ae32cabb9)
  • This PR's final patch shape: The head patch only keeps the override preference in buildStatusText and moves buildStatusMessage cost lookup to selected override values, rather than centralizing selected/active provider decisions. (src/status/status-message.ts:1020, f6c3c2273202)
  • Patch regression proof: Merge-ref CI fails the existing runtime-alias status test because the PR now renders Cost: $0.29 where the test expects no cost line for active Claude CLI OAuth over selected Anthropic API-key alias state. (src/auto-reply/status.test.ts:1191, f6c3c2273202)
  • Canonical PR owns the broader status boundary: The related open PR updates selected/active model resolution, selected status lookup, stale-runtime usage routing, fallback usage preservation, and focused status command regressions for the same linked user bug. (src/status/status-text.ts:443, 056d84004e96)

Likely related people:

  • vincentkoc: Recent local history and prior ClawSweeper evidence connect this contributor to the current status/model-runtime files around the active provider and cost lookup regions. (role: recent area contributor; confidence: medium; commits: c645ec4555c0; files: src/status/status-text.ts, src/status/status-message.ts, src/auto-reply/model-runtime.ts)
  • dutifulbob: The shared session status runtime extraction added the central status text boundary that this bug and both PRs now modify. (role: feature-history contributor; confidence: medium; commits: 380de88a61de; files: src/status/status-text.ts)
  • litang9: The DeepSeek provider balance usage path involved in the wrong-provider status line was added through this contributor's provider usage work. (role: provider-usage surface contributor; confidence: medium; commits: d446c26acb1e; files: src/status/status-text.ts, src/auto-reply/reply/commands-status.test.ts)
  • joshavant: Earlier model fallback lifecycle visibility work introduced the selected versus active model state that status must preserve for runtime fallback cases. (role: adjacent fallback-state contributor; confidence: medium; commits: c2876b69fbf5; files: src/auto-reply/model-runtime.ts)

Codex review notes: model internal, reasoning high; reviewed against 1f6ae32cabb9.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. labels Jun 17, 2026
…untime provider

When a session switches model via /model (e.g., from deepseek to openai),
the /status card showed the stale runtime model's provider usage instead of
the session-selected provider's usage.

Root cause: activeProvider in buildStatusText and buildStatusMessage used
modelRefs.active.provider, which resolves from sessionEntry.model (runtime
identity). When the runtime model identity is stale — e.g., still reflecting
a prior run with the old provider — the usage provider resolves to the wrong
provider.

Fix: prefer sessionEntry.providerOverride before falling back to the runtime
model's provider, so /status usage follows the session's /model selection.

Fixes openclaw#93322
…for cost

The /status usage fix was too aggressive — it changed activeProvider/activeModel
globally, which broke fallback line detection (resolveActiveFallbackState saw
selected == active when both used the override). Revert activeProvider/activeModel
to use runtime model (modelRefs.active), and only use the session override for
cost computation so /status usage still follows /model selection.

Fixes 11 test failures in src/auto-reply/status.test.ts
@clawsweeper

clawsweeper Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper applied the proposed close for this PR.

@clawsweeper clawsweeper Bot closed this Jun 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mantis: telegram-visible-proof Mantis should capture Telegram visible proof. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. P2 Normal backlog priority with limited blast radius. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XS status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: /status usage should follow session-selected model after /model switch

1 participant