From 49aaa52d33d6aaa379ad372c08b2ebf4c7997a28 Mon Sep 17 00:00:00 2001 From: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com> Date: Thu, 24 Sep 2026 13:57:46 +0530 Subject: [PATCH] fix(config): keep v41 off the unversioned ladder and drop the stamp read guard v41 walks every profile SOUL.md and deletes any "## Messaging other agents" section on a bare heading match. A config.yaml with no _config_version says nothing about where that SOUL text came from, so an unstamped current config must not authorize rewriting a user-owned file; the versioned 40->41 path is unchanged. The unversioned regression test now seeds a user-authored section under that heading and asserts it survives. has_version_stamp() loses its bare except: migrate_config() already called check_config_version(raise_on_parse_error=True) and the docker script returns early on the (latest, latest) a parse failure yields, so the raw read cannot fail at this point. Co-authored-by: JoaoMarcos44 --- hermes_cli/config_migrations.py | 12 ++++++------ .../hermes_cli/test_config_unversioned_migration.py | 4 ++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/hermes_cli/config_migrations.py b/hermes_cli/config_migrations.py index a4f1eff099..9eb21eb90e 100644 --- a/hermes_cli/config_migrations.py +++ b/hermes_cli/config_migrations.py @@ -39,11 +39,9 @@ def support_floor_message() -> str: def has_version_stamp() -> bool: """Whether config.yaml carries a ``_config_version`` key. ``check_config_version()`` reads a missing one as 0, but such a file is never-stamped current-schema content, not an ancient - install: the floor must not refuse it and only :data:`LEGACY_KEY_STEPS` may run on it.""" - try: - return "_config_version" in _cfg().read_user_config_raw() - except Exception: - return False + install: the floor must not refuse it and only :data:`LEGACY_KEY_STEPS` may run on it. + Callers have already gone through ``check_config_version()``, so the read cannot fail here.""" + return "_config_version" in _cfg().read_user_config_raw() def _cfg(): @@ -772,7 +770,9 @@ MIGRATIONS: Tuple[Tuple[int, Callable[[Dict[str, Any], bool], None]], ...] = ( #: cli-config.yaml.example; targeted writers never stamp), so it gets only these: every other step #: decides by a value or an absence that, in such a file, is the user's own choice. v13 is left #: out: it clears OPENAI_MODEL from .env, a generic name Hermes never reads but the user's tools may. -LEGACY_KEY_STEPS = frozenset({12, 14, 16, 17, 29, 33, 38, 39, 41, 42, 43, 46}) +#: v41 is left out too: it rewrites profile SOUL.md on a heading match, an artifact whose +#: provenance the config stamp says nothing about. +LEGACY_KEY_STEPS = frozenset({12, 14, 16, 17, 29, 33, 38, 39, 42, 43, 46}) def run_migrations( diff --git a/tests/hermes_cli/test_config_unversioned_migration.py b/tests/hermes_cli/test_config_unversioned_migration.py index 6b22b3d6d0..b3ecc8fa59 100644 --- a/tests/hermes_cli/test_config_unversioned_migration.py +++ b/tests/hermes_cli/test_config_unversioned_migration.py @@ -54,6 +54,9 @@ def test_update_keeps_user_values_of_an_unversioned_config_and_migrates_legacy_k plugin = hermes_home / "plugins" / "notes-helper" plugin.mkdir(parents=True) (plugin / "plugin.yaml").write_text("name: notes-helper\nversion: 0.1.0\n", encoding="utf-8") + # User-authored SOUL.md section that happens to carry the v41 heading. + soul = "# Me\n\n## Messaging other agents\nmy own notes\n\n## Prefs\nkeep\n" + (hermes_home / "SOUL.md").write_text(soul, encoding="utf-8") assert persist_personality("kawaii") for key, value in USER_CHOICES.items(): set_config_value(key, value) @@ -66,6 +69,7 @@ def test_update_keeps_user_values_of_an_unversioned_config_and_migrates_legacy_k assert "summary_model" not in after["compression"] assert _at(after, "auxiliary.compression.model") == _at(before, "compression.summary_model") assert after["_config_version"] == DEFAULT_CONFIG["_config_version"] + assert (hermes_home / "SOUL.md").read_text(encoding="utf-8") == soul def test_config_seeded_from_the_template_reads_as_current(hermes_home):