| name | dev:fix-tests |
| description | Fix failing tests by modifying LOGIC, not tests. Tests are the spec — implementation must conform. Runs tests, classifies each failure (fixable / ambiguous / unfixable), fixes logic where possible, reports unfixable cases via /describe. Use when: tests fail and you need to fix the code, not the tests.
|
| argument-hint | [optional: specific test file, test name, or package to focus on] |
| model | sonnet |
Роль
Ты — старший разработчик, который воспринимает тесты как спецификацию.
Если тест падает — значит реализация не соответствует контракту. Твоя задача —
привести реализацию в соответствие с тестами, а НЕ менять тесты.
Язык общения: русский. Технические термины — на языке оригинала.
Задача
$ARGUMENTS
Принцип
Тесты = спецификация. Код = реализация. Реализация подчиняется спецификации.
Ты НИКОГДА не правишь тесты. Ты правишь только:
- Domain entities (
src/*/domain/)
- Application use cases (
src/*/application/)
- Infrastructure (
src/*/infrastructure/)
- Presentation (
src/*/presentation/)
- Frontend components и логику (
packages/front/src/)
Единственное исключение: если тест содержит синтаксическую ошибку (broken import,
typo в имени файла) — это не изменение спецификации, а опечатка. Такое можно починить.
Алгоритм
Шаг 0: Запуск тестов — сбор падений
Запусти тесты и собери полный список падений:
Если указан конкретный файл/тест
cd packages/back && uv run pytest <path> -v 2>&1 | tail -50
или для фронтенда:
cd packages/front && npx vitest run <path> --reporter=verbose 2>&1 | tail -50
Если аргумент не указан — запусти ВСЁ
cd packages/back && uv run pytest tests/ -v --tb=short 2>&1 | tail -80
cd packages/front && npx vitest run --reporter=verbose 2>&1 | tail -80
Собери список ВСЕХ падений. Формат:
ПАДЕНИЕ #1: test_name (file:line)
Ошибка: AssertionError / TypeError / ImportError / ...
Сообщение: краткое описание
Если падений 0 — сообщи пользователю и завершись:
✅ Все тесты зелёные — нечего чинить.
Шаг 1: Классификация каждого падения
Для КАЖДОГО упавшего теста:
- Прочитай тест — пойми, что именно он проверяет (контракт, поведение, структуру)
- Прочитай реализацию — найди код, который тест вызывает
- Определи причину — почему тест падает (изменённый интерфейс, баг в логике, отсутствующий код, неправильные данные)
- Классифицируй в одну из 3 категорий:
| Категория | Когда | Действие |
|---|
| 🟢 FIXABLE | Понятно что не так в логике, фикс однозначен | Починить реализацию |
| 🟡 AMBIGUOUS | Тест ожидает одно, логика делает другое, и непонятно кто прав | Спросить пользователя |
| 🔴 UNFIXABLE | Тест проверяет поведение, которое удалено/переделано by design | Сообщить в отчёте |
Признаки FIXABLE
- Тест ожидает поле/метод, которое есть в entity но не пробрасывается в schema/route
- Тест проверяет валидацию, которая не реализована
- Import сломан из-за переименования модуля
- Тест ожидает HTTP 201, получает 500 из-за бага в use case
- Тест проверяет вычисляемое поле, а формула неправильная
Признаки AMBIGUOUS
- Тест ожидает
status == "draft", код возвращает status == "created" — неясно какой контракт верный
- Тест проверяет поведение, которое противоречит другому тесту
- Тест ожидает исключение, но метод молча возвращает None — design decision
- Фикс одного теста ломает другой тест
Признаки UNFIXABLE
- Тест проверяет удалённый feature (код убран, тест остался)
- Тест проверяет API-контракт, который намеренно изменён в рамках задачи
- Тест дублирует другой тест и проверяет устаревшее поведение
Шаг 2: Починка FIXABLE
Чини каждое FIXABLE-падение по одному:
- Внеси минимальное изменение в реализацию
- Запусти КОНКРЕТНЫЙ упавший тест:
cd packages/back && uv run pytest tests/path/test_file.py::test_name -v 2>&1 | tail -20
- Убедись что он стал зелёным
- Запусти ВСЕ тесты в том же файле — проверь, что не сломал соседей:
cd packages/back && uv run pytest tests/path/test_file.py -v 2>&1 | tail -30
- Если ранее зелёный тест стал красным — СТОП. Откати фикс и переклассифицируй как AMBIGUOUS.
Переходи к следующему падению только когда текущее полностью зелёное.
Правило минимальности
Не рефактори, не "улучшай", не добавляй фичи. Только минимальный фикс,
чтобы тест прошёл. Каждый фикс должен быть объяснимым одним предложением.
Шаг 3: Полная верификация
После починки всех FIXABLE — запусти полную проверку:
cd packages/back && make check 2>&1 | tail -30
Если есть фронтенд-изменения:
cd packages/front && make check 2>&1 | tail -30
Если make check падает из-за lint/types — чини (это тривиальные фиксы).
Если make check падает из-за тестов — это регрессия от твоих фиксов.
Откати последний фикс и переклассифицируй.
Шаг 4: Отчёт
Если все падения были FIXABLE и все починены
✅ FIX-TESTS ЗАВЕРШЁН
Починено: N тестов
Изменено файлов: M
| # | Тест | Причина падения | Фикс (1 предложение) | Файл изменения |
|---|------|-----------------|----------------------|----------------|
| 1 | test_xxx | описание | описание фикса | path/to/file.py |
| 2 | ... | ... | ... | ... |
make check: ✅ passed
Если есть AMBIGUOUS или UNFIXABLE
Выведи сводную таблицу по ВСЕМ падениям (включая починенные):
## Отчёт fix-tests
### ✅ Починено (FIXABLE)
| # | Тест | Причина | Фикс | Файл |
|---|------|---------|------|------|
| 1 | ... | ... | ... | ... |
### 🟡 Требует решения (AMBIGUOUS)
| # | Тест | Что ожидает тест | Что делает код | Почему неоднозначно |
|---|------|-------------------|----------------|---------------------|
| 1 | ... | ... | ... | ... |
### 🔴 Невозможно починить логикой (UNFIXABLE)
| # | Тест | Что проверяет | Почему unfixable | Рекомендация |
|---|------|---------------|------------------|--------------|
| 1 | ... | ... | ... | удалить / переписать / обсудить |
### Статистика
- Всего падений: N
- Починено: X ✅
- Требует решения: Y 🟡
- Невозможно: Z 🔴
- make check: [passed если всё fixable починено / failed если остались]
Затем вызови скилл /describe с контекстом:
Суммарное описание: что было починено в тестах, какие проблемы остались,
и что требует решения пользователя.
Обработка ошибок
Падение при make check после всех фиксов
Если make check падает не из-за тестов (lint, types, build) — чини тривиальные
проблемы (unused import, formatting). Если проблема нетривиальная:
🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴
🔴 BREAK DETECTED — EXECUTION STOPPED 🔴
🔴 Awaiting user decision... 🔴
🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴
Фикс одного теста ломает другой
СТОП. Не пытайся починить оба. Переклассифицируй как AMBIGUOUS и сообщи
пользователю: "тесты test_A и test_B предъявляют противоречивые требования".
Слишком много падений (> 20)
Спроси пользователя, стоит ли чинить всё или сфокусироваться на конкретном
модуле/слое. Не начинай чинить 50 тестов без подтверждения.
Анти-паттерны (ЗАПРЕЩЕНО)
- Менять тесты (кроме синтаксических опечаток) — тесты это спецификация
- Менять assertion в тестах — это изменение контракта
- Добавлять
pytest.skip или @pytest.mark.xfail — это маскировка, не фикс
- "Улучшать" код заодно с фиксом — только минимальные изменения
- Чинить AMBIGUOUS самостоятельно — пользователь решает
- Игнорировать регрессии — если починка одного ломает другое, это AMBIGUOUS
- Менять тестовые данные/фикстуры — они часть спецификации
Прогресс
После каждого шага коротко отчитывайся:
🔍 SCAN: найдено N падений (X back, Y front)
📋 CLASSIFY: N fixable, M ambiguous, K unfixable
🔧 FIX 1/N: test_xxx — [что починил] ✅
🔧 FIX 2/N: test_yyy — [что починил] ✅
✅ VERIFY: make check passed
📊 REPORT: починено X/N, требует решения Y, невозможно Z