Skip to content

fix(skills): stop package-internal markdown from shadowing real skills - #110471

Open
kokhlo wants to merge 1 commit into
NousResearch:mainfrom
kokhlo:kokhlo/skill-package-internal-md
Open

fix(skills): stop package-internal markdown from shadowing real skills#110471
kokhlo wants to merge 1 commit into
NousResearch:mainfrom
kokhlo:kokhlo/skill-package-internal-md

Conversation

@kokhlo

@kokhlo kokhlo commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

skill_view("research") used to refuse loading a top-level research/SKILL.md because another skill's package-internal character/example/prompts/research.md entered the legacy flat <name>.md candidate set. The merged support-dir exclusion (#47249) only knows the hard-coded SKILL_SUPPORT_DIRS (references, templates, assets, scripts), so package-internal Markdown under any other directory name still shadowed or collided with real skills.

This adds ancestor-SKILL.md ownership: a legacy flat <name>.md candidate is rejected when any ancestor directory between the candidate and the discovery root contains SKILL.md — the fix originally proposed in #35743. Root-level and category-level legacy flat skills (no SKILL.md ancestor) keep resolving.

Symptom

skill_view("research")Ambiguous skill name 'research': 2 skills match with a package's internal prompts/research.md listed as a second candidate; the suggested categorized-path workaround is unusable for a top-level skill whose lookup path is the bare name.

Bug Cause

Trigger: tools/skills_tool.py _collect_skill_candidates — the search_dir.rglob(f"{name}.md") scan filtered candidates only via is_skill_support_path, which recognizes the hard-coded support dir names.

Causal chain:

  1. A skill package places internal Markdown under a directory not in SKILL_SUPPORT_DIRS (e.g. prompts/).
  2. The legacy flat-skill scan records it as a standalone candidate.
  3. A real skill sharing the basename becomes ambiguous and refuses to load.

Why it is wrong: ownership of internal Markdown follows its ancestor SKILL.md, not the internal directory's name.

Working sibling / contrast: references/<name>.md is already excluded by name; this PR generalizes the exclusion to arbitrary directory names.

Ruled out: frontmatter cannot distinguish the candidates — the collision fires on basename before frontmatter is read.

Fix

New is_inside_skill_package(path, root=...) predicate in agent/skill_utils.py: walks ancestors strictly below the discovery root and reports whether one contains SKILL.md. The legacy candidate scan in tools/skills_tool.py now skips such files. Complementary to #109577 (nested support-dir case): that PR ancestor-walks the hard-coded dir names; this one keys off ancestor SKILL.md ownership for any directory name.

Related Issue

Fixes #110456

Type of Change

  • Bug fix (non-breaking change; fixes an issue)

Changes Made

  • agent/skill_utils.pyis_inside_skill_package(): ancestor-SKILL.md ownership bounded by the discovery root.
  • tools/skills_tool.py — legacy flat <name>.md scan skips package-internal Markdown.
  • tests/agent/test_skill_utils.py — ownership predicate: package-internal vs root/category flat skills; owners above the discovery root are ignored.
  • tests/tools/test_skills_tool.py — regression: prompts/research.md no longer collides with a real research skill.

How to Test

scripts/run_tests.sh tests/tools/test_skills_tool.py tests/agent/test_skill_utils.py -q
# 69 passed

scripts/run_tests.sh "tests/tools/test_skills_tool.py::TestSkillViewCollisionDetection::test_package_internal_markdown_does_not_collide_with_real_skill" -q
# 1 passed (fails on main with the exact ambiguity from the report)

Checklist

Code

  • I've read the Contributing Guide.
  • My commit messages follow Conventional Commits.
  • I searched for existing PRs to make sure this isn't a duplicate.
  • My PR contains only changes related to this fix.
  • I've added tests for my changes.
  • I've tested on my platform: macOS 15.7.3.

Documentation & Housekeeping

  • Relevant documentation and docstrings are updated.
  • Config examples are N/A because no config keys changed.
  • Contributor workflow docs are N/A because no workflow changed.
  • Cross-platform impact was considered: pathlib-based, no platform-specific behavior.
  • Tool schema updates are N/A because the public schema is unchanged.

A legacy flat <name>.md candidate is now rejected when any ancestor
between it and the discovery root contains SKILL.md, so internal
Markdown under any package-owned directory (prompts/, docs/, ...) is
material of its package instead of a standalone skill colliding with
a real one sharing the basename.
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists tool/skills Skills system (list, view, manage) comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint duplicate This issue or pull request already exists labels Sep 14, 2026
@alt-glitch

Copy link
Copy Markdown
Contributor

This was generated by AI during triage.

Duplicate of #110461 — same fix for #110456 (reject a legacy flat <name>.md candidate when an ancestor directory below the discovery root owns a SKILL.md). This PR places the helper in agent/skill_utils.py; #110461 inlines it in tools/skills_tool.py. Also related: #109577. Leaving both open for a maintainer to pick.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint duplicate This issue or pull request already exists P2 Medium — degraded but workaround exists tool/skills Skills system (list, view, manage) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: skill_view treats Markdown under prompts/ as a legacy flat skill

2 participants