Validators Reference
This document provides comprehensive documentation for all validator types available in Drift, including the failure_details feature that enables detailed, actionable violation messages.
Table of Contents
Ignore Patterns
Validators support three levels of ignore configuration to control which files are processed:
Global Ignore Patterns
Global patterns apply to all validators. Configure in .drift.yaml:
global_ignore:
- "**/*.tmp"
- ".venv/**"
- "node_modules/**"
Validator-Specific Ignore Patterns
Validator-specific patterns apply only to a specific validator type:
validator_ignore:
core:markdown_link:
patterns:
- "https://example.com/**"
- "http://localhost:**"
core:file_size:
patterns:
- "**/*.svg"
- "**/*.png"
Pattern Matching
Drift supports three pattern types:
Glob patterns (recommended):
*.md- All .md files in root**/*.py- All .py files recursivelysrc/**- Everything under src/
Regex patterns (auto-detected by metacharacters):
^https://example\.com/.*- URLs matching pattern.*\.test\.js$- Files ending with .test.js
Literal paths:
README.md- Exact file namedocs/guide.md- Exact relative path
How Ignores Work
When a validator processes files:
Global patterns from
global_ignoreare collectedValidator-specific patterns from
validator_ignore[validator_type].patternsare collectedBoth sets are merged
Files matching any pattern are skipped
Example:
global_ignore:
- "**/*.tmp"
validator_ignore:
core:file_size:
patterns:
- "**/*.svg"
When core:file_size runs:
Ignores:
**/*.tmp,**/*.svgProcesses: All other files
When core:file_exists runs:
Ignores:
**/*.tmpProcesses: All other files (including
.svg)
Which Validators Support Ignores
All validators that process file content support ignore patterns:
core:file_existscore:file_sizecore:token_countcore:block_line_countcore:regex_matchcore:list_matchcore:list_regex_matchcore:markdown_linkcore:json_schemacore:yaml_schemacore:yaml_frontmatter
Validators that analyze structure (dependency validators, Claude Code validators) do not use ignore patterns.
See the Configuration Guide for complete details on ignore configuration.
Default Failure Messages
Validators provide sensible default failure messages, eliminating the need to specify failure_message and expected_behavior in every validation rule.
Overview
Every validator includes built-in default messages that describe what failed and what’s expected. You can still override these with custom messages when needed, and custom messages support template variable substitution using {placeholder} syntax.
How It Works
No custom message: Validator uses its default message
Custom message with templates: Variables like
{file_path}are replaced with actual valuesCustom message without templates: Validator appends relevant details automatically
Benefits
Less configuration: Validators work out-of-the-box without requiring message configuration
Consistent messaging: Built-in messages follow a standard format across all validators
Flexible customization: Override defaults when you need custom wording or localization
Always show details: Even custom messages without placeholders get relevant details appended
Example: Using Defaults
# Minimal configuration - uses validator defaults
phases:
- name: Check README
type: core:file_exists
file_path: README.md
# No failure_message needed!
# Default: "File {file_path} does not exist"
Example: Custom Message with Templates
# Custom message with template variables
phases:
- name: Check README
type: core:file_exists
file_path: README.md
failure_message: "⚠️ Missing required file: {file_path}"
expected_behavior: "All projects must have a README"
Example: Custom Message without Templates
# Custom message without templates still gets details appended
phases:
- name: Check Dependencies
type: core:circular_dependencies
params:
resource_dirs: [".claude/skills"]
failure_message: "Circular dependency found"
# Result: "Circular dependency found: skill-a → skill-b → skill-a"
Default Messages by Validator
All validators provide contextual default messages. Here are some examples:
File Validators:
core:file_exists→ “File {file_path} does not exist”core:file_size→ “File size constraint violated”core:token_count→ “File token count constraint violated”core:block_line_count→ “Block line count validation failed”
Dependency Validators:
core:circular_dependencies→ “Circular dependency detected: {circular_path}”core:max_dependency_depth→ “Dependency depth {actual_depth} exceeds maximum {max_depth}”core:dependency_duplicate→ “Duplicate dependency found: {dependency}”
Format Validators:
core:json_schema→ “JSON schema validation failed”core:yaml_schema→ “YAML schema validation failed”core:yaml_frontmatter→ “YAML frontmatter validation failed”
Regex & List Validators:
core:regex_match→ “Pattern match validation failed”core:list_match→ “List match validation failed”core:markdown_link→ “Broken link found: {link}”
See individual validator documentation below for complete details and available template variables.
Custom Validator Plugins
Drift supports loading custom validators from third-party Python packages, enabling extensible validation beyond the built-in core: validators.
Overview
The plugin system allows you to:
Create custom validators for domain-specific checks (security scans, license compliance, etc.)
Package and distribute validators as Python packages
Load validators dynamically without modifying Drift’s codebase
Use namespaced validation types to prevent conflicts
Creating a Custom Validator
Create a validator class that inherits from
BaseValidator:
# my_package/validators.py
from typing import List, Literal, Optional
from drift.validation.validators import BaseValidator
from drift.config.models import ValidationRule
from drift.core.types import DocumentBundle, DocumentRule
class SecurityScanValidator(BaseValidator):
"""Custom validator for security vulnerability scanning."""
@property
def validation_type(self) -> str:
"""Return the namespaced validation type."""
return "security:vulnerability_scan"
@property
def computation_type(self) -> Literal["programmatic", "llm"]:
"""This is a programmatic validator."""
return "programmatic"
@property
def default_failure_message(self) -> str:
"""Default message when vulnerabilities found."""
return "Security vulnerabilities detected: {vulnerability_count} issues"
@property
def default_expected_behavior(self) -> str:
"""Expected behavior description."""
return "No security vulnerabilities should exist"
def validate(
self,
rule: ValidationRule,
bundle: DocumentBundle,
all_bundles: Optional[List[DocumentBundle]] = None,
) -> Optional[DocumentRule]:
"""Run security scan on files in bundle."""
# Your validation logic here
vulnerabilities = self._scan_for_vulnerabilities(bundle)
if vulnerabilities:
failure_details = {
"vulnerability_count": len(vulnerabilities),
"vulnerabilities": vulnerabilities
}
return DocumentRule(
bundle_id=bundle.bundle_id,
bundle_type=bundle.bundle_type,
file_paths=[v["file"] for v in vulnerabilities],
observed_issue=self._get_failure_message(rule, failure_details),
expected_quality=self._get_expected_behavior(rule),
rule_type="",
context=f"Validation rule: {rule.description}",
failure_details=failure_details,
)
return None # Validation passed
Package your validator as an installable Python package:
# pyproject.toml
[project]
name = "my-drift-validators"
version = "1.0.0"
dependencies = ["ai-drift>=0.4.0"]
Install the package:
pip install my-drift-validators
Use in
.drift.yamlwith theproviderfield:
phases:
- name: Security Scan
type: security:vulnerability_scan
provider: "my_package.validators:SecurityScanValidator"
params:
scan_level: "strict"
# Optional: override default messages
failure_message: "🔒 Found {vulnerability_count} security issues"
expected_behavior: "Code must pass security scan"
Namespace Requirements
Format:
namespace:type(e.g.,security:scan,compliance:license)Pattern: Lowercase letters, numbers, underscores only (
^[a-z_][a-z0-9_]*:[a-z_][a-z0-9_]*$)Reserved namespace:
core:is reserved for built-in validatorsCustom namespaces: Use your company/project name (e.g.,
acme:security_scan)
Provider Format
The provider field specifies how to load your validator:
Format: module.path:ClassName
Examples:
my_package.validators:SecurityValidatorcompany.drift_plugins:LicenseCheckermy_app.custom_validators:CustomValidator
Validation Rules
Core validators (core:*) must not have a provider field, while custom validators (any other namespace) require a provider field. Invalid combinations will raise a validation error at configuration time.
# Core validator - no provider needed
phases:
- type: core:file_exists
file_path: README.md
# Custom validator - provider required
phases:
- type: security:scan
provider: "my_pkg.validators:SecurityValidator"
Best Practices
Namespace uniquely: Use your organization or package name as the namespace
Provide defaults: Always implement
default_failure_messageanddefault_expected_behaviorUse templates: Include placeholder variables in default messages for flexibility
Return details: Populate
failure_detailswith actionable informationHandle errors: Raise clear exceptions for invalid configuration
Document thoroughly: Provide docstrings explaining parameters and behavior
Example: Full Custom Validator
from pathlib import Path
from typing import List, Literal, Optional
from drift.validation.validators import BaseValidator
from drift.config.models import ValidationRule
from drift.core.types import DocumentBundle, DocumentRule
class LicenseCheckValidator(BaseValidator):
"""Validates that all code files have proper license headers."""
@property
def validation_type(self) -> str:
return "compliance:license_check"
@property
def computation_type(self) -> Literal["programmatic", "llm"]:
return "programmatic"
@property
def default_failure_message(self) -> str:
return "Missing license headers in {file_count} files"
@property
def default_expected_behavior(self) -> str:
return "All source files should have license headers"
def validate(
self,
rule: ValidationRule,
bundle: DocumentBundle,
all_bundles: Optional[List[DocumentBundle]] = None,
) -> Optional[DocumentRule]:
required_header = rule.params.get("header", "Copyright")
file_extensions = rule.params.get("extensions", [".py", ".js"])
missing_headers = []
for file in bundle.files:
if any(file.relative_path.endswith(ext) for ext in file_extensions):
content = Path(file.file_path).read_text()
if required_header not in content[:500]: # Check first 500 chars
missing_headers.append(file.relative_path)
if missing_headers:
failure_details = {
"file_count": len(missing_headers),
"files": missing_headers,
"required_header": required_header
}
return DocumentRule(
bundle_id=bundle.bundle_id,
bundle_type=bundle.bundle_type,
file_paths=missing_headers,
observed_issue=self._get_failure_message(rule, failure_details),
expected_quality=self._get_expected_behavior(rule),
rule_type="",
context=f"Validation rule: {rule.description}",
failure_details=failure_details,
)
return None
Usage:
phases:
- name: License Check
type: compliance:license_check
provider: "my_company.validators:LicenseCheckValidator"
params:
header: "Copyright (C) 2025"
extensions: [".py", ".js", ".ts"]
Caching and Performance
Drift automatically caches loaded validator classes for performance:
Validators are loaded once per validation run
Class-level caching prevents redundant imports
Multiple rules can share the same validator instance
No special configuration needed - caching is automatic.
Failure Details Feature
The failure_details feature allows validators to return structured data about validation failures, which can then be interpolated into failure messages using template placeholders.
How It Works
When a validator detects a failure, it can populate a failure_details dictionary with specific information about what went wrong. This data is then used to format the failure_message template defined in your rule.
Template Placeholders
Use {placeholder} syntax in your failure_message to reference values from failure_details:
failure_message: "Circular dependency detected: {circular_path}"
When a circular dependency is found, the validator populates failure_details:
failure_details = {
"circular_path": "agent_a → agent_b → agent_a"
}
The final message becomes:
Circular dependency detected: agent_a → agent_b → agent_a
Benefits
Actionable Messages: Users see exactly what’s wrong with specific details
Reusable Rules: Same rule can handle different failure scenarios
Structured Data: Failure details can be consumed programmatically (JSON output)
Backward Compatible: If
failure_detailsis not populated, the message is used as-is
Example Usage
phases:
- name: check_depth
type: max_dependency_depth
params:
max_depth: 3
resource_dirs:
- .claude/agents
- .claude/skills
failure_message: "Dependency depth {actual_depth} exceeds maximum {max_depth}"
expected_behavior: "Keep dependency chains under 3 levels"
Output when validation fails:
Dependency depth 5 exceeds maximum 3. Chain: agent_a → skill_b → skill_c → skill_d → skill_e
File Validators
file_exists
Check if specified file(s) exist in the project.
Computation Type: Programmatic (no LLM required)
Parameters:
Parameter |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
string |
Yes |
- |
Path to file (supports glob patterns) |
Supports: Single files and glob patterns (*.md, **/*.py)
Example:
phases:
- name: check_readme
type: file_exists
file_path: README.md
failure_message: "README.md is missing"
expected_behavior: "Project must have README.md"
Example with glob pattern:
phases:
- name: check_tests
type: file_exists
file_path: "tests/**/*.py"
failure_message: "No test files found"
expected_behavior: "Project must have test files"
Failure Messages:
Without file_path:
FileExistsValidator requires rule.file_pathFile not found:
<failure_message from rule>No glob matches:
<failure_message from rule>
Failure Details: None (simple existence check)
Example Output:
When a specific file is missing:
README.md is missing
When using glob patterns and no files match:
No test files found
file_size
Validate file size constraints (line count or byte size).
Computation Type: Programmatic (no LLM required)
Parameters:
Parameter |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
string |
Yes |
- |
Path to file to check |
|
integer |
No |
- |
Maximum number of lines |
|
integer |
No |
- |
Minimum number of lines |
|
integer |
No |
- |
Maximum file size in bytes |
|
integer |
No |
- |
Minimum file size in bytes |
Example (line count):
phases:
- name: check_claude_md_size
type: file_size
file_path: CLAUDE.md
max_count: 200
failure_message: "CLAUDE.md exceeds 200 lines"
expected_behavior: "CLAUDE.md should be concise (under 200 lines)"
Example (byte size):
phases:
- name: check_config_size
type: file_size
file_path: .drift.yaml
max_size: 10240
failure_message: "Config file exceeds 10KB"
expected_behavior: "Config should be under 10KB"
Failure Messages:
File not found:
File {file_path} does not existLine count exceeded:
File has {actual} lines (exceeds max {max_count})Line count too low:
File has {actual} lines (below min {min_count})Byte size exceeded:
File is {actual} bytes (exceeds max {max_size})Byte size too low:
File is {actual} bytes (below min {min_size})
Failure Details: None
Example Output:
When file exceeds maximum line count:
File has 250 lines (exceeds max 200)
When file is below minimum byte size:
File is 512 bytes (below min 1024)
block_line_count
Validate line counts within paired delimiters (code blocks, YAML sections, etc.).
Computation Type: Programmatic (no LLM required)
Parameters:
Parameter |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
string |
Yes |
- |
Regex pattern for opening delimiter |
|
string |
Yes |
- |
Regex pattern for closing delimiter |
|
integer |
No |
- |
Minimum lines required (inclusive) |
|
integer |
No |
- |
Maximum lines allowed (inclusive) |
|
integer |
No |
- |
Exact line count required |
|
list[string] |
No |
|
File patterns to validate (glob patterns) |
Note: At least one threshold (min_lines, max_lines, or exact_lines) must be specified. Line counts exclude the delimiter lines themselves.
Example (minimum lines):
phases:
- name: check_code_examples
type: block_line_count
params:
pattern_start: "^```"
pattern_end: "^```"
min_lines: 3
files: ["README.md", "docs/**/*.md"]
failure_message: "Code blocks should be substantial"
expected_behavior: "Code examples should have at least 3 lines"
Example (maximum lines):
phases:
- name: check_yaml_sections
type: block_line_count
params:
pattern_start: "^---$"
pattern_end: "^---$"
max_lines: 20
files: [".claude/agents/*.md"]
failure_message: "YAML frontmatter exceeds maximum size"
expected_behavior: "Frontmatter should be concise (under 20 lines)"
Example (exact lines):
phases:
- name: check_header_blocks
type: block_line_count
params:
pattern_start: "^<!--\\s*HEADER\\s*START\\s*-->"
pattern_end: "^<!--\\s*HEADER\\s*END\\s*-->"
exact_lines: 5
files: ["docs/**/*.html"]
failure_message: "Header blocks must be exactly 5 lines"
expected_behavior: "Standard header format requires 5 lines"
Failure Messages:
Missing parameters:
BlockLineCountValidator requires params.pattern_start(orparams.pattern_end)No threshold:
BlockLineCountValidator requires at least one of: params.min_lines, params.max_lines, or params.exact_linesInvalid pattern:
Invalid regex pattern: {error}Unpaired delimiters:
Unpaired delimiters in {file_path}: opening and closing delimiter counts don't matchFile read error:
Failed to read file {file_path}: {error}Threshold violation:
Block line count validation failed(with detailed per-block violations)
Failure Details:
Field |
Type |
Description |
|---|---|---|
|
integer |
Total number of blocks found |
|
list[object] |
Detailed info for each violation |
|
string |
Threshold description |
Each violation object contains:
file: File pathstart_line: Block start line number (1-indexed)end_line: Block end line number (1-indexed)line_count: Actual line countviolation: Violation description
Example Output:
When code blocks are too short:
Code blocks should be substantial
Found 5 total blocks, 2 in violation
Threshold: min 3
README.md:10-12: 1 lines (expected at least 3)
README.md:45-47: 2 lines (expected at least 3)
When blocks exceed maximum:
YAML frontmatter exceeds maximum size
Found 3 total blocks, 1 in violation
Threshold: max 20
.claude/agents/developer.md:1-25: 23 lines (expected at most 20)
When unpaired delimiters are found:
Unpaired delimiters in README.md: opening and closing delimiter counts don't match
Edge Cases:
Empty blocks (0 lines between delimiters): Counted as 0 lines
Same delimiter pattern: For patterns like `^````, delimiters are paired sequentially (1st-2nd, 3rd-4th, etc.)
Different delimiter patterns: Each start delimiter is paired with the next end delimiter
No matching files: Validation passes if no files match the
filespatternsMissing files parameter: Validates all files in the bundle if
filesis not specified
Regex Validators
regex_match
Check if file content matches a regex pattern.
Computation Type: Programmatic (no LLM required)
Parameters:
Parameter |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
string |
Yes |
- |
Regular expression pattern |
|
string |
No |
- |
Specific file to check (if omitted, checks all bundle files) |
|
integer |
No |
|
Regex flags (e.g., |
Common Regex Flags:
8=re.MULTILINE-^and$match line boundaries2=re.IGNORECASE- Case-insensitive matching16=re.DOTALL-.matches newlines
Example:
phases:
- name: check_tools_format
type: regex_match
pattern: '^tools:\s+[A-Z][\w_]+(?:,\s*[A-Z][\w_]+)*\s*$'
flags: 8
failure_message: "Agent tools field uses wrong format"
expected_behavior: "Tools should be comma-separated on single line"
Example (bundle mode):
phases:
- name: check_frontmatter_name
type: regex_match
pattern: '^name:\s+\w+$'
flags: 8
failure_message: "Frontmatter missing name field"
expected_behavior: "All files must have name in frontmatter"
Failure Messages:
Pattern not provided:
RegexMatchValidator requires rule.patternInvalid pattern:
Invalid regex pattern: {error}File not found:
File not found: {file_path}Pattern not matched:
<failure_message from rule>
Failure Details: None
Example Output:
When pattern is not found in a specific file:
Agent tools field uses wrong format
When validating multiple files in a bundle (context provides details):
Agent tools field uses wrong format
List Validators
list_match
Check if list items match expected values.
Computation Type: Programmatic (no LLM required)
Parameters:
Parameter |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
param_spec |
Yes |
- |
List to check (supports |
|
param_spec |
Yes |
- |
List to compare against (supports |
|
string |
No |
|
Match mode: |
Match Modes:
all_in: All items must be in targetnone_in: No items should be in targetexact: Lists must match exactly (order-independent)
Example:
phases:
- name: check_dependencies
type: list_match
params:
items:
type: resource_list
value: skills
target:
type: file_content
value: CLAUDE.md
match_mode: all_in
failure_message: "Skills not documented in CLAUDE.md"
expected_behavior: "All skills should be mentioned in CLAUDE.md"
Failure Messages:
Missing params:
ListMatchValidator requires 'items' and 'target' paramsItems not found:
Items not found in target: {missing}Items found (none_in):
Items found in target but should not be: {found}Exact mismatch:
Lists do not match exactly. Items: {items}, Target: {target}
Failure Details: None
Example Output:
When items are not found in target (all_in mode):
Skills not documented in CLAUDE.md
When items are found but shouldn’t be (none_in mode):
Skills not documented in CLAUDE.md
When lists don’t match exactly (exact mode):
Skills not documented in CLAUDE.md
list_regex_match
Check if list items match regex patterns extracted from files.
Computation Type: Programmatic (no LLM required)
Parameters:
Parameter |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
param_spec |
Yes |
- |
List to check (supports |
|
param_spec |
Yes |
- |
File path to search in |
|
string |
Yes |
- |
Regex pattern to extract matches from file |
|
string |
No |
|
Match mode: |
Example:
phases:
- name: check_skills_documented
type: list_regex_match
params:
items:
type: resource_list
value: skills
file_path:
type: string
value: CLAUDE.md
pattern: '\b([\w-]+)\s+skill'
match_mode: all_in
failure_message: "Skills not mentioned in CLAUDE.md"
expected_behavior: "All skills should be referenced in documentation"
Failure Messages:
Missing params:
ListRegexMatchValidator requires 'items', 'file_path', and 'pattern' paramsItems not found:
Items not found in file: {missing}. Found: {found}Items found (none_in):
Items found in file but should not be: {found}
Failure Details: None
Example Output:
When items are not found in file:
Skills not mentioned in CLAUDE.md
When items are found but shouldn’t be (none_in mode):
Skills not mentioned in CLAUDE.md
Markdown Validators
markdown_link
Validate markdown links (local files, external URLs, resource references).
Computation Type: Programmatic (no LLM required)
Parameters:
Parameter |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
boolean |
No |
|
Validate local file references |
|
boolean |
No |
|
Validate external URLs (HTTP/HTTPS) |
|
boolean |
No |
|
Validate resource references (agents, skills, commands) |
|
list[string] |
No |
|
Regex patterns to extract resource names |
|
boolean |
No |
|
Skip example.com and similar placeholder domains |
|
boolean |
No |
|
Skip links in code blocks |
|
boolean |
No |
|
Skip placeholder paths like |
|
list[string] |
No |
|
Custom regex patterns to skip |
Example:
phases:
- name: check_links
type: markdown_link
params:
check_local_files: true
check_external_urls: false
skip_code_blocks: true
failure_message: "Found broken links"
expected_behavior: "All file references should be valid"
Example (with resource checking):
phases:
- name: check_skill_references
type: markdown_link
params:
check_local_files: true
check_resource_refs: true
resource_patterns:
- '\[([^\]]+)\]\(#skill-(\w+)\)'
failure_message: "Found broken skill references"
expected_behavior: "All skill references should be valid"
Failure Messages:
Broken local file:
{file_path}: [{link}] - local file not foundBroken external URL:
{file_path}: [{link}] - external URL unreachableBroken resource:
{file_path}: [{resource_name}] - {resource_type} reference not found
Failure Details: None
Example Output:
When local file references are broken:
Found broken links: README.md: [../docs/guide.md] - local file not found; CLAUDE.md: [.claude/agents/missing.md] - local file not found
When external URLs are unreachable:
Found broken links: README.md: [https://example-broken-url.com] - external URL unreachable
When resource references are broken:
Found broken links: agent.md: [testing] - skill reference not found
Format Validators
json_schema
Validate JSON files against JSON Schema specifications.
Computation Type: Programmatic (no LLM required)
Parameters:
Parameter |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
string |
Yes |
- |
Path to JSON file |
|
object |
No |
- |
Inline JSON schema |
|
string |
No |
- |
Path to schema file (relative to project) |
Note: Must provide either schema or schema_file.
Example (inline schema):
phases:
- name: check_package_json
type: json_schema
file_path: package.json
params:
schema:
type: object
properties:
name:
type: string
version:
type: string
pattern: '^\d+\.\d+\.\d+$'
required:
- name
- version
failure_message: "Invalid package.json structure"
expected_behavior: "package.json must have name and version"
Example (external schema):
phases:
- name: check_config
type: json_schema
file_path: config.json
params:
schema_file: schemas/config-schema.json
failure_message: "Invalid configuration"
expected_behavior: "Config must match schema"
Failure Messages:
File not found:
File {file_path} does not existInvalid JSON:
Invalid JSON: {error}Schema not provided:
JsonSchemaValidator requires 'schema' or 'schema_file' in paramsSchema file not found:
Schema file not found: {schema_file}Validation failed:
Schema validation failed at {path}: {message}Invalid schema:
Invalid schema: {message}
Failure Details: None
Example Output:
When JSON schema validation fails:
Schema validation failed at /version: '1.0.0' does not match '^\\d+\\.\\d+\\.\\d+$'
When required fields are missing:
Schema validation failed at root: 'name' is a required property
When JSON is invalid:
Invalid JSON: Expecting ',' delimiter: line 5 column 3 (char 89)
yaml_schema
Validate YAML files against schema specifications (JSON Schema format).
Computation Type: Programmatic (no LLM required)
Parameters:
Parameter |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
string |
Yes |
- |
Path to YAML file |
|
object |
No |
- |
Inline schema (JSON Schema format) |
|
string |
No |
- |
Path to schema file (YAML or JSON) |
Note: Must provide either schema or schema_file.
Example:
phases:
- name: check_drift_config
type: yaml_schema
file_path: .drift.yaml
params:
schema:
type: object
properties:
providers:
type: object
models:
type: object
required:
- providers
- models
failure_message: "Invalid .drift.yaml structure"
expected_behavior: "Config must have providers and models"
Failure Messages:
File not found:
File {file_path} does not existInvalid YAML:
Invalid YAML: {error}Schema validation failed:
Schema validation failed at {path}: {message}
Failure Details: None
Example Output:
When YAML schema validation fails:
Schema validation failed at /model: 'gpt-4' is not one of ['sonnet', 'opus', 'haiku']
When YAML is invalid:
Invalid YAML: mapping values are not allowed here
When file doesn’t exist:
File .drift.yaml does not exist
yaml_frontmatter
Validate YAML frontmatter in Markdown files.
Computation Type: Programmatic (no LLM required)
Parameters:
Parameter |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
string |
No |
- |
Specific file (if omitted, validates all bundle files) |
|
list[string] |
No |
|
List of required frontmatter fields |
|
object |
No |
- |
JSON Schema for frontmatter validation |
Example (required fields):
phases:
- name: check_frontmatter
type: yaml_frontmatter
params:
required_fields:
- name
- description
failure_message: "Missing required frontmatter fields"
expected_behavior: "All agents must have name and description"
Example (with schema):
phases:
- name: check_agent_frontmatter
type: yaml_frontmatter
params:
required_fields:
- name
- description
- model
schema:
type: object
properties:
name:
type: string
pattern: '^[a-z][a-z0-9-]*$'
model:
type: string
enum: ['sonnet', 'opus', 'haiku']
required:
- name
- description
failure_message: "Invalid frontmatter"
expected_behavior: "Frontmatter must match schema"
Failure Messages:
File not found:
File {file_path} does not existNo frontmatter:
No YAML frontmatter found (must start with ---)Not closed:
YAML frontmatter not properly closed (missing closing ---)Empty:
YAML frontmatter is emptyInvalid YAML:
Invalid YAML in frontmatter: {error}Missing fields:
Missing required frontmatter fields: {fields}Schema validation failed:
Frontmatter schema validation failed at {path}: {message}
Failure Details: None
Example Output:
When required frontmatter fields are missing:
Missing required frontmatter fields: description, model
When frontmatter is missing:
No YAML frontmatter found (must start with ---)
When frontmatter schema validation fails:
Frontmatter schema validation failed at /name: 'My-Agent' does not match '^[a-z][a-z0-9-]*$'
When validating multiple files:
.claude/agents/developer.md: Missing required frontmatter fields: description; .claude/agents/qa.md: No YAML frontmatter found (must start with ---)
Dependency Validators
These validators analyze dependency graphs to detect structural issues. They work with any DependencyGraph implementation and are extensible for different file-based dependency systems.
dependency_duplicate
Detect redundant transitive dependencies in dependency chains.
Computation Type: Programmatic (no LLM required)
Parameters:
Parameter |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
list[string] |
Yes |
- |
Directories containing resources to analyze |
Example:
phases:
- name: check_duplicates
type: dependency_duplicate
params:
resource_dirs:
- .claude/agents
- .claude/skills
failure_message: "Found redundant dependency '{duplicate_resource}'"
expected_behavior: "Only declare direct dependencies"
Failure Messages:
Single duplicate:
{failure_message}: '{duplicate_resource}' is redundant (already declared by '{declared_by}')Multiple duplicates:
{failure_message}: {duplicate_count} duplicates detected ({details})
Failure Details:
Field |
Type |
Description |
|---|---|---|
|
string |
Name of the duplicate resource |
|
string |
Resource that already declares this dependency |
|
integer |
Total number of duplicates found |
|
list[object] |
Detailed info for each duplicate |
Example Output:
Found redundant dependency 'skill_b': 'skill_b' is redundant (already declared by 'skill_a')
circular_dependencies
Detect circular dependencies in resource graphs.
Computation Type: Programmatic (no LLM required)
Parameters:
Parameter |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
list[string] |
Yes |
- |
Directories containing resources to analyze |
Example:
phases:
- name: check_cycles
type: circular_dependencies
params:
resource_dirs:
- .claude/agents
- .claude/skills
failure_message: "Circular dependency detected: {circular_path}"
expected_behavior: "Dependencies must be acyclic"
Failure Messages:
Single cycle:
{failure_message}: {circular_path}Multiple cycles:
{failure_message}: {cycle_count} cycles detected ({details})
Failure Details:
Field |
Type |
Description |
|---|---|---|
|
string |
Dependency cycle path (e.g., “A → B → C → A”) |
|
integer |
Total number of cycles found |
|
list[object] |
Detailed info for each cycle |
Example Output:
Circular dependency detected: agent_a → skill_b → skill_c → agent_a
max_dependency_depth
Detect when dependency chains exceed maximum depth.
Computation Type: Programmatic (no LLM required)
Parameters:
Parameter |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
integer |
No |
|
Maximum allowed dependency chain depth |
|
list[string] |
Yes |
- |
Directories containing resources to analyze |
Depth Calculation:
Depth is the longest path from a resource to any leaf dependency:
Resource with no dependencies: depth 0
Resource depending on one resource (which has no dependencies): depth 1
Chain A → B → C → D: A has depth 3
Example:
phases:
- name: check_depth
type: max_dependency_depth
params:
max_depth: 3
resource_dirs:
- .claude/agents
- .claude/skills
failure_message: "Dependency depth {actual_depth} exceeds max {max_depth}"
expected_behavior: "Keep dependency chains under 3 levels"
Failure Messages:
Single violation:
{failure_message}: Depth {actual_depth} exceeds maximum {max_depth}. Chain: {dependency_chain}Multiple violations:
{failure_message}: {violation_count} violations detected ({details})
Failure Details:
Field |
Type |
Description |
|---|---|---|
|
integer |
Actual depth of the dependency chain |
|
integer |
Maximum allowed depth |
|
string |
Full dependency chain path |
|
integer |
Total number of violations |
|
list[object] |
Detailed info for each violation |
Example Output:
Dependency depth 5 exceeds max 3. Chain: agent_a → skill_b → skill_c → skill_d → skill_e
Claude Code Validators
These validators are specific to Claude Code project structure and configuration. They only run when client_type is set to claude-code.
claude_skill_settings
Validate that all skills have permission entries in .claude/settings.json.
Computation Type: Programmatic (no LLM required)
Client Support: Claude Code only
Parameters: None
Example:
rule_definitions:
claude_skill_permissions:
description: "All skills must have Skill() permissions in settings.json"
scope: project_level
supported_clients:
- claude-code
document_bundle:
bundle_type: project
file_patterns:
- .claude/settings.json
bundle_strategy: collection
phases:
- name: check_permissions
type: claude_skill_settings
failure_message: "Skills missing from permissions"
expected_behavior: "All skills need Skill() entries in settings.json"
Failure Messages:
Settings not found:
settings.json not found at .claude/settings.jsonInvalid JSON:
Invalid JSON in settings.json: {error}Missing skills:
Skills missing from permissions.allow: {skills}. Add entries like 'Skill({skill})' to .claude/settings.json
Failure Details: None
Example Output:
When skills are missing from permissions:
Skills missing from permissions
When settings.json is not found:
Skills missing from permissions
claude_settings_duplicates
Validate that .claude/settings.json permissions list has no duplicates.
Computation Type: Programmatic (no LLM required)
Client Support: Claude Code only
Parameters: None
Example:
phases:
- name: check_duplicates
type: claude_settings_duplicates
failure_message: "Duplicate permissions in settings.json"
expected_behavior: "Permission entries should be unique"
Failure Messages:
Invalid JSON:
Invalid JSON in settings.json: {error}Duplicates found:
Duplicate permission entries found: {duplicates}. Remove duplicates from permissions.allow
Failure Details: None
Example Output:
When duplicate permissions are found:
Duplicate permissions in settings.json
When settings.json has invalid JSON:
Duplicate permissions in settings.json
claude_mcp_permissions
Validate that all MCP servers have permission entries in .claude/settings.json.
Computation Type: Programmatic (no LLM required)
Client Support: Claude Code only
Parameters: None
Example:
phases:
- name: check_mcp_permissions
type: claude_mcp_permissions
failure_message: "MCP servers missing from permissions"
expected_behavior: "All MCP servers need mcp__ entries or enableAllProjectMcpServers: true"
Failure Messages:
Settings not found:
settings.json not found at .claude/settings.json but .mcp.json existsInvalid JSON:
Invalid JSON in {file}: {error}Missing servers:
MCP servers missing from permissions.allow: {servers}. Add entries like 'mcp__{server}' or set 'enableAllProjectMcpServers: true'
Note: If enableAllProjectMcpServers: true is set in settings.json, individual permissions are not required.
Failure Details: None
Example Output:
When MCP servers are missing from permissions:
MCP servers missing from permissions
When settings.json is not found but .mcp.json exists:
MCP servers missing from permissions
LLM-Based Validators
prompt
Use an LLM to analyze content for semantic validation.
Computation Type: LLM (requires API key/provider configuration)
Parameters:
Parameter |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
string |
Yes |
- |
Prompt for the LLM to analyze content |
|
string |
Yes |
- |
Model to use (must be defined in config) |
|
list[string] |
No |
|
Resources to make available ( |
Example:
phases:
- name: check_completeness
type: prompt
model: haiku
prompt: |
Analyze this skill for completeness.
CRITICAL ISSUES (report these):
1. Missing "when to use" guidance
2. No actionable instructions
3. Missing examples
ACCEPTABLE (do NOT report):
- Brief descriptions
- References to project files
available_resources:
- skill
failure_message: "Skill is incomplete"
expected_behavior: "Skills should be self-contained with clear guidance"
Failure Messages:
Model not configured:
Model '{model}' not found in configurationAPI error:
LLM API error: {error}Custom message:
{failure_message from rule}(when LLM reports an issue)
Failure Details: Varies based on LLM response
Example Output:
When LLM detects issues based on the prompt:
Skill is incomplete
When model is not configured:
Model 'haiku' not found in configuration
When there’s an API error:
LLM API error: Rate limit exceeded
Troubleshooting
Validator Not Found
If you get “Unsupported validation rule type” error:
Check validator name matches exactly (case-sensitive)
Verify validator is registered in
ValidatorRegistryFor client-specific validators, ensure
supported_clientsis set
Missing Parameters
If you get “requires ‘X’ param” error:
Check required parameters table in this document
Ensure params are nested under
params:keyVerify parameter types match expectations
Placeholder Not Replaced
If placeholders remain in output (e.g., {circular_path}):
Verify validator supports
failure_details(check this doc)Check placeholder name matches
failure_detailskey exactlyEnsure validator is populating
failure_detailson failure
Performance Issues
For slow validation:
Use
--no-llmflag to skip LLM-based validatorsUse
--rulesto run specific rules onlyConsider splitting large rulesets into separate files
Use programmatic validators where possible
Contributing
To add a new validator:
Inherit from
BaseValidatorImplement
computation_typepropertyImplement
validate()methodPopulate
failure_detailsdictionary for actionable messagesUse
_format_message()helper for template interpolationRegister in
ValidatorRegistryDocument in this file
Example validator skeleton:
class MyValidator(BaseValidator):
@property
def computation_type(self) -> Literal["programmatic", "llm"]:
return "programmatic"
def validate(
self,
rule: ValidationRule,
bundle: DocumentBundle,
all_bundles: Optional[List[DocumentBundle]] = None,
) -> Optional[DocumentRule]:
# Perform validation logic
if validation_fails:
failure_details = {
"specific_detail": "value",
"count": 42
}
message = self._format_message(
rule.failure_message,
failure_details
)
return DocumentRule(
bundle_id=bundle.bundle_id,
bundle_type=bundle.bundle_type,
file_paths=affected_files,
observed_issue=message,
expected_quality=rule.expected_behavior,
rule_type="",
context=f"Validation rule: {rule.description}",
failure_details=failure_details
)
return None