Merge pull request #936 from benrfairless/fix/frontmatter-yaml-validation

fix(frontmatter): repair 14 unloadable YAML blocks + add gate G10
This commit is contained in:
Alireza Rezvani 2026-08-21 10:43:35 +02:00 committed by GitHub
commit 4570768781
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 271 additions and 12 deletions

View file

@ -1,5 +1,5 @@
---
description: Deep-dive feature repair — systematically fix an entire feature/module. Usage: /focused-fix <feature-path>
description: "Deep-dive feature repair — systematically fix an entire feature/module. Usage: /focused-fix <feature-path>"
---
Systematically repair the feature/module at `$ARGUMENTS` using the focused-fix 5-phase protocol.

View file

@ -102,6 +102,13 @@ jobs:
run: |
python3 scripts/check_paths.py --all
# Every other gate reads frontmatter with a regex, so malformed YAML used
# to pass CI while Claude Code silently loaded the skill with no
# description. G10 parses it properly.
- name: Frontmatter YAML validator (gate G10 — blocking)
run: |
python3 scripts/check_frontmatter.py --all
- name: Dual-publish drift guard (gate G4 — blocking)
run: |
python3 scripts/check_dual_publish.py

View file

@ -1,5 +1,5 @@
---
description: Top-level Business Operations router. Routes the inquiry to one of six BizOps sub-skills (process, vendor, capacity, comms, knowledge, procurement) and returns a digest. Invokes the business-operations-skills orchestrator (context: fork).
description: "Top-level Business Operations router. Routes the inquiry to one of six BizOps sub-skills (process, vendor, capacity, comms, knowledge, procurement) and returns a digest. Invokes the business-operations-skills orchestrator (context: fork)."
argument-hint: "<inquiry>"
---

View file

@ -1,6 +1,6 @@
---
name: cs-arquiteto
description: Company Architect — a senior chief of staff who builds a business from scratch as an OKF (Open Knowledge Format) bundle: a tree of version-controllable .md files with frontmatter type, links forming a graph, and reserved index.md/log.md. Guides the founder through a 12-phase interview (foundation, strategy, market, financial, sales, marketing, product, operations, tech, people, legal, governance), one phase at a time, at most 3-5 questions per block, confirming before generating each concept. Trigger when the user wants to create, structure, or document an entire company as folders and markdown files, or mentions company as code, company knowledge base for AI, OKF, or knowledge bundle. Works in English. Never dumps the company all at once — it interviews, validates, and builds phase by phase.
description: "Company Architect — a senior chief of staff who builds a business from scratch as an OKF (Open Knowledge Format) bundle: a tree of version-controllable .md files with frontmatter type, links forming a graph, and reserved index.md/log.md. Guides the founder through a 12-phase interview (foundation, strategy, market, financial, sales, marketing, product, operations, tech, people, legal, governance), one phase at a time, at most 3-5 questions per block, confirming before generating each concept. Trigger when the user wants to create, structure, or document an entire company as folders and markdown files, or mentions company as code, company knowledge base for AI, OKF, or knowledge bundle. Works in English. Never dumps the company all at once — it interviews, validates, and builds phase by phase."
skills: c-level-advisor/arquiteto-de-empresa/skills/arquiteto-de-empresa
domain: c-level
model: opus

View file

@ -1,3 +1,8 @@
---
name: devils-advocate
description: "Adversarial reviewer for executive plans, proposals, and decisions. Returns exactly three specific concerns, each severity-rated CRITICAL / HIGH / MEDIUM, with the evidence that would confirm or kill it. Use before committing resources to a plan, before a board or investor presentation, or when feedback so far has been one-sidedly positive. Not a code reviewer."
---
# Devil's Advocate Agent
**Role:** Adversarial thinker. Finds what's wrong before others do.

View file

@ -1,5 +1,5 @@
---
description: Top-level Commercial router. Routes the inquiry to one of seven Commercial sub-skills (pricing, deal, partner, channel, policy, RFP, forecast) and returns a digest. Invokes the commercial-skills orchestrator (context: fork).
description: "Top-level Commercial router. Routes the inquiry to one of seven Commercial sub-skills (pricing, deal, partner, channel, policy, RFP, forecast) and returns a digest. Invokes the commercial-skills orchestrator (context: fork)."
argument-hint: "<inquiry>"
---

View file

@ -1,6 +1,6 @@
---
name: cs-ai-act-compliance
description: EU AI Act (Regulation (EU) 2024/1689) Article-cited compliance operator. Three decisions: AI system risk tier (Article 5 / 6+ Annex III / 50 / minimal), conformity assessment routing (Article 43 Module A vs H + Annex IV docs), per-role obligation matrix (provider/deployer/importer/distributor + GPAI). NOT executive AI strategy (see cs-caio-advisor). NOT a legal substitute (engage counsel for novel cases).
description: "EU AI Act (Regulation (EU) 2024/1689) Article-cited compliance operator. Three decisions: AI system risk tier (Article 5 / 6+ Annex III / 50 / minimal), conformity assessment routing (Article 43 Module A vs H + Annex IV docs), per-role obligation matrix (provider/deployer/importer/distributor + GPAI). NOT executive AI strategy (see cs-caio-advisor). NOT a legal substitute (engage counsel for novel cases)."
skills: ra-qm-team/skills/eu-ai-act-specialist
domain: compliance-os
model: opus

View file

@ -1,6 +1,6 @@
---
name: cs-aims-iso42001
description: ISO/IEC 42001:2023 AI Management System (AIMS) implementation + internal audit operator. Three decisions: AIMS gaps against Clauses 4-10, AI risk register per Annex A + ISO 23894, Clause 9.2 internal audit plan. NOT executive AI strategy (see cs-caio-advisor). NOT EU AI Act conformity (see cs-ai-act-compliance).
description: "ISO/IEC 42001:2023 AI Management System (AIMS) implementation + internal audit operator. Three decisions: AIMS gaps against Clauses 4-10, AI risk register per Annex A + ISO 23894, Clause 9.2 internal audit plan. NOT executive AI strategy (see cs-caio-advisor). NOT EU AI Act conformity (see cs-ai-act-compliance)."
skills: ra-qm-team/skills/iso42001-specialist
domain: compliance-os
model: opus

View file

@ -1,6 +1,6 @@
---
name: cs-fda-qsr-auditor
description: FDA 21 CFR 820 (QSR / QMSR) auditor persona. Substantially harmonized with ISO 13485 post-Feb 2026 via FDA Final Rule incorporating ISO 13485 by reference. Adds FDA-specific overlays: labeling (21 CFR 801), complaint handling (21 CFR 820.198), MDR reporting (21 CFR 803), 510(k) / PMA submissions. NOT FDA submission strategy (route to fda-consultant-specialist for that).
description: "FDA 21 CFR 820 (QSR / QMSR) auditor persona. Substantially harmonized with ISO 13485 post-Feb 2026 via FDA Final Rule incorporating ISO 13485 by reference. Adds FDA-specific overlays: labeling (21 CFR 801), complaint handling (21 CFR 820.198), MDR reporting (21 CFR 803), 510(k) / PMA submissions. NOT FDA submission strategy (route to fda-consultant-specialist for that)."
skills: ra-qm-team/skills/fda-consultant-specialist
domain: compliance-os
model: opus

View file

@ -1,3 +1,8 @@
---
name: experiment-runner
description: "Runs one iteration of an autoresearch experiment loop. Reads experiment state from .autoresearch/{domain}/{name}/, makes exactly ONE change to the target file, commits it, evaluates via run_experiment.py, and reports KEEP / DISCARD / CRASH. Spawned per iteration by /ar:run and /ar:loop. Never modifies the evaluator. Not for general refactoring or multi-change edits."
---
# Experiment Runner Agent
You are an autonomous experimenter. Your job is to optimize a target file by a measurable metric, one change at a time.

View file

@ -1,6 +1,6 @@
---
name: design-system
description: Captures the user's brand identity once via a 10-question onboarding wizard (primary/accent HEX + heading + body Google Fonts + design style editorial/technical/minimal/playful + default output directory + syntax theme + TOC behavior + optional logo/company), validates body-text and link contrast against WCAG 2.2 AA, derives 12 CSS custom properties in HSL space, and stores the result for every markdown-html converter to consume. Use before any markdown-html conversion. Triggers on first-run onboarding ("set up the brand", "configure markdown-html", "run onboarding"), on explicit reset ("reset the design system", "re-onboard"), and is checked by every converter via config_loader.py before rendering. Refuses to save if body-text contrast fails AA 4.5:1 or the output dir isn't writable. Precedence: project (./.markdown-html/) > global (~/.config/markdown-html/) > built-in defaults; MARKDOWN_HTML_NO_CONFIG=1 bypasses.
description: Captures the user's brand identity once via a 10-question onboarding wizard (primary/accent HEX + heading + body Google Fonts + design style editorial/technical/minimal/playful + default output directory + syntax theme + TOC behavior + optional logo/company), validates body-text and link contrast against WCAG 2.2 AA, derives 12 CSS custom properties in HSL space, and stores the result for every markdown-html converter to consume. Use before any markdown-html conversion. Triggers on first-run onboarding ("set up the brand", "configure markdown-html", "run onboarding"), on explicit reset ("reset the design system", "re-onboard"), and is checked by every converter via config_loader.py before rendering. Refuses to save if body-text contrast fails AA 4.5:1 or the output dir isn't writable. Precedence is project (./.markdown-html/) > global (~/.config/markdown-html/) > built-in defaults; MARKDOWN_HTML_NO_CONFIG=1 bypasses.
version: 2.10.0
author: Alireza Rezvani
license: MIT

View file

@ -1,6 +1,6 @@
---
name: md-slides
description: Converts a markdown deck (slides separated by `---` HR boundaries or by `# ` H1 headings, with optional `<!-- notes: ... -->` presenter notes blocks) into a single-file HTML presentation with arrow-key / space / PgDn / PgUp / Home / End / P / Esc keyboard navigation, presenter mode (split view with current slide + speaker notes + clock + next-slide preview), URL-hash deep linking, and `@media print` page-per-slide for PDF export. Triggers when the markdown-html-orchestrator classifies an input as SLIDES, or when invoked directly via /cs:md-slides. Reuses md-document's markdown parser for slide-body rendering and reads design-system tokens via config_loader.py. Refuses if input has no clear slide boundaries, produces a 1-slide deck, or `--strict-notes` is on with < 50% notes coverage. Use after orchestrator routing.
description: "Converts a markdown deck (slides separated by `---` HR boundaries or by `# ` H1 headings, with optional `<!-- notes: ... -->` presenter notes blocks) into a single-file HTML presentation with arrow-key / space / PgDn / PgUp / Home / End / P / Esc keyboard navigation, presenter mode (split view with current slide + speaker notes + clock + next-slide preview), URL-hash deep linking, and `@media print` page-per-slide for PDF export. Triggers when the markdown-html-orchestrator classifies an input as SLIDES, or when invoked directly via /cs:md-slides. Reuses md-document's markdown parser for slide-body rendering and reads design-system tokens via config_loader.py. Refuses if input has no clear slide boundaries, produces a 1-slide deck, or `--strict-notes` is on with < 50% notes coverage. Use after orchestrator routing."
version: 2.10.3
author: Alireza Rezvani
license: MIT

View file

@ -1,6 +1,6 @@
---
name: cs-andreessen
description: Marc Andreessen-mode operator. Runs on a fixed anti-sycophancy operating prompt — leads with the strongest counterargument, never validates premises or praises the question, no disclaimers, no morals lectures, explicit confidence levels (high/moderate/low/unknown), never apologizes for disagreeing, never capitulates without new evidence. Pressure-tests ventures/ideas/features/career bets through Andreessen's documented frameworks: market dominates team and product, product/market fit is the only milestone that matters, bias to build. Issues hard verdicts (BUILD-POUR-FUEL / MARKET-FIRST-DERISK / KILL-OR-REPICK-MARKET) backed by deterministic tools. Also runs the 3x5-card + Anti-Todo daily routine. Refuses to soften a dead-market verdict. Refuses to cite a quote without a confidence level.
description: "Marc Andreessen-mode operator. Runs on a fixed anti-sycophancy operating prompt — leads with the strongest counterargument, never validates premises or praises the question, no disclaimers, no morals lectures, explicit confidence levels (high/moderate/low/unknown), never apologizes for disagreeing, never capitulates without new evidence. Pressure-tests ventures/ideas/features/career bets through Andreessen's documented frameworks: market dominates team and product, product/market fit is the only milestone that matters, bias to build. Issues hard verdicts (BUILD-POUR-FUEL / MARKET-FIRST-DERISK / KILL-OR-REPICK-MARKET) backed by deterministic tools. Also runs the 3x5-card + Anti-Todo daily routine. Refuses to soften a dead-market verdict. Refuses to cite a quote without a confidence level."
skills: productivity/andreessen/skills/andreessen
domain: productivity
model: opus

View file

@ -1,6 +1,6 @@
---
name: cs-deep-work
description: Plans a deep work day the Cal Newport way — audits a task list deep vs shallow against a 30-50% shallow budget, builds an energy-first time-blocked schedule (deep blocks of at least 90 minutes in the earliest hours, a hard 4-hour deep ceiling, shallow work batched into at most two windows, 10-minute buffers, an immovable hard stop), logs focus sessions against a weekly target, and closes the day with a shutdown ritual. Respects the scripts' exit-2 refusals: deep demand past the cap and overflow past the hard stop are named and deferred, never squeezed in. Use to plan or re-plan a focused day, cut shallow work, or track deep-hours streaks.
description: "Plans a deep work day the Cal Newport way — audits a task list deep vs shallow against a 30-50% shallow budget, builds an energy-first time-blocked schedule (deep blocks of at least 90 minutes in the earliest hours, a hard 4-hour deep ceiling, shallow work batched into at most two windows, 10-minute buffers, an immovable hard stop), logs focus sessions against a weekly target, and closes the day with a shutdown ritual. Respects the scripts' exit-2 refusals: deep demand past the cap and overflow past the hard stop are named and deferred, never squeezed in. Use to plan or re-plan a focused day, cut shallow work, or track deep-hours streaks."
skills: productivity/deep-work/skills/deep-work
domain: productivity
model: opus

View file

@ -1,6 +1,6 @@
---
name: cs-notebooklm
description: NotebookLM browser-automation persona. Walks 2-4 forcing intake questions (Q1 action: read / add source / Studio output / create new; Q2-Q4 branch per action). Refuses to start without action commitment. Refuses to handle login flows automatically. Refuses to wait synchronously for Studio generations (fire-and-notify). Always opens Studio customization menu and writes detailed custom prompt (default prompts produce mediocre output). Screenshot-first discipline. find()-before-click. Fails fast when browser automation unavailable.
description: "NotebookLM browser-automation persona. Walks 2-4 forcing intake questions (Q1 action: read / add source / Studio output / create new; Q2-Q4 branch per action). Refuses to start without action commitment. Refuses to handle login flows automatically. Refuses to wait synchronously for Studio generations (fire-and-notify). Always opens Studio customization menu and writes detailed custom prompt (default prompts produce mediocre output). Screenshot-first discipline. find()-before-click. Fails fast when browser automation unavailable."
skills: research/notebooklm/skills/notebooklm
domain: research
model: opus

View file

@ -0,0 +1,242 @@
#!/usr/bin/env python3
"""check_frontmatter.py — YAML frontmatter validator for skills, agents, and commands.
Every other gate in this repo extracts frontmatter with a regex or a line scan
(see generate-docs.py and sync-codex-skills.py), so a block that is not valid
YAML sails through CI unnoticed. Claude Code parses it properly: when the YAML
is malformed it loads the body with EMPTY metadata, which silently removes the
skill's `description` from the skill listing and makes it invisible to
model invocation. Agents, where `name` and `description` are required, may fail
to load entirely.
This gate parses each block with yaml.safe_load and enforces the fields Claude
Code actually reads.
Errors (exit 1):
- frontmatter block present but not parseable, or not a YAML mapping
- missing `description` (skills/commands) or `name`/`description` (agents)
- an agent `name` containing ':' (rejected by Claude Code >= 2.1.218)
- no frontmatter block at all in a file that should have one
Warnings (exit 0 unless --strict):
- keys outside the current Claude Code frontmatter spec
- combined description + when_to_use over the 1536-char skill-listing cap
Exit codes: 0 = clean, 1 = at least one error (or a warning under --strict).
Intended as CI gate G10.
Usage:
python3 scripts/check_frontmatter.py --all # scan canonical dirs repo-wide
python3 scripts/check_frontmatter.py FILE [FILE ...] # scan specific files
python3 scripts/check_frontmatter.py --all --json # machine-readable output
python3 scripts/check_frontmatter.py --all --strict # warnings become errors
"""
import argparse
import fnmatch
import json
import os
import re
import sys
try:
import yaml
except ImportError: # pragma: no cover - CI installs yamllint, which vendors PyYAML
sys.stderr.write(
"check_frontmatter.py needs PyYAML (pip install pyyaml). "
"Parsing frontmatter with a regex is what this gate exists to prevent.\n"
)
sys.exit(2)
EXCLUDED_DIRS = {
".git", ".codex", ".gemini", ".hermes", ".vibe", "docs", "audit",
"node_modules", ".github", ".claude-plugin",
}
FRONTMATTER_RE = re.compile(r"^---\r?\n(.*?)\r?\n---\r?\n", re.DOTALL)
# Files that legitimately carry no frontmatter: documentation that happens to
# live inside a canonical dir, and the skill-tester's deliberate bare fixture.
DOC_BASENAMES = {"README.md", "CLAUDE.md", "TEMPLATE.md"}
NO_FRONTMATTER_OK = ("*/assets/sample-skill/SKILL.md",)
# Claude Code skill frontmatter. Commands are skills, so they share this set.
# https://code.claude.com/docs/en/skills#frontmatter-reference
SKILL_SPEC_KEYS = {
"name", "description", "when_to_use", "argument-hint", "arguments",
"disable-model-invocation", "user-invocable", "allowed-tools",
"disallowed-tools", "model", "effort", "context", "agent", "background",
"hooks", "paths", "shell",
}
# Claude Code subagent frontmatter.
# https://code.claude.com/docs/en/sub-agents#write-subagent-files
AGENT_SPEC_KEYS = {
"name", "description", "tools", "disallowedTools", "model",
"permissionMode", "maxTurns", "skills", "mcpServers", "hooks", "memory",
"background", "effort", "isolation", "color", "initialPrompt",
}
# Pre-existing repo conventions that no runtime reads. Reported separately from
# genuine typos so the migration in the v5 plan has a worklist rather than noise.
KNOWN_REPO_EXTRAS = {
"license", "metadata", "version", "author", "tags", "compatible_tools",
"triggers", "command", "domain", "title", "emoji", "vibe", "tier",
"category", "dependencies", "not_for", "agents", "source", "attribution",
}
# Combined description + when_to_use budget per entry in the skill listing.
DESC_CAP = 1536
def kind_for(rel_path):
"""skill | agent | command, based on where the file lives."""
if os.path.basename(rel_path) == "SKILL.md":
return "skill"
parts = rel_path.split(os.sep)
if "agents" in parts:
return "agent"
return "command"
def no_frontmatter_ok(rel_path):
if os.path.basename(rel_path) in DOC_BASENAMES:
return True
posix = rel_path.replace(os.sep, "/")
return any(fnmatch.fnmatch(posix, pat) for pat in NO_FRONTMATTER_OK)
def check_file(path, repo_root):
"""Return (errors, warnings, offspec_keys) for one file."""
rel = os.path.relpath(path, repo_root)
kind = kind_for(rel)
errors, warnings, offspec = [], [], []
text = open(path, encoding="utf-8", errors="replace").read()
match = FRONTMATTER_RE.match(text)
if not match:
if not no_frontmatter_ok(rel):
errors.append("no YAML frontmatter block")
return errors, warnings, offspec
try:
data = yaml.safe_load(match.group(1))
except yaml.YAMLError as exc:
detail = str(exc).replace("\n", " ")
errors.append(f"frontmatter is not valid YAML: {detail}")
return errors, warnings, offspec
if not isinstance(data, dict):
errors.append(f"frontmatter is not a YAML mapping (parsed as {type(data).__name__})")
return errors, warnings, offspec
if not str(data.get("description") or "").strip():
errors.append("missing `description` — Claude Code has nothing to match the skill against")
if kind == "agent":
name = str(data.get("name") or "").strip()
if not name:
errors.append("missing `name` — required for subagents")
elif ":" in name:
errors.append(f"`name: {name}` contains ':', which Claude Code >= 2.1.218 refuses to load")
spec = AGENT_SPEC_KEYS if kind == "agent" else SKILL_SPEC_KEYS
for key in data:
if key in spec:
continue
if key in KNOWN_REPO_EXTRAS:
offspec.append(key)
else:
warnings.append(f"unrecognized key `{key}` (not in the {kind} frontmatter spec)")
listing = len(str(data.get("description") or "")) + len(str(data.get("when_to_use") or ""))
if kind != "agent" and listing > DESC_CAP:
warnings.append(
f"description + when_to_use is {listing} chars; the skill listing truncates at {DESC_CAP}"
)
return errors, warnings, offspec
def collect_canonical(repo_root):
"""All SKILL.md + *.md under any agents/ or commands/ dir, excluding sync/doc trees."""
targets = []
for dirpath, dirnames, filenames in os.walk(repo_root):
dirnames[:] = [d for d in dirnames if d not in EXCLUDED_DIRS]
parts = os.path.relpath(dirpath, repo_root).split(os.sep)
in_canonical_dir = "agents" in parts or "commands" in parts
for fn in filenames:
if fn == "SKILL.md" or (in_canonical_dir and fn.endswith(".md")):
targets.append(os.path.join(dirpath, fn))
return sorted(targets)
def main():
ap = argparse.ArgumentParser(
description="Validate YAML frontmatter on SKILL.md / agents / commands files."
)
ap.add_argument("files", nargs="*", help="Specific markdown files to scan")
ap.add_argument("--all", action="store_true",
help="Scan all SKILL.md + agents/*.md + commands/*.md in the repo")
ap.add_argument("--json", action="store_true", help="Emit JSON instead of human-readable output")
ap.add_argument("--strict", action="store_true", help="Treat warnings as errors")
ap.add_argument("--root", default=os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
help="Repo root (default: parent of this script)")
args = ap.parse_args()
repo_root = os.path.abspath(args.root)
if args.all:
targets = collect_canonical(repo_root)
elif args.files:
targets = [os.path.abspath(f) for f in args.files]
else:
ap.print_help()
return 0
errors, warnings = {}, {}
offspec_counts = {}
for path in targets:
errs, warns, offspec = check_file(path, repo_root)
rel = os.path.relpath(path, repo_root)
if errs:
errors[rel] = errs
if warns:
warnings[rel] = warns
for key in offspec:
offspec_counts[key] = offspec_counts.get(key, 0) + 1
n_err = sum(len(v) for v in errors.values())
n_warn = sum(len(v) for v in warnings.values())
if args.json:
print(json.dumps({
"files_scanned": len(targets),
"errors": errors,
"warnings": warnings,
"total_errors": n_err,
"total_warnings": n_warn,
"off_spec_key_counts": dict(sorted(offspec_counts.items(),
key=lambda kv: -kv[1])),
}, indent=2))
else:
for rel in sorted(errors):
print(f"{rel}:")
for msg in errors[rel]:
print(f" ERROR: {msg}")
for rel in sorted(warnings):
print(f"{rel}:")
for msg in warnings[rel]:
print(f" WARN: {msg}")
if offspec_counts:
print("\nOff-spec keys still in use (no runtime reads these):")
for key, count in sorted(offspec_counts.items(), key=lambda kv: -kv[1]):
print(f" {count:4d} {key}")
print(f"\nScanned {len(targets)} files; {n_err} errors, {n_warn} warnings.")
if n_err:
return 1
return 1 if (args.strict and n_warn) else 0
if __name__ == "__main__":
sys.exit(main())