Skip to content

Commit 81d4a82

Browse files
committed
Add agent payload front-matter type validation
1 parent 52d9d5b commit 81d4a82

1 file changed

Lines changed: 39 additions & 2 deletions

File tree

tests/validate-agents-payload.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,16 @@ def parse_front_matter(path: str) -> dict | None:
8181
return None
8282
yaml_block = content[3:end]
8383
try:
84-
return yaml.safe_load(yaml_block) or {}
84+
payload = yaml.safe_load(yaml_block)
8585
except yaml.YAMLError:
8686
fail("YAML front matter is invalid", path)
8787
return None
88+
if payload is None:
89+
return {}
90+
if not isinstance(payload, dict):
91+
fail("YAML front matter must be a mapping", path)
92+
return None
93+
return payload
8894

8995

9096
def assert_front_matter_field(fm: dict, field: str, context: str) -> None:
@@ -107,6 +113,22 @@ def assert_front_matter_field_exists(fm: dict, field: str, context: str) -> None
107113
ok(f"Front matter field '{field}' declared in {context}")
108114

109115

116+
def assert_front_matter_field_type(fm: dict, field: str, expected_type: type | tuple[type, ...], context: str, *, allow_empty: bool = False) -> None:
117+
if field not in fm:
118+
return
119+
120+
value = fm[field]
121+
if not isinstance(value, expected_type):
122+
fail(f"Front matter field '{field}' must be {expected_type}", context)
123+
return
124+
125+
if not allow_empty and (value == "" or value == []):
126+
fail(f"Front matter field '{field}' must not be empty", context)
127+
return
128+
129+
ok(f"Front matter field '{field}' has expected type in {context}")
130+
131+
110132
def assert_markdown_section(path: str, section: str) -> None:
111133
"""Assert that '## section' is present in a Markdown file."""
112134
content = read_file(path)
@@ -258,7 +280,9 @@ def known_skill_slugs() -> list[str]:
258280
assert_front_matter_field(fm, "name", ctx)
259281
assert_front_matter_field(fm, "description", ctx)
260282
assert_front_matter_field(fm, "primary-skill", ctx)
283+
assert_front_matter_field_type(fm, "primary-skill", str, ctx)
261284
assert_front_matter_field_exists(fm, "supporting-skills", ctx)
285+
assert_front_matter_field_type(fm, "supporting-skills", list, ctx)
262286

263287
# 'name' slug must match the slug derived from the filename.
264288
expected_slug = re.sub(r"^fast-forward-", "", os.path.splitext(filename)[0])
@@ -508,7 +532,11 @@ def known_skill_slugs() -> list[str]:
508532
continue
509533

510534
fm = parse_front_matter(path)
511-
if fm is None or not isinstance(fm.get("primary-skill"), str):
535+
if fm is None:
536+
continue
537+
538+
assert_front_matter_field_type(fm, "primary-skill", str, filename)
539+
if not isinstance(fm.get("primary-skill"), str):
512540
continue
513541

514542
primary = fm["primary-skill"]
@@ -533,6 +561,15 @@ def known_skill_slugs() -> list[str]:
533561
continue
534562

535563
supporting = fm["supporting-skills"]
564+
if not isinstance(supporting, list):
565+
fail(f"supporting-skills must be a list", filename)
566+
continue
567+
568+
for idx, slug in enumerate(supporting):
569+
if not isinstance(slug, str):
570+
fail(f"supporting-skills[{idx}] must be a string", filename)
571+
continue
572+
536573
if not supporting:
537574
ok(f"No supporting-skills declared (empty list OK) in {filename}")
538575
continue

0 commit comments

Comments
 (0)