From 2fb2978f448e39c7fcad8b65f682d379ba93dba3 Mon Sep 17 00:00:00 2001 From: Teknium Date: Sun, 5 Apr 2026 12:32:07 -0700 Subject: [PATCH] fix(honcho): migration guard for observation mode default change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Existing honcho.json configs without an explicit observationMode now default to 'unified' (the old default) instead of being silently switched to 'directional'. New installations get 'directional' as the new default. Detection: _explicitly_configured (host block exists or enabled=true) signals an existing config. When true and no observationMode is set anywhere in the config chain, falls back to 'unified'. When false (fresh install), uses 'directional'. Users who explicitly set observationMode or granular observation booleans are unaffected — explicit config always wins. 5 new tests covering all migration paths. --- plugins/memory/honcho/client.py | 9 ++++- tests/honcho_plugin/test_client.py | 63 ++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/plugins/memory/honcho/client.py b/plugins/memory/honcho/client.py index 8ca1e8d1d4..e460fd75c2 100644 --- a/plugins/memory/honcho/client.py +++ b/plugins/memory/honcho/client.py @@ -366,16 +366,21 @@ class HonchoClientConfig: or raw.get("recallMode") or "hybrid" ), + # Migration guard: existing configs without an explicit + # observationMode keep the old "unified" default so users + # aren't silently switched to full bidirectional observation. + # New installations (no host block, no credentials) get + # "directional" (all observations on) as the new default. observation_mode=_normalize_observation_mode( host_block.get("observationMode") or raw.get("observationMode") - or "directional" + or ("unified" if _explicitly_configured else "directional") ), **_resolve_observation( _normalize_observation_mode( host_block.get("observationMode") or raw.get("observationMode") - or "directional" + or ("unified" if _explicitly_configured else "directional") ), host_block.get("observation") or raw.get("observation"), ), diff --git a/tests/honcho_plugin/test_client.py b/tests/honcho_plugin/test_client.py index 6a49ce514c..71f48351ee 100644 --- a/tests/honcho_plugin/test_client.py +++ b/tests/honcho_plugin/test_client.py @@ -437,6 +437,69 @@ class TestProfileScopedConfig: assert config.peer_name == "dreamer-user" +class TestObservationModeMigration: + """Existing configs without explicit observationMode keep 'unified' default.""" + + def test_existing_config_defaults_to_unified(self, tmp_path): + """Config with host block but no observationMode → 'unified' (old default).""" + cfg_file = tmp_path / "config.json" + cfg_file.write_text(json.dumps({ + "apiKey": "k", + "hosts": {"hermes": {"enabled": True, "aiPeer": "hermes"}}, + })) + cfg = HonchoClientConfig.from_global_config(config_path=cfg_file) + assert cfg.observation_mode == "unified" + + def test_new_config_defaults_to_directional(self, tmp_path): + """Config with no host block and no credentials → 'directional' (new default).""" + cfg_file = tmp_path / "config.json" + cfg_file.write_text(json.dumps({})) + cfg = HonchoClientConfig.from_global_config(config_path=cfg_file) + assert cfg.observation_mode == "directional" + + def test_explicit_directional_respected(self, tmp_path): + """Existing config with explicit observationMode → uses what's set.""" + cfg_file = tmp_path / "config.json" + cfg_file.write_text(json.dumps({ + "apiKey": "k", + "hosts": {"hermes": {"enabled": True, "observationMode": "directional"}}, + })) + cfg = HonchoClientConfig.from_global_config(config_path=cfg_file) + assert cfg.observation_mode == "directional" + + def test_explicit_unified_respected(self, tmp_path): + """Existing config with explicit observationMode unified → stays unified.""" + cfg_file = tmp_path / "config.json" + cfg_file.write_text(json.dumps({ + "apiKey": "k", + "observationMode": "unified", + "hosts": {"hermes": {"enabled": True}}, + })) + cfg = HonchoClientConfig.from_global_config(config_path=cfg_file) + assert cfg.observation_mode == "unified" + + def test_granular_observation_overrides_preset(self, tmp_path): + """Explicit observation object overrides both preset and migration default.""" + cfg_file = tmp_path / "config.json" + cfg_file.write_text(json.dumps({ + "apiKey": "k", + "hosts": {"hermes": { + "enabled": True, + "observation": { + "user": {"observeMe": True, "observeOthers": False}, + "ai": {"observeMe": False, "observeOthers": True}, + }, + }}, + })) + cfg = HonchoClientConfig.from_global_config(config_path=cfg_file) + # observation_mode falls back to "unified" (migration), but + # granular booleans from the observation object win + assert cfg.user_observe_me is True + assert cfg.user_observe_others is False + assert cfg.ai_observe_me is False + assert cfg.ai_observe_others is True + + class TestResetHonchoClient: def test_reset_clears_singleton(self): import plugins.memory.honcho.client as mod