_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
3.8 KiB
| paths | |||
|---|---|---|---|
|
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 только изнутри контейнера:
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.