fix: repair unreadable component frontmatter and gate it in CI
Summary
A component whose YAML frontmatter doesn't parse is still published by the catalog and still installs — it just never loads in Claude Code, and nothing tells anyone. This PR fixes every component in that state, and adds a check so the next one can't land.
Three commits, each reviewable on its own:
fix(validation)— correct two rules inStructuralValidatorthat were reporting healthy components as broken.feat(validation)— a narrow frontmatter check (npm run frontmatter:check/:fix) plus a CI job that blocks a PR introducing unreadable frontmatter.fix(components)— the mechanical repair of 293 agents and commands.
Why the existing audit didn't catch this
component-security-validation.yml does detect these files, but it runs with continue-on-error: true and reports every content-quality finding at once, so the signal never blocks anything. It also can't be made blocking as-is: on main today it fails 411 of 770 components, most of them for reasons a contributor can't act on.
The new check answers exactly one question — can Claude Code read this file's frontmatter? — and runs only against the files a PR touches. That makes it safe to block on, and it leaves the components that still need a frontmatter block from their original author out of everyone else's way.
What was actually broken
| Problem | Files | Effect |
|---|---|---|
argument-hint: [issue-number] unquoted | 266 | YAML reads the brackets as a flow sequence, so the hint arrives as a list. With two bracket groups on one line ([name] [--flag]) the whole block fails to parse. |
description / tools with an unquoted YAML indicator | 22 | description: Beast Mode 2.0: a powerful… and tools: * — frontmatter doesn't parse. |
model set to a human-readable label | 5 | Claude Sonnet 4.5, Claude Sonnet 4.5 (copilot), Claude Sonnet 4 — not values Claude Code accepts. |
The two validator rules that were wrong, per the current docs:
toolswas required on agents. It's optional — an agent without it inherits every tool. (sub-agents)namewas required on commands. A slash command takes its name from its file name;nameonly sets the display label. (slash-commands)
Together those two produced ~300 false failures. The accepted model list was also stale (claude-3-* only), so inherit, fable and current IDs like claude-opus-5 were all reported as unknown.
Effect on npm run security-audit
Run against the full component tree, before and after:
| before | after | |
|---|---|---|
| passing | 359 | 662 |
| failing | 411 | 108 |
STRUCT_E002 invalid YAML | 222 | 0 |
STRUCT_E006 missing required field | 96 | 0 |
SEM_* semantic findings | 47 | 47 (untouched) |
The remaining 108 are 61 components with no frontmatter block at all — a content problem that needs each component's author, not a mechanical fix — plus the pre-existing semantic findings, which this PR deliberately doesn't touch.
How the repair was produced
npm run frontmatter:fix. It quotes plain scalars YAML would misread, promotes multi-line descriptions to block scalars, and maps human-readable model labels onto their alias. Every repair is re-parsed before it's written, so it can only replace valid frontmatter with valid frontmatter.
Verified across all 293 changed files:
- no body content changed — the diff touches frontmatter lines only;
- all 293 parse as a YAML mapping;
- every field keeps its author's value — the only semantic changes are the 66
argument-hints that were wrongly a list and are now the string the docs show, and the 5modellabels.
Tests
cli-tool/tests/unit/validate-frontmatter.test.js — 22 tests covering the quoting rules, model normalisation, each validation code, and a round-trip property: the repair never emits frontmatter that fails to parse.
The 6 pre-existing failures in tests/validation/ (ReferenceValidator, IntegrityValidator) are unchanged by this PR — they fail identically on main.
Not included
The catalog (docs/components.json and the dashboard artifacts) is regenerated daily by the Update JSON Data workflow, so it's left alone here rather than adding several MB of generated diff.
Follow-ups worth a separate issue
- 58 components ship with no frontmatter block at all (53 commands, 5 agents). Each needs a
name/descriptionfrom someone who knows the component. - Agent files committed at duplicated nested paths —
cli-tool/components/agents/business-marketing/cli-tool/components/agents/business-marketing/…, three levels deep. CONTRIBUTING.mddocuments a component format with no frontmatter at all, which is likely where some of these files came from.