Files
orion/docs/development/architecture-rules.md
Samir Boulahtit 7a9dda282d refactor(scripts): reorganize scripts/ into seed/ and validate/ subfolders
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>
2026-02-09 21:35:53 +01:00

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

# 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

  1. Separation of Concerns - API endpoints handle HTTP, services handle business logic
  2. Layered Architecture - Routes → Services → Models
  3. Type Safety - Pydantic for API, SQLAlchemy for database
  4. Proper Exception Handling - Domain exceptions in services, HTTPException in routes
  5. Multi-Tenancy - All queries scoped to store_id
  6. 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 models
  • models/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 extend errors/base.html instead
  • test-*.html - Test/development templates
  • base.html - The base template itself
  • partials/*.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 title
  • extra_head - Additional head content (CSS, meta tags)
  • alpine_data - Alpine.js component function name
  • content - Main page content
  • extra_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 → use extra_scripts
  • scripts → use extra_scripts
  • js → use extra_scripts
  • head → use extra_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 HTTPException
  • app/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:

  1. Edit .architecture-rules.yaml
  2. Update scripts/validate/validate_architecture.py if implementing new checks
  3. Run validator to test changes
  4. Update this documentation


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