fix(skill-validator): preserve blank lines inside block scalars in parse_frontmatter#238
Merged
potiuk merged 1 commit intoMay 20, 2026
Conversation
…rse_frontmatter `parse_frontmatter` treated any blank line as a terminator for the current key, so a block scalar with a paragraph break (a blank line between two indented paragraphs in a `|` or `>` value) silently lost everything after the first paragraph. Two consequences: 1. `validate_frontmatter` measures `len(fm["description"]) + len(fm["when_to_use"])` against `MAX_METADATA_CHARS` (the Claude Code truncation budget). With the dropped paragraphs the measurement was too small, so a frontmatter that actually exceeds the budget could pass the check. 2. `validate_principle_compliance` and `validate_trigger_preservation` only saw the truncated string, missing forbidden patterns or trigger phrasing that lived in the dropped paragraphs. No existing SKILL.md hit the bug, so it was latent. The `init_skill.py` scaffolding emits multi-line description and when_to_use blocks, and any author writing a two-paragraph description would have been silently mis-validated. Fix: in real YAML, a blank line inside a block scalar is part of the value, not a terminator. Only a new top-level key finalises the current value. Append blank lines to the value lines and let `.strip()` at finalisation discard leading/trailing blanks so single-line values are unaffected. Adds a regression test that asserts a `|` block scalar with an internal blank line keeps both paragraphs.
potiuk
approved these changes
May 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Fixes a latent correctness bug in
skill-validator'sparse_frontmatter, surfaced by a code review.The bug
parse_frontmattertreated any blank line as a terminator for the current key. A YAML block scalar (|or>) with a paragraph break (a blank line between two indented paragraphs) silently lost everything after the first paragraph.Two consequences:
validate_frontmattermeasureslen(fm["description"]) + len(fm["when_to_use"])againstMAX_METADATA_CHARS(the Claude Code truncation budget). With the dropped paragraphs the measurement was too small, so frontmatter that actually exceeded the budget could pass the check.validate_principle_complianceandvalidate_trigger_preservationonly saw the truncated string, missing forbidden patterns or trigger phrasing that lived in the dropped paragraphs.Why latent
No existing SKILL.md frontmatter currently uses a paragraph break in a block scalar, so nothing in-tree exercised the bug. But
init_skill.pyscaffolding emits multi-linedescriptionandwhen_to_useblocks, and any author writing a two-paragraph description would have been silently mis-validated.The fix
In real YAML, a blank line inside a block scalar is part of the value, not a terminator. Only a new top-level key finalises the current value. The parser now appends blank lines to the value lines and lets
.strip()at finalisation discard leading/trailing blanks so single-line values are unaffected.Changes
src/skill_validator/__init__.py—parse_frontmatter: blank line is content, not terminatortests/test_validator.py— regression test for a|block scalar with an internal blank lineValidation
pytest: 77 passedruff check/ruff format/mypy: cleanprekincludingskill-validate (.claude/skills/**): all hooks pass — every existing SKILL.md still validates after the parser change