refactor(honcho): accept pinUserPeer as backwards-compatible alias for pinPeerName

The original key 'pinPeerName' from #14984 is ambiguous: a fresh
reader can't tell whether it pins the user peer or the AI peer from
the name alone.  The resolver only ever pins the user-side
(_resolve_user_peer_id short-circuits when pin_peer_name is true; the
AI peer is already pinned by construction via aiPeer).

Add 'pinUserPeer' as the canonical alias.  Both keys land on the
same internal pin_peer_name field; precedence is host pinUserPeer →
host pinPeerName → root pinUserPeer → root pinPeerName → default.
Host-level always beats root-level regardless of alias, so a host
block can still explicitly disable a root-level pin even via the new
key.

Make _resolve_bool variadic so it can express the four-value
precedence chain.  All existing callers pass two positional args +
default keyword, which the new signature accepts unchanged.

Internal var name (pin_peer_name) stays the same to keep the
cherry-picked #27371 commits clean and avoid a noisy rename diff.
This commit is contained in:
erosika 2026-05-21 22:20:47 +00:00 committed by kshitij
parent 0bac880991
commit 3cf5e8225d
2 changed files with 86 additions and 6 deletions

View file

@ -91,12 +91,17 @@ def _normalize_recall_mode(val: str) -> str:
return val if val in _VALID_RECALL_MODES else "hybrid"
def _resolve_bool(host_val, root_val, *, default: bool) -> bool:
"""Resolve a bool config field: host wins, then root, then default."""
if host_val is not None:
return bool(host_val)
if root_val is not None:
return bool(root_val)
def _resolve_bool(*vals, default: bool) -> bool:
"""Resolve a bool config field: first non-None wins, else default.
Variadic to support aliased keys (e.g. ``pinUserPeer`` shadowing
``pinPeerName`` for backwards compatibility). Pass values in
precedence order: caller's preferred alias first, then fallback
aliases, in (host, root) interleaving as needed.
"""
for val in vals:
if val is not None:
return bool(val)
return default
@ -488,7 +493,15 @@ class HonchoClientConfig:
peer_name=host_block.get("peerName") or raw.get("peerName"),
ai_peer=ai_peer,
pin_peer_name=_resolve_bool(
# ``pinUserPeer`` is the clearer name (the resolver pins
# the user-side peer to ``peerName``, ignoring runtime
# identity). ``pinPeerName`` is the original key from
# #14984 and stays accepted for backward compatibility.
# Host-level keys win over root-level; among same-level
# keys, ``pinUserPeer`` wins over ``pinPeerName``.
host_block.get("pinUserPeer"),
host_block.get("pinPeerName"),
raw.get("pinUserPeer"),
raw.get("pinPeerName"),
default=False,
),

View file

@ -614,3 +614,70 @@ class TestCrossPlatformMemoryUnification:
"multi-user default MUST keep users separate — a regression "
"here would silently merge unrelated users' memory"
)
class TestPinUserPeerAlias:
"""``pinUserPeer`` is the canonical name; ``pinPeerName`` is the
backwards-compatible alias.
Both keys land on the same internal ``pin_peer_name`` field. When
both appear, the precedence is: host pinUserPeer host pinPeerName
root pinUserPeer root pinPeerName default. This matches the
rule for every other host/root override in the plugin and lets a
host block explicitly disable a root-level pin even via the legacy
key.
"""
def test_root_pinUserPeer_true_pins(self, tmp_path):
from plugins.memory.honcho.client import HonchoClientConfig
import json
config_file = tmp_path / "honcho.json"
config_file.write_text(json.dumps({
"apiKey": "***",
"peerName": "eri",
"pinUserPeer": True,
}))
config = HonchoClientConfig.from_global_config(config_path=config_file)
assert config.pin_peer_name is True
def test_host_pinUserPeer_wins_over_root_pinPeerName(self, tmp_path):
from plugins.memory.honcho.client import HonchoClientConfig
import json
config_file = tmp_path / "honcho.json"
config_file.write_text(json.dumps({
"apiKey": "***",
"peerName": "eri",
"pinPeerName": False,
"hosts": {"hermes": {"pinUserPeer": True}},
}))
config = HonchoClientConfig.from_global_config(config_path=config_file)
assert config.pin_peer_name is True
def test_host_pinUserPeer_false_disables_root_pinPeerName(self, tmp_path):
from plugins.memory.honcho.client import HonchoClientConfig
import json
config_file = tmp_path / "honcho.json"
config_file.write_text(json.dumps({
"apiKey": "***",
"peerName": "eri",
"pinPeerName": True,
"hosts": {"hermes": {"pinUserPeer": False}},
}))
config = HonchoClientConfig.from_global_config(config_path=config_file)
assert config.pin_peer_name is False, (
"Host-level pinUserPeer=false must override the legacy "
"root-level pinPeerName=true, otherwise a host can never "
"unpin a globally-pinned profile via the new alias."
)
def test_pinPeerName_still_works_unchanged(self, tmp_path):
from plugins.memory.honcho.client import HonchoClientConfig
import json
config_file = tmp_path / "honcho.json"
config_file.write_text(json.dumps({
"apiKey": "***",
"peerName": "eri",
"hosts": {"hermes": {"pinPeerName": True}},
}))
config = HonchoClientConfig.from_global_config(config_path=config_file)
assert config.pin_peer_name is True