Skip to content

test: add failing tests for BOM frontmatter and missing SkillName#1583

Merged
Aaronontheweb merged 5 commits into
netclaw-dev:devfrom
Aaronontheweb:test/skill-scanner-bom-and-skillname
Jul 6, 2026
Merged

test: add failing tests for BOM frontmatter and missing SkillName#1583
Aaronontheweb merged 5 commits into
netclaw-dev:devfrom
Aaronontheweb:test/skill-scanner-bom-and-skillname

Conversation

@Aaronontheweb

@Aaronontheweb Aaronontheweb commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator

Fixes the two SkillScanner bugs from #1582 — and, following review, hardens a related crash path. This PR ships both the tests and the fixes (all green).

close #1582

Fixes

  1. UTF-8 BOM frontmatter parsing — SKILL.md files saved with a UTF-8 BOM were rejected by ExtractFrontmatter because content.StartsWith("---") failed when the BOM preceded the delimiter. ExtractFrontmatter now strips the leading BOM before the delimiter check, so BOM-prefixed skills parse correctly. (Note: on the disk-scan path File.ReadAllText already strips the BOM; the strip in ExtractFrontmatter covers direct-string API callers.)

  2. Missing SkillName on issues — every SkillScanIssue now populates SkillName, so netclaw skill issues can identify which skill is broken. Names are canonicalized via NormalizeSkillName so errored rows render the same lowercased name as accepted skills.

Review-driven hardening

  1. Scan-aborting crash on degenerate frontmatter — a degenerate ---\n--- block (empty YAML body) made ExtractFrontmatter slice a negative-length range and throw ArgumentOutOfRangeException. Because Scan's parse calls are unguarded, this propagated out and aborted the entire skill-discovery pass (no skills loaded at all) — reachable on disk since File.ReadAllText strips the BOM down to a plain ---\n---. Now guarded to return null, reported as invalid frontmatter.

  2. ResourceEnumerationFailed SkillName — was derived from the parent directory (Path.GetDirectoryName(skillDirectory)), yielding the container name (e.g. files) instead of the skill name. Fixed to use the leaf directory.

  3. Dead-code cleanup — removed no-op caller-side content.TrimStart('') guards; BOM tolerance now lives solely in ExtractFrontmatter.

Tests

  • ExtractFrontmatter handles UTF-8 BOM
  • SkillScanIssue populates SkillName for broken frontmatter, and normalizes it from a mixed-case directory
  • ExtractFrontmatter returns null (does not throw) for degenerate blocks — plain, BOM-prefixed, and no-trailing-newline
  • Scan does not abort on a degenerate-frontmatter skill and keeps discovering healthy siblings

All 45 SkillScannerTests pass; dotnet slopwatch analyze reports 0 issues.

… all issues

- Strip UTF-8 BOM (\uFEFF) before checking for YAML frontmatter delimiter
- Populate SkillName on all SkillScanIssue records created in SkillScanner
- Fix BOM check in ParseSkillFile and ParseFlatSkillFile for issue kind determination

Fixes netclaw-dev#1582
@Aaronontheweb Aaronontheweb added skills Skill loading, handling, authoring, indexing, and evals. bug Something isn't working labels Jul 5, 2026
…ormalize issue SkillNames

Review follow-ups on the BOM/SkillName change:

- ExtractFrontmatter threw ArgumentOutOfRangeException on a degenerate
  "---\n---" block (empty YAML body): the opening line's newline is also
  the closing delimiter, so the slice computed a negative-length range.
  Because Scan's parse calls are unguarded, this propagated out and aborted
  the entire skill-discovery pass (no skills loaded at all). Since
  File.ReadAllText strips the BOM, a plain "---\n---" SKILL.md on disk hit
  this too. Now guarded to return null (reported as invalid frontmatter).

- ResourceEnumerationFailed derived SkillName from the parent directory
  (Path.GetDirectoryName(skillDirectory)), yielding the container name
  ("files") instead of the skill name. Fixed to use the leaf directory.

- Normalized all error-path SkillName derivations via NormalizeSkillName so
  errored rows render the canonical lowercased name, consistent with
  accepted skills.

- Removed dead caller-side content.TrimStart('') no-ops: content comes
  from File.ReadAllText which already strips the BOM, so BOM tolerance lives
  solely in ExtractFrontmatter.

Adds regression tests: degenerate-block returns null (does not throw), Scan
survives a degenerate SKILL.md and keeps discovering healthy siblings, and
issue SkillNames are normalized from mixed-case directories.

@Aaronontheweb Aaronontheweb left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Aaronontheweb
Aaronontheweb enabled auto-merge (squash) July 5, 2026 20:08
@Aaronontheweb
Aaronontheweb merged commit 0bf919b into netclaw-dev:dev Jul 6, 2026
15 checks passed
Aaronontheweb added a commit that referenced this pull request Jul 8, 2026
)

* test: add failing tests for BOM frontmatter parsing and missing SkillName in issues

* fix: handle UTF-8 BOM in ExtractFrontmatter and populate SkillName on all issues

- Strip UTF-8 BOM (\uFEFF) before checking for YAML frontmatter delimiter
- Populate SkillName on all SkillScanIssue records created in SkillScanner
- Fix BOM check in ParseSkillFile and ParseFlatSkillFile for issue kind determination

Fixes #1582

* fix(skills): prevent scan-aborting crash on degenerate frontmatter; normalize issue SkillNames

Review follow-ups on the BOM/SkillName change:

- ExtractFrontmatter threw ArgumentOutOfRangeException on a degenerate
  "---\n---" block (empty YAML body): the opening line's newline is also
  the closing delimiter, so the slice computed a negative-length range.
  Because Scan's parse calls are unguarded, this propagated out and aborted
  the entire skill-discovery pass (no skills loaded at all). Since
  File.ReadAllText strips the BOM, a plain "---\n---" SKILL.md on disk hit
  this too. Now guarded to return null (reported as invalid frontmatter).

- ResourceEnumerationFailed derived SkillName from the parent directory
  (Path.GetDirectoryName(skillDirectory)), yielding the container name
  ("files") instead of the skill name. Fixed to use the leaf directory.

- Normalized all error-path SkillName derivations via NormalizeSkillName so
  errored rows render the canonical lowercased name, consistent with
  accepted skills.

- Removed dead caller-side content.TrimStart('') no-ops: content comes
  from File.ReadAllText which already strips the BOM, so BOM tolerance lives
  solely in ExtractFrontmatter.

Adds regression tests: degenerate-block returns null (does not throw), Scan
survives a degenerate SKILL.md and keeps discovering healthy siblings, and
issue SkillNames are normalized from mixed-case directories.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working skills Skill loading, handling, authoring, indexing, and evals.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: skill scanner doesn't handle UTF-8 BOM and SkillScanIssue missing SkillName

1 participant