gendesign/.claude/rules/tradein.md
bot-backend 2e20b6307b
Some checks failed
CI Trade-In / changes (pull_request) Successful in 10s
CI / changes (pull_request) Successful in 11s
CI Trade-In / backend-tests (pull_request) Failing after 21s
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Successful in 55s
CI Trade-In / frontend-checks (pull_request) Successful in 1m26s
CI / openapi-codegen-check (pull_request) Successful in 2m7s
CI / backend-tests (pull_request) Successful in 16m53s
fix(tradein): гейт номеров миграций берёт эталон из git, ручной манифест удалён
_manifest_applied.txt по построению не мог покраснеть. Тест считал «новым»
любой файл, которого нет в списке, а новые файлы от списка освобождены
(докстринг test_manifest_covers_all_but_new_files: «НЕ требует, чтобы новый
файл уже был в manifest»). Забытое имя и новая миграция PR для гейта — одно и
то же, поэтому дрейф был не пропуском проверки, а её штатным исключением.
Замер на main 2026-08-07: 15 имён не дописано, все четыре теста зелёные —
через сутки после того, как #2692 догнал список руками.

Список при этом был лишь копией того, что git и так знает: deploy-tradein.yml
применяет КАЖДЫЙ data/sql/*.sql из main под ON_ERROR_STOP, то есть «файл
доехал до main» и есть «имя закреплено на проде». Ведём эталон в git — и
дрейфовать становится нечему.

Кросс-ветковая дыра закрыта тем же ходом: номер нового файла сверяется с
ПОЛНЫМ origin/main, а не с рабочим деревом, поэтому коллизия с миграцией,
смерженной после ветвления, находится. Проверено на живом PR #2754
(234_trade_in_estimates_retain_until против 234_scrape_runs_ban_kind_unknown
из main): старый гейт зелёный, новый красный.

Удаление/переименование применённой миграции сверяется с ТОЧКОЙ ВЕТВЛЕНИЯ, а
не с origin/main: иначе ветка недельной давности краснела бы за чужие
миграции. Проверено — ветка от 2026-07-30 при +43 миграциях в main зелёная.

CI: checkout переведён на fetch-depth 0 + отдельный fetch main. Этот Forgejo
не публикует refs/pull/N/merge (1620 */head, ноль */merge), а на depth=1 нет
ни origin/main, ни общего предка — без этого гейту не с чем сверять, и он
намеренно красный, а не тихо пропущенный.

Контракт сведён к одной формулировке — докстринг test_migration_numbering.py;
шапка манифеста, правило 3, хвост манифеста и рецепт из .claude/rules
удалены или заменены ссылкой. Заодно исправлен сам рецепт: `git ls-tree` без
`-r` печатает каталог, а не файлы.

Refs #2683
2026-08-07 14:35:28 +05:00

65 lines
3.8 KiB
Markdown
Raw Permalink 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.

---
paths:
- tradein-mvp/**/*.py
- tradein-mvp/**/*.sql
- tradein-mvp/frontend/**/*.{ts,tsx}
---
# trade-in (Mera) conventions — `tradein-mvp/`
Отдельный продукт + отдельный стек от Site Finder. Backend `tradein-mvp/backend/app/**`,
SQL `tradein-mvp/backend/data/sql/NN_*.sql`, frontend `tradein-mvp/frontend/`. Backend Python
подчиняется `.claude/rules/backend.md` (psycopg v3, CAST, ruff-100), SQL — `.claude/rules/sql.md`
(NN naming, idempotency). Ниже — то, что СПЕЦИФИЧНО для trade-in.
## Две БД — не путай
- **`postgres-tradein`** (db=tradein) — скрейпленные листинги avito/cian/yandex, estimator,
coverage, houses. Для ЛЮБОЙ tradein-задачи метрики/схему бери отсюда (`mcp__postgres-tradein__*`).
- **`postgres-gendesign`** (db=gendesign) — Site Finder, НЕ trade-in.
## Тестировать HTTP только ВНУТРИ контейнера
SSH-туннель `localhost:8000``gendesign-backend` (Site Finder, db=gendesign, старый код),
**НЕ** tradein-backend (порт не опубликован на хост). curl на туннель:8000 по trade-in endpoint =
мусор / чужая БД (стоило ~2ч). Тест trade-in API только изнутри контейнера:
```bash
ssh gendesign # затем:
docker exec tradein-backend curl -s localhost:8000/<route> # админ-роуты: -H "X-Authenticated-User: admin"
docker exec tradein-postgres psql -U <user> -d tradein -c "..."
```
## Scheduler крутится в `tradein-scraper`, не `tradein-backend`
In-app scheduler (`scrape_schedules`, tick 60s, `python -m app.scheduler_main`,
`SCHEDULER_ENABLE=true`) живёт в контейнере **`tradein-scraper`**; в `tradein-backend` намеренно
`false`. Статус scheduled-задач смотри в scraper-контейнере (logs/printenv), не в backend.
Ручной smoke: `UPDATE scrape_schedules SET next_run_at=now() WHERE source='X'` → подхват ≤60s.
## SQL авто-применяется на ПРОД (strict)
`tradein-mvp/backend/data/sql/NN_*.sql` применяется автоматически на деплое через `_schema_migrations`
в `.forgejo/workflows/deploy-tradein.yml` (НЕ init-only, strict exit-1). Idempotency критична —
деструктивный DDL хитит прод на деплое.
**Номер новой миграции сверяй с `origin/main`, не с локальным `ls`** — локальное дерево не видит
миграций, смерженных после ветвления (так разъехались 212 в #2682 и 234 в #2754):
```bash
git fetch origin main
git ls-tree -r --name-only origin/main -- tradein-mvp/backend/data/sql | tail
```
`-r` обязателен — без него `ls-tree` печатает сам каталог одной строкой, а не файлы.
Правило целиком — в докстринге `tradein-mvp/backend/tests/test_migration_numbering.py` (единственная
формулировка контракта, #2683); он же гейтит его в CI. Дописывать имя в какой-либо список НЕ надо:
`_manifest_applied.txt` удалён — он отставал и по построению не мог покраснеть.
## Rapid-merge trap
2 tradein-PR мержа за секунды → backend `test`-job cancelled → `build-backend` пропущен →
«deploy success» на СТАРОМ образе (нет нового кода/deps). Сверяй `:latest` Created-timestamp vs
время мержа + smoke в контейнере; не верь «деплой прошёл». Recovery: ручной `workflow_dispatch`
для `deploy-tradein.yml`.