fix(config): v32 migration flips baked-in verify_on_stop=true to false (#54740)

The first ship of verify-on-stop (config v30) defaulted
DEFAULT_CONFIG agent.verify_on_stop to a literal True, and migrate_config
persists defaults with strip_defaults=False — so every install that updated
through v30 had verify_on_stop: true written into config.yaml as a literal.

The v30->v31 migration only flipped missing/'auto' values to false and
deliberately preserved an explicit bool, so it skipped that entire population
and left verify-on-stop ON for everyone who had updated. A literal true was
never a user choice: the feature had no off-switch worth setting it against
until v31 introduced one, so a true persisted before v32 is always the old
machine default.

v32 migration flips a literal true -> false once, for both v30 (skipped v31)
and v31 (preserved-by-bug) installs. A true the user sets AFTER v32 is a
deliberate opt-in and is never touched.
This commit is contained in:
Teknium 2026-06-29 01:51:08 -07:00 committed by GitHub
parent 75317d82d0
commit bf0d8fed8e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 59 additions and 2 deletions

View file

@ -1365,11 +1365,40 @@ class TestVerifyOnStopMigration:
raw = yaml.safe_load((tmp_path / "config.yaml").read_text())
assert raw["agent"]["verify_on_stop"] is False
def test_explicit_true_preserved(self, tmp_path):
def test_pre_v32_literal_true_flipped_to_false(self, tmp_path):
# The first ship of verify-on-stop baked a literal `true` into configs
# as the silent default (config v30). It was never a user choice, so the
# v31→v32 migration flips it off. v31's block preserved it (the bug this
# fixes); v32 catches the whole stranded population.
with patch.dict(os.environ, {"HERMES_HOME": str(tmp_path)}):
self._write(tmp_path, "_config_version: 30\nagent:\n verify_on_stop: true\n")
migrate_config(interactive=False, quiet=True)
raw = yaml.safe_load((tmp_path / "config.yaml").read_text())
assert raw["agent"]["verify_on_stop"] is False
def test_v31_literal_true_flipped_to_false(self, tmp_path):
# Teknium's case: a v30 install that already ran the v31 migration kept
# its baked-in literal `true` (v31 preserved explicit bools). v32 flips
# it off.
with patch.dict(os.environ, {"HERMES_HOME": str(tmp_path)}):
self._write(tmp_path, "_config_version: 31\nagent:\n verify_on_stop: true\n")
migrate_config(interactive=False, quiet=True)
raw = yaml.safe_load((tmp_path / "config.yaml").read_text())
assert raw["agent"]["verify_on_stop"] is False
def test_post_v32_explicit_true_preserved(self, tmp_path):
# A `true` the user sets AFTER v32 (config already at current version) is
# a deliberate opt-in and must never be flipped.
from hermes_cli.config import DEFAULT_CONFIG
with patch.dict(os.environ, {"HERMES_HOME": str(tmp_path)}):
self._write(
tmp_path,
f"_config_version: {DEFAULT_CONFIG['_config_version']}\n"
"agent:\n verify_on_stop: true\n",
)
migrate_config(interactive=False, quiet=True)
raw = yaml.safe_load((tmp_path / "config.yaml").read_text())
assert raw["agent"]["verify_on_stop"] is True
def test_explicit_false_preserved(self, tmp_path):