diff --git a/hermes_constants.py b/hermes_constants.py index d3a7790a54..21dda94ba5 100644 --- a/hermes_constants.py +++ b/hermes_constants.py @@ -217,23 +217,14 @@ def profile_name_for_home(path: str | Path | None) -> str | None: current = Path(path).expanduser() try: default_root = get_default_hermes_root() - if current == default_root: - return "default" - if current.parent.name == "profiles": - named = named_profile_home(current) + for candidate in (current, current.resolve(strict=False)): + if candidate == default_root or candidate == default_root.resolve(strict=False): + return "default" + named = named_profile_home(candidate) if named is not None: return named.name - if current.name and not current.name.startswith("."): - return current.name - if current.resolve(strict=False) == default_root.resolve(strict=False): - return "default" - named = named_profile_home(current) except (OSError, RuntimeError, ValueError): return None - if named is not None: - return named.name - if current.parent.name == "profiles" and current.name and not current.name.startswith("."): - return current.name return None diff --git a/tests/tui_gateway/test_default_profile_session_name.py b/tests/tui_gateway/test_default_profile_session_name.py index 8b59c327ab..16767354a4 100644 --- a/tests/tui_gateway/test_default_profile_session_name.py +++ b/tests/tui_gateway/test_default_profile_session_name.py @@ -26,6 +26,9 @@ def test_default_home_aliases_are_reported_as_default(tmp_path, monkeypatch): for alias in (default_home.name, "hermes"): assert server._response_profile_name(alias) == "default" assert server._session_info(None, {"profile_home": str(default_home)})["profile_name"] == "default" + # "hermes" is a legal profile id: a REAL named profile of that name is never swallowed by the alias. + (default_home / "profiles" / "hermes").mkdir(parents=True) + assert server._response_profile_name("hermes") == "hermes" def test_profile_home_resolution_stamps_default_rows(tmp_path, monkeypatch): @@ -165,4 +168,3 @@ def test_custom_default_root_real_session_db_owner_stamping(tmp_path, monkeypatc assert launch_db.get_session("lazy-default") is None assert launch_db.get_session("seeded-default") is None assert launch_db.get_session("branched-default") is None - diff --git a/tests/tui_gateway/test_profile_target_unavailable.py b/tests/tui_gateway/test_profile_target_unavailable.py index 1c8528d619..a632342e0c 100644 --- a/tests/tui_gateway/test_profile_target_unavailable.py +++ b/tests/tui_gateway/test_profile_target_unavailable.py @@ -35,13 +35,9 @@ def test_explicit_profile_target_never_falls_back(tmp_path, monkeypatch): # A real resolution I/O failure must propagate, too (no predicate patch). profiles = home / "profiles" profiles.rename(home / "saved-profiles") - try: - profiles.symlink_to("profiles") - except OSError: - pass - else: - with pytest.raises((OSError, RuntimeError)): - server._profile_home("worker") + profiles.symlink_to("profiles") + with pytest.raises((OSError, RuntimeError)): + server._profile_home("worker") def test_custom_root_basename_target_fails_closed_when_unavailable(tmp_path, monkeypatch): @@ -61,4 +57,3 @@ def test_custom_root_basename_target_fails_closed_when_unavailable(tmp_path, mon with pytest.raises(FileNotFoundError): with server._profile_db({"profile": "customer-data"}): pass - diff --git a/tui_gateway/server.py b/tui_gateway/server.py index 50e3099c76..e3c486b48e 100644 --- a/tui_gateway/server.py +++ b/tui_gateway/server.py @@ -448,12 +448,14 @@ def _profile_db(params: dict | None = None): def _canonical_profile_request(name: str) -> str: """Canonicalize profile basenames emitted by older session-info payloads. - ``Path(default_home).name`` was historically sent as a profile id. Those - basenames are installation details, while explicit unknown profile names - must continue to fail closed in ``_profile_home``. + ``Path(default_home).name`` was historically sent as a profile id. Those basenames are + installation details — unless a real named profile of that name exists (``hermes`` is a legal + id), in which case it wins; other unknown names keep failing closed in ``_profile_home``. """ if name.casefold() in {".hermes", "hermes"}: - return "default" + from hermes_cli import profiles as profiles_mod + if not Path(profiles_mod.get_profile_dir(name)).is_dir(): + return "default" return name @@ -478,12 +480,8 @@ def _profile_home(profile: str | None) -> Path | None: home = Path(profiles_mod.get_profile_dir(name)) if not home.is_dir(): raise FileNotFoundError(f"Profile '{name}' does not exist.") - launch = Path(_hermes_home) - if home == launch: + if home.resolve() == Path(_hermes_home).resolve(): return None # already the launch profile (no override needed) - if home.name == launch.name or home.is_symlink() or launch.is_symlink(): - if home.resolve() == launch.resolve(): - return None _served_profile_homes.add(home) # the change watcher must stat every served sibling store too return home