Skip to content

fix(models): respect models.mode: "replace" in models list output#94722

Closed
ZOOWH wants to merge 3 commits into
openclaw:mainfrom
ZOOWH:fix/94705-models-list-replace-mode
Closed

fix(models): respect models.mode: "replace" in models list output#94722
ZOOWH wants to merge 3 commits into
openclaw:mainfrom
ZOOWH:fix/94705-models-list-replace-mode

Conversation

@ZOOWH

@ZOOWH ZOOWH commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Summary

When models.mode is set to "replace" in openclaw.json, the openclaw models list command should only show models from user-configured providers — not built-in catalog entries from openai, groq, cerebras, etc. that the user explicitly excluded.

The model picker (src/flows/model-picker.ts:148) and embedded backend (src/tui/embedded-backend.ts:126-136) already handle this correctly by short-circuiting to buildConfiguredModelCatalog({ cfg }) when mode is "replace". But the models list command code path never checked models.mode, so it always appended built-in catalog rows regardless of the config setting.

What changed

  • src/commands/models/list.source-plan.ts: When cfg.models?.mode === "replace", planAllModelListSources returns a registry-only source plan (no manifest/provider-index catalog rows). This prevents loading 231+ built-in catalog entries that would be filtered out anyway.
  • src/commands/models/list.row-sources.tsappendConfiguredModelRowSources (default list path): Skip appendAuthenticatedCatalogRows when mode is "replace". Configured rows + configured provider rows already cover all user-specified models.
  • src/commands/models/list.row-sources.tsappendAllModelRowSources (--all/provider-filter path): Skip all built-in catalog row sources (manifest, provider-index, provider-runtime, supplement) when mode is "replace". Only registry and configured rows remain, mirroring the model picker behavior.
  • src/commands/models/list.source-plan.test.ts: Two new tests verifying the replace-mode gate.

What did NOT change (scope boundary)

  • The model-picker and embedded-backend replace-mode handling — already correct.
  • The buildConfiguredModelCatalog function — unchanged.
  • The underlying catalog loading functions (loadModelCatalog, loadManifestModelCatalog, etc.) — unchanged, they still return full catalogs; the filtering is done at the orchestration level by simply not calling them in replace mode.

Linked context

Closes #94705

Real behavior proof

  • Behavior addressed: models.mode: "replace" now causes models list to only show user-configured providers, not built-in catalog entries.

  • Environment tested: Node v24.13.1 on Linux x86_64; imported real production functions planAllModelListSources and buildConfiguredModelCatalog.

  • Steps run after the patch: node --import tsx --no-warnings proof-models-list-replace.mjs

  • Evidence after fix: Terminal capture of console output from running node --import tsx:

    === Real behavior proof: models.mode replace filters models list ===
    
    --- Proof 1: planAllModelListSources returns registry-only plan in replace mode ---
    Result: { "kind": "registry", "requiresInitialRegistry": true, "manifestCatalogRows": 0, "providerIndexCatalogRows": 0 }
    ✅ PASS: replace mode returns registry-only plan with no built-in catalog rows.
    
    --- Proof 2: planAllModelListSources loads built-in catalogs in merge/default mode ---
    Result: { "kind": "registry", "requiresInitialRegistry": true, "manifestCatalogRows": 231, "providerIndexCatalogRows": 4 }
    ✅ PASS: merge mode loads built-in catalogs (registry-backed plan).
    
    --- Proof 3: buildConfiguredModelCatalog only returns user-configured providers ---
    Configured catalog providers: [ "deepseek", "xai" ]
    Configured catalog entries: 3
    ✅ PASS: buildConfiguredModelCatalog returns only user-configured providers (deepseek, xai), not built-in ones (openai, groq).
    
    --- Proof 4: planAllModelListSources in replace mode with provider filter ---
    Result: { "kind": "registry", "requiresInitialRegistry": true, "manifestCatalogRows": 0, "providerIndexCatalogRows": 0 }
    ✅ PASS: replace mode with provider filter returns registry-only plan, skips built-in manifest catalogs.
    
    --- Proof 5: Source code contains replace-mode gate checks ---
    list.row-sources.ts has replace-mode gate: true
    list.source-plan.ts has replace-mode gate: true
    ✅ PASS: Both source files contain replace-mode gate checks.
    
    === Proof complete ===
    
  • Observed result after the fix: All 5 proof checks pass. In replace mode, planAllModelListSources returns a registry-only plan with 0 built-in catalog rows (vs. 231 in merge mode). buildConfiguredModelCatalog correctly returns only the 2 configured providers (deepseek, xai), excluding all built-in providers.

  • What was not tested: Full openclaw models list CLI end-to-end output (requires a running gateway instance with auth configuration). Only the source-plan and catalog functions were tested directly. The row-sources gate (Proof 5) was verified via source code inspection.

  • Proof limitations or environment constraints: CMake 3.18 on this machine (< 3.19 required by node-llama-cpp), so pnpm install --frozen-lockfile fails at node-llama-cpp postinstall. Core dependencies (vitest, tsx) are installed and functional. No live gateway instance for end-to-end CLI testing.

Tests and validation

  • node_modules/.bin/vitest run src/commands/models/list.source-plan.test.ts — 10 tests pass (8 existing + 2 new replace-mode tests)
  • Real behavior proof script — 5 checks, all pass (see above)

Risk checklist

  • Did user-visible behavior change? Yesopenclaw models list in replace mode now only shows configured providers instead of all providers.
  • Did config, environment, or migration behavior change? No — the fix only affects display; runtime model selection (which already respected replace mode via the model picker) is unchanged.
  • Did security, auth, secrets, network, or tool execution behavior change? No
  • Highest-risk area: appendAllModelRowSources replace-mode gate — could incorrectly skip catalog rows that a user wants. Mitigation: the gate mirrors the model-picker logic which has been in production since the replace mode was introduced; merge/undefined mode (the default) is unaffected.
  • Risk mitigation: The fix is additive (gating) rather than structural. No existing catalog loading is removed; it is simply skipped in replace mode. The merge/undefined mode behavior is completely unchanged.

When models.mode is set to "replace", the openclaw models list command
should only show models from user-configured providers — not built-in
catalog entries from openai, groq, cerebras, etc.

The model picker and embedded backend already handle this correctly by
short-circuiting to buildConfiguredModelCatalog({ cfg }) when mode is
"replace". But the models list command's code path never checked
models.mode, so it always appended built-in catalog rows regardless
of the config setting.

Three changes:
1. list.source-plan.ts: When cfg.models?.mode === "replace", return a
   registry-only source plan (no manifest/provider-index catalog rows).
   This prevents loading 231+ built-in catalog entries that would be
   filtered out anyway.
2. list.row-sources.ts — appendConfiguredModelRowSources (default list
   path): Skip appendAuthenticatedCatalogRows when mode is "replace".
   Configured rows + configured provider rows already cover all
   user-specified models.
3. list.row-sources.ts — appendAllModelRowSources (--all/provider-filter
   path): Skip all built-in catalog row sources (manifest, provider-index,
   provider-runtime, supplement) when mode is "replace". Only registry
   and configured rows remain, mirroring the model picker's behavior.

Closes openclaw#94705

Co-Authored-By: Claude <[email protected]>
@openclaw-barnacle openclaw-barnacle Bot added commands Command implementations size: S triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 19, 2026
@clawsweeper

clawsweeper Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 18, 2026, 9:48 PM ET / 01:48 UTC.

Summary
The PR adds replace-mode gates to model-list source planning and row assembly so built-in catalog rows are skipped, plus source-plan tests.

PR surface: Source +19, Tests +39. Total +58 across 3 files.

Reproducibility: yes. at source level: current main appends authenticated catalog rows in the default model-list path without checking models.mode, and the linked issue provides concrete replace-mode config plus leaked-provider output. I did not run the live CLI because this review is read-only.

Review metrics: 1 noteworthy metric.

  • Full-Catalog Browse Paths: 1 explicit browse path narrowed. The diff changes --all and provider-filtered catalog browsing under replace-mode configs, which is a compatibility-sensitive CLI behavior.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #94705
Summary: This PR is a candidate fix for the linked replace-mode model-list issue; another open candidate PR exists but is not a safe superseding target because it remains unmerged and review-blocked.

Members:

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

Merge readiness
Overall: 🧂 unranked krab
Proof: 🦪 silver shellfish
Patch quality: 🧂 unranked krab
Result: blocked until stronger real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Preserve explicit --all and provider-filtered full-catalog browse behavior while keeping default replace-mode output configured-only.
  • [P1] Add redacted terminal output, logs, screenshot, or recording that exercises actual after-fix openclaw models list behavior for default and --all paths.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: The PR body includes terminal output from source-plan/configured-catalog helpers, but not actual openclaw models list output or an equivalent execution of the changed default and --all row-source paths. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P1] Merging as-is would make explicit openclaw models list --all and provider-filtered catalog browsing hide built-in catalog rows under replace-mode configs, despite docs reserving those paths for full-catalog diagnostics.
  • [P1] The supplied proof exercises source planning and configured catalog helpers, but not actual openclaw models list output or the changed default and --all row-source builders.

Maintainer options:

  1. Preserve Full-Catalog Browse (recommended)
    Keep the default replace-mode filter but let explicit --all and provider-filtered browsing continue through the existing manifest, provider-index, registry, and runtime catalog cascade.
  2. Redefine Replace-Mode Browse Deliberately
    Maintainers can intentionally make replace mode override --all, but that needs matching docs, tests, and product acceptance before merge.
  3. Pause For Command Proof
    Hold the PR until the contributor posts redacted terminal output, logs, or a recording that exercises the actual after-fix command path.

Next step before merge

  • [P1] Needs contributor or maintainer repair of the --all compatibility regression plus contributor-supplied real behavior proof; automation should not take over while external setup proof is the merge gate.

Security
Cleared: The diff only changes TypeScript command orchestration and tests; it does not add dependencies, workflows, secrets handling, install scripts, or package metadata.

Review findings

  • [P1] Preserve explicit full-catalog browsing — src/commands/models/list.source-plan.ts:87-88
Review details

Best possible solution:

Keep default replace-mode models list output configured-only, preserve --all and provider-filtered full-catalog browsing, then add regression coverage and real command-output proof.

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

Yes, at source level: current main appends authenticated catalog rows in the default model-list path without checking models.mode, and the linked issue provides concrete replace-mode config plus leaked-provider output. I did not run the live CLI because this review is read-only.

Is this the best way to solve the issue?

No. The default-list gate is the right boundary, but applying the same replace shortcut to explicit --all conflicts with the documented full-catalog browse contract.

Full review comments:

  • [P1] Preserve explicit full-catalog browsing — src/commands/models/list.source-plan.ts:87-88
    This replace-mode early return also runs for explicit --all and provider-filtered source planning. Current docs reserve openclaw models list --all and Gateway view: "all" for full-catalog diagnostics, so replace-mode users would lose that browse path; scope the suppression to default configured output unless maintainers intentionally change the public contract with docs and tests.
    Confidence: 0.88

Overall correctness: patch is incorrect
Overall confidence: 0.88

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 10f0588ee39b.

Label changes

Label justifications:

  • P2: The PR targets a real but limited model-list command display bug tied to provider/model visibility.
  • merge-risk: 🚨 compatibility: The diff changes documented models list --all catalog-browse behavior for existing replace-mode configurations.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🦪 silver shellfish and patch quality is 🧂 unranked krab.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The PR body includes terminal output from source-plan/configured-catalog helpers, but not actual openclaw models list output or an equivalent execution of the changed default and --all row-source paths. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +19, Tests +39. Total +58 across 3 files.

View PR surface stats
Area Files Added Removed Net
Source 2 52 33 +19
Tests 1 39 0 +39
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 3 91 33 +58

What I checked:

Likely related people:

  • steipete: Recent history includes authenticated catalog rows, restored provider catalog listing, and docs clarifying model-list source behavior. (role: recent area contributor; confidence: high; commits: 9d7f83b17504, d5c094f1691a, 56c4f9761c75; files: src/commands/models/list.row-sources.ts, src/commands/models/list.source-plan.ts, docs/cli/models.md)
  • shakkernerd: History shows provider-filtered model-list fixes that define the cascade this PR bypasses in replace mode. (role: earlier source-plan contributor; confidence: high; commits: 4e4f9204d75c, 2c1be64d977d, 8ac10cf1645d; files: src/commands/models/list.source-plan.ts, src/commands/models/list.row-sources.ts)
  • fuller-stack-dev: Recent history added the live provider model catalog helper used by provider/runtime catalog planning paths affected by the replace-mode bypass. (role: recent provider catalog contributor; confidence: medium; commits: 57e0bdaabe0a; files: src/commands/models/list.source-plan.ts, src/commands/models/list.provider-catalog.ts)
  • vincentkoc: Local blame and GitHub history touch the current source-plan, row-source, and docs surfaces involved in this review. (role: recent model visibility contributor; confidence: medium; commits: 6c83e8e7e419, a48e5091cba7; files: src/commands/models/list.source-plan.ts, src/commands/models/list.row-sources.ts, docs/cli/models.md)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. labels Jun 19, 2026
@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 19, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Jun 19, 2026
@ZOOWH

ZOOWH commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

Closing as superseded by #82638 (platinum hermit, proof: sufficient, ready for maintainer look). The canonical fix targets the provider discovery layer (resolveProvidersForModelsJsonWithDeps early-return on mode=replace), not the display layer.

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

Labels

commands Command implementations merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. 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: S 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.

models.mode: "replace" does not filter openclaw models list output

1 participant