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

3.8 KiB
Raw Permalink Blame History

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:8000gendesign-backend (Site Finder, db=gendesign, старый код), НЕ tradein-backend (порт не опубликован на хост). curl на туннель:8000 по trade-in endpoint = мусор / чужая БД (стоило ~2ч). Тест trade-in API только изнутри контейнера:

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):

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.