.NET: Fix SearchDirectoriesForSkills to stop recursing after finding SKILL.md - #6686
Conversation
When a directory contains SKILL.md, subdirectories are part of that skill and should not be treated as independent skill roots. Add a return after adding the directory to results to prevent incorrect recursion. Also adds a regression test verifying nested SKILL.md files are not discovered as separate skills. Fixes microsoft#6683 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adjusts .NET file-based skill discovery so that once a directory containing SKILL.md is identified as a skill root, discovery stops recursing into that directory’s subfolders—preventing nested directories from being incorrectly treated as independent skill roots.
Changes:
- Add an early
returninSearchDirectoriesForSkillsafter findingSKILL.md, establishing a skill boundary. - Add a unit test intended to verify nested
SKILL.mdfiles are not discovered as separate skills.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| dotnet/src/Microsoft.Agents.AI/Skills/File/AgentFileSkillsSource.cs | Stops recursion into subdirectories once a SKILL.md skill root is found. |
| dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/FileAgentSkillLoaderTests.cs | Adds coverage for the nested-skill-root scenario (but currently needs a fix to actually validate the behavior). |
…alidation The child skill's frontmatter name must match its directory name, otherwise it gets rejected by validation regardless of the recursion fix. This ensures the test actually validates the stop-recursing behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Automated Code Review
Reviewers: 5 | Confidence: 94%
✓ Correctness
The one-line
returnfix is correct. It only exits the current recursion frame when a SKILL.md is found, so sibling directories at the same level are still discovered normally. The test properly validates the behavior: the child directory is namedchildand its SKILL.md declaresname: child, so it would pass the directory-name validation (line 287) if discovered—confirming the test actually exercises the new early-return logic rather than relying on a pre-existing name-mismatch rejection.
✓ Security Reliability
This is a clean, minimal fix that adds early return after discovering a SKILL.md to prevent subdirectories from being incorrectly treated as independent skill roots. No security or reliability issues found. The recursive method already has depth-limiting via MaxSkillDirectorySearchDepth, and the paths are properly normalized via Path.GetFullPath. No new trust boundaries, injection vectors, resource leaks, or unhandled failure modes are introduced. The test properly validates the fix by using a child directory name ('child') that matches the frontmatter name, so the child would pass validation if incorrectly discovered.
✓ Test Coverage
The test coverage for this PR is adequate. The new test correctly validates the fix by creating a nested child skill with
name: childmatching the directory namechild, ensuring the child would pass all validation if discovered. This properly addresses the resolved review comment about the earlier version using a non-matching name. The test would fail without the fix (child would be independently discovered) and passes with it (recursion stops at parent). No significant test coverage gaps found.
✓ Failure Modes
The change is a clean, minimal fix that adds an early return after discovering a SKILL.md to prevent recursion into subdirectories. The logic is straightforward with no silent failure paths, exception swallowing, partial writes, or race conditions introduced. The test correctly validates the behavior by using a child directory name ('child') that matches the child SKILL.md frontmatter name, ensuring the child would have been discovered as a valid independent skill without the fix.
✗ Design Approach
The new early return fixes the reported nested-skill case for valid parent skills, but it also changes discovery to treat the mere presence of
SKILL.mdas a hard traversal boundary before the loader knows whether that file is a valid skill. That introduces a false-negative path where an invalid parentSKILL.mdnow prevents discovery of valid nested skills beneath it.
Flagged Issues
-
SearchDirectoriesForSkillsnow stops at any directory containingSKILL.md(AgentFileSkillsSource.cs:163-168), butParseSkillDirectorycan still reject that file later for invalid frontmatter or a name/directory mismatch (lines 187-190, 213-216, 286-295). A tree likeparent/SKILL.md(invalid) +parent/child/SKILL.md(valid) silently loses the child skill entirely, because discovery never descends intoparentonce the invalid marker file exists.
Automated review by SergeyMenshykh's agents
|
Flagged issue
Source: automated DevFlow PR review |
Motivation & Context
SearchDirectoriesForSkillsfinds aSKILL.mdin a directory and adds it as a skill, but then keeps recursing into subdirectories. This causes subdirectories beneath a skill to be incorrectly treated as independent skill roots.Description & Review Guide
returnafterresults.Add(...)so recursion stops once aSKILL.mdis found.SKILL.md). Script/resource scanning is unaffected — those use their own separate recursion.Related Issue
Fixes #6683
Contribution Checklist