diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..f19d327d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,375 @@ +# Instructions for Claude — GenDesign + +> Эти инструкции читаются автоматически при старте сессии Claude Code в этом +> репозитории. Они описывают: что за проект, как работать с кодом и инфрой, +> какие правила и какие "грабли" не наступать. Источник истины для бизнес-знания +> — Obsidian vault (см. ниже), а **этот файл — про сам процесс разработки**. + +--- + +## ⚠️ TL;DR — обязательно прочитай первым + +1. **Все memory / knowledge только в Obsidian vault** + `obsidian.gendsgn.ru` (CouchDB) ↔ `C:\Users\user\Documents\gendesign-memory` (mirror). + Доступ через `mcp__obsidian__*` tools. Для **анализа любой задачи** и **записи + любой документации / решения / research** — используй **ТОЛЬКО vault**. + +2. **НЕ читай и НЕ пиши** в legacy JSONL knowledge graph + (`memory/memory-gendesign.jsonl`) — он deprecated. Файл оставлен в репе как + историчекий снапшот, но не отражает текущее состояние. + +3. **НЕ создавай новые `.md` файлы в `docs/`, `notes/`, `wiki/`** репозитория — + knowledge живёт **только** в vault. Исключения для репа: `README.md`, + `CLAUDE.md`, `docs/*-strategy.md` (исторические снапшоты бизнес-плана), + inline-комментарии в коде. + +4. **Workflow для новой задачи:** + ``` + 1. obsidian_simple_search "" → найди MOC / related entities + 2. obsidian_get_file_contents для нужных → подгрузи контекст + 3. Делай работу в коде + 4. По итогу: append observation / create entity в vault через + obsidian_patch_content / obsidian_append_content / Write (filesystem fallback) + ``` + +5. **No auto-commit, no `--no-verify`** — пишу сообщение коммита в чат, пользователь коммитит. + +--- + +## Project at a glance + +**GenDesign** — две продуктовые линии: **Site Finder** (AI-подбор инвест-участков) ++ **Generative Design** (автогенерация концепций застройки) для девелоперов РФ. +Регион Discovery — Свердловская обл. (ЕКБ, ПЗЗ, МСК-66). Live на +`https://gendsgn.ru/`. + +### Tech stack + +| Слой | Stack | +|---|---| +| Backend | Python 3.12, FastAPI, SQLAlchemy 2.0, GeoAlchemy2, Pydantic v2, Celery + Redis, httpx, Shapely 2, scikit-learn | +| DB | PostgreSQL 16 + PostGIS 3.4 (partitioned `rosreestr_deals`, GIST на geom, ~50 предметных таблиц) | +| Frontend | Next.js 15 (app router), React 19, TypeScript 5, Tailwind 4, TanStack Query, Leaflet/Mapbox, ECharts | +| Export | WeasyPrint (PDF), ezdxf (DXF), openpyxl (Excel) | +| Tooling | `uv` (Python deps), ruff, mypy strict (selective), pytest, pre-commit, prettier | +| Deploy | Beget VPS · GitHub Actions → GHCR → SSH → docker-compose · Caddy auto-TLS | +| Knowledge | Obsidian vault на `obsidian.gendsgn.ru` (Self-hosted LiveSync + CouchDB) | + +## Repository layout + +``` +backend/ FastAPI + Celery (services, scrapers, workers, api/v1/*) +frontend/ Next.js (app router; analytics, admin, concept, site-finder) +data/sql/ ETL артефакты + миграции схемы (numbered NN_*.sql) +ops/ backup.sh + deploy скрипты +scripts/ утилиты (migrate_kg_to_obsidian.py, setup-couchdb.sh, etc) +docs/ стратегические снапшоты (источник истины — Obsidian) +memory/ legacy JSONL knowledge graph (deprecated) +docker-compose.prod.yml main стек (backend, frontend, postgres, redis, worker, beat, caddy) +docker-compose.obsidian.yml obsidian-стек (CouchDB) — деплоится отдельно +.github/workflows/ + deploy.yml main stack (триггер: backend/**, frontend/**, Caddyfile) + deploy-obsidian.yml obsidian stack (триггер: docker-compose.obsidian.yml, scripts/setup-couchdb.sh) +``` + +## Where knowledge lives — read THIS first + +**Obsidian vault на `obsidian.gendsgn.ru`** — единственный source of truth для +бизнес-знания (268+ entities + relations). Локальная папка vault'а: +`C:\Users\user\Documents\gendesign-memory`. + +Структура vault: +- `meta/` — Project_GenDesign, TechStack, NorthStar + setup guides (00_credentials, 01_connect_devices, 02_claude_usage, 03_obsidian_plugins, 04_mcp_obsidian_setup) +- `domains/{domrf,cadastre,rosreestr,analytics,ui,api,geo,infra,sitefinder}/` +- `code/{modules,schemas,patterns,hooks,tasks}/` +- `decisions/` `fixes/` `sessions/` `sprints/` `events/` `research/` `sources/` `feedback/` `github/` `limitations/` +- MOC.md (Map of Content) в каждой папке + `00_index.md` в корне vault'а + +**MCP** для доступа к vault: `mcp__obsidian__*` tools (см. `meta/02_claude_usage.md`): +- `obsidian_simple_search` — BM25 fulltext (Obsidian syntax: `tag:#x`, `path:`, `["yaml"]`) +- `obsidian_get_file_contents` / `obsidian_batch_get_file_contents` +- `obsidian_list_files_in_dir` +- `obsidian_patch_content` (под heading) / `obsidian_append_content` +- `obsidian_complex_search` (JsonLogic) + +**При работе над задачей:** сначала `obsidian_simple_search` по ключевому слову → найди MOC или related entity → читай target файл. Это в 5-10 раз дешевле по токенам чем `Grep + Read` по всему репо. + +## Setup & dev commands + +```bash +# Локально (Docker Desktop + Node 20 + uv): +cp backend/.env.example backend/.env +cp frontend/.env.example frontend/.env +docker compose up -d --build +curl http://localhost:8000/health # → {"status":"ok","environment":"dev"} + +# Backend dev (без Docker): +cd backend && uv sync +uv run uvicorn app.main:app --reload + +# Frontend dev: +cd frontend && npm install && npm run dev + +# Migrations (Alembic — стандартный путь): +make migration NAME=add_foo_column +# или сырой SQL (data/sql/NN_xxx.sql) — для DDL артефактов которые +# не сделаны Alembic'ом (большие views, материализации, partitions). + +# Tests: +cd backend && uv run pytest +cd frontend && npm test + +# Lint + typecheck (запускается в pre-commit, но можно явно): +cd backend && uv run --no-project --with ruff ruff check app/ +cd backend && uv run mypy app/services/generative app/services/site_finder/scorer +cd frontend && npx tsc --noEmit -p tsconfig.json +cd frontend && npm run lint + +# Codegen TS типов из Pydantic OpenAPI: +cd frontend && npm run codegen +``` + +## Code style & conventions + +### Python (backend) +- **Line length 100** (ruff) +- Ruff rules: `E F I B UP N RUF ASYNC`; ignored `RUF001/002/003` (Cyrillic в комментариях — нормально) +- mypy `strict` только для `app.services.generative.*` и `app.services.site_finder.scorer` (см. `Validation_Findings_Apr25` entity в vault) +- **psycopg v3 only** (`psycopg[binary]>=3.2.0`) — НЕ психcopg2 (его нет в зависимостях; импорт упадёт ModuleNotFoundError). Для bulk INSERT use `cur.executemany()` или COPY, НЕ `execute_values`. +- Type hints везде, `Any` — только обоснованно +- Docstrings на русском OK (но без emoji-spam), bullet-факты предпочтительнее художественного описания +- Async: FastAPI handlers — `async def`, Celery tasks — `def` (Celery sync) +- Tests: pytest + `asyncio_mode = auto` + +### Frontend (TS/React) +- **Prettier** + ESLint (запускается в pre-commit) +- React 19 + Next 15 app router (НЕ pages router) +- TanStack Query для data-fetching, без `useEffect` для HTTP +- TS strict, **никаких `any`**; для unknown shape → `unknown` + narrow +- Styling: Tailwind 4 утилитарные классы, inline styles только для динамики +- TanStack Query keys как массивы: `["scrape-logs", scraperType, runIdFilter]` + +### SQL +- Файлы в `data/sql/` именуются `NN_topic.sql` (NN = sequential int, e.g. `78_drop_use_rosreestr2coord.sql`) +- `BEGIN ... COMMIT` для DDL чтобы атомарно +- `IF EXISTS` / `IF NOT EXISTS` для идемпотентности +- Комментарии в начале файла: контекст + порядок применения + dependencies +- View'ы пересоздаются `CREATE OR REPLACE VIEW` +- Колонки удаляются `ALTER TABLE ... DROP COLUMN IF EXISTS` (после удаления зависимостей) + +## Git workflow + +### Branches +- `main` — protected, triggers prod deploy on push +- Feature branches: `feat/short-description` / `fix/short-description` +- Trunk-based, мелкие PR'ы (1-2 файла или 1 концептуальное изменение) + +### Commit messages +- Imperative mood: "fix worker crash" не "fixed worker crash" +- Длинный prefix: `feat(scope): ...`, `fix(scope): ...`, `refactor(scope): ...`, `docs(scope): ...`, `chore: ...` +- Body — почему, не что (что видно в diff) +- Атомарные коммиты — один логический шаг = один коммит + +### PR review +- Обязательный self-review перед request review +- Описание PR: links на issue / vault entity, скрин для UI changes +- Squash on merge + +### Critical workflow rules (НЕ нарушать) + +1. **Никогда не делай `git commit` сам** — пользователь коммитит лично. Ты пишешь сообщение в чат, пользователь применяет. +2. **Никогда не используй `--no-verify` / `--no-edit` / `--amend`** — pre-commit hooks обязательны. Если hook падает — fix root cause, не bypass. +3. **Никогда не пуш с `--force` в main** — катастрофа для всей команды. +4. **Не вызывай destructive команды без явного approval**: `git reset --hard`, `git clean -fdx`, `DROP TABLE`, `TRUNCATE`, `rm -rf` за пределами `node_modules/.next`. + +## Pre-commit hooks + +Запускаются автоматически при `git commit`. Если упали — **не** обходи через `--no-verify`. Пиши код **сразу под hooks**: + +- **ruff** — line ≤100, типичные Python issues +- **prettier** — frontend formatting +- **no large files** (>500 KB) +- **trailing whitespace / end-of-file fixer** +- **mixed-line-ending** (LF only в репе) +- **detect-private-key** (защита от случайного коммита ключей) + +Если pre-commit перепишет файл (auto-fix) — нужно `git add` повторно и `git commit`. + +## Database & migrations + +### Подключение к prod DB + +Доступ через SSH-tunnel (порт 15432 на ноуте → 5432 на VPS, см. `~/.ssh/config`): + +```bash +# Tunnel поднимается автоматом при `ssh gendesign` (LocalForward в config), +# или вручную: +ssh -N gendesign + +# Подключение psql: +psql postgresql://gendesign:@localhost:15432/gendesign +``` + +Credentials — в Obsidian vault `meta/00_credentials.md` (НЕ в этом файле, НЕ в коммитах!). + +### Migration pattern + +1. **Alembic migrations** для prod schema changes (`backend/alembic/versions/`). +2. **Raw SQL artifacts** (`data/sql/NN_xxx.sql`) для: + - Bootstrap / dev seed + - Большие view с UNION + - Material partitions / GIST индексы + - Удалить колонку с CASCADE-зависимостями + +### Applying migrations к prod + +**Order matters:** +1. Apply migration first (SQL → prod) +2. Deploy backend code that matches new schema +3. Никогда наоборот — иначе deployed code напорется на старую/новую несовместимую схему + +Если миграция меняет существующие FK / column type / drop column — read-then-write дискретно: верифицируй на staging, имей rollback ready. + +### Postgres MCP + +Доступ из Claude к prod DB через `mcp__postgres-gendesign__*` tools (после SSH tunnel). Для прод-чтения нужен explicit approval — safety guard для production reads. + +## Deploy + +GitHub Actions: +- `.github/workflows/deploy.yml` — main: build & push backend (lean + worker-with-chromium) + frontend → SSH → `compose pull && up -d` + `caddy reload` + `/health` check +- `.github/workflows/deploy-obsidian.yml` — obsidian: SSH `compose up -d` для couchdb + idempotent `scripts/setup-couchdb.sh` bootstrap + +**Триггеры по path:** +- Push в `backend/**`, `frontend/**`, `Caddyfile`, `docker-compose.prod.yml` → `deploy.yml` +- Push в `docker-compose.obsidian.yml`, `scripts/setup-couchdb.sh`, `docs/obsidian-livesync.md` → `deploy-obsidian.yml` +- Push только в `data/sql/**` или `docs/**` (не из триггеров выше) — деплоев не вызывает + +Sentry release tracking: `SENTRY_RELEASE=$IMAGE_TAG` пишется в `backend/.env.runtime` на VPS через `sed` (НЕ полная перезапись файла — там user-managed `COUCHDB_PASSWORD` и пр.). + +## When working on a task + +### 1. Understand context — ВСЕГДА vault-first +- Прочитай user request один раз +- **`mcp__obsidian__obsidian_simple_search ""`** — обязательный первый шаг для нетривиальной задачи. Найди MOC + related entities. +- Открой связанный MOC.md (например `domains/domrf/domrf-MOC.md` для DOM.РФ работы) — он покажет relevant entities +- Если в vault'е есть `fixes/Bug_X.md` про похожую проблему — обязательно прочитай +- НЕ ищи в `memory/memory-gendesign.jsonl` — он deprecated, состояние не отражает + +### 2. Plan, then act +- Для нетривиальных задач (>3 файла или >50 строк) — **сначала набросай план** (или используй `Plan` subagent), потом выполняй +- Для тривиальных (typo, mass-rename, обновить deps) — сразу делай + +### 3. Verify locally +- ruff + tsc + ast.parse() где применимо +- Не пиши тесты пока user не попросил, НО **не ломай** существующие +- Pre-commit hooks упадут на CI → проверь локально через `pre-commit run --all-files` если делал большие изменения + +### 4. Communicate +- Не объясняй очевидное (если правил один файл — не нужен абзац summary) +- Для каждого fix: что было сломано, что починил, что осталось проверить пользователю +- Если изменения в нескольких репо/системах — финальный bullet-list "Что осталось у тебя" + +### 5. Commit message +- Когда задача готова — **напиши** сообщение коммита в чат (не коммить сам) +- Conventional commits style + краткое описание зачем + +### 6. Update vault (важно!) +Для нетривиальных изменений — обнови vault: +- **Bug fix** → создай или обнови `fixes/Bug_.md` с root cause + fix +- **ADR / architectural decision** → создай `decisions/.md` +- **Новый scraper / endpoint / module** → создай entity в `domains//` или `code/modules/` +- **Гайд по операционной задаче** → `runbooks/` (или в `meta/0X_*.md` если про сам repo / workflow) +- **Session log** (опционально для большой работы): `sessions/Session__.md` + +Используй `mcp__obsidian__obsidian_append_content` для добавления observation в existing entity, либо `Write` напрямую в `C:\Users\user\Documents\gendesign-memory\/.md` (LiveSync запушит в CouchDB через ~60 сек). + +## Common pitfalls (learned the hard way) + +### Worker crash на старте +- Скорее всего import error — pyproject.toml имеет dep, но `uv.lock` ещё не обновлён +- Fix: `cd backend && uv lock` → коммит lock-файла → push → CI пересобирает image + +### `psycopg2` ImportError +- В проекте только psycopg v3. Если видишь `import psycopg2` — порти. +- `psycopg2.extras.execute_values` → `cur.executemany()` (v3 pipeline mode справится) + +### Caddy не подхватывает Caddyfile после `up -d` +- Caddyfile bind-mounted — нужен явный `caddy reload` +- `deploy.yml` это делает после `compose up -d` + +### `git pull` на VPS перетирает локальные правки +- `deploy.yml` использует `git reset --hard origin/main` — любые ручные правки в `/opt/gendesign` будут стёрты +- `.env`-файлы выживают (они в `.gitignore`) + +### `docker compose restart` не перечитывает `env_file` +- После правки `.env`: `up -d --force-recreate --no-deps ` + +### Frontend build хочет TS-типы которых нет +- `npm run codegen` обновляет `src/types/openapi.ts` из live OpenAPI schema backend'а +- Если backend меняется → нужно перегенерить TS типы + +### NSPD WAF / rate limiting +- Используй rosreestr2coord lib (v5+), НЕ свой urllib +- Rate limit делается worker'ом (`time.sleep(rate_ms / 1000)`), НЕ библиотекой (в v5 убрали delay параметр) + +### CouchDB / LiveSync — данные не появляются в Obsidian +- Local vault на ноуте — single source of write +- Если LiveSync не запушил после изменения → команда `Self-hosted LiveSync: Replicate now` +- Если file body пустой → `Verify and repair all files` → Apply Remote (см. `04_mcp_obsidian_setup.md`) + +### `git status` показывает trailing-whitespace / line-ending warnings +- Это от core.autocrlf на Windows. Pre-commit `mixed-line-ending` hook fix'ит автоматом. + +## Don't do these (no exceptions) + +### Knowledge / memory +- ❌ **Читать или писать в `memory/memory-gendesign.jsonl`** — он deprecated. Knowledge только в Obsidian vault. +- ❌ **Создавать новые `.md` для knowledge вне vault'а** — никаких `docs/research/*.md`, `notes/`, `wiki/` etc. Всё что про предметную область → vault. +- ❌ Использовать `aim_memory_*` MCP tools (старая knowledge-graph MCP server'а нет, поэтому даже не пытайся). +- ❌ Удалять MOC.md файлы в vault'е — они нужны для навигации. +- ❌ Писать новые сущности в `old/` папку vault'а — это архив legacy paths. + +### Code / git +- ❌ **Commit сам через `git commit`** — пользователь коммитит лично, я пишу сообщение в чат. +- ❌ `--no-verify`, `--amend`, `--no-edit`, `--no-gpg-sign` — обходить hooks/signing нельзя. +- ❌ `git push --force` в main или shared branches. +- ❌ Hardcode credentials в код / в коммиты. Используй `os.environ.get()` + `.env`/`.env.runtime`. +- ❌ `print(...)` для логирования в prod-коде — использовать `logger.info/warning/error`. +- ❌ Catching `Exception` без re-raise или явного логирования (silent failures). +- ❌ Использовать `requests` напрямую — есть `httpx` в стеке. +- ❌ Импортировать `psycopg2` — в проекте только psycopg v3. + +### Schema / migrations +- ❌ Запускать миграции на prod без понимания зависимостей (views на колонки, FK). +- ❌ Менять `categorize_v2` в `scripts/migrate_kg_to_obsidian.py` без сопутствующей миграции (path scheme должен оставаться backwards-compatible). +- ❌ `DROP TABLE` / `TRUNCATE` без явного approval пользователя. + +## Token efficiency tips для Claude + +- **Не читай 00_index.md → каждый entity по списку** — это 268 read'ов, ~500K токенов. Иди через MOC + tags. +- **`grep -l` (или `Grep` с `output_mode: files_with_matches`)** дешевле полного content reading. +- **Один `obsidian_simple_search`** часто заменяет 3-5 `Read` + parse. +- При write — обновляй MOC.md только если структурное изменение (новый entity, переименование, удаление). Append observation в existing entity не требует MOC update. +- Когда видишь много `[[wiki-links]]` — НЕ читай каждый, спроси у user'а что именно интересует. + +## Project-specific knowledge to load on first read + +При старте новой задачи в этом репо — first principles в vault'е: +- `meta/02_claude_usage.md` — этот гайд (источник истины — vault'овая копия) +- `meta/00_credentials.md` — secrets (когда нужны для debug-задач) +- `decisions/` папка — почему мы делаем так а не иначе (ADR-style) +- `fixes/` папка — что уже ломалось и как чинили +- `limitations/` папка — что НЕ работает / не реализовано + +Для специфичной работы — открывай MOC по релевантному домену (`domains//-MOC.md`). + +--- + +**Maintenance:** этот файл обновляется когда: +- Меняется tech stack (миграция фреймворка / DB) +- Появляются новые workflow rules (новые hooks, новые workflow в CI) +- Команда сталкивается с recurring pitfall → запиши его в "Common pitfalls" +- Меняется структура vault'а / MOC scheme + +Не превращай в новеллу — держи функциональный, action-oriented, без воды.