Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions tools/skill-validator/src/skill_validator/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,12 +226,15 @@ def parse_frontmatter(text: str) -> dict[str, str] | None:
# Strip trailing whitespace but keep leading (for folded scalars)
line = raw_line.rstrip()

# Empty line ends a folded scalar
# Blank line: 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. Preserve the blank so
# multi-paragraph descriptions are measured and validated in
# full; a trailing/leading blank is removed by `.strip()` at
# finalisation, so single-line values are unaffected.
if line == "":
if current_key is not None:
result[current_key] = "\n".join(current_value_lines).strip()
current_key = None
current_value_lines = []
current_value_lines.append("")
continue

# New top-level key?
Expand Down
24 changes: 24 additions & 0 deletions tools/skill-validator/tests/test_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,30 @@ def test_folded_scalar(self) -> None:
assert "First line" in fm["description"]
assert "Second line" in fm["description"]

def test_block_scalar_preserves_internal_blank_line(self) -> None:
"""Blank lines inside a ``|`` block scalar are part of the value.

Regression: the parser used to treat any blank line as a
terminator, silently dropping everything after the first
paragraph break. That made ``MAX_METADATA_CHARS`` measurement
and principle/trigger validation operate on truncated text.
"""
text = (
"---\n"
"name: my-skill\n"
"description: |\n"
" Paragraph one.\n"
"\n"
" Paragraph two, which used to be dropped.\n"
"license: Apache-2.0\n"
"---\n"
)
fm = parse_frontmatter(text)
assert fm is not None
assert "Paragraph one." in fm["description"]
assert "Paragraph two" in fm["description"]
assert fm["license"] == "Apache-2.0"

def test_missing_frontmatter(self) -> None:
assert parse_frontmatter("# no frontmatter\n") is None

Expand Down
Loading