Skip to content

fix: prevent config dual-write conflict in gateway model switch (#37751)#37765

Closed
alaamohanad169-ship-it wants to merge 1 commit into
NousResearch:mainfrom
alaamohanad169-ship-it:auto-fix-37751
Closed

fix: prevent config dual-write conflict in gateway model switch (#37751)#37765
alaamohanad169-ship-it wants to merge 1 commit into
NousResearch:mainfrom
alaamohanad169-ship-it:auto-fix-37751

Conversation

@alaamohanad169-ship-it

Copy link
Copy Markdown
Contributor

Summary

Fixes the Desktop↔Gateway config dual-write conflict described in #37751 by replacing the full-dict save_config() call with atomic per-key save_config_value() calls in the gateway's _handle_model_command --global path.

Root Cause

When /model <name> --global is used in the gateway, the old code:

  1. Read the full config.yaml
  2. Mutated the model dict in memory
  3. Called save_config(cfg) which does a full YAML rewrite via atomic_yaml_write()

This full-dict rewrite can overwrite concurrent changes made by the Desktop UI (which uses regex field replacements), causing the contradictory config states described in #37751.

Fix

The CLI already avoids this problem by using save_config_value() (at cli.py:7802-7805) which calls atomic_roundtrip_yaml_update() — this updates only the requested dotted keys while preserving any other changes in the file.

This fix aligns the gateway with the same safe pattern:

  • save_config_value("model.default", result.new_model)
  • save_config_value("model.provider", result.target_provider) (only if provider changed)
  • save_config_value("model.base_url", result.base_url) (only if base URL changed)

The scalar model coercion (model: <name>model: {default: ...}) is handled internally by atomic_roundtrip_yaml_update(), so the explicit YAML parsing and dict manipulation code is no longer needed.

Testing

  • Verified the gateway /model <name> --global command path uses save_config_value instead of save_config
  • save_config_value is already tested in tests/cli/test_cli_save_config_value.py
  • CLI /model --global behavior is unchanged (still uses save_config_value)
  • Gateway picker flow (non-global session override) is unchanged

Closes #37751

@alt-glitch alt-glitch added type/bug Something isn't working comp/gateway Gateway runner, session dispatch, delivery area/config Config system, migrations, profiles P2 Medium — degraded but workaround exists labels Jun 3, 2026
@alaamohanad169-ship-it

Copy link
Copy Markdown
Contributor Author

🔧 Test failure fixed — was using from cli import save_config_value which uses the module-level _hermes_home symbol directly, bypassing the test's monkeypatched get_hermes_home\(). The new function was silently writing to the real ~/.hermes/config.yaml instead of the test's tmp_path.

Replaced with atomic_roundtrip_yaml_update() using get_config_path() from hermes_cli.config so test isolation works while still doing atomic per-key updates (preserving the dual-write fix from #37751).

✅ Local test run: 3/3 pass in test_model_command_flat_string_config.py, plus 48/48 across related gateway tests.

@alaamohanad169-ship-it alaamohanad169-ship-it marked this pull request as ready for review June 3, 2026 11:17
@alaamohanad169-ship-it

Copy link
Copy Markdown
Contributor Author

👋 Ready for review — just flipped from DRAFT. Fixes config dual-write conflict between Desktop & Gateway (#37751). Test bug was fixed (wrong path resolution in save_config_value — now uses atomic_roundtrip_yaml_update with get_config_path()). CI green, all 3 regression tests passing. Small change: +19/-30.

Replace full-dict save_config() with atomic per-key save_config_value()
in the gateway's _handle_model_command --global path. This eliminates
the race condition where Gateway's full YAML rewrite could overwrite
concurrent Desktop UI config changes (issue NousResearch#37751).

The CLI already uses save_config_value (atomic_roundtrip_yaml_update)
for model persistence (cli.py:7802-7805). This fix aligns Gateway with
the same safe pattern — only the requested dotted keys are updated,
preserving any concurrent edits made by another process.

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

Labels

area/config Config system, migrations, profiles comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Config] Desktop与Gateway双写冲突导致配置矛盾状态

2 participants