fix(hindsight): corrupt config.json falls through to the legacy file and env, as before

read_json_or_empty returns {} for malformed JSON; returning that directly from
_load_config made a corrupt profile config.json yield an empty (silently
unconfigured) mapping where the pre-dedup loop kept walking to the legacy
file and then the env branch. Only a non-empty parsed object is now returned.
This commit is contained in:
teknium1
2026-09-12 23:48:30 -07:00
committed by Teknium
parent 4e74beb064
commit 43ec2036ec
2 changed files with 20 additions and 2 deletions

View File

@@ -235,8 +235,9 @@ def _load_config() -> dict:
"""$HERMES_HOME/hindsight/config.json (profile-scoped), else ~/.hindsight/config.json
(legacy, shared), else environment variables."""
for path in (get_hermes_home() / "hindsight" / "config.json", Path.home() / ".hindsight" / "config.json"):
if path.exists():
return read_json_or_empty(path)
# A corrupt (or empty) file falls through to the next source, as before the dedup.
if path.exists() and (data := read_json_or_empty(path)):
return data
# Mode, bank (the data partition), endpoint and retain shaping are per-profile .env values like
# the key beside them: read through the secret scope so a multiplexed secondary never inherits
# the default profile's bank/mode. Tuning knobs (timeouts, budget) stay process-global.

View File

@@ -1540,6 +1540,23 @@ def test_save_config_sets_owner_only_permissions(tmp_path):
assert mode == 0o600, f"Expected 0o600 (owner-only), got {oct(mode)}"
def test_load_config_corrupt_profile_file_falls_through_to_env(tmp_path, monkeypatch):
"""A corrupt $HERMES_HOME/hindsight/config.json is not the config: the loader falls through
(legacy file, then env) instead of returning an empty, silently-unconfigured mapping."""
home = tmp_path / "home"
(home / "hindsight").mkdir(parents=True)
(home / "hindsight" / "config.json").write_text("{not json", encoding="utf-8")
monkeypatch.setenv("HERMES_HOME", str(home))
monkeypatch.setattr(Path, "home", lambda: tmp_path / "nohome")
monkeypatch.setenv("HINDSIGHT_MODE", "local")
monkeypatch.setenv("HINDSIGHT_BANK_ID", "from-env")
cfg = _load_config()
assert cfg["mode"] == "local"
assert cfg["banks"]["hermes"]["bankId"] == "from-env"
class TestLoadSimpleEnv:
def test_bom_first_key_is_recognized(self, tmp_path):
"""A Notepad-edited .env carries a BOM; the first key must still parse