gendesign/CLAUDE.md

458 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 "<keyword>" → найди 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:<password>@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` и пр.).
## Specialized subagents
В `.claude/agents/` лежит 6 subagent'ов — 4 worker'a + 2 read-only (analyst, reviewer). Каждый имеет:
- Свой context window (не засоряет main session)
- Restricted tool set (не делает что-то вне его scope)
- Pre-loaded knowledge о доменных конвенциях
### Workers (4) — пишут код
| Agent | Когда использовать |
|---|---|
| **backend-engineer** (blue) | Работа в `backend/app/` — FastAPI, Celery, scrapers, services, ETL |
| **frontend-engineer** (green) | Работа в `frontend/` — Next.js pages, components, hooks, admin UI |
| **devops-engineer** (orange) | `docker-compose*.yml`, `Caddyfile`, `.github/workflows/`, deploy issues, SSH operations |
| **database-expert** (purple) | `data/sql/NN_*.sql` migrations, Alembic, EXPLAIN ANALYZE, index design, schema changes |
### Read-only (2) — не пишут код
| Agent | Когда использовать |
|---|---|
| **tech-analyst** (yellow) | Нечёткий запрос ("надо ускорить", "что чинить дальше"), cross-domain задачи (2+ слоя), рефакторинг-разборы, аудит. Возвращает план, main session раздаёт worker'ам. |
| **code-reviewer** (red) | Review ПОСЛЕ worker'а ДО `git push`. Проверяет: security (secrets, SQL injection, auth bypass), correctness (edge cases, transactions, idempotency), performance (N+1, индексы, re-renders), conventions (ruff/prettier/CLAUDE.md), arch (DRY, dead code). Возвращает verdict ✅/⚠️/❌. |
### Routing rules
- **Тривиальная задача** (typo, rename, обновить one-liner) — main session сам, без делегирования
- **Single-domain task с чёткими шагами** → один соответствующий worker через `Agent(subagent_type="backend-engineer", ...)`
- **Нечёткая задача / cross-domain (2+ слоя)** → **сначала** `tech-analyst` → план → main session раздаёт worker'ам по плану
- **После worker'а перед push критичных изменений** → `code-reviewer` → если ❌ блок → вернуть worker'у. Опционально для тривиальных.
- **Pure exploration** (поиск файлов / понимание структуры) — встроенный `Explore` (Haiku, дёшево)
- **Crisis fix** (горящий prod баг) — пропускаешь tech-analyst и code-reviewer, идёшь напрямую к worker'у
### Cross-domain pattern (рекомендуемый)
Для задач затрагивающих 2+ слоя:
```
User request
tech-analyst → структурированный план: Шаг 1 (database-expert) → Шаг 2 (backend-engineer) → Шаг 3 (frontend-engineer)
Main session (ты) показывает план пользователю → получает approval
Main session запускает worker'ов в order'е (по dependencies)
Каждый worker возвращает summary → main session собирает в финальный отчёт
```
### Что считается "max 4 worker agents"
По best practices (Anthropic, community) — **больше 4 worker-агентов** снижает productivity (классификация "кто что делает" становится сложнее). У нас 4 worker'а + 2 read-only (tech-analyst планирует, code-reviewer проверяет) — read-only не считаются worker'ами.
### Code-reviewer workflow
```
User request → tech-analyst (если cross-domain)
Worker (backend/frontend/devops/database) пишет код
**code-reviewer** проверяет staged changes:
• ✅ APPROVE → main session коммитит + пушит
• ⚠️ MINOR → main session решает (или пускает, или возвращает worker'у)
• ❌ MAJOR → BLOCK push, возвращает worker'у на исправление
Loop until ✅ → git push origin HEAD:main
```
Используй code-reviewer для:
- Backend endpoints (auth, SQL injection vector)
- DB migrations (idempotency, индексы)
- Production deploys (compose changes, secrets)
- Изменения в job_settings / async tasks (race conditions)
Можно пропустить для:
- UI-only style/cosmetic
- Documentation
- Frontend type tweaks
- Trivial fixes (typo, rename)
### Subagents не могут спавнить друг друга
Только main session может вызывать subagents через `Agent` tool. Если worker обнаружил что нужна работа из чужого scope — возвращает control в main session, main делегирует следующему.
## When working on a task
### 1. Understand context — ВСЕГДА vault-first
- Прочитай user request один раз
- **`mcp__obsidian__obsidian_simple_search "<keyword>"`** — обязательный первый шаг для нетривиальной задачи. Найди 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_<short_name>.md` с root cause + fix
- **ADR / architectural decision** → создай `decisions/<Name>.md`
- **Новый scraper / endpoint / module** → создай entity в `domains/<area>/` или `code/modules/`
- **Гайд по операционной задаче** → `runbooks/` (или в `meta/0X_*.md` если про сам repo / workflow)
- **Session log** (опционально для большой работы): `sessions/Session_<date>_<topic>.md`
Используй `mcp__obsidian__obsidian_append_content` для добавления observation в existing entity, либо `Write` напрямую в `C:\Users\user\Documents\gendesign-memory\<path>/<File>.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 <service>`
### 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/<area>/<area>-MOC.md`).
---
**Maintenance:** этот файл обновляется когда:
- Меняется tech stack (миграция фреймворка / DB)
- Появляются новые workflow rules (новые hooks, новые workflow в CI)
- Команда сталкивается с recurring pitfall → запиши его в "Common pitfalls"
- Меняется структура vault'а / MOC scheme
Не превращай в новеллу — держи функциональный, action-oriented, без воды.