Files
Timmy-time-dashboard/CLAUDE.md
Trip T c7f92f6d7b
All checks were successful
Tests / lint (push) Successful in 3s
Tests / test (push) Successful in 30s
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
2026-03-11 22:21:07 -04:00

280 lines
9.8 KiB
Markdown

# CLAUDE.md — AI Assistant Guide for Timmy Time
**Tech stack:** Python 3.11+ · FastAPI · Jinja2 + HTMX · SQLite · Agno ·
Ollama · pydantic-settings · WebSockets · Docker
For agent roster and conventions, see [`AGENTS.md`](AGENTS.md).
---
## Architecture Patterns
### Config access
```python
from config import settings
url = settings.ollama_url # never use os.environ.get() directly in app code
```
### Singletons
```python
from dashboard.store import message_log
from infrastructure.notifications.push import notifier
from infrastructure.ws_manager.handler import ws_manager
```
### HTMX response pattern
```python
return templates.TemplateResponse(
"partials/chat_message.html",
{"request": request, "role": "user", "content": message}
)
```
### Graceful degradation
Optional services (Ollama, Redis, AirLLM) degrade gracefully — log the error,
return a fallback, never crash.
### Route registration
New routes: `src/dashboard/routes/<name>.py` → register in `src/dashboard/app.py`.
---
## Development Environments (tox)
**tox is the single source of truth for all Python environments.**
Never run `poetry run` directly — always go through tox. All environment
config (deps, markers, flags) lives in `tox.ini`. The Makefile and CI
both delegate to tox.
### Quick reference
```bash
tox -e unit # Fast unit tests (default pre-commit gate)
tox -e ci # Full CI suite with coverage + JUnit XML
tox -e lint # black --check + isort --check + bandit
tox -e format # Auto-format (black + isort)
tox -e dev # Start dashboard with auto-reload
```
### All tox environments
| Environment | Purpose |
|---|---|
| `lint` | Check formatting + imports + security |
| `format` | Auto-format code |
| `typecheck` | mypy static analysis |
| `unit` | Fast unit tests, parallel |
| `integration` | Integration tests, parallel |
| `functional` | Functional tests, sequential |
| `e2e` | End-to-end tests |
| `fast` | unit + integration combined |
| `ollama` | Live LLM tests (requires Ollama) |
| `ci` | Coverage + JUnit XML (mirrors GitHub Actions) |
| `coverage` | Coverage terminal + XML |
| `coverage-html` | Coverage HTML report |
| `pre-push` | Lint + full CI suite (mirrors GitHub Actions exactly) |
| `dev` | uvicorn with auto-reload |
| `all` | All tests, parallel |
### Testing notes
- **Stubs in conftest:** `agno`, `airllm`, `pyttsx3`, `telegram`, `discord`
stubbed via `sys.modules.setdefault()` — tests run without those packages
- **Test mode:** `TIMMY_TEST_MODE=1` set automatically in conftest
- **FastAPI testing:** Use the `client` fixture
- **Async:** `asyncio_mode = "auto"` — async tests detected automatically
- **Coverage threshold:** 73% (`fail_under` in `pyproject.toml`)
---
## Key Conventions
1. **Tests must stay green.** Run `tox -e unit` before committing. Run `tox -e pre-push` before pushing (mirrors CI exactly).
2. **No cloud AI dependencies.** All inference on localhost.
3. **Keep the root directory clean.** No new top-level files without purpose.
4. **Follow existing patterns** — singletons, graceful degradation, pydantic config.
5. **Security defaults:** Never hard-code secrets.
6. **XSS prevention:** Never use `innerHTML` with untrusted content.
7. **Keep routes thin** — business logic lives in the module, not the route.
8. **Prefer editing existing files** over creating new ones.
9. **Use `from config import settings`** for all env-var access.
10. **Use tox for everything.** Never run `poetry run` directly — use `tox -e <env>`.
11. **No inline CSS in templates.** All styles go in `static/style.css` (base theme) or `static/css/mission-control.css` (page-specific). `{% block extra_styles %}` must stay empty. If you need new styles, append them to the appropriate CSS file.
12. **Use the panel macro** for repeated `.card.mc-panel` markup: `{% from "macros.html" import panel %}` / `{% call panel("TITLE") %}...{% endcall %}`. See `macros.html` for kwargs (id, hx_get, etc.).
13. **Toast for transient messages.** Use `McToast.show(msg, level)` (info/warn/error) instead of rendering errors inline in the chat log. The toast container lives in `base.html`.
14. **Never block the event loop.** Wrap synchronous I/O (HTTP calls, file reads) in `asyncio.to_thread()`. Cache health-check results with a TTL when polling external services.
---
## 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
| File | Purpose |
|------|---------|
| `static/style.css` | Base theme: palette, layout, header, chat, Bootstrap overrides, mobile breakpoints |
| `static/css/mission-control.css` | Page-specific styles (tasks, briefing, swarm, calm, etc.), toast system, connection indicator |
**Rule:** No `<style>` blocks in Jinja2 templates. All CSS lives in the two static files above. The `{% block extra_styles %}` hook exists for emergencies only and must remain empty in committed code.
### CSS custom properties
Defined in `:root` in `style.css`. Use these — never hard-code colours:
```css
var(--green) var(--amber) var(--red) var(--purple) var(--orange)
var(--text-bright) var(--text) var(--text-dim)
var(--bg-deep) var(--bg-panel) var(--bg-card) var(--border)
```
### Jinja2 macros
`src/dashboard/templates/macros.html` — reusable components:
```jinja2
{% from "macros.html" import panel %}
{% call panel("TITLE", hx_get="/endpoint", hx_trigger="every 10s") %}
<p>Panel body content</p>
{% endcall %}
```
### Toast notifications (JS)
```javascript
McToast.show('Ollama reconnected', 'info'); // green border
McToast.show('Connection lost', 'warn'); // amber border
McToast.show('Request failed', 'error'); // red border
```
---
## Security-Sensitive Areas
- `src/timmy_serve/` — API server, payment configuration
- Any file handling secrets or authentication tokens
---
## Entry Points
| Command | Module | Purpose |
|---------|--------|---------|
| `timmy` | `src/timmy/cli.py` | Chat, think, status |
| `timmy-serve` | `src/timmy_serve/cli.py` | API server (port 8402) |
---
## Module Map (8 packages)
| Package | Purpose |
|---------|---------|
| `timmy/` | Core agent, personas, agent interface, semantic memory |
| `dashboard/` | FastAPI web UI, routes, templates |
| `infrastructure/` | WebSocket, notifications, events, LLM router |
| `integrations/` | Discord, Telegram, Siri Shortcuts, voice NLU |
| `spark/` | Event capture and advisory engine |
| `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 |