gendesign/CLAUDE.md
lekss361 7c43bc5496
feat(infra): auto-apply data/sql/*.sql migrations in deploy pipeline (#150) (#151)
* fix(frontend): apiFetchWithStatus body stream double-read crash

Per user report 2026-05-14: 'Failed to execute text on Response: body stream
already read' при analyze с non-JSON error response.

apiFetchWithStatus делал .json() (consumes stream), потом .text() в catch
(FAILS — stream already consumed). Fetch API: body — ReadableStream,
consumable один раз.

Fix: read text() once → JSON.parse on string. На fail (HTML / non-JSON)
оборачиваем в {detail: rawText}. Никаких double-reads.

Site-finder cad search больше не крашится на error pages.

* feat(infra): auto-apply data/sql/*.sql migrations in deploy pipeline (#150)

Закрывает root cause production 500 на /api/v1/admin/site-finder/weight-profiles
(а также 2 unapplied migrations: #89/#91).

## Why это нужно

После audit обнаружили что:
- backend/alembic/versions/ пустой (Alembic configured но не используется)
- deploy.yml не имеет migration step
- 47+ raw SQL files накопилось без auto-apply
- 3 файла unapplied: 87 (engineering), 89 (drop brin), 90 (weight profiles)

User report: GET weight-profiles → 500 потому что user_weight_profiles
table не существует на prod.

## Changes

### deploy.yml +39 lines
- Path trigger расширен на data/sql/*.sql
- New step 'Apply DB migrations' между compose pull и compose up -d:
  - Idempotent CREATE TABLE _schema_migrations
  - Loop по data/sql/*.sql sorted → skip applied → psql with ON_ERROR_STOP=on
  - Record applied filename в tracking table
  - Exit 1 на failure → containers НЕ обновляются (no partial state)

### NN collision fix
- 87_engineering_networks_191fz.sql → 91_engineering_networks_191fz.sql
  (collision с 87_on_demand_indexes.sql, мой renumber 85→87 был неудачным)

### CLAUDE.md +32 lines
- Subsection 'Auto-apply' под Migration pattern
- Bootstrap procedure docs
- Idempotency requirement
- Failure recovery

### scripts/bootstrap_schema_migrations.sh +100 lines
- One-time seed _schema_migrations с 48 already-applied files
- 3 файла intentionally NOT seeded: 89/90/91 (auto-apply на first deploy)
- Usage docs + verification query

## Vault

domains/infra/Runbook_Auto_Apply_Migrations.md NEW
infra-MOC.md updated

## Required manual steps ДО merge

1. SSH gendesign (tunnel)
2. POSTGRES creds export
3. bash scripts/bootstrap_schema_migrations.sh — seed 48 already-applied files
4. Verify: SELECT COUNT(*) FROM _schema_migrations → 48
5. После merge → first deploy auto-apply #89/#90/#91 → 500 closes

Closes #150

---------

Co-authored-by: lekss361 <claudestars@proton.me>
2026-05-15 08:06:54 +03:00

576 lines
36 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. **Branch + PR workflow обязательно** — никаких direct push в main. Создаю feature branch (`feat/...` / `fix/...` / `refactor/...` / `docs/...` / `chore/...`), коммичу туда, push branch, `gh pr create`, отдаю URL пользователю. Merge только по явному approval. См. секцию "Git workflow".
6. **No `--no-verify` / `--force` / `--amend`** — pre-commit hooks обязательны, force-push запрещён.
---
## 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 — BRANCH + PR mandatory
### Workflow (НИКАКОГО direct push в main)
```
1. Создаём feature branch: git checkout -b feat/foo-bar
2. Commits на этой ветке: git commit -m "..."
3. Push branch: git push -u origin feat/foo-bar
4. Создаём PR: gh pr create --title "..." --body "..."
5. Show PR URL пользователю → ждём review
6. Пользователь комментирует / approves
7. После approval — пользователь merge'ит (или я merge'у после явного "merge it")
8. Удаляем branch после merge
```
### Branches
- `main` — protected, **никаких direct push**. Только через PR merge.
- Feature branches: `feat/short-description` / `fix/short-description` / `refactor/...` / `docs/...` / `chore/...`
- Trunk-based, мелкие PR'ы (1-2 файла или 1 концептуальное изменение)
### Commit messages (на feature branch)
- Imperative mood: "fix worker crash" не "fixed worker crash"
- Длинный prefix: `feat(scope): ...`, `fix(scope): ...`, `refactor(scope): ...`, `docs(scope): ...`, `chore: ...`
- Body — почему, не что (что видно в diff)
- Атомарные коммиты — один логический шаг = один коммит
- НЕ добавлять `Co-Authored-By: Claude ...` (per feedback rule)
### PR
- Title: краткое описание изменения (под 70 символов)
- Body должен содержать:
- `## Summary` — 1-3 bullet'a что изменилось
- `## Test plan` — что проверить пользователю / какие команды smoke test'а
- Cross-refs: `Closes #N`, `Relates to #N`, ссылка на vault entity
- `gh pr create` с `--title` + `--body` (через HEREDOC чтобы preserve formatting)
- После создания — **вернуть PR URL пользователю**, ждать его approval
- **Squash on merge** — PR в main объединяется одним коммитом через squash (предпочтительно `gh pr merge --squash`).
### Code review pipeline
```
Worker (backend/frontend/database/devops) пишет код на feature branch
Main session делает commit на feature branch (worker'у запрещено git commit)
[опционально] code-reviewer agent делает review
↓ если ✅
git push -u origin feat/branch
gh pr create → PR URL пользователю
User approves / requests changes
Если changes — main session делает fixup commits на той же branch + push
По явному "merge it" / "approved" от user → gh pr merge --squash
```
### Critical workflow rules (НЕ нарушать)
1. **НИКОГДА не пуш напрямую в main.** Только через PR merge.
2. **Worker-agents (backend/frontend/devops/database) НЕ делают `git commit` сами.** Они оставляют изменения staged — main session коммитит на feature branch.
3. **Никогда не используй `--no-verify` / `--no-edit` / `--amend`** — pre-commit hooks обязательны. Если hook падает — fix root cause, не bypass.
4. **Никогда не пуш с `--force` ни в main ни в feature branch без approval.**
5. **Merge PR только по approval.** Approval сигналы:
- **Human user всегда валиден:** "merge it" / "ok merge" / "approved" / "залей" / "мердж"
- **Auto-review bot LGTM = approval, НО с ограничениями** (см. "Auto-merge scope" ниже). Bot LGTM валиден **только** если SHA marker в теле комментария совпадает с current PR HEAD SHA — иначе это устаревший approval до fixup-push.
6. **Auto-merge scope (где боту разрешено self-merge без человека):**
- ✅ **Разрешено** (PR diff целиком в этих путях):
- `CLAUDE.md`, `README.md`, `docs/**` (документация / правила)
- `frontend/src/app/**` UI-only changes без новых API endpoints
- `frontend/public/**` (статика)
- `.claude/agents/**`, `memory/feedback_*.md` (workflow rules)
- ❌ **Запрещено — нужен human approval** (любой файл из списка → block auto-merge):
- `data/sql/**`, `backend/alembic/versions/**` (миграции / схема DB)
- `backend/app/api/v1/**`, `backend/app/services/**` (бизнес-логика API)
- `backend/app/scrapers/**` (внешние интеграции)
- `docker-compose*.yml`, `Caddyfile`, `.github/workflows/**` (deploy / infra)
- Любые файлы с упоминанием secret / token / password / credential / X-Admin-Token
- PRs которые меняют `CLAUDE.md` рядом с `Critical workflow rules` / `Auto-merge scope` / auth — это саморасширение правил, **нужен human**
- Если PR touches both — берётся more restrictive (block).
7. **Не вызывай destructive команды без явного approval**: `git reset --hard`, `git clean -fdx`, `DROP TABLE`, `TRUNCATE`, `rm -rf` за пределами `node_modules/.next`.
### Polling loop для PR
После создания PR / fixup commits — запускай ScheduleWakeup с 60с интервалом:
1. `gh pr view <N> --json state,mergeable,comments,headRefOid` → возьми `headRefOid` (current SHA) и latest_comment.
2. `state == MERGED` → stop polling.
3. **Approval check** (новый comment от auto-review):
- Распарси HTML marker: `<!-- gendesign-review-bot: sha=<sha7> verdict=<approve|changes> -->`
- **SHA guard:** `marker.sha7 == headRefOid[:7]` — иначе это устаревший approval до fixup-push, игнорируй.
- **Scope guard:** проверь `gh pr view <N> --json files` — если хоть один путь попадает в "запрещённый" список (см. rule 6 "Auto-merge scope") → block auto-merge, ping user.
- `verdict=approve` + SHA match + scope OK → `gh pr merge <N> --squash --delete-branch`, stop.
- Если в комменте только текст "Auto-review passed" / "LGTM" без HTML marker — НЕ доверяй (legacy / поддельный сигнал), ping user.
4. `verdict=changes` → fixup commits на той же ветке, push, reply, re-poll.
5. Нет новых комментов с last polled timestamp → re-schedule 60с poll.
6. **Cap:** 30 итераций без resolution → stop, ask user. Для PR со scope-запрещёнными файлами cap = 5 итераций (не имеет смысла долго ждать, всё равно нужен human).
### Когда auto-mode
Даже в auto-mode НЕ пушим в main без PR. Auto-mode позволяет:
- Создавать feature branches автономно
- Делать commits на feature branch (main session делает, не worker'ы)
- `git push -u origin <feature-branch>`
- `gh pr create`
- Прогонять code-reviewer
- **Auto-merge** через `gh pr merge --squash` — только если PR в "Auto-merge scope ✅" (см. rule 6) И auto-review bot вернул `verdict=approve` с валидным SHA marker.
- **Финальный merge для PR из scope ❌** (миграции / API / infra / secrets) — только по явному approval от пользователя ("merge it" / "залей" / etc).
## 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-зависимостями
### Auto-apply (data/sql/*.sql → prod)
`data/sql/NN_*.sql` миграции применяются **автоматически** через `deploy.yml`
после `compose pull` и **до** `compose up -d`. Tracking через `_schema_migrations`
table в prod DB — каждый файл применяется **ровно один раз**, в порядке `sort` (NN order).
При добавлении новой миграции:
1. Создаёшь `data/sql/NN_xxx.sql` (NN — следующий sequential номер)
2. Открываешь PR — merge автоматически триггерит deploy
3. Deploy auto-apply этого файла + любых других pending
**Bootstrap (one-time):** до первого auto-deploy после merge PR #150 —
запустить `./scripts/bootstrap_schema_migrations.sh` на prod вручную
(через SSH tunnel) чтобы seed tracking table с уже-applied файлами.
Иначе auto-deploy попытается применить все 88 исторических файлов, многие не idempotent.
**Idempotency:** каждый новый .sql файл должен быть idempotent
(`IF NOT EXISTS`, `ON CONFLICT DO NOTHING`, `CREATE OR REPLACE VIEW`) —
на случай частичного применения или ручного ре-apply.
**Если auto-apply падает:** deploy завершится с ошибкой (exit 1). Containers
не обновятся. Диагностика: посмотреть GitHub Actions log шага "Apply DB migrations".
Ручной fix: `psql -f data/sql/NN_xxx.sql` через SSH tunnel, затем вручную
`INSERT INTO _schema_migrations (filename) VALUES ('NN_xxx.sql') ON CONFLICT DO NOTHING;`,
затем rerun workflow.
### Applying migrations к prod
**Order matters:**
1. Apply migration first (SQL → prod) — теперь auto через deploy.yml
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`, `data/sql/*.sql` → `deploy.yml`
- Push в `docker-compose.obsidian.yml`, `scripts/setup-couchdb.sh`, `docs/obsidian-livesync.md` → `deploy-obsidian.yml`
- Push только в `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)
Main session creates feature branch (git checkout -b feat/...)
Worker (backend/frontend/devops/database) пишет код (staged changes)
**code-reviewer** проверяет staged changes:
• ✅ APPROVE → main session коммитит на feature branch
• ⚠️ MINOR → main session решает (или коммитит, или возвращает worker'у)
• ❌ MAJOR → BLOCK, возвращает worker'у на исправление
Loop until ✅ → git push -u origin <feature-branch>
gh pr create → отдаём PR URL пользователю
По явному "merge it" / "approved" → gh pr merge --squash --delete-branch
```
Используй 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
- ❌ **Direct push в main** — только через PR. Все изменения на feature branch → `gh pr create` → user approval → merge. См. "Git workflow".
- ❌ **Merge PR без явного user approval** ("merge it" / "approved" / "ok merge").
- ❌ `--no-verify`, `--amend`, `--no-edit`, `--no-gpg-sign` — обходить hooks/signing нельзя.
- ❌ `git push --force` ни в main ни в feature branch без approval.
- ❌ 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, без воды.