fix(tui_gateway): commands.catalog keeps a discovery-failure warning over the skill collision note
A skill whose name collides with a built-in command produced a collision note that overwrote a quick_commands / plugin discovery failure already in `warning`, so a broken config.yaml was silently reported as a mere name collision. Failures now win; the collision note only fills an otherwise empty warning. `_catalog_skills` still runs unconditionally so skills keep listing when a loader failed.
This commit is contained in:
@@ -57,3 +57,22 @@ def test_built_in_name_collision_is_visible_on_every_listing_surface(monkeypatch
|
||||
|
||||
gateway_commands = _exec_commands(CommandContext(args="", options={"page_size": 500})).text
|
||||
assert f"⚠ {NOTE}" in gateway_commands and "`/tidy-notes`" in gateway_commands
|
||||
|
||||
|
||||
def test_catalog_discovery_failure_warning_outranks_the_collision_note(monkeypatch):
|
||||
"""A colliding skill must not hide a real discovery failure: the failure stays in ``warning``."""
|
||||
import tools.skills_tool as skills_tool
|
||||
from tui_gateway import server
|
||||
|
||||
_write_skill("handoff")
|
||||
_write_skill("tidy-notes") # control: skills still list when a loader failed
|
||||
monkeypatch.setattr(skills_tool, "_SKILLS_CACHE", {})
|
||||
|
||||
def _broken_cfg():
|
||||
raise RuntimeError("config.yaml unreadable")
|
||||
|
||||
monkeypatch.setattr(server, "_load_cfg", _broken_cfg)
|
||||
|
||||
catalog = server._methods["commands.catalog"](1, {})["result"]
|
||||
assert catalog["warning"] == "quick_commands discovery unavailable: config.yaml unreadable"
|
||||
assert "/tidy-notes" in catalog["skills"]
|
||||
|
||||
@@ -442,8 +442,8 @@ def _catalog_skills(cat: _Catalog, skills: dict[str, dict]) -> str:
|
||||
@_rpc("commands.catalog", 5020)
|
||||
def _(rid, params: dict) -> dict:
|
||||
"""Registry-backed slash metadata, categorized, no aliases. Discovery failures land in ``warning``
|
||||
(skills' message wins, then quick commands', then plugins'); with no failure it carries the
|
||||
built-in-name collision notice for skills that have no ``/<name>`` (empty when none). Skill
|
||||
(skills' message wins, then quick commands', then plugins'); only with no failure does it carry
|
||||
the built-in-name collision notice for skills that have no ``/<name>`` (empty when none). Skill
|
||||
discovery is bound to the calling session's profile and workspace (``_completion_cwd``: its record,
|
||||
else the cwd a new session would be seeded with) so project-local skills register for the repo the
|
||||
session is actually in (#114359)."""
|
||||
@@ -461,7 +461,8 @@ def _(rid, params: dict) -> dict:
|
||||
skills: dict[str, dict] = {}
|
||||
try:
|
||||
with _session_home_scope(_sessions.get(params.get("session_id", "")), cwd=_completion_cwd(params)):
|
||||
warning = _catalog_skills(cat, skills) or warning
|
||||
collision_note = _catalog_skills(cat, skills) # always runs: skills must list even when a loader failed
|
||||
warning = warning or collision_note
|
||||
except Exception as e:
|
||||
warning = f"skill discovery unavailable: {e}"
|
||||
return _ok(rid, {
|
||||
|
||||
Reference in New Issue
Block a user