From f5bacee274e069ef094629a7cfe1a1778022bf7f Mon Sep 17 00:00:00 2001 From: loes5050 <88768844+loes5050@users.noreply.github.com> Date: Sun, 19 Jul 2026 00:48:53 -0700 Subject: [PATCH] feat(config): derive _KNOWN_ROOT_KEYS from DEFAULT_CONFIG + warn on unknown root keys Extracted from the config-validation portion of PR #67345 (the token-cost half was not salvaged). Unknown top-level config keys now warn (naming the key) instead of being silently ignored; known roots derive from DEFAULT_CONFIG.keys() plus a small extras set for valid-on-disk roots absent from defaults. --- hermes_cli/config.py | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/hermes_cli/config.py b/hermes_cli/config.py index aa3a527f1071..c01c45d6b883 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -5493,14 +5493,17 @@ def check_config_version() -> Tuple[int, int]: # Config structure validation # ============================================================================= -# Fields that are valid at root level of config.yaml -_KNOWN_ROOT_KEYS = { - "_config_version", "model", "providers", "fallback_model", - "fallback_providers", "credential_pool_strategies", "toolsets", - "agent", "terminal", "display", "compression", "delegation", - "auxiliary", "moa", "custom_providers", "context", "memory", "gateway", - "sessions", "streaming", "updates", "mcp_servers", +# Fields that are valid at root level of config.yaml. +# DEFAULT_CONFIG is the single source of truth for documented roots; keep this +# set derived so new defaults (skills, security, browser, …) are accepted +# automatically. A few optional/legacy roots are valid on disk but intentionally +# absent from DEFAULT_CONFIG (omitted when unused / alternate schema forms). +_EXTRA_KNOWN_ROOT_KEYS = { + "custom_providers", # legacy list form; modern equivalent is providers: {} + "fallback_model", # optional single dict or chain list; omitted when disabled + "mcp_servers", # MCP server definitions written by setup/tools flows } +_KNOWN_ROOT_KEYS = frozenset(DEFAULT_CONFIG.keys()) | _EXTRA_KNOWN_ROOT_KEYS # Valid fields inside a custom_providers list entry _VALID_CUSTOM_PROVIDER_FIELDS = { @@ -5655,16 +5658,28 @@ def validate_config_structure(config: Optional[Dict[str, Any]] = None) -> List[" " base_url: https://...", )) - # ── Root-level keys that look misplaced ────────────────────────────── + # ── Unknown / misplaced root-level keys ────────────────────────────── + # Typos like skillz:/secrity: were previously silent (only provider-like + # fields were flagged). Warn on any unknown top-level key so config + # hygiene surfaces without breaking startup. for key in config: if key.startswith("_"): continue - if key not in _KNOWN_ROOT_KEYS and key in _CUSTOM_PROVIDER_LIKE_FIELDS: + if key in _KNOWN_ROOT_KEYS: + continue + if key in _CUSTOM_PROVIDER_LIKE_FIELDS: issues.append(ConfigIssue( "warning", f"Root-level key '{key}' looks misplaced — should it be under 'model:' or inside a 'custom_providers' entry?", f"Move '{key}' under the appropriate section", )) + else: + issues.append(ConfigIssue( + "warning", + f"Unknown top-level config key '{key}' — it will be ignored", + "Check for typos, or remove the key if it is not a supported config root. " + "Run 'hermes doctor' for more detail.", + )) return issues