Files
hermes-agent/tests/skills/test_authoring_standards.py
T
teknium1 55982159dd feat(tests): CI-enforce skill authoring standards; clear all remaining debt
New tests/skills/test_authoring_standards.py parametrizes every bundled +
optional SKILL.md (1148 checks) against the mechanically-verifiable subset
of the hardline standards:
- required frontmatter fields (name/description/version/author/license/
  platforms) + tags
- frontmatter name == directory name
- description <= 60 chars, ends with period, no marketing words
- related_skills resolve in-repo
- no machine-local paths
- <= 100k chars
Grandfather dict for legacy debt ships EMPTY — all pre-existing violations
fixed in this PR:

- 13 frontmatter names canonicalized to their directory names (the install
  identifier); all related_skills references updated (comfyui -> stable-
  diffusion). Fixes the class behind PR #42788's report; also fixes
  here.now's invalid dot-name.
- optional-skills/devops/cli -> inference-sh-cli (dir was the generic
  'cli'; fm name was right) incl. docs pages (en + zh-Hans), catalog row,
  sidebar entry.
- pytorch-fsdp: 157k generated 'Quick Reference' dump moved to
  references/common-patterns.md; SKILL.md 159k -> 2.5k with a pointer.
- research-paper-writing: 31.7k Phase 5 drafting section moved to
  references/phase5-paper-drafting.md; SKILL.md 103k -> 71k.

Docs regenerated with scope discipline.
2026-08-08 15:43:00 -07:00

150 lines
5.0 KiB
Python

"""CI enforcement of the skill authoring standards (AGENTS.md hardline).
Every bundled (skills/) and optional (optional-skills/) SKILL.md must satisfy
the programmatically-checkable subset of the authoring standards. Judgment
calls (tier placement, router-skill smell, prose quality) stay with review;
everything here is mechanical.
Pre-existing violations that need non-trivial content work are grandfathered
in the GRANDFATHER dict below. Do NOT add new entries for new skills — fix
the skill instead. Remove entries as the debt is paid down.
"""
import re
from pathlib import Path
import pytest
import yaml
REPO = Path(__file__).resolve().parents[2]
MARKETING = re.compile(
r"\b(powerful|comprehensive|seamless|revolutionary|cutting-edge|state-of-the-art)\b",
re.I,
)
MACHINE_LOCAL = re.compile(r"/home/(?!runner\b)[a-z0-9_-]+/|[A-Z]:\\+Users\\+(?!<)")
# ---------------------------------------------------------------------------
# Grandfathered pre-existing debt. Shrink this list; never grow it.
# ---------------------------------------------------------------------------
GRANDFATHER: dict[str, set[str]] = {
# (empty — the Aug 2026 sweep cleared all mechanical violations)
}
def _skill_paths():
return sorted(
list(REPO.glob("skills/**/SKILL.md"))
+ list(REPO.glob("optional-skills/**/SKILL.md"))
)
def _rel(p: Path) -> str:
return str(p.parent.relative_to(REPO))
def _params():
return [pytest.param(p, id=_rel(p)) for p in _skill_paths()]
def _grandfathered(p: Path, rule: str) -> bool:
return rule in GRANDFATHER.get(_rel(p), set())
def _frontmatter(p: Path):
content = p.read_text(encoding="utf-8")
assert content.startswith("---"), f"{_rel(p)}: SKILL.md must start with ---"
m = re.search(r"\n---\s*\n", content[3:])
assert m, f"{_rel(p)}: unclosed frontmatter"
fm = yaml.safe_load(content[3 : m.start() + 3])
assert isinstance(fm, dict), f"{_rel(p)}: frontmatter must be a YAML mapping"
return fm, content
ALL_SKILL_NAMES = None
def _all_names():
global ALL_SKILL_NAMES
if ALL_SKILL_NAMES is None:
names = set()
for p in _skill_paths():
names.add(p.parent.name)
ALL_SKILL_NAMES = names
return ALL_SKILL_NAMES
def test_at_least_the_expected_population():
# sanity: the globs actually find the trees (not a count snapshot)
paths = _skill_paths()
assert any("optional-skills" in str(p) for p in paths)
assert any(str(p.parent).startswith(str(REPO / "skills")) for p in paths)
@pytest.mark.parametrize("p", _params())
def test_required_frontmatter_fields(p):
fm, _ = _frontmatter(p)
missing = [
f
for f in ("name", "description", "version", "author", "license", "platforms")
if f not in fm
]
if missing and not _grandfathered(p, "fields"):
pytest.fail(f"{_rel(p)}: missing frontmatter fields: {missing}")
hermes = (fm.get("metadata") or {}).get("hermes") or {}
if not (hermes.get("tags") or fm.get("tags")) and not _grandfathered(p, "tags"):
pytest.fail(f"{_rel(p)}: no tags (metadata.hermes.tags or top-level tags)")
@pytest.mark.parametrize("p", _params())
def test_name_matches_directory(p):
fm, _ = _frontmatter(p)
if fm.get("name") != p.parent.name and not _grandfathered(p, "name"):
pytest.fail(
f"{_rel(p)}: frontmatter name {fm.get('name')!r} != dir {p.parent.name!r}"
)
@pytest.mark.parametrize("p", _params())
def test_description_hardline(p):
fm, _ = _frontmatter(p)
desc = str(fm.get("description") or "")
if _grandfathered(p, "description"):
return
assert len(desc) <= 60, f"{_rel(p)}: description {len(desc)} chars (hardline 60)"
assert desc.rstrip().endswith("."), f"{_rel(p)}: description must end with a period"
m = MARKETING.search(desc)
assert not m, f"{_rel(p)}: marketing word in description: {m.group(0)!r}"
@pytest.mark.parametrize("p", _params())
def test_related_skills_resolve(p):
fm, _ = _frontmatter(p)
hermes = (fm.get("metadata") or {}).get("hermes") or {}
dangling = [
rs for rs in (hermes.get("related_skills") or []) if rs not in _all_names()
]
if dangling and not _grandfathered(p, "related"):
pytest.fail(f"{_rel(p)}: dangling related_skills: {dangling}")
@pytest.mark.parametrize("p", _params())
def test_no_machine_local_paths(p):
_, content = _frontmatter(p)
m = MACHINE_LOCAL.search(content)
if m and not _grandfathered(p, "paths"):
pytest.fail(f"{_rel(p)}: machine-local path {m.group(0)!r}")
@pytest.mark.parametrize("p", _params())
def test_size_limit(p):
_, content = _frontmatter(p)
if len(content) > 100_000 and not _grandfathered(p, "size"):
pytest.fail(
f"{_rel(p)}: {len(content)} chars > 100k — split into references/"
)
def test_grandfather_entries_still_needed():
"""A grandfather entry whose violation is fixed must be removed."""
for rel in GRANDFATHER:
assert (REPO / rel / "SKILL.md").exists(), f"stale grandfather entry: {rel}"