Add naming convention validator

Automated naming standard enforcement:
- Validates lowercase-with-hyphens
- Checks date formats (YYYY-MM-DD)
- Detects spaces/underscores
- Photo naming exceptions

Per Fresh Claude Review Priority #2.

Usage: python3 automation/validate-naming.py [--verbose]

Date: 2026-02-16
This commit is contained in:
2026-02-16 07:05:42 -06:00
parent f3b45a0160
commit 6464624536

View File

@@ -0,0 +1,165 @@
#!/usr/bin/env python3
"""
Naming Convention Validator for Firefrost Operations Manual
Validates file and directory naming follows FFG standards:
- lowercase-with-hyphens for files and directories
- UPPERCASE for root-level critical docs
- Proper date formats (YYYY-MM-DD)
- No spaces, underscores (except photos), or special chars
Usage:
python3 validate-naming.py [--fix] [--verbose]
"""
import os
import re
import sys
from pathlib import Path
from typing import List, Tuple
class NamingValidator:
def __init__(self, repo_root: str, fix: bool = False, verbose: bool = False):
self.repo_root = Path(repo_root)
self.fix = fix
self.verbose = verbose
self.violations: List[Tuple[str, str]] = []
# Naming rules
self.valid_root_uppercase = [
'README.md',
'DOCUMENT-INDEX.md',
'SESSION-HANDOFF-PROTOCOL.md',
'DERP.md',
'LICENSE',
'.gitignore'
]
def is_valid_lowercase_hyphen(self, name: str) -> bool:
"""Check if name follows lowercase-with-hyphens convention"""
# Allow: lowercase letters, numbers, hyphens, dots (for extensions)
pattern = r'^[a-z0-9\-\.]+$'
return bool(re.match(pattern, name))
def is_valid_date_format(self, name: str) -> bool:
"""Check if name contains properly formatted dates"""
# Look for YYYY-MM-DD patterns
date_pattern = r'\d{4}-\d{2}-\d{2}'
dates = re.findall(date_pattern, name)
for date in dates:
year, month, day = date.split('-')
# Basic validation
if not (1900 <= int(year) <= 2100):
return False
if not (1 <= int(month) <= 12):
return False
if not (1 <= int(day) <= 31):
return False
return True
def check_path(self, path: Path):
"""Check a single file or directory path"""
relative_path = path.relative_to(self.repo_root)
parts = relative_path.parts
# Check each part of the path
for i, part in enumerate(parts):
is_root_level = (i == 0)
is_file = (i == len(parts) - 1 and path.is_file())
# Skip hidden files/dirs
if part.startswith('.'):
continue
# Root level uppercase exceptions
if is_root_level and part in self.valid_root_uppercase:
continue
# Photos directory exception (allows underscores)
if 'photos' in parts:
# Photos can use YYYY-MM-DD_subject_description_NN.jpg format
if is_file and part.endswith(('.jpg', '.jpeg', '.png', '.gif')):
photo_pattern = r'^\d{4}-\d{2}-\d{2}_[a-z0-9\-_]+\d*\.(jpg|jpeg|png|gif)$'
if re.match(photo_pattern, part):
continue
# Check for violations
if ' ' in part:
self.violations.append((str(relative_path), "Contains spaces"))
elif part != part.lower() and not (is_root_level and part in self.valid_root_uppercase):
self.violations.append((str(relative_path), "Not lowercase"))
elif '_' in part and 'photos' not in parts:
self.violations.append((str(relative_path), "Contains underscore (use hyphens)"))
elif not self.is_valid_lowercase_hyphen(part) and 'photos' not in parts:
if self.verbose:
print(f" ⚠️ {relative_path}: {part}")
# Check date formats
if not self.is_valid_date_format(part):
self.violations.append((str(relative_path), "Invalid date format"))
def run(self):
"""Run naming validator on all files and directories"""
print("🔍 Scanning repository structure...")
for root, dirs, files in os.walk(self.repo_root):
# Skip hidden directories
dirs[:] = [d for d in dirs if not d.startswith('.')]
root_path = Path(root)
# Check directories
for dir_name in dirs:
self.check_path(root_path / dir_name)
# Check files
for file_name in files:
if not file_name.startswith('.'):
self.check_path(root_path / file_name)
self.report()
def report(self):
"""Generate report of naming violations"""
print("\n" + "="*80)
print("📊 NAMING CONVENTION REPORT")
print("="*80)
print(f"\nViolations found: {len(self.violations)}\n")
if self.violations:
print("❌ NAMING VIOLATIONS:")
print("-" * 80)
for path, reason in self.violations:
print(f"\n📄 {path}")
print(f" Issue: {reason}")
print("\n" + "="*80)
print("\n💡 Fix suggestions:")
print(" - Replace spaces with hyphens: 'My File.md''my-file.md'")
print(" - Use lowercase: 'MyFile.md''my-file.md'")
print(" - Replace underscores: 'my_file.md''my-file.md'")
print(" - Date format: YYYY-MM-DD")
print("\n" + "="*80)
sys.exit(1)
else:
print("✅ All naming conventions followed!")
print("="*80)
sys.exit(0)
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Validate naming conventions")
parser.add_argument("--fix", action="store_true", help="Attempt to fix violations")
parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
parser.add_argument("--repo", default=".", help="Repository root path")
args = parser.parse_args()
validator = NamingValidator(
repo_root=args.repo,
fix=args.fix,
verbose=args.verbose
)
validator.run()