fix(profiles): keep tombstoned profiles out of the name-only listing

`list_profile_names()` — the hot-path enumerator behind cron's Bot Chat
delivery targets (`cron_delivery_targets()`) and kanban's profile hints —
listed every directory under profiles/, so a stale process re-creating a
deleted profile's directory (`profiles/<name>/logs`, `state.db`) made
`bot-chat:<name>` reappear as a delivery target. Every other enumerator
(`list_profiles`, `profiles_to_serve`, `HostedRoomService.local_profiles`)
already honors the tombstone; this one now uses the same live-only walk.

Live probe (fresh temp HERMES_HOME, real create/delete, real
cron_delivery_targets): base lists `bot-chat:worker` after the stale
re-mkdir; head lists only default/ops.

Co-authored-by: 686f6c61 <github@00b.tech>
Co-authored-by: ryrenz <dazhang@Davids-Mac-mini.local>
This commit is contained in:
teknium1
2026-09-16 13:11:18 -07:00
committed by Teknium
parent 65aaf16a4d
commit 4346cdcdc2
2 changed files with 12 additions and 3 deletions

View File

@@ -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:<name>`` 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

View File

@@ -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)