---
name: avtero-bot-e2e
description: Полный детерминированный e2e-прогон Telegram-бота avtero (@avtero_bot, подбор автозапчастей) БЕЗ браузера и без реального Telegram — гоняет настоящие хендлеры aiogram через Dispatcher.feed_update с фейковым Bot API против живого API :8201. Брать когда: меняли bot/handlers/* (ask/start/admin/feedback), поток FSM, рендер отчёта, avtero_client, парс VIN/фрейма/госномера; или просят «проверь бота avtero», «прогони e2e автеро», «работает ли бот после доработки», «подбери по фрейму/VIN через бота». НЕ для других ботов (VakHunter→vakhunter-e2e, zemlya→zemlya-report-e2e, gaechkin/прочие→tg-web-e2e) и не для сайта Автеро (там прямой /api/ask_async).
---

# /avtero-bot-e2e — полный прогон бота без браузера

Гоняет **реальный код бота** (aiogram 3.x: роутеры, FSM `AskFlow`, рендер, вызов
`avtero_client.submit_ask` → API :8201) через `Dispatcher.feed_update` с фейковым
Bot API, который записывает исходящие сообщения. Никакого Playwright/TG Web —
поэтому нет флейков виртуализованной истории и проблемы админ-аккаунта
(см. скилл `tg-web-e2e`, где реальный TG Web ломается на засорённой истории
админа `8688355181`). Подбор при этом НАСТОЯЩИЙ — идёт через живой API.

## Где живёт
- Харнесс: `~/Services/avtero/bot/tests/e2e_harness.py` (`FakeBot` + `BotHarness` + CLI)
- pytest: `~/Services/avtero/bot/tests/test_e2e_dialog.py` (submit_ask замокан)
- Обёртка: `~/.claude/skills/avtero-bot-e2e/tools/run.sh` (ssh на mini + прогон)

## Доступ / запуск
Бот и API на mac mini: `ssh -p 2222 cebers_mini@100.84.177.25`. API :8201 должен
быть жив (`curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8201/healthz` = 200;
если нет — `launchctl kickstart -k gui/$(id -u)/local.avtero`, ждать ~14с).

Быстрый прогон (CLI): аргументы = шаги диалога. Первый — идентификатор
(VIN / «Марка Модель Год [+ номер кузова]» / госномер), дальше — работы (текст)
или `cb:<callback_data>` для нажатия кнопки.

```bash
ssh -p 2222 cebers_mini@100.84.177.25 'cd ~/Services/avtero && set -a; . ./.env 2>/dev/null; set +a; \
  BOT_DB_PATH=/tmp/harness.db PYTHONPATH=. ~/Services/avtero-venv/bin/python3 \
  -m bot.tests.e2e_harness "Toyota Altezza 2003 GXE10-0088644" "cb:veh:ok" "шкив генератора"'
```
Печатает `>>> USER:` и каждое `<<< BOT [edit?] <текст> | buttons=[...]`, подсвечивает
OEM-токены и в конце `OEM found: [...]`.

Или через обёртку: `bash ~/.claude/skills/avtero-bot-e2e/tools/run.sh "<ид>" ["cb:veh:ok"] "<работа>"`.

## Поток FSM — КРИТИЧНО для правильной последовательности
`AskFlow`: waiting_vin → waiting_work → processing. Харнесс перед первым шагом
ставит waiting_vin (как после «🔍 Новый запрос»).
- **VIN (17 симв.)** в waiting_vin → сразу waiting_work. Дальше просто текст работы.
- **Фрейм / «Марка Модель Год [+кузов]»** в waiting_vin → бот показывает карточку
  авто, но ОСТАЁТСЯ в waiting_vin до подтверждения. Нужна вставка **`cb:veh:ok`**
  («✅ Верно») перед текстом работы — иначе работа падает обратно в VIN-парсер.
- **Госномер** → как VIN (через resolve_plate внутри), затем текст работы.
- Кнопки-подсказки работ: `cb:work:t:<id>` + `cb:work:go`; свой запрос: `cb:work:custom`
  затем текст (в CLI можно и просто текст в waiting_work).

## ОБЯЗАТЕЛЬНО: кэш-бастер (иначе ложная «проверка»)
API кэширует ответ 5 минут по (vin/frame, work_text, …) и кэш ПЕРЕЖИВАЕТ рестарт.
Повторный идентичный запрос вернёт старый ответ. Всегда меняй текст работы
(добавь nonce-слово, напр. «шкив генератора проверка<N>») — классификатор всё
равно матчит по существительному, а ключ кэша меняется.

## Эталонные ожидания (реальные, проверены)
- Фрейм `"Toyota Altezza 2003 GXE10-0088644"` → `cb:veh:ok` → «шкив генератора»
  ⇒ OEM **27411-42010** (epc-data, genuine по кузову) + аналоги WAI/FEBI.
- VIN `"KNAGC4A87DA232516"` → «шкив генератора»
  ⇒ Kia Optima 2013, OEM **37322-2G500** + VALEO/GATES.
Общий смысл проверки после правок: узел ≠ запчасть узла (шкив→PULLEY, не весь
генератор), номер полный (с суффиксом), при неоднозначности — честное пусто.

## pytest (детерминированно, без сети)
`cd ~/Services/avtero && set -a; . ./.env; set +a; PYTHONPATH=. ~/Services/avtero-venv/bin/python3 -m pytest bot/tests/test_e2e_dialog.py -q`
(submit_ask/stream_events замоканы — проверяют парс/FSM/рендер без API).

## Грабли (набито)
- `BOT_DB_PATH=:memory:` в `bot/config` делает `Path(":memory:").resolve()` →
  файл `<cwd>/:memory:` (НЕ true in-memory). Для CLI задавай явный путь-файл
  (`/tmp/harness.db`); после — `rm -f ~/Services/avtero/':memory:'` если появился.
- Тест-юзер харнесса `700700700` (не-админ), регистрируется+аппрувится через
  реальный `get_session`; иначе `access_required` молча глотает апдейты.
- Два `BotHarness` в одном процессе — `Router already attached`; харнесс детачит
  `_parent_router=None` перед include (уже сделано).
- Токен FakeBot `12345:TEST_TOKEN` (bot.id парсится до `:`, нужен int для StorageKey).
- Предсуществующие fail в `bot/tests/`: `test_admin_guard` ×2 (загруженный `.env`
  переопределяет `BOT_ADMIN_USER_IDS` мимо conftest) и `test_rendering`
  (ждёт англ. «premium», код рендерит «премиум») — НЕ регрессия харнесса.
- Долгий подбор: `feed_text` с текстом работы ждёт submit_ask 30-120с — таймаут
  шага должен это учитывать (в CLI уже заложено).
