fix(honcho): default the wizard to the detected shape on every existing install

The identity step treated any host block without a mapping key as a new install and defaulted the choice to single peer. An install with enabled, workspace and peerName then had Enter write pinUserPeer: true and merge every gateway account onto the operator. cmd_setup now decides new-ness before its prompts populate the block, and only a block with none of the mapping, peerName, workspace or enabled keys defaults to single.
This commit is contained in:
Erosika
2026-09-09 16:40:27 -04:00
committed by kshitij
parent 7dacc2ed35
commit e05f6d82ce
2 changed files with 11 additions and 5 deletions
+5 -3
View File
@@ -482,7 +482,7 @@ def _configure_raw_identity_mapping(hermes_host, current_pin, current_aliases, c
"runtimePeerPrefix — namespace for unknown IDs (blank for none)")
def _setup_identity_mapping(cfg: dict, hermes_host: dict, current_peer: str) -> None:
def _setup_identity_mapping(cfg: dict, hermes_host: dict, current_peer: str, new_host: bool) -> None:
"""Gateway identity mapping step. Only the gateway supplies a runtime user ID (CLI/TUI/
desktop fall through to peerName), so the step is gated on gateway detection."""
current_pin, current_aliases, current_prefix, aliases_from_root, prefix_from_root = (
@@ -505,7 +505,7 @@ def _setup_identity_mapping(cfg: dict, hermes_host: dict, current_peer: str) ->
peer_target = hermes_host.get("peerName") or current_peer or "user"
ai_peer_label = hermes_host.get("aiPeer") or cfg.get("aiPeer") or "hermes"
# Fresh configs default to the personal shape; configured ones keep their detected shape.
identity_configured = any(k in hermes_host or k in cfg for k in _IDENTITY_MAPPING_KEYS)
identity_configured = not new_host or any(k in cfg for k in _IDENTITY_MAPPING_KEYS)
default_choice = {"single": "1", "hybrid": "2", "multi": "3"}[current_shape] if identity_configured else "1"
print("\n This step covers the HUMAN mapping only. Each account using the\n"
" gateway resolves to a peer — the entity Honcho reasons about over\n"
@@ -836,6 +836,8 @@ def _setup_wizard(args) -> None:
hermes_host = cfg.setdefault("hosts", {}).setdefault(_host_key(), {})
_migrate_pin_key(cfg) # canonicalize legacy pinPeerName before detection/writes
_migrate_pin_key(hermes_host)
# Taken before the prompts populate the block: an existing install must not default to pinning every account.
new_host = not any(k in hermes_host for k in (*_IDENTITY_MAPPING_KEYS, "peerName", "workspace", "enabled"))
# --- 1. Cloud or local? ---
print(" Deployment:\n cloud -- Honcho cloud (api.honcho.dev)\n local -- self-hosted Honcho server")
@@ -858,7 +860,7 @@ def _setup_wizard(args) -> None:
if new := _prompt(label, default=default):
hermes_host[key] = new
_setup_identity_mapping(cfg, hermes_host, current_peer)
_setup_identity_mapping(cfg, hermes_host, current_peer, new_host)
print("\n For a gateway with many users and agents, run\n"
" 'hermes honcho peers map' to map accounts interactively.")
+6 -2
View File
@@ -561,10 +561,14 @@ class TestSetupWizardDeploymentShape:
@pytest.mark.parametrize("initial_cfg, expected_pin", [
(None, True),
({"apiKey": "***", "hosts": {"hermes": {}}}, True),
({"apiKey": "***", "hosts": {"hermes": {"pinUserPeer": False, "peerName": "eri"}}}, False),
], ids=["fresh-config-defaults-to-single", "configured-multi-keeps-multi"])
({"apiKey": "***", "hosts": {"hermes": {"enabled": True, "workspace": "hermes", "peerName": "eri"}}}, False),
], ids=["fresh-config-defaults-to-single", "empty-host-block-defaults-to-single",
"configured-multi-keeps-multi", "existing-install-without-mapping-keys-keeps-multi"])
def test_choice_default_follows_config(self, monkeypatch, tmp_path, initial_cfg, expected_pin):
"""Enter on a fresh config picks the pinned personal shape; a chosen shape stays the default."""
"""Enter on a fresh config picks the pinned personal shape. An existing install, with or
without mapping keys, keeps its detected shape so Enter never merges every account onto one peer."""
answers = ["cloud", "", "eri", "hermetika", "hermes"]
host = self._run_setup(monkeypatch, tmp_path, answers=answers, initial_cfg=initial_cfg)
assert host["pinUserPeer"] is expected_pin