diff --git a/hermes_cli/config.py b/hermes_cli/config.py index c67dd2d167e5..04e4887e645f 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -8645,6 +8645,20 @@ def _validate_config_key(key: str) -> tuple[bool, Optional[str]]: segments = key.split(".") top = segments[0] + + # ── Underscore-prefixed keys are internal/test markers ─────────── + # A leading underscore on the top-level segment (e.g. ``_test.shim_marker``) + # signals an intentionally non-schema, internal key. Test harnesses and + # tooling use these to write a deterministic marker into config.yaml + # without polluting the user-facing schema (see the Docker privilege-drop + # shim test, which writes ``_test.shim_marker`` to probe file ownership). + # Python's own convention treats a leading underscore as "private"; we + # honour that here so schema validation never blocks deliberately-internal + # keys. This is narrow: only the FIRST segment is checked, so a real typo + # like ``agent._max_turns`` still gets caught at the sub-key level. + if top.startswith("_"): + return True, None + known = _known_top_level_keys() # ── First-segment validation ───────────────────────────────────── diff --git a/tests/hermes_cli/test_set_config_value.py b/tests/hermes_cli/test_set_config_value.py index 9b8c3a90de71..86ba5aa3c921 100644 --- a/tests/hermes_cli/test_set_config_value.py +++ b/tests/hermes_cli/test_set_config_value.py @@ -587,3 +587,25 @@ class TestValidateConfigKey: if expected_in_suggestion is not None: assert suggestion is not None and expected_in_suggestion in suggestion, \ f"Expected suggestion to contain {expected_in_suggestion!r}, got {suggestion!r}" + + @pytest.mark.parametrize("key", [ + "_test.shim_marker", + "_internal", + "_test.nested.deep.marker", + "_x", + ]) + def test_underscore_prefixed_keys_are_accepted(self, key): + """Underscore-prefixed top-level keys are internal/test markers and + bypass schema validation. The Docker privilege-drop shim test writes + ``_test.shim_marker`` to probe config.yaml ownership; that must not + be rejected. (Regression: #34250 schema validation broke this.)""" + from hermes_cli.config import _validate_config_key + is_known, _ = _validate_config_key(key) + assert is_known, f"Expected underscore-prefixed {key!r} to be accepted" + + def test_underscore_only_first_segment_escapes(self): + """The underscore escape only applies to the FIRST segment. A real + typo in a sub-key (e.g. agent._max_turns) is still caught.""" + from hermes_cli.config import _validate_config_key + is_known, suggestion = _validate_config_key("agent._max_turns") + assert not is_known, "Sub-key typo under a known top-level key must still be flagged"