From c7f92f6d7b599464bc655557bcb3e53b9df922a9 Mon Sep 17 00:00:00 2001 From: Trip T Date: Wed, 11 Mar 2026 22:21:07 -0400 Subject: [PATCH] docs: add error handling patterns and module dependencies to CLAUDE.md - Document 3 graceful degradation patterns with code examples - Add Service Fallback Matrix for optional services - Add module dependency tree with change impact guide chore: fix typecheck environment - Add mypy to dev dependencies in pyproject.toml - Fix tox.ini typecheck environment to install mypy explicitly --- CLAUDE.md | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 1 + tox.ini | 3 ++ 3 files changed, 100 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 32a6c76e..b31c8f16 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -111,6 +111,55 @@ tox -e dev # Start dashboard with auto-reload --- +## Error Handling Patterns + +All optional services (Ollama, Redis, AirLLM, voice, etc.) must degrade gracefully. Three approved patterns: + +### Pattern 1: Optional Return (data retrieval) +```python +def get_user_setting(key: str) -> Optional[str]: + try: + return db.query(...) + except Exception as exc: + logger.warning("Setting lookup failed: %s", exc) + return None +``` + +### Pattern 2: Fallback Value (computations) +```python +def compute_embedding(text: str) -> list[float]: + try: + return model.encode(text) + except Exception as exc: + logger.warning("Embedding failed, using hash: %s", exc) + return hash_based_fallback(text) +``` + +### Pattern 3: Feature Disable (optional services) +```python +async def transcribe_audio(audio: bytes) -> str: + try: + return await voice_service.transcribe(audio) + except ServiceUnavailable: + logger.warning("Voice service unavailable — feature disabled") + return "" # Silently disable feature +``` + +### Service Fallback Matrix + +| Service | When Unavailable | Fallback Behavior | +|---------|------------------|-------------------| +| Ollama | No local LLM | Claude backend (if ANTHROPIC_API_KEY set) | +| Redis | Cache/storage down | In-memory dict (ephemeral) | +| AirLLM | Import error or no Apple Silicon | Ollama backend | +| Voice (Piper) | Service down | Browser Web Speech API | +| WebSocket | Connection failed | HTTP polling (degraded) | +| Telegram/Discord | No token configured | Bot doesn't start (logs only) | + +**Rule:** Never crash the app when an optional service is down. Log at WARNING level and continue. + +--- + ## Frontend Architecture ### Stylesheets @@ -181,3 +230,50 @@ McToast.show('Request failed', 'error'); // red border | `brain/` | Identity system, memory interface | | `timmy_serve/` | API server | | `config.py` | Pydantic settings (foundation for all modules) | + +## Module Dependencies + +``` +config.py # Foundation — no internal dependencies + │ + ├── timmy/ # Core agent + │ ├── Uses: config, brain, infrastructure + │ └── Impact: 🔴 High — used by dashboard, integrations, timmy_serve + │ + ├── brain/ # Memory system + │ ├── Uses: config + │ └── Impact: 🟡 Medium — used by timmy, spark + │ + ├── infrastructure/ # Shared services + │ ├── Uses: config + │ └── Impact: 🔴 High — used by ALL packages + │ ├── ws_manager # WebSocket connections + │ ├── notifier # Push notifications + │ └── event_bus # Pub/sub events + │ + ├── spark/ # Intelligence engine + │ ├── Uses: config, brain + │ └── Impact: 🟢 Low — self-contained + │ + ├── integrations/ # External services + │ ├── Uses: config, infrastructure + │ └── Impact: 🟡 Medium — isolated by vendor + │ + ├── dashboard/ # Web UI + │ ├── Uses: ALL packages + │ └── Impact: 🔴 High — entry point, orchestrates everything + │ + └── timmy_serve/ # API server + ├── Uses: config, timmy, infrastructure + └── Impact: 🟡 Medium — standalone service +``` + +### Change Impact Guide + +| If you modify... | You should test... | +|------------------|-------------------| +| `config.py` | Full suite (`tox -e ci`) — affects everything | +| `infrastructure/` | Unit + integration + functional | +| `timmy/` | Unit tests + chat/CLI tests | +| `dashboard/routes/` | Dashboard route tests (usually isolated) | +| `brain/` | Brain client/worker tests + timmy tests | diff --git a/pyproject.toml b/pyproject.toml index 0d450c05..bee516da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,6 +73,7 @@ selenium = ">=4.20.0" pytest-randomly = "^4.0.1" pytest-xdist = "^3.8.0" ruff = ">=0.8.0" +mypy = ">=1.0.0" [tool.poetry.scripts] timmy = "timmy.cli:main" diff --git a/tox.ini b/tox.ini index dab85e45..5bcb7fec 100644 --- a/tox.ini +++ b/tox.ini @@ -38,6 +38,9 @@ commands = [testenv:typecheck] description = Static type checking with mypy +commands_pre = +deps = + mypy>=1.0.0 commands = mypy src --ignore-missing-imports --no-error-summary