Skip to content

docs: add rules frontmatter paths: syntax examples and validation hook#26914

Open
Johntycour wants to merge 1 commit intoanthropics:mainfrom
Johntycour:fix/rules-frontmatter-paths-documentation
Open

docs: add rules frontmatter paths: syntax examples and validation hook#26914
Johntycour wants to merge 1 commit intoanthropics:mainfrom
Johntycour:fix/rules-frontmatter-paths-documentation

Conversation

@Johntycour
Copy link

Summary

  • Add examples/rules/ directory with correct and incorrect frontmatter syntax examples
  • Add examples/hooks/rules_frontmatter_validator.py — a PostToolUse hook that detects broken paths: syntax
  • Document the root cause of the silent failure for future users

Root Cause

The paths: field in rules frontmatter is parsed by an internal CSV parser (_9A()) that expects a string and iterates character by character. When YAML array syntax is used:

paths:
  - "**/*.ts"
  - "**/*.tsx"

The YAML parser returns a JavaScript Array, which _9A() then iterates element by element instead of character by character. This concatenates all globs without any separator (e.g., **/*.ts**/*.tsx), producing an invalid glob that matches nothing.

Proposed one-line fix (in _9A() caller)

Array.isArray(A.paths) ? A.paths.join(",") : String(A.paths)

This normalizes the input before it reaches the CSV parser.

What's included

File Description
examples/rules/README.md Documents correct CSV syntax, broken YAML/JSON syntax, and root cause
examples/rules/typescript-style.md Working example with paths: "**/*.ts,**/*.tsx"
examples/rules/python-style.md Working example with globs: "**/*.py,**/*.pyi"
examples/hooks/rules_frontmatter_validator.py PostToolUse hook detecting broken frontmatter syntax

Related Issues

Closes #19377 (documentation aspect)
Related: #13905, #21858, #17204

Test plan

  • Verify rules_frontmatter_validator.py detects YAML list syntax in frontmatter
  • Verify rules_frontmatter_validator.py detects JSON array syntax in frontmatter
  • Verify rules_frontmatter_validator.py ignores correct CSV syntax
  • Verify example rules files use correct frontmatter format

🤖 Generated with Claude Code

The `paths:` field in rules frontmatter silently fails when using YAML
array syntax or JSON inline array syntax. This is because the internal
CSV parser iterates the array elements instead of characters, producing
concatenated invalid globs that match nothing.

This PR adds:
- examples/rules/README.md documenting correct vs broken syntax
- examples/rules/typescript-style.md and python-style.md as working examples
- examples/hooks/rules_frontmatter_validator.py to detect broken syntax

Fixes are tracked in:
- anthropics#19377 (YAML array syntax fails)
- anthropics#13905 (paths: not triggering)
- anthropics#21858 (rules not applying)
- anthropics#17204 (quoted paths: broken)
Copilot AI review requested due to automatic review settings February 19, 2026 14:37
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds documentation and tooling examples to help users avoid silent failures when using paths: in Claude Code rules frontmatter by providing correct/incorrect examples and a hook-based validator.

Changes:

  • Added example rules files demonstrating working CSV-string frontmatter usage (paths: / globs:).
  • Added a PostToolUse Python hook that warns when rules frontmatter uses YAML-list or JSON-array syntax.
  • Documented the root cause behind silent failures and linked related issues.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

File Description
examples/rules/typescript-style.md Adds a TypeScript rules example using CSV-string paths: syntax.
examples/rules/python-style.md Adds a Python rules example using CSV-string globs: syntax.
examples/rules/README.md Documents correct vs broken frontmatter formats and explains the failure mode.
examples/hooks/rules_frontmatter_validator.py Adds a hook script to detect broken frontmatter syntax after Write/Edit.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


def extract_frontmatter(content: str) -> str | None:
"""Extract YAML frontmatter from a markdown file."""
match = re.match(r"^---\s*\n(.*?)\n---", content, re.DOTALL)
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extract_frontmatter only matches LF line endings (\n). On Windows/CRLF rules files, this won’t detect frontmatter and the validator will silently do nothing. Consider using \r?\n in the regex (and optionally allowing ---\s*\r?\n for both delimiters).

Suggested change
match = re.match(r"^---\s*\n(.*?)\n---", content, re.DOTALL)
match = re.match(r"^---\s*\r?\n(.*?)\r?\n---", content, re.DOTALL)

Copilot uses AI. Check for mistakes.
Comment on lines +59 to +62
# Pattern 1: YAML list syntax (field: followed by newline then " - ")
if re.search(rf"^{field}:\s*$", frontmatter, re.MULTILINE) and re.search(
r"^\s+-\s+", frontmatter, re.MULTILINE
):
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The YAML-list detection can false-positive because it checks for paths: (or globs:) being present and then any list item anywhere in the frontmatter, even if the list belongs to a different key. Tighten the regex to ensure the - items are actually under the same field (e.g., match the block starting at ^{field}: and subsequent indented - lines).

Suggested change
# Pattern 1: YAML list syntax (field: followed by newline then " - ")
if re.search(rf"^{field}:\s*$", frontmatter, re.MULTILINE) and re.search(
r"^\s+-\s+", frontmatter, re.MULTILINE
):
# Pattern 1: YAML list syntax (field: followed by newline then indented "- " lines)
yaml_list_pattern = rf"^{field}:\s*\n(?:[ \t]+-\s+[^\n]*\n?)+"
if re.search(yaml_list_pattern, frontmatter, re.MULTILINE):

Copilot uses AI. Check for mistakes.
Comment on lines +91 to +92
rules_indicators = ["/rules/", "/.claude/rules/"]
if not any(indicator in file_path for indicator in rules_indicators):
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two issues that will prevent this from working reliably on Windows paths: (1) rules_indicators only checks for forward-slash substrings, so C:\\...\\.claude\\rules\\x.md won’t match; (2) the docstring says it targets .claude/rules/ but the code also matches any /rules/ directory. Consider normalizing file_path to POSIX separators before substring checks and narrowing (or documenting) the intended directories.

Suggested change
rules_indicators = ["/rules/", "/.claude/rules/"]
if not any(indicator in file_path for indicator in rules_indicators):
# Normalize to POSIX-style separators for reliable substring checks
normalized_path = Path(file_path).as_posix()
# Only process rules files in .claude/rules/ as documented
rules_indicators = ["/.claude/rules/", ".claude/rules/"]
if not any(indicator in normalized_path for indicator in rules_indicators):

Copilot uses AI. Check for mistakes.
print(
f"WARNING: Rules file {path.name} has frontmatter issues:\n"
+ "\n".join(f" - {issue}" for issue in issues),
file=sys.stderr,
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This prints the main warning to stderr but exits with code 0. Per the hooks docs, PostToolUse only reliably shows stdout on exit 0; stderr is typically only surfaced on exit 2. To ensure the warning is visible, print to stdout (or emit the standard JSON output with systemMessage) and keep exit 0.

Suggested change
file=sys.stderr,

Copilot uses AI. Check for mistakes.

```yaml
---
paths: "**/*.ts"
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The heading says “Single glob (unquoted)”, but the example value is quoted ("**/*.ts"). Either update the heading to say quoted, or change the example to be truly unquoted to avoid confusing readers about what’s required/allowed.

Suggested change
paths: "**/*.ts"
paths: **/*.ts

Copilot uses AI. Check for mistakes.
Comment on lines +60 to +70
### Quoted single value with paths: — MAY BREAK

```yaml
---
# In some versions, quoted paths: may also fail
# Use globs: if you need quoted values
paths: "**/*.cs"
---
```

See [#17204](https://github.com/anthropics/claude-code/issues/17204) — `paths:` with quoted values may undergo additional processing that strips or misinterprets the value. Use `globs:` as a more reliable alternative.
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section labels quoted paths: as “MAY BREAK”, but earlier in “Correct Syntax” the recommended examples for paths: are also quoted. Please clarify the guidance (e.g., recommend unquoted paths: vs globs: consistently, and specify which versions/contexts the quoted form fails in).

Copilot uses AI. Check for mistakes.

## Validation Hook

See [`examples/hooks/rules_frontmatter_validator.py`](../hooks/rules_frontmatter_validator.py) — a PreToolUse hook that detects rules files using broken frontmatter syntax.
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README describes the validator as a “PreToolUse hook”, but the validator script and its sample config are PostToolUse. Please align the README with the intended hook event so users configure it correctly.

Suggested change
See [`examples/hooks/rules_frontmatter_validator.py`](../hooks/rules_frontmatter_validator.py) — a PreToolUse hook that detects rules files using broken frontmatter syntax.
See [`examples/hooks/rules_frontmatter_validator.py`](../hooks/rules_frontmatter_validator.py) — a PostToolUse hook that detects rules files using broken frontmatter syntax.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[DOCS] paths: frontmatter syntax in rules files is incorrect

2 participants