# 12. Coding Standards

**MANDATORY FOR AI AGENTS**

### 12.1 Core Standards

- **Language:** Python 3.10+ (use modern syntax)
- **Linting:** Ruff (replaces flake8, black, isort)
- **Type Checking:** mypy in strict mode
- **Test Organization:** Tests mirror source in `tests/unit/`, `tests/integration/`

### 12.2 Critical Rules

| Rule | Description |
|------|-------------|
| **Use Pydantic for ALL API responses** | Never use raw dicts. Always validate with `extra='forbid'`. |
| **Never log secrets or tokens** | Use `[REDACTED]` placeholder. |
| **All errors must include remediation** | No bare "Error: something failed" messages. |
| **Phone numbers: normalize on input** | Use `utils.normalize_phone_number()`. |
| **Use services, not direct API calls** | CLI commands call services, never GraphClient directly. |
| **Async operations require polling** | `assignNumber`/`unassignNumber` return 202. Always poll. |
| **CSV cache is optional** | Services must work when CSV files are missing. |
| **Exit codes are mandatory** | Every error maps to a specific exit code. |
| **--json output must be valid JSON** | Output ONLY valid JSON to stdout when flag used. |
| **Tests must be independent** | No shared state between tests. |

### 12.3 Pydantic Model Standards

```python
from pydantic import BaseModel, Field, ConfigDict

class NumberAssignment(BaseModel):
    model_config = ConfigDict(
        extra="forbid",           # CRITICAL: Fail on unknown fields
        populate_by_name=True,    # Allow both snake_case and camelCase
    )

    id: str
    telephone_number: str = Field(alias="telephoneNumber")
    number_type: NumberType = Field(alias="numberType")
```

---

