fix: repair unreadable component frontmatter and gate it in CI

Closed
GU
8d ago
+1026-341

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:

  1. fix(validation) — correct two rules in StructuralValidator that were reporting healthy components as broken.
  2. feat(validation) — a narrow frontmatter check (npm run frontmatter:check / :fix) plus a CI job that blocks a PR introducing unreadable frontmatter.
  3. fix(components) — the mechanical repair of 293 agents and commands.

Closes #708. Closes #788.

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

ProblemFilesEffect
argument-hint: [issue-number] unquoted266YAML 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 indicator22description: Beast Mode 2.0: a powerful… and tools: * — frontmatter doesn't parse.
model set to a human-readable label5Claude 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:

  • tools was required on agents. It's optional — an agent without it inherits every tool. (sub-agents)
  • name was required on commands. A slash command takes its name from its file name; name only 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:

beforeafter
passing359662
failing411108
STRUCT_E002 invalid YAML2220
STRUCT_E006 missing required field960
SEM_* semantic findings4747 (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 5 model labels.

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/description from 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.md documents a component format with no frontmatter at all, which is likely where some of these files came from.