Move 9 init/seed scripts into scripts/seed/ and 7 validation scripts (+ validators/ subfolder) into scripts/validate/ to reduce clutter in the root scripts/ directory. Update all references across Makefile, CI/CD configs, pre-commit hooks, docs (~40 files), and Python imports. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
28 KiB
Architecture Rules Reference
This document provides a comprehensive reference for all architectural rules enforced by the scripts/validate/validate_architecture.py validator.
Overview
The architecture validator ensures consistent patterns, separation of concerns, and best practices across the entire codebase. Rules are organized by category and severity level.
Version: 2.0
Total Rules: 50+
Configuration File: .architecture-rules.yaml
Running the Validator
Using Make Commands (Recommended)
# Check all files
make arch-check
# Check a single file
make arch-check-file file="app/api/v1/admin/stores.py"
# Check all files related to an entity (merchant, store, user, etc.)
make arch-check-object name="merchant"
make arch-check-object name="store"
# Full QA (includes arch-check)
make qa
Using Python Directly
# Check all files
python scripts/validate/validate_architecture.py
# Check specific directory
python scripts/validate/validate_architecture.py -d app/api/
# Check a single file
python scripts/validate/validate_architecture.py -f app/api/v1/admin/stores.py
# Check all files for an entity
python scripts/validate/validate_architecture.py -o merchant
python scripts/validate/validate_architecture.py -o store
# Verbose output
python scripts/validate/validate_architecture.py --verbose
# JSON output (for CI/CD)
python scripts/validate/validate_architecture.py --json
Output Format
The validator displays a summary table for validated files:
📋 FILE SUMMARY:
--------------------------------------------------------------------------------
File Status Errors Warnings
----------------------------- -------- ------- --------
app/api/v1/admin/merchants.py ❌ FAILED 6 9
app/services/merchant_service.py ✅ PASSED 0 0
models/database/merchant.py ✅ PASSED 0 0
--------------------------------------------------------------------------------
Total: 3 files | ✅ 2 passed | ❌ 1 failed | 6 errors | 9 warnings
Severity Levels
| Severity | Description | Exit Code | Action Required |
|---|---|---|---|
| Error | Critical architectural violation | 1 | Must fix before committing |
| Warning | Pattern deviation | 0 | Should fix, doesn't block |
| Info | Suggestion for improvement | 0 | Optional improvement |
Core Architectural Principles
- Separation of Concerns - API endpoints handle HTTP, services handle business logic
- Layered Architecture - Routes → Services → Models
- Type Safety - Pydantic for API, SQLAlchemy for database
- Proper Exception Handling - Domain exceptions in services, HTTPException in routes
- Multi-Tenancy - All queries scoped to store_id
- Consistent Naming - API files: plural, Services: singular+service, Models: singular
Backend Rules
API Endpoint Rules (app/api/v1/**/*.py)
API-001: Use Pydantic Models for Request/Response
Severity: Error
All API endpoints must use Pydantic models (BaseModel) for request bodies and response models. Never use raw dicts or SQLAlchemy models directly.
# ✅ Good
@router.post("/stores", response_model=StoreResponse)
async def create_store(store: StoreCreate):
return store_service.create_store(db, store)
# ❌ Bad
@router.post("/stores")
async def create_store(data: dict):
return {"name": data["name"]} # No validation!
API-002: No Business Logic in Endpoints
Severity: Error
API endpoints should only handle HTTP concerns (validation, auth, response formatting). All business logic must be delegated to service layer.
# ✅ Good
@router.post("/stores")
async def create_store(store: StoreCreate, db: Session = Depends(get_db)):
result = store_service.create_store(db, store)
return result
# ❌ Bad
@router.post("/stores")
async def create_store(store: StoreCreate, db: Session = Depends(get_db)):
db_store = Store(name=store.name)
db.add(db_store) # Business logic in endpoint!
db.commit()
return db_store
Anti-patterns detected:
db.add(db.commit()db.query(
API-003: No HTTPException in Endpoints
Severity: Error
API endpoints must NOT raise HTTPException directly. Instead, let domain exceptions bubble up to the global exception handler which converts them to appropriate HTTP responses.
# ✅ Good - Let domain exceptions bubble up
@router.post("/stores")
async def create_store(store: StoreCreate, db: Session = Depends(get_db)):
# Service raises StoreAlreadyExistsException if duplicate
# Global handler converts to 409 Conflict
return store_service.create_store(db, store)
# ❌ Bad - Don't raise HTTPException directly
@router.post("/stores")
async def create_store(store: StoreCreate, db: Session = Depends(get_db)):
if store_service.exists(db, store.subdomain):
raise HTTPException(status_code=409, detail="Store exists") # BAD!
return store_service.create_store(db, store)
Pattern: Services raise domain exceptions → Global handler converts to HTTP responses
API-004: Proper Authentication
Severity: Warning
Protected endpoints must use Depends() for authentication.
# ✅ Good - Protected endpoint with authentication
@router.post("/stores")
async def create_store(
store: StoreCreate,
current_user: User = Depends(get_current_admin),
db: Session = Depends(get_db)
):
pass
Auto-Excluded Files:
The validator automatically skips API-004 checks for authentication endpoint files (*/auth.py) since login, logout, and registration endpoints are intentionally public.
Marking Public Endpoints:
For other intentionally public endpoints (webhooks, health checks, etc.), use a comment marker:
# ✅ Good - Webhook endpoint marked as public
# public - Stripe webhook receives external callbacks
@router.post("/webhook/stripe")
def stripe_webhook(request: Request):
...
# ✅ Good - Using noqa style
@router.post("/health") # noqa: API-004
def health_check():
...
Recognized markers:
# public- descriptive marker for intentionally unauthenticated endpoints# noqa: API-004- standard noqa style to suppress the warning
API-005: Multi-Tenant Scoping
Severity: Error
All queries in store/shop contexts must filter by store_id.
Service Layer Rules (app/services/**/*.py)
SVC-001: No HTTPException in Services
Severity: Error
Services are business logic layer - they should NOT know about HTTP. Raise domain-specific exceptions instead.
# ✅ Good
class StoreService:
def create_store(self, db: Session, store_data):
if self._store_exists(db, store_data.subdomain):
raise StoreAlreadyExistsError(f"Store {store_data.subdomain} exists")
# ❌ Bad
class StoreService:
def create_store(self, db: Session, store_data):
if self._store_exists(db, store_data.subdomain):
raise HTTPException(status_code=409, detail="Store exists") # BAD!
SVC-002: Use Proper Exception Handling
Severity: Error
Services should raise meaningful domain exceptions, not generic Exception.
# ✅ Good
class StoreAlreadyExistsError(Exception):
pass
def create_store(self, db: Session, store_data):
if self._store_exists(db, store_data.subdomain):
raise StoreAlreadyExistsError(f"Subdomain {store_data.subdomain} taken")
# ❌ Bad
def create_store(self, db: Session, store_data):
if self._store_exists(db, store_data.subdomain):
raise Exception("Store exists") # Too generic!
SVC-003: Accept DB Session as Parameter
Severity: Error
Service methods should receive database session as a parameter for testability and transaction control.
# ✅ Good
def create_store(self, db: Session, store_data: StoreCreate):
store = Store(**store_data.dict())
db.add(store)
db.commit()
return store
# ❌ Bad
def create_store(self, store_data: StoreCreate):
db = SessionLocal() # Don't create session inside!
store = Store(**store_data.dict())
db.add(store)
db.commit()
SVC-004: Use Pydantic Models for Input
Severity: Warning
Service methods should accept Pydantic models for complex inputs to ensure type safety.
SVC-005: Scope Queries to store_id
Severity: Error
All database queries must be scoped to store_id to prevent cross-tenant data access.
Model Rules (models/**/*.py)
MDL-001: Database Models Use SQLAlchemy Base
Severity: Error
All database models must inherit from SQLAlchemy Base.
MDL-002: Never Mix SQLAlchemy and Pydantic
Severity: Error
# ❌ Bad
class Product(Base, BaseModel): # NEVER DO THIS!
pass
Keep them separate:
models/database/product.py- SQLAlchemy modelsmodels/schema/product.py- Pydantic models
MDL-003: Use from_attributes in Pydantic
Severity: Error
Pydantic response models must enable from_attributes to work with SQLAlchemy models.
# ✅ Good
class ProductResponse(BaseModel):
id: int
name: str
class Config:
from_attributes = True
MDL-004: Use Singular Table Names
Severity: Warning
Database table names should be singular lowercase (e.g., 'store' not 'stores').
Exception Handling Rules
EXC-001: Define Custom Exceptions
Severity: Warning
Create domain-specific exceptions in app/exceptions/ for better error handling.
# ✅ Good - app/exceptions/store.py
class StoreError(Exception):
"""Base exception for store-related errors"""
pass
class StoreNotFoundError(StoreError):
pass
class StoreAlreadyExistsError(StoreError):
pass
EXC-002: Never Use Bare Except
Severity: Error
# ❌ Bad
try:
result = service.do_something()
except: # Too broad!
pass
# ✅ Good
try:
result = service.do_something()
except ValueError as e:
logger.error(f"Validation error: {e}")
except Exception as e:
logger.error(f"Unexpected error: {e}")
EXC-003: Log Exceptions with Context
Severity: Warning
When catching exceptions, log them with context and stack trace.
# ✅ Good
try:
result = service.process()
except Exception as e:
logger.error(f"Processing failed: {e}", exc_info=True)
Naming Convention Rules
NAM-001: API Files Use PLURAL Names
Severity: Error
✅ app/api/v1/admin/stores.py
✅ app/api/v1/admin/products.py
❌ app/api/v1/admin/store.py
Exceptions: __init__.py, auth.py, health.py
NAM-002: Service Files Use SINGULAR + service
Severity: Error
✅ app/services/store_service.py
✅ app/services/product_service.py
❌ app/services/stores_service.py
NAM-003: Model Files Use SINGULAR Names
Severity: Error
✅ models/database/product.py
✅ models/schema/store.py
❌ models/database/products.py
NAM-004: Use 'store' not 'shop'
Severity: Warning
Use consistent terminology: 'store' for shop owners, 'shop' only for customer-facing frontend.
# ✅ Good
store_id
store_service
# ❌ Bad
shop_id
shop_service
NAM-005: Use 'inventory' not 'stock'
Severity: Warning
# ✅ Good
inventory_service
inventory_level
# ❌ Bad
stock_service
stock_level
Frontend Rules
JavaScript Rules (static//js//*.js)
JS-001: Use Centralized Logger
Severity: Error
Never use console.log, console.error, console.warn directly. Use window.LogConfig.createLogger().
// ✅ Good
const pageLog = window.LogConfig.createLogger('dashboard');
pageLog.info('Dashboard loaded');
pageLog.error('Failed to load data', error);
// ❌ Bad
console.log('Dashboard loaded');
console.error('Failed to load data', error);
Exceptions: Bootstrap messages with console.log('✅' allowed.
JS-002: Use Lowercase apiClient
Severity: Error
// ✅ Good
await apiClient.get('/api/v1/stores');
await apiClient.post('/api/v1/products', data);
// ❌ Bad
await ApiClient.get('/api/v1/stores');
await API_CLIENT.post('/api/v1/products', data);
JS-003: Alpine Components Must Inherit ...data()
Severity: Error
All Alpine.js components must inherit base layout data using spread operator.
// ✅ Good
function adminDashboard() {
return {
...data(), // Inherit base layout data
currentPage: 'dashboard',
// ... component-specific data
};
}
// ❌ Bad
function adminDashboard() {
return {
currentPage: 'dashboard',
// Missing ...data()!
};
}
JS-004: Set currentPage in Components
Severity: Error
All Alpine.js page components must set a currentPage identifier.
// ✅ Good
return {
...data(),
currentPage: 'dashboard', // Required!
// ...
};
JS-005: Initialization Guards
Severity: Error
Init methods should prevent duplicate initialization.
// ✅ Good
init() {
if (window._pageInitialized) return;
window._pageInitialized = true;
// Initialization logic...
}
JS-006: Error Handling for Async Operations
Severity: Error
All API calls and async operations must have error handling.
// ✅ Good
async loadData() {
try {
const response = await apiClient.get('/api/v1/data');
this.items = response.data;
} catch (error) {
window.LogConfig.logError(error, 'Load Data');
Utils.showToast('Failed to load data', 'error');
}
}
JS-007: Set Loading State
Severity: Warning
Loading state should be set before and cleared after async operations.
// ✅ Good
async loadData() {
this.loading = true;
try {
const response = await apiClient.get('/api/v1/data');
this.items = response.data;
} catch (error) {
// Error handling
} finally {
this.loading = false;
}
}
Template Rules (app/templates/**/*.html)
TPL-001: Admin Templates Extend admin/base.html
Severity: Error
All admin templates must extend the base template for consistent layout (sidebar, navigation, etc.).
{# ✅ Good - Extends base template #}
{% extends "admin/base.html" %}
{% block content %}
...
{% endblock %}
Auto-Excluded Files:
The validator automatically skips TPL-001 checks for:
login.html- Standalone login page (no sidebar/navigation needed)errors/*.html- Error pages extenderrors/base.htmlinsteadtest-*.html- Test/development templatesbase.html- The base template itselfpartials/*.html- Partial templates included in other templates
Marking Standalone Templates:
For other templates that intentionally don't extend base.html, use a comment marker in the first 5 lines:
{# standalone - Minimal monitoring page without admin chrome #}
<!DOCTYPE html>
<html>
...
Recognized markers:
{# standalone #}- Jinja comment style{# noqa: TPL-001 #}- Standard noqa style<!-- standalone -->- HTML comment style
TPL-002: Store Templates Extend store/base.html
Severity: Error
✅ {% extends "store/base.html" %}
TPL-003: Shop Templates Extend shop/base.html
Severity: Error
✅ {% extends "shop/base.html" %}
TPL-004: Use x-text for Dynamic Content
Severity: Warning
Use x-text directive for dynamic content to prevent XSS vulnerabilities.
✅ <p x-text="item.name"></p>
❌ <p>{{ item.name }}</p>
TPL-005: Use x-html ONLY for Safe Content
Severity: Error
Use x-html only for trusted content like icons, never for user-generated content.
✅ <span x-html="$icon('home', 'w-5 h-5')"></span>
❌ <div x-html="userComment"></div> <!-- XSS vulnerability! -->
TPL-006: Implement Loading State
Severity: Warning
All templates that load data should show loading state.
✅ <div x-show="loading">Loading...</div>
TPL-007: Implement Empty State
Severity: Warning
Show empty state when lists have no items.
✅ <template x-if="items.length === 0">
<p>No items found</p>
</template>
TPL-008: Use Valid Block Names
Severity: Error
Templates must use block names that exist in their base template. Using undefined blocks silently fails (content is not rendered).
Valid Admin Template Blocks:
title- Page titleextra_head- Additional head content (CSS, meta tags)alpine_data- Alpine.js component function namecontent- Main page contentextra_scripts- Additional JavaScript
{# ✅ Good - Valid block names #}
{% block extra_scripts %}
<script src="/static/admin/js/dashboard.js"></script>
{% endblock %}
{# ❌ Bad - Invalid block name (silently ignored!) #}
{% block page_scripts %}
<script src="/static/admin/js/dashboard.js"></script>
{% endblock %}
Common mistakes detected:
page_scripts→ useextra_scriptsscripts→ useextra_scriptsjs→ useextra_scriptshead→ useextra_head
Jinja Macro Rules (app/templates/**/*.html)
MAC-001: Use Shared Macros for Repeated Patterns
Severity: Error
Never copy-paste HTML patterns. Use or create shared macros in app/templates/shared/macros/.
{# ✅ Good - Using shared macro #}
{% from 'shared/macros/tables.html' import table_wrapper, table_header %}
{% call table_wrapper() %}
{{ table_header(['Name', 'Email', 'Status']) }}
<tbody>...</tbody>
{% endcall %}
{# ❌ Bad - Copy-pasting table HTML #}
<div class="w-full overflow-hidden rounded-lg shadow-xs">
<div class="w-full overflow-x-auto">
<table class="w-full whitespace-no-wrap">
<thead>...</thead>
<tbody>...</tbody>
</table>
</div>
</div>
MAC-002: Import Macros at Template Top
Severity: Warning
All macro imports should be at the top of the template, after {% extends %}.
{# ✅ Good #}
{% extends "admin/base.html" %}
{% from 'shared/macros/pagination.html' import pagination %}
{% from 'shared/macros/tables.html' import table_wrapper %}
{% from 'shared/macros/alerts.html' import loading_state %}
{% block content %}
...
{% endblock %}
MAC-003: Use {% call %} for Wrapper Macros
Severity: Error
Wrapper macros that use {{ caller() }} must be invoked with {% call %}.
{# ✅ Good #}
{% call table_wrapper() %}
{{ table_header(['Col1', 'Col2']) }}
{% endcall %}
{% call tabs_nav() %}
{{ tab_button('tab1', 'First') }}
{% endcall %}
{# ❌ Bad - Missing call block #}
{{ table_wrapper() }} {# This won't render inner content! #}
MAC-004: Document New Macros
Severity: Warning
New macros must have JSDoc-style documentation comments.
{# ✅ Good #}
{#
Number Stepper
==============
A number input with +/- buttons.
Parameters:
- model: Alpine.js x-model variable (required)
- min: Minimum value (default: 1)
- max: Maximum value (optional)
Usage:
{{ number_stepper(model='quantity', min=1, max=99) }}
#}
{% macro number_stepper(model, min=1, max=none) %}
...
{% endmacro %}
MAC-005: Add Components to Reference Page
Severity: Warning
New shared components should be added to /admin/components page with live demos.
Frontend Component Rules (app/templates/**/*.html)
FE-008: Use number_stepper Macro for Quantity Inputs
Severity: Warning
Use the shared number_stepper macro instead of raw <input type="number"> for consistent styling and dark mode support.
{# ✅ Good - Using number_stepper macro #}
{% from 'shared/macros/inputs.html' import number_stepper %}
{{ number_stepper(model='quantity', min=1, max=99) }}
{# ❌ Bad - Raw number input #}
<input type="number" x-model="quantity" min="1" max="99" />
Suppress with noqa for ID fields:
{# noqa: FE-008 - User ID is typed directly, not incremented #}
<input type="number" x-model="userId" placeholder="User ID" />
Styling Rules (app/templates/**/*.html)
CSS-001: Use Tailwind Utility Classes
Severity: Warning
Prefer Tailwind utility classes over custom CSS.
CSS-002: Support Dark Mode
Severity: Warning
All color classes should include dark mode variants.
✅ class="bg-white dark:bg-gray-800 text-gray-900 dark:text-white"
❌ class="bg-white text-gray-900"
CSS-003: Shop Templates Use CSS Variables
Severity: Error
Shop templates must use CSS variables for store-specific theming.
✅ <button style="background-color: var(--color-primary)">Buy Now</button>
❌ <button class="bg-blue-600">Buy Now</button>
CSS-004: Mobile-First Responsive Design
Severity: Warning
Use mobile-first responsive classes.
✅ class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4"
❌ class="grid grid-cols-4"
Module Structure Rules
Module rules enforce consistent structure and completeness across all modules in app/modules/.
MOD-020: Module Definition Completeness
Severity: Warning
Module definitions should include required attributes: code, name, description, version, and features.
# ✅ Good - Complete definition
module = ModuleDefinition(
code="billing",
name="Billing & Subscriptions",
description="Platform subscription management",
version="1.0.0",
features=["subscription_management", "billing_history"],
permissions=[...],
)
# ❌ Bad - Missing features
module = ModuleDefinition(
code="billing",
name="Billing",
description="...",
version="1.0.0",
# Missing features and permissions
)
MOD-021: Modules with Menus Should Have Features
Severity: Warning
If a module defines menu items or menu sections, it should also define features.
# ❌ Bad - Has menus but no features
module = ModuleDefinition(
code="billing",
menus={FrontendType.ADMIN: [...]},
# Missing features!
)
MOD-022: Feature Modules Should Have Permissions
Severity: Info
Modules with features should define permissions for RBAC, unless:
is_internal=True(internal tools)- Storefront-only module (session-based, no admin UI)
# ✅ Good - Features with permissions
module = ModuleDefinition(
code="billing",
features=["subscription_management"],
permissions=[
PermissionDefinition(
id="billing.view_subscriptions",
label_key="billing.permissions.view_subscriptions",
description_key="billing.permissions.view_subscriptions_desc",
category="billing",
),
],
)
MOD-023: Router Pattern Consistency
Severity: Info
Modules with routers should use the get_*_with_routers() pattern for lazy imports.
# ✅ Good - Lazy router pattern
def _get_admin_router():
from app.modules.billing.routes.api.admin import admin_router
return admin_router
def get_billing_module_with_routers() -> ModuleDefinition:
billing_module.admin_router = _get_admin_router()
return billing_module
See Module System Architecture for complete MOD-001 to MOD-019 rules.
Security & Multi-Tenancy Rules
Multi-Tenancy Rules
MT-001: Scope All Queries to store_id
Severity: Error
In store/shop contexts, all database queries must filter by store_id.
# ✅ Good
def get_products(self, db: Session, store_id: int):
return db.query(Product).filter(Product.store_id == store_id).all()
# ❌ Bad
def get_products(self, db: Session):
return db.query(Product).all() # Cross-tenant data leak!
MT-002: No Cross-Store Data Access
Severity: Error
Queries must never access data from other stores.
Authentication & Authorization Rules
AUTH-001: Use JWT Tokens
Severity: Error
Authentication must use JWT tokens in Authorization: Bearer header.
AUTH-002: Role-Based Access Control
Severity: Error
Use Depends(get_current_admin/store/customer) for role checks.
# ✅ Good
@router.get("/admin/users")
async def list_users(current_user: User = Depends(get_current_admin)):
pass
AUTH-003: Never Store Plain Passwords
Severity: Error
Always hash passwords with bcrypt before storing.
Middleware Rules
MDW-001: Middleware Naming
Severity: Warning
Middleware files should be named with simple nouns (auth.py, not auth_middleware.py).
✅ middleware/auth.py
✅ middleware/context.py
❌ middleware/auth_middleware.py
MDW-002: Store Context Injection
Severity: Error
Store context middleware must set request.state.store_id and request.state.store.
Code Quality Rules
QUAL-001: Format with Ruff
Severity: Error
All code must be formatted with Ruff before committing.
make format
QUAL-002: Pass Ruff Linting
Severity: Error
All code must pass Ruff linting before committing.
make lint
QUAL-003: Use Type Hints
Severity: Warning
Add type hints to function parameters and return types.
# ✅ Good
def create_product(self, db: Session, data: ProductCreate) -> Product:
pass
# Better
from typing import Optional
def get_product(self, product_id: int) -> Optional[Product]:
pass
Ignored Patterns
The validator ignores these files/patterns:
- Test files:
**/*_test.py,**/test_*.py - Cache:
**/__pycache__/** - Migrations:
**/alembic/versions/** - Dependencies:
**/node_modules/**,**/.venv/**,**/venv/** - Build artifacts:
**/build/**,**/dist/**
Special exceptions:
app/core/exceptions.py- Allowed to use HTTPExceptionapp/exceptions/handler.py- Converts to HTTPException
Quick Reference
Pre-Commit Checklist
Before committing code:
- Run
make format(Ruff formatting) - Run
make lint(Ruff + mypy) - Run
python scripts/validate/validate_architecture.py - Fix all Error level violations
- Review Warning level violations
- Run relevant tests
Common Violations and Fixes
| Violation | Quick Fix |
|---|---|
| HTTPException in service | Create custom exception in app/exceptions/ |
| HTTPException in endpoint | Let domain exceptions bubble up to global handler |
| Business logic in endpoint | Move to service layer |
| console.log in JS | Use window.LogConfig.createLogger() |
| Missing ...data() | Add spread operator in component return |
| Missing currentPage | Add currentPage: 'page-name' in component return |
| Invalid block name | Use valid block: extra_scripts, extra_head, etc. |
| Bare except clause | Specify exception type |
| Raw dict return | Create Pydantic response model |
| Template not extending base | Add {% extends %} or {# standalone #} marker |
Configuration
All rules are defined in .architecture-rules.yaml. To modify rules:
- Edit
.architecture-rules.yaml - Update
scripts/validate/validate_architecture.pyif implementing new checks - Run validator to test changes
- Update this documentation
Related Documentation
- Code Quality Guide
- Contributing Guide
- Architecture Overview
- Backend Development
- Frontend Development
Summary Statistics
| Category | Rules | Errors | Warnings | Info |
|---|---|---|---|---|
| Backend | 20 | 15 | 5 | 0 |
| Module Structure | 23 | 7 | 10 | 6 |
| Frontend JS | 7 | 6 | 1 | 0 |
| Frontend Templates | 8 | 4 | 4 | 0 |
| Frontend Macros | 5 | 2 | 3 | 0 |
| Frontend Components | 1 | 0 | 1 | 0 |
| Frontend Styling | 4 | 1 | 3 | 0 |
| Naming | 5 | 3 | 2 | 0 |
| Security | 5 | 5 | 0 | 0 |
| Quality | 3 | 2 | 1 | 0 |
| Total | 81 | 45 | 30 | 6 |
Last Updated: 2026-02-02 Version: 2.5