fix(skills): ignore support docs in skill discovery

Support files under references/, templates/, assets/, and scripts/ are progressive-disclosure data loaded through skill_view(..., file_path=...). They should not be treated as standalone skills during discovery or collision checks.

This prevents archived skill packages or support markdown files inside a real skill from shadowing active skills with the same name while still allowing top-level categories named scripts/templates/assets/references.

Tests cover:
- pruning nested SKILL.md files inside skill support directories
- preserving support-named top-level categories
- avoiding skill_view collisions from support markdown
- keeping archived package SKILL.md files accessible only through file_path
This commit is contained in:
Wolfram Ravenwolf
2026-06-16 13:08:34 -07:00
committed by Teknium
parent 7493de7fc3
commit 9137b86a52
4 changed files with 211 additions and 15 deletions
+83
View File
@@ -1225,6 +1225,89 @@ class TestSkillViewCollisionDetection:
assert result["success"] is True
assert "LOCAL VERSION" in result["content"]
def test_support_markdown_does_not_collide_with_real_skill(self, tmp_path):
"""Supporting reference docs named <skill>.md are not skills.
A real-world regression had creative/sketch/SKILL.md become
unloadable because another skill carried
references/styles/sketch.md. Support files are loaded via
skill_view(skill, file_path=...), not as bare skill names.
"""
local_dir = tmp_path / "local"
external_dir = tmp_path / "external"
local_dir.mkdir()
external_dir.mkdir()
_make_skill(local_dir, "article-illustrator", category="creative")
support_file = (
local_dir
/ "creative"
/ "article-illustrator"
/ "references"
/ "styles"
/ "sketch.md"
)
support_file.parent.mkdir(parents=True, exist_ok=True)
support_file.write_text("# Sketch style support doc\n")
_make_skill(local_dir, "sketch", category="creative", body="REAL SKETCH SKILL")
p1, p2 = self._patch_dirs(local_dir, [external_dir])
with p1, p2:
raw = skill_view("sketch")
result = json.loads(raw)
assert result["success"] is True
assert result["path"] == "creative/sketch/SKILL.md"
assert "REAL SKETCH SKILL" in result["content"]
def test_reference_package_skill_md_is_not_active_skill(self, tmp_path):
"""Curator-preserved package SKILL.md files under references stay data.
Umbrella consolidations may preserve an old skill as
references/old-skill-package/SKILL.md. That package must not appear in
skills_list/system prompts and must not resolve as skill_view("old-skill").
The package can still be opened explicitly through the umbrella's
file_path progressive-disclosure channel.
"""
local_dir = tmp_path / "local"
external_dir = tmp_path / "external"
local_dir.mkdir()
external_dir.mkdir()
_make_skill(local_dir, "umbrella", category="creative", body="UMBRELLA")
package = (
local_dir
/ "creative"
/ "umbrella"
/ "references"
/ "old-skill-package"
)
package.mkdir(parents=True, exist_ok=True)
(package / "SKILL.md").write_text(
"---\nname: old-skill\ndescription: Preserved old skill.\n---\n\nOLD BODY\n"
)
p1, p2 = self._patch_dirs(local_dir, [external_dir])
with p1, p2:
names = {skill["name"] for skill in _find_all_skills()}
old_raw = skill_view("old-skill")
direct_package_raw = skill_view("creative/umbrella/references/old-skill-package")
package_raw = skill_view(
"umbrella", file_path="references/old-skill-package/SKILL.md"
)
assert "umbrella" in names
assert "old-skill" not in names
old_result = json.loads(old_raw)
assert old_result["success"] is False
assert "not found" in old_result["error"]
direct_package_result = json.loads(direct_package_raw)
assert direct_package_result["success"] is False
assert "not found" in direct_package_result["error"]
package_result = json.loads(package_raw)
assert package_result["success"] is True
assert "OLD BODY" in package_result["content"]
def test_external_skill_resolves_when_no_collision(self, tmp_path):
"""External-only skills still resolve normally when there's no
local skill of the same name."""