# Naming Conventions

## Files
- **snake_case** for all Python files: `database.py`, `explorer_helpers.py`, `motion_cache.py`
- **PascalCase** NOT used for files

## Functions
- **snake_case**: `get_svd_vectors()`, `compute_party_coords()`, `build_scatter_trace()`
- Private helpers prefixed with `_`: `_get_window_data()`

## Classes
- **PascalCase**: `MotionDatabase`, `Config`
- **Dataclass pattern** for Config: `@dataclass` decorator with typed fields

## Variables
- **snake_case**: `party_map`, `mp_name`, `svd_vectors`, `party_centroids`
- **CONSTANT_SNAKE_CASE** for module-level constants: `PARTY_COLOURS`, `DEFAULT_WINDOW`

## Module-Level Exports
- **Singleton instance**: `db = MotionDatabase()` at module bottom (not class-level)
- **Config instance**: `config = Config(...)` at module bottom
- **Dicts**: `PARTY_COLOURS` exported from `config.py`

---

# Error Handling

## Known Patterns
1. **Bare except with pass** (ANTI-PATTERN - see anti-patterns.yaml)
   ```python
   except:
       pass  # database.py:47
   ```

2. **Graceful degradation**: catch specific exceptions, fall back to default
   ```python
   try:
       result = compute_svd()
   except ImportError:
       result = DEFAULT_SVD
   ```

3. **Optional dependency fallbacks**:
   ```python
   try:
       import umap
       use_umap = True
   except ImportError:
       use_umap = False
   ```

4. **Nested exception handling** (ANTI-PATTERN - see anti-patterns.yaml):
   ```python
   try:
       ...
   except Exception:
       try:
           ...
       except Exception:
           pass
   ```

## Rules
- Never use bare `except:` — always specify exception type
- Never swallow exceptions silently — log or return a sensible default
- For optional deps, use `ImportError` or `ModuleNotFoundError` explicitly
- Avoid nested try/except blocks

---

# Code Organization

## Singleton Pattern
Each module owns one shared instance:
```python
# database.py
db = MotionDatabase()

# config.py
config = Config(...)
PARTY_COLOURS = {...}
```

## Pure Functions in Helpers
`explorer_helpers.py` contains only pure functions (no IO, no Streamlit calls):
```python
def compute_party_coords(svd_vectors, party_map):
    """Pure: no side effects, no imports from this module"""
    ...

def build_scatter_trace(df, color_col):
    """Pure: returns Plotly trace dict"""
    ...
```

## Cached Data Loaders
Use `@st.cache_data` for expensive data loading:
```python
@st.cache_data
def load_svd_vectors(window: str) -> pd.DataFrame:
    return db.get_svd_vectors(window)
```

## Dataclass Config
```python
@dataclass
class Config:
    db_path: str = "data/stemwijzer.duckdb"
    default_window: str = "2023"
    party_colours: dict = field(default_factory=lambda: PARTY_COLOURS)
```

---

# Imports

## Ordering (convention)
1. Standard library
2. Third-party (streamlit, ibis, plotly, sklearn, umap)
3. Local/relative imports

## Avoid
- Wildcard imports (`from module import *`)
- Circular imports (ensure dependency direction: helpers → database → config)
