fix(tui): a real named profile "hermes" is not swallowed by the legacy-basename alias

"hermes" matches the profile-id regex, so canonicalising it unconditionally at the RPC
boundary misrouted a genuine <root>/profiles/hermes to the default profile. Alias only
when no such named profile exists; ".hermes" can never be a real id and stays aliased.

Also: profile_name_for_home collapses its duplicated pre/post-resolve block into one
loop over (path, resolved path) and drops the bare "parent named profiles" fallback
that bypassed named_profile_home's root check; _profile_home goes back to main's
single resolve() comparison; the symlink-loop assertion in the target-unavailable
test is no longer wrapped in a try/except that could silently skip it.
This commit is contained in:
kshitijk4poor
2026-09-09 11:27:07 +05:30
committed by kshitij
parent 81ff1a4022
commit a39fedff34
4 changed files with 17 additions and 31 deletions
+4 -13
View File
@@ -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
@@ -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
@@ -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
+7 -9
View File
@@ -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