From 7fbb2c7613e43959dd8cf178725c3841fcabcace Mon Sep 17 00:00:00 2001 From: Jesse Keisling Date: Thu, 18 Jun 2026 18:58:19 +0000 Subject: [PATCH 1/3] fix(honcho): honcho_search returns empty in directional observation mode search_context() resolved observer->target as hermes-about-Jesse, whose representation slot is empty in directional mode; the peer's own self-representation holds the data. Unlike get_peer_card(), search_context() had no fallback, so honcho_search returned '' for every query while honcho_profile/context/reasoning worked. Mirror the get_peer_card() target-peer fallback: when the directional fetch yields no representation and no card, refetch directly against the target peer. Preserves directional precedence (only falls back when empty) and is a no-op for the ai-peer path where observer == target. Verified against the live backend: queries that returned 0 chars now return 1840-2949 char representations. --- plugins/memory/honcho/session.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/memory/honcho/session.py b/plugins/memory/honcho/session.py index e83c714b51bb..c98d7e4fb7f9 100644 --- a/plugins/memory/honcho/session.py +++ b/plugins/memory/honcho/session.py @@ -1134,6 +1134,12 @@ def search_context( search_query=query, target=target, ) + # In directional observation mode the observer->target slot + # (e.g. hermes-about-Jesse) is often empty while the data lives + # on the target peer's own self-representation. Mirror the + # get_peer_card fallback so semantic search still returns results. + if not ctx["representation"] and not ctx["card"] and target and target != observer_peer_id: + ctx = self._fetch_peer_context(target, search_query=query) parts = [] if ctx["representation"]: parts.append(ctx["representation"]) From 72ad60a85ac378f643f86614b4f54d0f890af64c Mon Sep 17 00:00:00 2001 From: Jesse Keisling Date: Thu, 18 Jun 2026 19:04:37 +0000 Subject: [PATCH 2/3] test(honcho): cover search_context directional-empty fallback Regression test for the honcho_search empty-result fix: in directional observation mode the assistant->user slot is empty (representation and card), so search_context must fall back to the user peer's own self-context. Mirrors test_get_peer_card_falls_back_to_target_peer_own_card. --- tests/honcho_plugin/test_session.py | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/honcho_plugin/test_session.py b/tests/honcho_plugin/test_session.py index e8dadf2f5763..ad51369aed1c 100644 --- a/tests/honcho_plugin/test_session.py +++ b/tests/honcho_plugin/test_session.py @@ -297,6 +297,44 @@ def test_search_context_accepts_explicit_ai_peer_id(self): search_query="assistant", ) + def test_search_context_falls_back_to_target_self_context_when_directional_empty(self): + # In directional mode the observer->target slot (assistant-about-user) + # is often empty because conclusions are auto-derived onto the target + # peer's own self-representation, not written through the directional + # path. Mirror the get_peer_card fallback: when the directional fetch + # yields no representation and no card, refetch the target peer's own + # self-context so honcho_search still returns results. + mgr, session = self._make_cached_manager() + assistant_peer = MagicMock() + assistant_peer.context.return_value = SimpleNamespace( + representation="", # directional observer->target slot empty + peer_card=[], + ) + assistant_peer.representation.return_value = "" + assistant_peer.get_card.return_value = None # directional card slot empty + user_peer = MagicMock() + user_peer.context.return_value = SimpleNamespace( + representation="Robert runs neuralancer", + peer_card=["Location: Melbourne"], + ) + + def _peer(peer_id: str) -> MagicMock: + return assistant_peer if peer_id == session.assistant_peer_id else user_peer + + mgr._get_or_create_peer = MagicMock(side_effect=_peer) + + result = mgr.search_context(session.key, "neuralancer") + + assert "Robert runs neuralancer" in result + assert "- Location: Melbourne" in result + # Directional fetch attempted first against the user target... + assistant_peer.context.assert_called_once_with( + target=session.user_peer_id, + search_query="neuralancer", + ) + # ...then fell back to the target peer's own self-context. + user_peer.context.assert_called_once_with(search_query="neuralancer") + def test_get_prefetch_context_fetches_user_and_ai_from_peer_api(self): mgr, session = self._make_cached_manager() user_peer = MagicMock() From 2b13e54d36ddaa673566a49b822ca5c944acc48d Mon Sep 17 00:00:00 2001 From: Jesse Keisling Date: Thu, 18 Jun 2026 19:19:04 +0000 Subject: [PATCH 3/3] test(honcho): assert search_context does not fall back when directional slot populated Complements the fallback test by locking in the happy path: when the observer->target directional fetch returns data, no second fetch against the target peer is made. Prevents regression and redundant network calls. (Addresses independent code-review suggestion.) --- tests/honcho_plugin/test_session.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/honcho_plugin/test_session.py b/tests/honcho_plugin/test_session.py index ad51369aed1c..e2aef29de5c8 100644 --- a/tests/honcho_plugin/test_session.py +++ b/tests/honcho_plugin/test_session.py @@ -335,6 +335,35 @@ def _peer(peer_id: str) -> MagicMock: # ...then fell back to the target peer's own self-context. user_peer.context.assert_called_once_with(search_query="neuralancer") + def test_search_context_does_not_fall_back_when_directional_slot_populated(self): + # Guard the happy path: when the observer->target directional fetch + # returns data, search_context must NOT make a second fetch against + # the target peer. Protects against regressing existing behavior and + # against a redundant network call. + mgr, session = self._make_cached_manager() + assistant_peer = MagicMock() + assistant_peer.context.return_value = SimpleNamespace( + representation="Robert runs neuralancer", + peer_card=["Location: Melbourne"], + ) + user_peer = MagicMock() + + def _peer(peer_id: str) -> MagicMock: + return assistant_peer if peer_id == session.assistant_peer_id else user_peer + + mgr._get_or_create_peer = MagicMock(side_effect=_peer) + + result = mgr.search_context(session.key, "neuralancer") + + assert "Robert runs neuralancer" in result + assert "- Location: Melbourne" in result + # Directional fetch served the result; the target peer was never touched. + assistant_peer.context.assert_called_once_with( + target=session.user_peer_id, + search_query="neuralancer", + ) + user_peer.context.assert_not_called() + def test_get_prefetch_context_fetches_user_and_ai_from_peer_api(self): mgr, session = self._make_cached_manager() user_peer = MagicMock()