fix(ai): rules use paths: frontmatter, only task-scoped guidance becomes skills - #6613
fix(ai): rules use paths: frontmatter, only task-scoped guidance becomes skills#6613blunteshwar wants to merge 8 commits into
Conversation
Claude Code inlines every file under the .claude/rules directory symlink regardless of alwaysApply/globs frontmatter, defeating the on-demand design of .ai/rules (~35K tokens/session). Only branch-naming and styles are genuinely alwaysApply: true, so they stay as rules; every other rule is converted to a .ai/skills/*/SKILL.md entry, which is already loaded on demand via the existing .cursor/skills and .claude/skills symlinks. - Move jira-ticket, github-description, code-conformance, consistency-pass, migration-phase-awareness, component-readme, storybook-mdx-conversion, stories-documentation, stories-format, and text-formatting to skills - Fold contributor-doc-update into the existing contributor-docs-nav skill - Fold the deep-understanding rule into its existing skill - Remove the corresponding .cursor/rules/*.mdc symlinks - Update all cross-references and the .ai/README.md rules/skills catalog
|
📚 Branch Preview Links🔍 Gen1 Visual Regression Test ResultsWhen a visual regression test fails (or has previously failed while working on this branch), its results can be found in the following URLs:
Deployed to Azure Blob Storage: If the changes are expected, update the |
Coverage Report for CI Build 31788372534Warning No base build found for commit Coverage: 96.241%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsRequires a base build to compare against. How to fix this → Coverage Stats💛 - Coveralls |
…de-off Self-review of PR #6613 found two skills with an unquoted `globs:` value starting with `**`, which YAML parses as an invalid alias reference (verified with js-yaml) rather than a literal string: storybook-mdx-conversion (regressed by the rename) and text-formatting (pre-existing, carried forward). Quote both. Also documents that a skill's `globs:` field is relevance metadata, not a guaranteed auto-trigger the way Cursor's rule glob was — call this out in .ai/README.md and refresh the stale "glob-triggered rules" wording in AGENTS.md now that only two rules remain.
The rules-to-skills conversion moved content over without adding explicit invocation guidance, since Cursor rules applied automatically via globs and never needed it. Skills require an agent to match intent or a glob hint to the skill's purpose, so every migrated skill now states when it applies and how to invoke it. Glob-scoped skills (component-readme, storybook-mdx-conversion, stories-documentation, stories-format, text-formatting) call out that a matching file being in context is itself a trigger, not just an explicit ask. Task-scoped skills (jira-ticket, github-description, code-conformance, consistency-pass, migration-phase-awareness) list the natural-language phrases that should invoke them, reusing existing "when to run"/"when to apply" content where present rather than duplicating it.
…tter The rules-to-skills migration assumed Claude Code has no mechanism for conditional rule loading. It does: a `paths:` YAML frontmatter field (distinct from Cursor's `globs:`) on files under `.claude/rules/`, honored even through the `.claude/rules -> ../.ai/rules` directory symlink. A rule without `paths:` loads unconditionally; that's why the whole `.ai/rules/` directory was inlining every session, since none of those files used the key Claude actually reads. Move component-readme, contributor-doc-update, storybook-mdx-conversion, stories-documentation, stories-format, and text-formatting back from `.ai/skills/` to `.ai/rules/` as path-scoped rules, adding a `paths:` list alongside the existing Cursor `globs:` so both tools load them deterministically when a matching file is in context. Restore their `.cursor/rules/*.mdc` symlinks, and revert contributor-docs-nav to its pre-fold state now that contributor-doc-update exists again as its own path-scoped rule pointing to it. Guidance with no natural file-path scope (jira-ticket, github-description, code-conformance, consistency-pass, migration-phase-awareness, deep-understanding) stays as skills — that part of the original migration was correct, since Cursor never glob-triggered those either. Also quote `globs: '*.css'` in styles.md, which had the same unquoted leading-glob YAML bug found in the earlier self-review, just never triggered because it was never parsed as strict YAML until now. Rewrites .ai/README.md and AGENTS.md to describe the corrected mechanism and the rule-vs-skill decision (file-path scope -> path-scoped rule; task/intent scope -> skill), and fixes every cross-reference to the restored rule paths.
…nts into rulesTOskills
There was a problem hiding this comment.
The "Deep understanding" docs in .ai/README.md could use an update now that it's a skill and not a rule, I think that's the only really necessary change here, which is really minor, so I don't think it needs to block approval.
I flagged a few other .ai/-related things that I thought were a little confusing but will leave it to your discretion about whether to address or not since this was meant to be a quick fix and not a total refactor of the whole directory.
Happy to take another look if you think it needs one at any point!
There was a problem hiding this comment.
This will need an update now that it's a skill and not a rule, we probably can remove "enforced by an always-applied rule" and adjust some other parts that talk about usage like "no need to ask" and "every session"
There was a problem hiding this comment.
I'm questioning if we still need this? Or if it should/could be a skill? I think this might possibly have some overlap with generate-contributor-docs.mjs?
But I also think looking into it and making adjustments would increase the scope of this work; I feel ok with leaving it as it is right now and re-evaluating later.
| 2. Add one per-file symlink for Cursor (required — Cursor needs `.mdc` extension): | ||
|
|
||
| ```sh | ||
| ln -s “../../.ai/rules/rule-name.md” “.cursor/rules/rule-name.mdc” |
There was a problem hiding this comment.
not introduced here but these look like curly quotes
| ```json | ||
| { | ||
| "version": 1, | ||
| "rules": { | ||
| "category": { | ||
| "rule_name": { | ||
| "enabled": true, | ||
| "pattern": "regex_pattern", | ||
| "message": "Error message" | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` |
There was a problem hiding this comment.
We don't have to address it in this PR but this schema doesn't really reflect anything we currently have in config.json as far as I can tell?
- Fix stale "Deep understanding" catalog entry: it was never enforced by an always-applied rule (deep-understanding was alwaysApply: false even before this PR, and the rule file is now fully merged into the skill). Reword to match the skill's actual "apply intelligently" behavior. - Clarify storybook-mdx-conversion's relationship to generate-contributor-docs.mjs: the automated `yarn generate:contributor-docs` already converts all of CONTRIBUTOR-DOCS/ to Storybook MDX and shouldn't be hand-duplicated; this rule is for standalone manual conversions outside that pipeline. - Replace the config.json schema example in .ai/README.md with one that matches its actual structure (flat git/jira_tickets/text_formatting sections) instead of a generic rules.category.rule_name shape that doesn't exist in the file. - Straighten a pre-existing pair of curly quotes in a shell command example.
rise-erpelding
left a comment
There was a problem hiding this comment.
Just peeked at these changes, they look great! 🤩
Description
Claude Code inlines every rule file under
.claude/rulesinto every session unless that file's frontmatter tells it not to — but the key it reads for that ispaths:(a YAML list), not Cursor'sglobs:. None of this repo's.ai/rules/*.mdfiles usedpaths:, so all 14 loaded unconditionally on every Claude Code session (~35K tokens), regardless ofalwaysApplyorglobs. Cursor was unaffected the whole time — it readsglobs/alwaysApplylive via its own per-file.mdcsymlinks.This PR's first commits (mis-)diagnosed that as "Claude Code has no conditional rule loading at all" and moved every non-
alwaysApplyrule into.ai/skills/. That overcorrected: skills have no glob/path auto-trigger in either tool, so file-scoped guidance likestories-formatorcomponent-readmewould only surface if the agent happened to match the task's intent to the skill's description — a real regression from Cursor's previous guaranteed-fire-on-matching-file behavior.The actual fix, once
paths:was confirmed against the Claude Code docs (including that path-scoping is honored through a symlinked.claude/rulesdirectory, since v2.1.198): give every rule that has a Cursorglobs:value an equivalent Claudepaths:value too, and keep it a rule, not a skill. Only guidance with no natural file-path scope — task/intent-driven work like drafting a Jira ticket or running a consistency pass — belongs in.ai/skills/.Motivation and context
Raised internally (Slack) after profiling Claude Code session token usage on this repo. The skills-only approach was revised mid-PR after a reviewer pointed out that Claude Code does support path-scoped rules via
paths:frontmatter, and that routing genuinely file-scoped standards through skills makes them depend on model-triggered discovery instead of a deterministic trigger.Changes
Stay/become path-scoped rules (both
globs:for Cursor andpaths:for Claude Code, so both tools load them only when a matching file is in context):text-formatting,stories-documentation,stories-format,component-readme,contributor-doc-update,storybook-mdx-conversion.cursor/rules/*.mdcsymlinkscontributor-docs-navskill reverted to its pre-PR state now thatcontributor-doc-updateexists again as the path-scoped rule pointing to itStay skills (no natural file-path scope — task/intent-triggered in Cursor too, so this part of the original migration was correct):
jira-ticket,github-description,code-conformance,consistency-pass,migration-phase-awareness,deep-understandingStay rules, unaffected:
branch-naming,styles(genuinelyalwaysApply: true, nopaths:needed — they load unconditionally in both tools by design)Also fixed along the way (found during self-review, unrelated to the rule/skill split but same root cause — YAML frontmatter that was never actually valid):
globs: **/*.md,**/*.mdxand similar unquoted values starting with*parse as an invalid YAML alias, not a literal string (verified withjs-yaml). Quoted everyglobs:/paths:value that starts with*— this affectedstorybook-mdx-conversion,text-formatting, and (found in this second pass)styles.md'sglobs: *.css.Documentation: rewrote
.ai/README.mdandAGENTS.mdto describe the corrected mechanism (globsvspaths, which key each tool reads, symlink support) and the rule-vs-skill decision rule (file-path scope → path-scoped rule; task/intent scope → skill). Fixed every cross-reference to the files that moved back from.ai/skills/to.ai/rules/.Related issue(s)
Screenshots (if appropriate)
N/A — no UI change.
Author's checklist
Reviewer's checklist
patch,minor, ormajorfeaturesManual review test cases
Verify
.ai/rules/frontmatter is valid YAML and Claude-conditionalglobs:/paths:value starting with*is quoted in.ai/rules/*.md(storybook-mdx-conversion.md,text-formatting.md,stories-format.md,stories-documentation.md,styles.md)component-readme.md,contributor-doc-update.md,storybook-mdx-conversion.md,stories-documentation.md,stories-format.md,text-formatting.mdeach have apaths:list matching theirglobs:valuebranch-naming.mdandstyles.mdhave nopaths:field (always-active, unconditional in both tools)Verify
yarn lint:aipassesnode .ai/scripts/validate.js(oryarn lint:ai)Verify Cursor and Claude Code symlinks are consistent
.cursor/rules/has one.mdcsymlink per file in.ai/rules/(8 total:branch-naming,styles, and the 6 path-scoped rules).claude/rulesstill resolves to.ai/rules/via the existing directory symlink.ai/README.mdno longer listscomponent-readme,storybook-mdx-conversion,stories-documentation,stories-format, ortext-formattingas skillsDevice review
Accessibility testing checklist
Tooling/docs-only change; no component, DOM, or runtime behavior is affected.
.ai/rules/,.ai/skills/) and their cross-references are affected.