test(plugins): cover _discover_all_plugins recursion + cross-link loader

Add a TestDiscoverAllPlugins class covering the six cases the recursive
scan needs to handle:

- flat plugin uses its manifest ``name:`` as the key
- category-namespaced plugin keys off ``<category>/<dirname>`` even when
  the manifest ``name:`` is bare (regression test for the original bug —
  ``plugins/observability/langfuse/`` with ``name: langfuse`` must
  surface as ``observability/langfuse``, not ``langfuse``)
- user-installed plugin overrides bundled on key collision
- depth cap: anything below ``<root>/<category>/<plugin>/`` is ignored
- bundled ``memory/`` and ``context_engine/`` are skipped (they have
  their own loaders), but user plugins under those category names are
  still scanned

Also add an in-source comment next to the key derivation pointing at the
loader's matching line (``PluginManager._parse_manifest`` in
plugins.py:1027-1028), so future renames of one site flag the other.

Both items raised in Copilot review on #27161.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Guillaume Meyer 2026-05-16 23:14:26 +00:00 committed by Teknium
parent 21be7025c5
commit 5cbe0b1c4f
2 changed files with 116 additions and 0 deletions

View file

@ -761,6 +761,11 @@ def _discover_all_plugins() -> list:
description = manifest.get("description", "")
except Exception:
pass
# Path-derived key, intentionally ignoring the manifest
# ``name:`` field for category-namespaced plugins — mirrors
# ``PluginManager._parse_manifest`` in plugins.py:1027-1028
# so renaming a directory (without touching plugin.yaml) shifts
# the registry key in both places consistently.
key = f"{prefix}/{d.name}" if prefix else manifest_name
src_label = source
if source == "user" and (d / ".git").exists():