feat: ci-and-contributing#7
Conversation
Adds a "Validate SKILL.md files" step to the existing CI workflow that checks every SKILL.md has YAML frontmatter and a SECURITY GUARDRAIL comment (substring match, compatible with all existing skill files). Adds CONTRIBUTING.md explaining how to add mappings, fix examples, and create new skill files including the CI requirements. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 59 minutes and 36 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 894b614a15
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| skill_files = [f for f in Path('.').rglob('SKILL.md') if '.git' not in f.parts] | ||
| for skill_file in skill_files: | ||
| content = skill_file.read_text(encoding='utf-8') | ||
| if not content.startswith('---'): |
There was a problem hiding this comment.
Validate required frontmatter fields in SKILL checks
This check only verifies that SKILL.md starts with ---, so malformed or empty frontmatter still passes CI (for example, a file with just ---\n--- plus a guardrail comment). That means the workflow does not enforce the newly documented requirement that frontmatter include keys like name, description, and triggers, so invalid skill metadata can be merged unnoticed. Parse the frontmatter YAML block and assert required fields rather than using a prefix-only check.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
Adds contributor guidance and enforces (basic) SKILL.md structure checks in CI to keep skill files consistent across the repo.
Changes:
- Add
CONTRIBUTING.mdwith repo structure, contribution workflow, and local validation instructions - Add a CI step to validate that all
SKILL.mdfiles include YAML frontmatter and a SECURITY GUARDRAIL marker
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| CONTRIBUTING.md | Introduces contribution guidelines, skill-file requirements, and a local validation snippet |
| .github/workflows/ci.yml | Adds a new CI validation step for SKILL.md frontmatter/guardrail presence |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| | Directory | Contents | | ||
| |-----------|----------| | ||
| | `tailwind/` | Tailwind 4.x logical-property mapping tables and usage patterns | | ||
| | `flutter/` | Flutter directional layout API patterns and migration notes | | ||
| | `nextjs/` | Next.js `dir` propagation, App Router, and i18n routing patterns | | ||
| | `biome/` | Biome lint policy rules that block physical-direction regressions | | ||
| | `ci/` | CI workflow snippets for RTL validation in real pipelines | | ||
| | `skills/` | Claude Code / Gemini CLI skill files that load this toolkit automatically | | ||
| | `cheatsheet/` | Quick-reference cards for the most common patterns | |
There was a problem hiding this comment.
The markdown table under “What lives in this repo” has an extra leading | on each row (e.g., || Directory | Contents |), which renders as an unintended empty column in GitHub-flavored Markdown. Update the table rows to use a single leading pipe (| Directory | Contents |, etc.).
| 1. **Start with YAML frontmatter** (`---` block) containing at least `name`, `description`, and `triggers`. | ||
| 2. **Include the security guardrail comment** immediately after the frontmatter closing `---`: | ||
| ``` | ||
| <!-- SECURITY GUARDRAIL: ... --> | ||
| ``` | ||
| The comment text should describe what the skill does and does not do. See `skills/rtl-fix/SKILL.md` for a concrete example. | ||
| 3. **List specific triggers** — phrases that should cause an AI assistant to load this skill automatically (e.g., `"rtl fix"`, `"code review"`, `"tsx file edit"`). | ||
|
|
||
| CI will fail if either requirement is missing. |
There was a problem hiding this comment.
The contribution guidelines say SKILL.md frontmatter must include specific keys and that the SECURITY GUARDRAIL comment must appear immediately after the closing ---, but the validation snippet below (and the CI step) only checks content.startswith('---') and that the guardrail prefix appears somewhere. Either tighten the validator to match these documented requirements (parse the frontmatter and verify keys + placement), or relax/clarify the docs so they match what CI actually enforces.
| GUARDRAIL_PREFIX = '<!-- SECURITY GUARDRAIL:' | ||
| errors = [] | ||
| skill_files = [f for f in Path('.').rglob('SKILL.md') if '.git' not in f.parts] | ||
| for skill_file in skill_files: | ||
| content = skill_file.read_text(encoding='utf-8') | ||
| if not content.startswith('---'): | ||
| errors.append(f'{skill_file}: missing YAML frontmatter') | ||
| elif GUARDRAIL_PREFIX not in content: | ||
| errors.append(f'{skill_file}: missing SECURITY GUARDRAIL comment') |
There was a problem hiding this comment.
The new SKILL.md validator only checks that the file starts with --- and that '<!-- SECURITY GUARDRAIL:' appears somewhere in the file. This won’t catch malformed frontmatter (missing closing --- / invalid YAML), missing required keys (name/description/triggers), or a guardrail comment placed far away from the frontmatter—despite the docs implying those requirements. Consider parsing the frontmatter and validating required fields + ensuring the guardrail comment appears immediately after the frontmatter block (or adjust the docs/step name to match the looser checks).
| GUARDRAIL_PREFIX = '<!-- SECURITY GUARDRAIL:' | |
| errors = [] | |
| skill_files = [f for f in Path('.').rglob('SKILL.md') if '.git' not in f.parts] | |
| for skill_file in skill_files: | |
| content = skill_file.read_text(encoding='utf-8') | |
| if not content.startswith('---'): | |
| errors.append(f'{skill_file}: missing YAML frontmatter') | |
| elif GUARDRAIL_PREFIX not in content: | |
| errors.append(f'{skill_file}: missing SECURITY GUARDRAIL comment') | |
| import yaml | |
| GUARDRAIL_PREFIX = '<!-- SECURITY GUARDRAIL:' | |
| REQUIRED_FRONTMATTER_KEYS = ('name', 'description', 'triggers') | |
| errors = [] | |
| skill_files = [f for f in Path('.').rglob('SKILL.md') if '.git' not in f.parts] | |
| for skill_file in skill_files: | |
| content = skill_file.read_text(encoding='utf-8') | |
| lines = content.splitlines() | |
| if not lines or lines[0].strip() != '---': | |
| errors.append(f'{skill_file}: missing YAML frontmatter opening delimiter') | |
| continue | |
| closing_index = None | |
| for i in range(1, len(lines)): | |
| if lines[i].strip() == '---': | |
| closing_index = i | |
| break | |
| if closing_index is None: | |
| errors.append(f'{skill_file}: missing YAML frontmatter closing delimiter') | |
| continue | |
| frontmatter_text = '\n'.join(lines[1:closing_index]) | |
| try: | |
| frontmatter = yaml.safe_load(frontmatter_text) | |
| except yaml.YAMLError as exc: | |
| errors.append(f'{skill_file}: invalid YAML frontmatter ({exc})') | |
| continue | |
| if not isinstance(frontmatter, dict): | |
| errors.append(f'{skill_file}: frontmatter must be a YAML mapping') | |
| continue | |
| missing_keys = [ | |
| key for key in REQUIRED_FRONTMATTER_KEYS | |
| if key not in frontmatter or frontmatter[key] in (None, '', []) | |
| ] | |
| if missing_keys: | |
| errors.append( | |
| f"{skill_file}: missing required frontmatter keys: {', '.join(missing_keys)}" | |
| ) | |
| next_nonempty_line = None | |
| for line in lines[closing_index + 1:]: | |
| if line.strip(): | |
| next_nonempty_line = line | |
| break | |
| if next_nonempty_line is None: | |
| errors.append(f'{skill_file}: missing SECURITY GUARDRAIL comment immediately after frontmatter') | |
| elif not next_nonempty_line.startswith(GUARDRAIL_PREFIX): | |
| errors.append(f'{skill_file}: SECURITY GUARDRAIL comment must appear immediately after frontmatter') |
Automated: feat/ci-and-contributing