Skip to content

fix(quality): score non-mapping metadata instead of crashing - #226

Merged
rng1995 merged 3 commits into
NVIDIA:mainfrom
MaxFreedomPollard:fix/quality-non-mapping-metadata
Oct 28, 2026
Merged

rng1995 merged 3 commits into
NVIDIA:mainfrom
MaxFreedomPollard:fix/quality-non-mapping-metadata

Conversation

@MaxFreedomPollard

Copy link
Copy Markdown
Contributor

Summary

A SKILL.md whose metadata is not a mapping crashes the quality check, and the whole Tier 1 run with it:

---
name: odd-metadata
description: A focused skill. Use when testing malformed contributor metadata.
metadata: [author, tags]
---
$ skillevaluator validate ./odd-metadata --tiers 1
...
  File ".../skillevaluator/validators/quality_score.py", line 889, in _check_spec_fields
    if field_name not in metadata or metadata[field_name] is None:
TypeError: list indices must be integers or slices, not str

metadata: true, metadata: 1.5, an unquoted date, and any list or string containing author or tags fail the same way. validate, tier1 and quality-check stop with a traceback and write no report (a catalog run lists the skill as an unexpected error), so the user never sees the schema finding that already describes the problem: Field 'metadata': Input should be a valid dictionary or instance of SkillMetadata.

Cause. _check_spec_fields replaced only falsy values with {} (frontmatter.get("metadata") or {}), then ran field_name in metadata and metadata[field_name] on whatever was left. No other Tier 1 check that reads frontmatter crashes on these values.

Fix. A metadata value that is not a mapping is scored as absent metadata: metadata.author and metadata.tags count as missing, as they already did for null, empty and other non-mapping values such as metadata: Jane Doe. Mapping values score exactly as before.

Verification

  • I am familiar with the Contributing Guidelines
  • Added or updated focused tests
  • Updated documentation for user-visible changes (changelog)
  • Ran make lint
  • Ran make test
  • Ran make build
  • Did not add credentials, private datasets, or proprietary benchmark content

New tests in tests/validators/test_quality_score.py: one runs the quality check on list, string, boolean, float and date metadata values, and one runs run_validation(checks="schema,quality") and checks that the run completes and reports the schema finding. All six raise TypeError without the fix and pass with it.

make test on this branch, run with a 60 s per-test timeout because a few process-cleanup tests hang in my sandbox: 14,441 passed, 25 skipped, 107 failed. The failures are environmental. 106 of them fail the same way on unpatched main (the Seatbelt and Docker process tests my sandboxed machine can't run, parallel catalog workers, sockets and writes under /tmp, a Python 3.9 python3 on PATH, and a pip TLS error in the packaging test). The last one is the OSS boundary scan hitting the 60 s limit under load; it passes on its own. make lint passes, and python -m build builds the sdist and wheel (with --installer uv, for the same pip TLS reason). The quality tests also pass on Python 3.13 with only the dev extra.

Release Impact

  • No user-visible release note needed
  • Updated CHANGELOG.md

🤖 Generated with Claude Code

The quality check's SKILL_SPEC field check read the frontmatter with
`frontmatter.get("metadata") or {}`, so only a falsy value became an empty
mapping, and then ran `field_name in metadata` and `metadata[field_name]` on
whatever was left. A `metadata` value that is not a mapping is either not
iterable (`true`, `1.5`, an unquoted date) or passes the membership test and
cannot be indexed by a string (a list or string containing `author` or
`tags`), so the check raised TypeError. Nothing caught it, so `validate`,
`tier1` and `quality-check` ended in a traceback and wrote no report, hiding
the schema finding that already describes the malformed field.

Score any non-mapping `metadata` as absent metadata. `metadata.author` and
`metadata.tags` then count as missing, which is how null, empty and other
non-mapping values such as `metadata: Jane Doe` were already scored. Mapping
values score as before.

Signed-off-by: Max Freedom Pollard <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>

@chrisknvidia chrisknvidia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM. I checked that the 6 new tests fail against main's quality_score.py (TypeError on list, scalar and date metadata) and pass with this change. test_quality_score.py passes in full. Treating non-dict metadata as empty keeps the quality check running, and the schema check still reports the malformed field. version.py already guards the same way.

Say that a non-mapping metadata value is invalid per the schema rather
than that the schema check reports it, which only holds when the schema
check runs too.

Signed-off-by: Max Freedom Pollard <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>

@rng1995 rng1995 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Automated review: 0 findings (0 P1, 0 P2, 0 P3); 0 posted inline.

Reviewed at e99a58a. No findings. The isinstance(dict) guard covers every non-mapping shape PyYAML produces (list, string, int, float, bool, date, set, binary), and the other Tier 1 readers of metadata already guard. I ran validate --tiers 1 --no-llm, tier1 --no-llm and quality-check on 26 metadata variants at main and at this head. On main, a list or string that contains author, a number, a bool, a date, a set or a binary value ends in a traceback with no report. At this head each one scores the same as missing metadata, and the schema check reports one HIGH finding. The 6 new tests fail on main and pass here. The two wording threads are addressed by the applied suggestion and can be resolved.

Not caused by this PR, for a separate issue: an impossible calendar date anywhere in the frontmatter (for example metadata: 2026-02-30) makes yaml.safe_load raise ValueError. schema.py and quality_score._parse_frontmatter catch only yaml.YAMLError, so validate still ends in a traceback on both main and this head.

@rng1995
rng1995 enabled auto-merge October 27, 2026 17:55
@MaxFreedomPollard

Copy link
Copy Markdown
Contributor Author

Thanks, I used your suggested wording in e99a58a.

@rng1995
rng1995 disabled auto-merge October 28, 2026 15:14
@rng1995
rng1995 merged commit 9cc9d2e into NVIDIA:main Oct 28, 2026
17 checks passed
@rng1995 rng1995 mentioned this pull request Oct 30, 2026
15 of 20 tasks
chrisknvidia pushed a commit that referenced this pull request Oct 30, 2026
* fix(quality): score non-mapping metadata instead of crashing
* fix(quality): reword the non-mapping metadata comment

Signed-off-by: Max Freedom Pollard <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants