706f33d424
The shared checkout serves every profile, but hermes update migrated only the active profile's config.yaml. Siblings kept their old _config_version until their (correctly restarted, post-#91378) gateway hit a config shape the new code couldn't read — the last unabsorbed substance from the Phase-2 restart-swarm audit (#20438 earliest, 2026 field repro on #79048: sibling at v33 vs v37). _migrate_sibling_profile_configs(): per sibling home, scope config reads/writes via the context-local HERMES_HOME override (ContextVar — never os.environ), check version, run the NON-INTERACTIVE safe migration; prompt-requiring settings stay for the profile's own next interactive session (same contract as gateway-mode). Broken profiles are skipped without blocking the sweep; override always reset. Sabotage-verified; live E2E in a fresh process with real drifted config files: v12→v38 and v25→v38 on disk, provider preserved, the documented #81946 personality-reset migration correctly applied to siblings too, never-configured profile untouched, active home untouched, second run idempotent.
52 lines
1.8 KiB
Python
52 lines
1.8 KiB
Python
"""LIVE Windows E2E for the fleet-wide config migration (wine2e lane).
|
|
|
|
Real profile homes on the Windows filesystem, real migration pipeline,
|
|
fresh-process semantics via HERMES_HOME env — mirrors the Linux live E2E.
|
|
"""
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
import yaml
|
|
|
|
WORKTREE = Path(__file__).resolve().parents[2]
|
|
sys.path.insert(0, str(WORKTREE))
|
|
|
|
pytestmark = pytest.mark.skipif(sys.platform != "win32", reason="live Windows E2E")
|
|
|
|
|
|
def test_fleet_config_migration_live_windows(tmp_path, monkeypatch):
|
|
active = tmp_path / "hermes-home"
|
|
profiles = active / "profiles"
|
|
for name, ver in [("research", 12), ("work", 25)]:
|
|
home = profiles / name
|
|
home.mkdir(parents=True)
|
|
(home / "config.yaml").write_text(
|
|
yaml.safe_dump({"_config_version": ver, "model": {"provider": "nous"}}),
|
|
encoding="utf-8",
|
|
)
|
|
active.mkdir(exist_ok=True)
|
|
(active / "config.yaml").write_text(
|
|
yaml.safe_dump({"_config_version": 12}), encoding="utf-8"
|
|
)
|
|
monkeypatch.setenv("HERMES_HOME", str(active))
|
|
|
|
import hermes_cli.update_cmd as update_cmd
|
|
from hermes_cli.config import DEFAULT_CONFIG
|
|
|
|
latest = int(DEFAULT_CONFIG["_config_version"])
|
|
migrated = update_cmd._migrate_sibling_profile_configs()
|
|
|
|
by_name = {m[0]: m for m in migrated}
|
|
assert set(by_name) == {"research", "work"}, migrated
|
|
assert by_name["research"][1] == 12 and by_name["research"][2] == latest
|
|
|
|
for name in ("research", "work"):
|
|
on_disk = yaml.safe_load((profiles / name / "config.yaml").read_text())
|
|
assert on_disk["_config_version"] == latest
|
|
assert on_disk["model"]["provider"] == "nous"
|
|
|
|
# active untouched; idempotent second run
|
|
assert yaml.safe_load((active / "config.yaml").read_text())["_config_version"] == 12
|
|
assert update_cmd._migrate_sibling_profile_configs() == []
|