Skip to content

Coding Style Guide

Code conventions and standards for Nova AI contributors.

Table of Contents


Python Style

General Principles

  • Clarity over cleverness: Code should be easy to understand
  • Explicit over implicit: Be clear about intentions
  • Consistency: Follow existing patterns in the codebase
  • Security first: Validate inputs, handle errors, avoid vulnerabilities

Tools

# Formatter (auto-fix)
ruff format src/ tests/

# Linter (find issues)
ruff check src/ tests/

# Auto-fix linting issues
ruff check --fix src/ tests/

# Type checker
mypy src/

# Security scanner
bandit -r src/

Code Formatting

Line Length: - 100 characters maximum - Break long lines for readability

Indentation: - 4 spaces (not tabs) - Consistent indentation for continuation lines

Imports:

# 1. Future imports (if needed)
from __future__ import annotations

# 2. Standard library
import os
import sys
from pathlib import Path

# 3. Third-party
import anthropic
from pydantic import BaseModel

# 4. Local
from src.orchestrator.session_manager import SessionManager
from src.utils.path_security import validate_safe_path

# NO wildcard imports
# ❌ from module import *
# ✅ from module import specific_function

Naming Conventions:

# Classes: PascalCase
class ClaudeSDKExecutor:
    pass

# Functions/methods: snake_case
def execute_agent_task() -> ExecutorResult:
    pass

# Constants: UPPER_SNAKE_CASE
MAX_SESSION_TOKENS = 150_000
DEFAULT_MODEL = "sonnet"

# Private members: _leading_underscore
class MyClass:
    def __init__(self):
        self._internal_state = {}

    def _helper_method(self):
        pass

# Module-level private: _leading_underscore
_internal_cache = {}

Type Hints

Required for all public functions:

# ✅ Good: Complete type hints
def authenticate_user(
    username: str,
    password: str,
    *,
    timeout: int = 30,
) -> User:
    """Authenticate user."""
    ...

# ❌ Bad: Missing type hints
def authenticate_user(username, password, timeout=30):
    ...

# ✅ Good: Complex types
from collections.abc import Callable, Sequence

def process_items(
    items: Sequence[str],
    callback: Callable[[str], bool],
) -> list[str]:
    return [item for item in items if callback(item)]

# ✅ Good: Optional and Union types
from typing import Optional

def get_user(user_id: str) -> User | None:
    """Return user or None if not found."""
    ...

# ✅ Good: Generic types
from typing import TypeVar

T = TypeVar("T")

def first(items: list[T]) -> T | None:
    return items[0] if items else None

Avoid Any when possible:

# ❌ Bad: Lazy typing
def process(data: Any) -> Any:
    ...

# ✅ Good: Specific types
from typing import Protocol

class Serializable(Protocol):
    def to_dict(self) -> dict[str, Any]: ...

def process(data: Serializable) -> dict[str, Any]:
    ...

Docstrings

Format: Google style

Required for: - All public functions - All public classes - All public methods - Module-level docstrings

Example:

"""Module for user authentication.

This module provides JWT-based authentication for API endpoints.
Supports token generation, validation, and refresh flows.

Example:
    >>> authenticator = JWTAuthenticator()
    >>> user = authenticator.authenticate("admin", "password")
    >>> token = authenticator.generate_token(user)
"""

from __future__ import annotations

class JWTAuthenticator:
    """JWT-based authentication provider.

    Implements token generation, validation, and refresh using
    industry-standard JWT (RFC 7519) with RS256 signing.

    Attributes:
        secret_key: RSA private key for signing tokens
        public_key: RSA public key for validation
        token_ttl: Token time-to-live in seconds (default: 3600)

    Example:
        >>> auth = JWTAuthenticator(secret_key=key)
        >>> token = auth.generate_token(user)
        >>> is_valid = auth.validate_token(token)
    """

    def __init__(
        self,
        secret_key: str,
        public_key: str | None = None,
        *,
        token_ttl: int = 3600,
    ):
        """Initialize JWT authenticator.

        Args:
            secret_key: RSA private key PEM string
            public_key: RSA public key PEM string (optional, derived from private)
            token_ttl: Token lifetime in seconds (default: 3600)

        Raises:
            ValueError: If secret_key is invalid or empty
        """
        ...

    def authenticate(
        self,
        username: str,
        password: str,
    ) -> User:
        """Authenticate user with credentials.

        Args:
            username: User's username (3-50 characters)
            password: User's password (plaintext, will be hashed)

        Returns:
            Authenticated user object with populated fields

        Raises:
            AuthenticationError: If credentials are invalid
            ValueError: If username or password format invalid
            TimeoutError: If database query times out

        Example:
            >>> user = auth.authenticate("admin", "secret123")
            >>> print(user.username)
            admin

        Note:
            Passwords are hashed using bcrypt with 12 rounds.
            Authentication attempts are rate-limited (5/minute).
        """
        ...

    def validate_token(self, token: str) -> bool:
        """Validate JWT token signature and expiration.

        Args:
            token: JWT token string to validate

        Returns:
            True if token valid and not expired, False otherwise

        Example:
            >>> is_valid = auth.validate_token(token)
            >>> if not is_valid:
            ...     raise AuthenticationError("Invalid token")

        Security:
            - Validates signature using RSA public key
            - Checks expiration timestamp
            - Verifies issuer and audience claims
        """
        ...

Error Handling

# ✅ Good: Specific exceptions
class AuthenticationError(Exception):
    """Raised when authentication fails."""
    pass

def authenticate(username: str, password: str) -> User:
    if not is_valid(username, password):
        raise AuthenticationError(f"Invalid credentials for {username}")

# ✅ Good: Exception chaining
try:
    response = make_api_call()
except requests.RequestException as e:
    raise APIError(f"API call failed: {e}") from e

# ✅ Good: Cleanup with context managers
with open("file.txt") as f:
    data = f.read()
# File automatically closed

# ✅ Good: Multiple exception types
try:
    process_data()
except ValueError as e:
    logger.error(f"Invalid data: {e}")
except RuntimeError as e:
    logger.error(f"Runtime error: {e}")
except Exception as e:
    logger.exception(f"Unexpected error: {e}")
    raise

# ❌ Bad: Bare except
try:
    dangerous_operation()
except:  # Catches everything, including KeyboardInterrupt!
    pass

# ❌ Bad: Swallowing exceptions
try:
    might_fail()
except Exception:
    pass  # Silent failure - hard to debug

Security

# ✅ Good: Input validation
def validate_path(path: str | Path) -> Path:
    """Validate path is safe (no traversal, within allowed base)."""
    from src.utils.path_security import validate_safe_path
    return validate_safe_path(path, allowed_base=PROJECT_ROOT)

# ✅ Good: SQL injection prevention
def get_user(user_id: str) -> User | None:
    # Use parameterized queries
    cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))

# ❌ Bad: SQL injection vulnerability
def get_user(user_id: str) -> User | None:
    cursor.execute(f"SELECT * FROM users WHERE id = '{user_id}'")

# ✅ Good: XSS prevention
from src.orchestrator.security.xss_protection import sanitize_for_display

def render_user_input(text: str) -> str:
    return sanitize_for_display(text)

# ✅ Good: Command injection prevention
import shlex

def run_command(command: str):
    # Validate and escape
    safe_command = shlex.quote(command)
    subprocess.run(safe_command, shell=False)

# ❌ Bad: Command injection vulnerability
def run_command(command: str):
    os.system(command)  # Dangerous!

Performance

# ✅ Good: Use list comprehensions
squares = [x**2 for x in range(100)]

# ❌ Bad: Inefficient loop
squares = []
for x in range(100):
    squares.append(x**2)

# ✅ Good: Generator for large datasets
def read_large_file(path: Path):
    with open(path) as f:
        for line in f:  # Memory efficient
            yield line.strip()

# ❌ Bad: Load everything into memory
def read_large_file(path: Path):
    with open(path) as f:
        return f.readlines()  # Can cause OOM

# ✅ Good: Cache expensive computations
from functools import lru_cache

@lru_cache(maxsize=128)
def expensive_function(x: int) -> int:
    # Computed once, cached for repeated calls
    return complex_calculation(x)

TypeScript Style

General

  • 100 characters line length
  • 2 spaces indentation
  • Prettier for formatting
  • ESLint for linting

Type Annotations

// ✅ Good: Explicit types
interface User {
  id: string;
  username: string;
  email: string;
  createdAt: Date;
}

function getUser(userId: string): Promise<User | null> {
  return fetchUser(userId);
}

// ❌ Bad: Implicit any
function getUser(userId) {
  return fetchUser(userId);
}

// ✅ Good: Generic types
function first<T>(items: T[]): T | undefined {
  return items[0];
}

// ✅ Good: Union types
type Result<T> = { success: true; data: T } | { success: false; error: string };

Agent Development

Agent File Structure

---
name: my-agent
description: Brief one-line description of agent purpose
tools:
  - Read
  - Write
  - Edit
  - Grep
  - Glob
  - Bash
model: sonnet
color: blue  # Optional: purple, orange, blue, green, red, yellow
---

# Agent Instructions

## Purpose
Clear description of what this agent does and when to use it.

## Capabilities
- Capability 1
- Capability 2

## Constraints
- What this agent should NOT do
- Limitations

## Examples

### Example 1: [Use Case]
Input: ...
Output: ...

### Example 2: [Use Case]
Input: ...
Output: ...

## Best Practices
- Practice 1
- Practice 2

Agent Guidelines

  1. Single Responsibility: Each agent should have one clear purpose
  2. Minimal Tools: Only include tools actually needed
  3. Clear Instructions: Be explicit about expected behavior
  4. Use Sonnet: Anthropic recommends Sonnet 4.5 for agents
  5. Test Thoroughly: Test agent with various inputs

Skills Development

Skill File Structure

---
name: my-skill
description: Brief skill description
allowed-tools: Read, Write, Grep, Glob, Bash
---

# Skill Name

Brief introduction to the skill.

## When to Use

Describe scenarios where this skill should be activated.

## How It Works

Explain the skill's approach and methodology.

## Examples

### Example 1
Input/scenario description
Expected outcome

## Guidelines

- Guideline 1
- Guideline 2

## See Also

- [Related Skill](../related/SKILL.md)
- [Documentation](../../docs/guide.md)

Skill Guidelines

  1. Progressive Disclosure: Keep SKILL.md under 500 lines
  2. Link to Additional Files: Use markdown links for details
  3. Clear Scope: Define when skill should/shouldn't be used
  4. Include Examples: Show real usage scenarios
  5. Specify Tools: List all required tools in YAML

Documentation

Markdown Style

# Page Title

Brief introduction paragraph.

## Section

Content here.

### Subsection

More specific content.

#### Rarely Use H4

Only for very deep hierarchies.

## Code Examples

All code blocks must specify language:

\`\`\`python
def example():
    pass
\`\`\`

\`\`\`bash
command --flag value
\`\`\`

## Lists

- Unordered item 1
- Unordered item 2
  - Nested item
  - Nested item

1. Ordered item 1
2. Ordered item 2

## Links

- [Internal Link](./other-doc.md)
- [External Link](https://example.com)
- [Heading Link](#section)

## Tables

| Column 1 | Column 2 | Column 3 |
|----------|----------|----------|
| Data     | Data     | Data     |

## Admonitions

**Note:** Additional information

**Warning:** Caution required

**Important:** Critical information

Git Conventions

Commit Messages

Format:

<type>(<scope>): <subject>

<body>

<footer>

Types: - feat: New feature - fix: Bug fix - docs: Documentation only - style: Formatting, missing semi-colons, etc. - refactor: Code change that neither fixes bug nor adds feature - perf: Performance improvement - test: Adding missing tests - chore: Build process, auxiliary tools

Examples:

# Simple
feat: add JWT authentication

# With scope
feat(auth): add JWT authentication

# With body
feat(auth): add JWT authentication

Implements token generation, validation, and refresh.
Uses RS256 signing with 1-hour TTL.

# With breaking change
feat(auth): change authentication API

BREAKING CHANGE: authenticate() now returns User | None instead of User

Branch Naming

# Features
feat/user-authentication
feat/api-rate-limiting

# Fixes
fix/session-timeout
fix/memory-leak

# Documentation
docs/add-api-guide
docs/update-readme

# Refactoring
refactor/simplify-auth-flow
refactor/extract-validators

# Tests
test/add-integration-tests
test/improve-coverage

Code Review

Review Checklist

Security: - [ ] No secrets in code - [ ] Input validation present - [ ] SQL injection prevented - [ ] XSS prevention applied - [ ] Path traversal prevented

Correctness: - [ ] Logic is sound - [ ] Edge cases handled - [ ] Error handling appropriate - [ ] Tests cover new code

Performance: - [ ] No obvious inefficiencies - [ ] Appropriate data structures - [ ] No memory leaks - [ ] Database queries optimized

Maintainability: - [ ] Code is readable - [ ] Comments explain "why" - [ ] Functions are focused - [ ] DRY principle followed

Standards: - [ ] Follows style guide - [ ] Has type hints - [ ] Has docstrings - [ ] Passes linting


Last Updated: November 7, 2025 Version: 2.3.0