# CLAUDE.md — Padelnomics This file tells Claude Code how to work in this repository. ## Project Overview Padelnomics is a SaaS application built with Quart (async Python), HTMX, and SQLite. It includes a full data pipeline: ``` External APIs → extract → landing zone → SQLMesh transform → DuckDB → web app ``` **Packages** (uv workspace): - `web/` — Quart + HTMX web application (auth, billing, dashboard) - `extract/padelnomics_extract/` — data extraction to local landing zone - `transform/sqlmesh_padelnomics/` — 3-layer SQL transformation (staging → foundation → serving) - `src/padelnomics/` — CLI utilities, export_serving helper ## Skills: invoke these for domain tasks ### Working on extraction or transformation? Use the **`data-engineer`** skill for: - Designing or reviewing SQLMesh model logic - Adding a new data source (extract + staging model) - Performance tuning DuckDB queries - Data modeling decisions (dimensions, facts, aggregates) - Understanding the 3-layer architecture ``` /data-engineer (or ask Claude to invoke it) ``` ### Working on the web app UI or frontend? Use the **`frontend-design`** skill for UI components, templates, or dashboard layouts. ### Working on payments or subscriptions? Use the **`paddle-integration`** skill for billing, webhooks, and subscription logic. ## Key commands ```bash # Install all dependencies uv sync --all-packages # Lint & format ruff check . ruff format . # Run tests uv run pytest tests/ -v # Dev server ./scripts/dev_run.sh # Extract data LANDING_DIR=data/landing uv run extract # SQLMesh plan + run (from repo root) uv run sqlmesh -p transform/sqlmesh_padelnomics plan uv run sqlmesh -p transform/sqlmesh_padelnomics plan prod # Export serving tables (run after SQLMesh) DUCKDB_PATH=local.duckdb SERVING_DUCKDB_PATH=analytics.duckdb \ uv run python src/padelnomics/export_serving.py ``` ## Architecture documentation | Topic | File | |-------|------| | Extraction patterns, state tracking, adding new sources | `extract/padelnomics_extract/README.md` | | 3-layer SQLMesh architecture, materialization strategy | `transform/sqlmesh_padelnomics/README.md` | | Two-file DuckDB architecture (SQLMesh lock isolation) | `src/padelnomics/export_serving.py` docstring | | Email hub: delivery tracking, webhook handler, admin UI | `web/src/padelnomics/webhooks.py` docstring | | User flows (all admin + public routes) | `docs/USER_FLOWS.md` | ## Pipeline data flow ``` data/landing/ ├── overpass/{year}/{month}/courts.json.gz ├── eurostat/{year}/{month}/urb_cpop1.json.gz └── playtomic/{year}/{month}/tenants.json.gz data/lakehouse.duckdb ← SQLMesh exclusive (staging → foundation → serving) analytics.duckdb ← serving tables only, web app read-only └── serving.* ← atomically replaced by export_serving.py ``` ## Backup & disaster recovery | Data | Tool | Target | Frequency | |------|------|--------|-----------| | `app.db` (auth, billing) | Litestream | R2 `padelnomics/app.db` | Continuous (WAL) | | `.state.sqlite` (extraction state) | Litestream | R2 `padelnomics/state.sqlite` | Continuous (WAL) | | `data/landing/` (JSON.gz files) | rclone sync | R2 `padelnomics/landing/` | Every 30 min (systemd timer) | | `lakehouse.duckdb`, `analytics.duckdb` | N/A (derived) | Re-run pipeline | On demand | Recovery: ```bash # App database (auto-restored by Litestream container on startup) litestream restore -config /etc/litestream.yml /app/data/app.db # Extraction state (auto-restored by Litestream container on startup) litestream restore -config /etc/litestream.yml /data/landing/.state.sqlite # Landing zone files source /opt/padelnomics/.env && bash infra/restore_landing.sh ``` ## Secrets management (SOPS + age) Secrets are stored encrypted in the repo using SOPS with age encryption: | File | Purpose | |------|---------| | `.env.dev.sops` | Dev defaults (safe/blank values) | | `.env.prod.sops` | Production secrets | | `.sops.yaml` | Maps file patterns to age public keys | ```bash # Decrypt dev secrets to .env (one-time, or after changes) make secrets-decrypt-dev # Edit prod secrets (opens in $EDITOR, re-encrypts on save) make secrets-edit-prod # deploy.sh auto-decrypts .env.prod.sops → .env on the server ``` All env vars are defined in the sops files. See `.env.dev.sops` for the full list (decrypt with `make secrets-decrypt-dev` to read). ## Environment variables | Variable | Default | Description | |----------|---------|-------------| | `LANDING_DIR` | `data/landing` | Landing zone root (extraction writes here) | | `DUCKDB_PATH` | `local.duckdb` | SQLMesh pipeline DB (exclusive write) | | `SERVING_DUCKDB_PATH` | `analytics.duckdb` | Read-only DB for web app | | `RESEND_WEBHOOK_SECRET` | `""` | Resend webhook signature secret (skip verification if empty) | ## Coding philosophy - **Simple and procedural** — functions over classes, no "Manager" patterns - **Idempotent operations** — running twice produces the same result - **Explicit assertions** — assert preconditions at function boundaries - **Bounded operations** — set timeouts, page limits, buffer sizes Read `coding_philosophy.md` (if present) for the full guide.