diff --git a/hermes_cli/profiles.py b/hermes_cli/profiles.py index 6a20472a5e..50fefe33c4 100644 --- a/hermes_cli/profiles.py +++ b/hermes_cli/profiles.py @@ -351,11 +351,13 @@ def _iter_named_profile_dirs(*, live_only: bool = True) -> List[Path]: def list_profile_names() -> List[str]: - """Cheap name-only listing (``default`` + profile dirs). Unlike :func:`list_profiles` this - reads NO per-profile config — safe for hot paths (cron target listings, create validation).""" + """Cheap name-only listing (``default`` + LIVE profile dirs). Unlike :func:`list_profiles` this + reads NO per-profile config — safe for hot paths (cron target listings, create validation). + Tombstoned shells are skipped like everywhere else: a stale process that re-mkdirs a deleted + profile's directory must not resurface it as a ``bot-chat:`` cron target.""" names = ["default"] with contextlib.suppress(OSError): - names.extend(entry.name for entry in _iter_named_profile_dirs(live_only=False)) + names.extend(entry.name for entry in _iter_named_profile_dirs()) return names diff --git a/tests/hermes_cli/test_deleted_profile_tombstone.py b/tests/hermes_cli/test_deleted_profile_tombstone.py index 3b4feea99a..1a70208845 100644 --- a/tests/hermes_cli/test_deleted_profile_tombstone.py +++ b/tests/hermes_cli/test_deleted_profile_tombstone.py @@ -13,11 +13,13 @@ from unittest.mock import patch import pytest +from cron.scheduler_delivery import BOT_CHAT_PLATFORM, cron_delivery_targets from hermes_cli.config import ensure_hermes_home from hermes_cli.profiles import ( backfill_profile_envs, create_profile, delete_profile, + list_profile_names, list_profiles, profile_exists, profiles_to_serve, @@ -122,6 +124,11 @@ class TestDeletedProfileTombstone: assert "worker" not in _named_homes(profile_env) served = [name for name, _ in profiles_to_serve(True)] assert "worker" not in served + # The name-only hot path (cron Bot Chat targets, kanban profile hints) + # honors the same tombstone: a recreated shell must not resurface as a + # `bot-chat:worker` delivery target. + assert "worker" not in list_profile_names() + assert f"{BOT_CHAT_PLATFORM}:worker" not in [t["id"] for t in cron_delivery_targets()] def test_tombstoned_home_is_not_bootstrapped(self, profile_env, monkeypatch): profile_dir = create_profile("worker", no_alias=True, no_skills=True)