chore: fold skill-frontmatter into skill-size-check

skill-frontmatter was a 62-line bash script inlined in
.pre-commit-config.yaml, re-parsing SKILL.md frontmatter with grep and
awk to check for name/description/metadata.version fields.
skill-size-check.sh already parses the same frontmatter block with
PyYAML for its ADR-0020 checks, so the two checks belonged in one
script.

Adds a ~20-line required-frontmatter check (name, description,
metadata.version as three-part semver) to scripts/skill-size-check.sh.
Removes the inline skill-frontmatter hook from .pre-commit-config.yaml
and deletes tests/test-skill-frontmatter.sh (366 lines). Removes the
79-line "the other hook on that scope" discussion from
docs/spec/gates.md and its now-dangling cross-reference, replacing
both with a one-line note of the fold, and updates the pre-push hook
counts there.

Updates fixture builders in test-skill-size-check.sh,
test-adr0020-body-checks.sh, test-adr0020-targets.sh,
test-adr0020-differential.sh, and test-vale-hooks-consumer.sh to carry
valid metadata.version so the new check doesn't spuriously fail
existing fixtures that predate it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
This commit is contained in:
2026-09-12 20:00:28 +00:00
parent e647f14535
commit c8a7c9ea87
10 changed files with 62 additions and 537 deletions

View File

@@ -1353,6 +1353,26 @@ for path in files:
% (path, exc))
continue
# Required-field presence (folded in from the former standalone
# `skill-frontmatter` hook). description_value() above already proved the
# frontmatter is valid YAML and a mapping, so a second yaml.safe_load here
# cannot raise.
fm_data = yaml.safe_load(fm_match.group(1)) or {}
name_val = fm_data.get('name')
if not isinstance(name_val, str) or not name_val.strip():
error("%s: name field is missing or empty (required frontmatter field)."
% path)
version_val = None
metadata_val = fm_data.get('metadata')
if isinstance(metadata_val, dict):
version_val = metadata_val.get('version')
if version_val is None:
error("%s: metadata.version field is missing (required frontmatter "
"field, e.g. \"1.0.0\")." % path)
elif not re.match(r'^\d+\.\d+\.\d+$', str(version_val).strip().strip('\'"')):
error("%s: metadata.version is malformed (%r) -- expected a "
"three-part semver, e.g. \"1.0.0\"." % (path, version_val))
body = content[fm_match.end():]
skill_dir = os.path.dirname(os.path.abspath(path))
# ADR-0020's hand-invocation carve-out. See hand_invoked() for what it lifts