fix(delegate): always reload delegation config from disk, drop stale CLI_CONFIG cache (#18946)#18947
Open
yonefive71 wants to merge 1 commit into
Open
Conversation
…CLI_CONFIG cache `tools.delegate_tool._load_config()` consulted `cli.CLI_CONFIG` first and only fell through to a fresh disk read when CLI_CONFIG was empty. CLI_CONFIG is initialized once at `cli.py:600` and never refreshed, so `hermes config set delegation.<key> <value>` wrote config.yaml on disk correctly but had no effect on the running process — `delegate_task` kept using the previously-cached values until the gateway/CLI was restarted. The failure was silent: `hermes config set` reported success, config.yaml on disk was correct, but the running agent quietly kept running on stale config. This affected every `delegation.*` knob (model, provider, max_iterations, max_concurrent_children, reasoning_effort, base_url, api_key, etc.) since they all flow through the same _load_config() path. Fix: always read fresh from `hermes_cli.config.load_config()`. The disk read is cheap (config.yaml is small and only consulted at delegation boundaries — not on every API call). `cli.CLI_CONFIG` is preserved as a fallback only for test contexts where `hermes_cli.config` isn't importable but CLI_CONFIG has been mocked in directly. Tests: TestLoadConfigDiskFreshness covers the three branches — disk-fresh-wins-over-cached-CLI_CONFIG, fallback-to-CLI_CONFIG-when-disk-fails, both-sources-fail-returns-empty-dict. `pytest tests/tools/test_delegate.py` → 124 passed. Fixes NousResearch#18946
Collaborator
Collaborator
This was referenced May 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #18946.
hermes config set delegation.<key> <value>writes config.yaml on disk and reports success, but the change had no effect on the running process —delegate_taskcontinued using the previously-cached values until the CLI/gateway was restarted. Silent failure with no warning.The bug
tools/delegate_tool.py:2322—_load_config()consultedcli.CLI_CONFIGfirst and only fell through to a fresh disk read when CLI_CONFIG was empty:CLI_CONFIGis initialized once atcli.py:600(CLI_CONFIG = load_cli_config()) and never refreshed.hermes config setmutates the file on disk but has no IPC channel to notify the running process. Result: stale cache silently wins for the lifetime of the process.This affected every
delegation.*knob (model, provider, max_iterations, max_concurrent_children, reasoning_effort, base_url, api_key, inherit_mcp_toolsets, etc.) since they all flow through the same_load_config()path.Fix
Always read fresh from
hermes_cli.config.load_config(). The disk read is cheap — config.yaml is small and only consulted at delegation boundaries (i.e. when a subagent is spawned), not on every API call.cli.CLI_CONFIGis preserved as a fallback only for test contexts wherehermes_cli.configisn't importable but CLI_CONFIG has been mocked in directly. The order is inverted from before: disk first, in-memory cache second.Tests
tests/tools/test_delegate.py::TestLoadConfigDiskFreshness— three cases:test_disk_changes_visible_after_initial_load— the headline regression: disk holds new value, CLI_CONFIG holds old value,_load_config()returns the new valuetest_falls_back_to_cli_config_when_disk_read_fails— preserves test-context behaviour where CLI_CONFIG is mocked buthermes_cli.config.load_configraisestest_returns_empty_dict_when_both_sources_fail— defensive: both sources fail → returns{}, doesn't propagate exceptionVerified the empty-CLI_CONFIG fallback path implicitly through the existing 121 tests in
TestDelegationCredentialResolution/TestDelegationProviderIntegration/ etc., which all mock_load_configdirectly and therefore aren't affected by the implementation change.Full test run:
Notes for reviewers
_load_config()call is well within budget — delegation is invoked at subagent-spawn time (a heavy operation involving network roundtrips, agent construction, and tool schema enumeration). One additional YAML load adds microseconds to an operation that already takes hundreds of milliseconds minimum.cli.CLI_CONFIGdirectly (nothermes_cli.config.load_config) is preserved by the fallback — seetest_falls_back_to_cli_config_when_disk_read_fails. The 121 existing tests in this file mock_load_configitself, so they're indifferent to the change.Fixes #18946