fix(rebuild): refresh stale agent primary model on config restore (#7210) - #7216
Conversation
) Switching a managed model on a sandbox that has MCP servers cannot take the generic onboard recreate path (recreate is refused to preserve MCP state), so the switch is completed via `rebuild --yes --tool-disclosure progressive`. After that rebuild the gateway route, the vLLM backend, and `status` all show the new model, but `openclaw.json` -> `agents.defaults.model.primary` (and `openclaw agents list`) still show the OLD model. Root cause: the rebuild config-restore merge treats the whole `agents` section as backup-durable, so `agents.defaults.model.primary` is inherited from the pre-switch backup snapshot even though `models.providers` routing was already refreshed from the registry. Restore never reconciled the agent's model routing reference against the fresh rebuild. Re-own the primary model routing from the fresh config after the merge: rewrite `agents.defaults.model.primary` and the matching main/default `agents.list[]` model from the freshly generated config, and only when the fresh config carries a primary. Other agent config (durable tuning, customAgents, and intentional non-default per-agent model pins) is still inherited from the backup. This revives the approach proposed in the previously-closed #7063. Fixes #7210 Signed-off-by: Yanyun Liao <yanyunl@nvidia.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe restore merge now re-owns the agent primary model from the fresh configuration, updates the corresponding default routing entry, preserves backup-only settings and non-default pins, and retains backup routing when the fresh configuration omits a primary model. ChangesAgent routing reconciliation
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related issues
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Code Coverage OverviewLanguages: TypeScript TypeScript / code-coverage/pluginThe overall coverage in commit 66aed04 in the TypeScript / code-coverage/cliThe overall coverage in commit 66aed04 in the Show a code coverage summary of the most impacted files.
Updated |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/lib/state/openclaw-config-merge.ts`:
- Around line 487-489: Remove the repository-local retirement ledger comment
describing the temporary removal condition from the surrounding
configuration-merge code. Do not replace it with another migration-status or
completion note; track the retirement externally in the linked GitHub issue or
PR.
- Around line 468-470: Update the default-agent selection loop so `defaultAgent`
is assigned only when `entry.default === true` and `typeof entry.model ===
"string"`, ensuring later valid default entries remain eligible. Add a
regression test covering configuration without a `main` agent where an
invalid-model default precedes a routable default, and verify the latter
receives `primaryModelRef`.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: ef6babc5-8ffc-4526-9265-b3606feecadc
📒 Files selected for processing (2)
src/lib/state/openclaw-config-merge.test.tssrc/lib/state/openclaw-config-merge.ts
PR Review Advisor — InformationalAdvisor assessment: Informational / high confidence Model lanes
Nemotron output stays in workflow artifacts and does not change the assessment above. E2E guidanceAdvisory only. E2E / PR Gate selects and runs jobs independently. Recommended E2E: 3 optional E2E recommendations
This automated review informs maintainers. Warnings and suggestions do not require a response. A maintainer decides whether to merge. |
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
…build Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
ericksoa
left a comment
There was a problem hiding this comment.
Maintainer review complete on exact head 66aed04 against current main a5ccdca. Reviewed the config-restore ownership fix and regression coverage; 29/29 focused local tests passed after the latest base refresh, both advisor lanes reported no findings, CodeRabbit has no unresolved major/critical findings, and all required exact-head CI/E2E gates are green.
<!-- markdownlint-disable MD041 --> ## Summary Add the canonical dated changelog entry for NemoClaw `v0.0.89` before the release plan captures the tag commit. The entry summarizes the user-visible Station preparation, inference, recovery, policy-disclosure, and CLI-containment changes merged since `v0.0.88`. ## Changes - Add `docs/changelog/2026-07-20.mdx` with the exact `## v0.0.89` release heading, parser-safe SPDX comment, summary, and detailed bullets. - Link each shipped theme to the most specific published OpenClaw documentation routes. - Keep internal E2E, advisory-registry, and review-workflow refactors out of the user-facing release summary. Source summary: - #7214, #7241, #7237, #7223, #7204, #7202, #7183, and #7090 -> `docs/changelog/2026-07-20.mdx`: Summarize qualified DGX Station identity, package-state, PackageKit, DKMS, and reboot-handoff fixes. - #7242, #7221, #7186, #7164, and #6874 -> `docs/changelog/2026-07-20.mdx`: Summarize inference endpoint provenance, provider attachment, managed vLLM cleanup and progress, and Ollama selection guidance. - #7225, #7216, #7192, #7136, #7096, and #6910 -> `docs/changelog/2026-07-20.mdx`: Summarize sandbox readiness, recovery guidance, rebuilt model routing, durable Hermes state, and gateway restart behavior. - #7187 -> `docs/changelog/2026-07-20.mdx`: Summarize complete effective messaging-preset egress disclosure before policy mutation. - #7218, #7165, and #7184 -> `docs/changelog/2026-07-20.mdx`: Summarize structured output containment, terminal-safe route display, and Hermes forward cleanup. ## Type of Change - [ ] Code change (feature, bug fix, or refactor) - [ ] Code change with doc updates - [x] Doc only (prose changes, no code sample modifications) - [ ] Doc only (includes code sample changes) ## Quality Gates - [ ] Tests added or updated for changed behavior - [ ] Existing tests cover changed behavior — justification: - [x] Tests not applicable — justification: This PR adds release-history prose only; the focused changelog contract test validates its required structure and routes. - [x] Docs updated for user-facing behavior changes - [ ] Docs not applicable — justification: - [ ] Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging) - [ ] Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: - [ ] Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue: ## Verification - [x] PR description includes a `Signed-off-by:` line and every commit appears as `Verified` in GitHub - [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or `npm run check:diff` passed when hooks were skipped or unavailable - [x] Targeted behavior tests pass for the current change set, or tests are marked not applicable above — `mise exec node@22.23.1 -- npx vitest run test/changelog-docs.test.ts` (6 passed). - [ ] Applicable broad gate passed — `npm test` for broad runtime/test-harness changes; `npm run check` for repo-wide validation/coverage changes — not applicable to this doc-only release entry. - [x] Quality Gates section completed with required justifications or waivers - [x] No secrets, API keys, or credentials committed - [ ] `npm run docs` builds without warnings (doc changes only) — `mise exec node@22.23.1 -- npm run docs` completed with 0 errors and 2 existing site-wide warnings. - [x] Doc pages follow the [style guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md) (doc changes only) - [ ] New doc pages include SPDX header and frontmatter (new pages only) — the native changelog entry uses the required parser-safe MDX SPDX comment and does not use frontmatter. --- Signed-off-by: Aaron Erickson <aerickson@nvidia.com> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Expanded DGX Station installation support for qualified GB300 and OTA-upgraded environments. * Preserved selected inference providers, endpoints, model pins, and tuning settings during sandbox creation and rebuilds. * Improved sandbox recovery by validating availability and stability before restarting services. * Added clearer policy output showing the complete effective messaging egress configuration. * **Bug Fixes** * Hardened status and inference route displays by sanitizing terminal control characters. * Improved Hermes uninstall behavior by stopping detached dashboard forwarding. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Summary
Switching a managed model on a sandbox that has MCP servers completes via
rebuild --yes --tool-disclosure progressive(the generic onboard recreate is refused to preserve MCP state). After that rebuild the gateway route, the vLLM backend, andstatusshow the new model, butopenclaw.json→agents.defaults.model.primary(andopenclaw agents list) stay on the OLD model. This PR re-owns the agent's primary model routing from the fresh rebuild.Closes #7210.
Reproduction
Reproduced on our DGX Spark aarch64 test host (GB10 GPU) with managed vLLM. The reporter's Station-only models (Nemotron-3-Ultra 550B, DeepSeek-V4-Flash) don't fit a GB10, so two Spark-servable managed profiles are used —
nemotron-3-nano-4b→qwen3.6-27b; the mechanism (config-restore merge, MCP-forced rebuild path) is model-agnostic.Steps:
onboardmanaged vLLM model A (nemotron-3-nano-4b).nemoclaw <sb> mcp add deepwiki --url https://mcp.deepwiki.com/mcp --env DEEPWIKI_TOKEN.docker stop nemoclaw-vllm→onboardmanaged vLLM model B (qwen3.6-27b), reuse the sandbox → recreate is refused because MCP servers are present → run the directednemoclaw <sb> rebuild --yes --tool-disclosure progressive.Observed on
main(before fix) — route/backend new, agent binding stale:Observed on
fix/...(after fix) — agent binding follows the switch:Analysis
The rebuild config-restore merge (
src/lib/state/openclaw-config-merge.ts→mergeOpenClawRestoredConfig) overlays the backup onto the freshly generated config and then re-owns individual runtime sections (gateway/proxy/diagnostics,channels,models,plugins,tools). Butagentsis listed inbackupDurableSectionsand is inherited wholesale from the pre-switch backup snapshot — includingagents.defaults.model.primary. So even thoughmodels.providersrouting is refreshed from the registry (new model), the agent's own routing reference (agents.defaults.model.primary, and the main/defaultagents.list[].model) stays on the pre-switch model. Restore never reconciled that reference against the fresh rebuild. On the MCP-forcedrebuild --tool-disclosure progressivepath there is no subsequent onboard-recreate to rewrite the config, so the stale primary persists.Fix
After the section merges,
reconcileAgentPrimaryModelre-owns the agent's primary model routing from the fresh config: it rewritesagents.defaults.model.primaryand the matching main/defaultagents.list[].model(agent idmainwins, else the firstdefault: true, and only when itsmodelis a string routing reference) from the freshly generated config — and only when the fresh config carries a primary, so backups with no rebuild-owned routing are left untouched. All other agent config (durable tuning likethinkingDefault,customAgents, and intentional non-default per-agent model pins) is still inherited from the backup. AremovalConditionis documented on the helper. This revives the approach proposed in the previously-closed #7063.Tests lock the whole contract: the stale primary + main list model are re-owned to the fresh model; a non-default per-agent pin and durable tuning are left untouched; and a fresh config with no agent primary leaves the backup routing intact.
Changes
src/lib/state/openclaw-config-merge.ts: addagentPrimaryModelPathownership +reconcileAgentPrimaryModel(withreadAgentPrimaryModelRef/updateMainAgentListModel), called at the end ofmergeOpenClawRestoredConfig.src/lib/state/openclaw-config-merge.test.ts: cover the re-own contract and its boundaries.Type of Change
Verification
openclaw-config-merge.test.ts, 24 tests)npm run typecheck:cliandnpm run build:clipassrebuild --tool-disclosure progressive): before the fix the agent primary stayed on the old model; after the fix it follows the switch, consistent with the route/backend, and the MCP server survives the rebuild.AI Disclosure
Signed-off-by: Yanyun Liao yanyunl@nvidia.com
Summary by CodeRabbit