---
name: bugfix-protocol
version: 1.0.0
type: protocol
author: Lukas Geiger
created: 2026-03-12
updated: 2026-03-12
description: Систематический 6-фазный протокол отладки. Структурированный подход к багам с быстрыми проверками, изолированным тестированием, правилом 20 минут и шаблоном отчета об ошибке.

standalone: true
anthropic_compatible: true
bach_compatible: false
bach_origin: true
category: dev
tags: [debugging, bugfix, protocol, python, pyqt6, systematic]
language: ru
status: active
dependencies: {'tools': [], 'services': [], 'protocols': [], 'python': []}
provenance: {'origin': 'bach', 'origin_path': 'system/skills/workflows/bugfix-protokoll.md', 'origin_version': '1.0.0', 'origin_repo': 'github.com/ellmos-ai/bach', 'last_sync_from_origin': '2026-03-12', 'last_sync_to_origin': None, 'local_changes_since_sync': True}
---

<img src="banner.png" width="100%" alt="bugfix-protocol banner">

> **Русский** — Официальная русская версия `bugfix-protocol`.


# Bugfix Protocol: Систематическая 6-фазная отладка

Структурированный подход к ошибкам — от анализа симптомов до проверки.
Предотвращает бесцельный метод проб и ошибок и гарантирует устойчивость исправлений.

---

## Обзор и цель

| Фаза | Название | Цель | Макс. время |
|------|----------|------|-------------|
| 1 | Быстрые проверки | Исключить очевидные причины | 2 мин |
| 2 | Диагностика | Локализовать первопричину | 10 мин |
| 3 | Изолированный тест | Сделать баг воспроизводимым | 5 мин |
| 4 | Исправление | Минимальная коррекция | 10 мин |
| 5 | Верификация | Проверить исправление + проверить побочные эффекты | 5 мин |
| 6 | Документирование | Сохранить знания | 2 мин |

**Правило 20 минут:** Если через 20 минут прогресс отсутствует, измените подход или обратитесь за помощью.

---

## Фаза 1: Быстрые проверки (2 мин)

Прежде чем погружаться глубоко — проверьте наиболее частые причины:

### Чек-лист

- [ ] **Синтаксическая ошибка?** Внимательно прочитайте сообщение об ошибке, проверьте строку
- [ ] **Ошибка импорта?** Модуль установлен? Имя правильное? Циклический импорт?
- [ ] **Опечатка?** Правильно ли указаны имена переменных/функций?
- [ ] **Неверный тип данных?** String вместо int? None там, где ожидался объект?
- [ ] **Устаревший кэш?** Удалите `__pycache__`, перезапустите
- [ ] **Неверное окружение?** Активно ли правильное venv? Правильная ли версия Python?
- [ ] **Кодировка?** UTF-8 против cp1252 (классический Windows)

### Быстрые действия

```bash
# Очистить кэш
find . -name "__pycache__" -type d -exec rm -rf {} + 2>&1
find . -name "*.pyc" -delete 2>&1

# Проверить импорты
python -c "import modulename"

# Проверить синтаксис
python -m py_compile file.py
```

---

## Фаза 2: Диагностика (10 мин)

### Стратегия: Снаружи внутрь (Outside-In)

1. **Анализ сообщения об ошибке** — Читайте трассировку стека (traceback) снизу вверх
2. **Проверка последних изменений** — `git diff`, `git log --oneline -10`
3. **Использование диагностических утилит** — Используйте специфичные для проекта инструменты

### Диагностические утилиты (Примеры)

В зависимости от проекта могут пригодиться специализированные скрипты диагностики:

| Инструмент | Назначение |
|------------|------------|
| `import_diagnose.py` | Анализ проблем с импортом |
| `method_analyzer.py` | Проверка сигнатур методов |
| `env_checker.py` | Валидация переменных окружения/путей |

> **Примечание:** Создавайте специфичные для проекта диагностические утилиты или используйте существующие.
> Важен систематический подход, а не конкретный инструмент.

### Методы отладки

```python
# 1. Отладка через print (быстро, но эффективно)
print(f"DEBUG: variable={variable!r}, type={type(variable)}")

# 2. Точка останова (интерактивно)
breakpoint()  # Python 3.7+

# 3. Расширенный traceback
import traceback
traceback.print_exc()

# 4. Логирование вместо print
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
logger.debug(f"State: {state!r}")
```

---

## Фаза 3: Изолированный тест (5 мин)

### Минимальный воспроизводимый пример (MRE)

Цель: Воспроизвести баг с минимальным количеством кода.

```python
# test_bug.py — Минимальный тест воспроизведения
"""
Bug: [Краткое описание]
Expected: [Что должно произойти]
Actual: [Что происходит вместо этого]
"""

# Минимальная настройка
# ... только самое необходимое

# Триггер бага
# ... точный код, вызывающий баг

# Ожидаемый результат
# assert result == expected, f"Got {result}"
```

### Стратегии изоляции

1. **Новый файл:** Воспроизведите баг в отдельном файле
2. **Удаление зависимостей:** По одной, пока баг не исчезнет
3. **Бинарный поиск:** Разделите блок кода пополам, проверьте, в какой половине баг
4. **Git bisect:** `git bisect start`, `git bisect bad`, `git bisect good <commit>`

---

## Фаза 4: Исправление (10 мин)

### Принципы

1. **Минимальность:** Меняйте как можно меньше
2. **Понимание:** Никогда не чините вслепую — поймите, ПОЧЕМУ код сломан
3. **Одна задача:** Одно исправление на коммит, не чините несколько проблем одновременно
4. **Обратная совместимость:** Не ломайте существующий функционал

### Шаблоны исправлений

```python
# ПЛОХО: Лечение симптома
try:
    result = broken_function()
except:  # Подавление всех исключений
    result = default_value

# ХОРОШО: Исправление первопричины
def broken_function():
    if input_data is None:  # Настоящая причина: отсутствие проверки на None
        return default_value
    return process(input_data)
```

### Распространенные категории исправлений

| Категория | Типичное исправление |
|-----------|----------------------|
| None/Null | Защитное условие: `if x is None: return default` |
| Ошибка индекса | Проверка границ: `if i < len(lst)` |
| Ошибка типа | Явное приведение: `str(x)`, `int(x)` |
| Ошибка импорта | Исправить путь, установить пакет |
| Кодировка | Явно указать UTF-8: `encoding='utf-8'` |
| Состояние гонки | Блокировка/Мьютекс или изменение порядка |
| Баг состояния | Проверить инициализацию, добавить сброс |

---

## Фаза 5: Верификация (5 мин)

### Чек-лист

- [ ] **Баг исправлен:** Исходная проблема больше не возникает
- [ ] **MRE проходит:** Изолированный тест выполняется успешно
- [ ] **Нет регрессий:** Существующие тесты по-прежнему проходят
- [ ] **Граничные случаи:** Проверены пустой ввод, None, большие объемы данных
- [ ] **Утилиты проекта:** Проверьте директорию утилит проекта на наличие тестов/валидаторов

### Команды тестирования

```bash
# Юнит-тесты
python -m pytest tests/ -v

# Только затронутые тесты
python -m pytest tests/test_module.py -v -k "test_name"

# Проверка типов
python -m mypy file.py

# Линтер
python -m flake8 file.py
```

---

## Фаза 6: Документирование (2 мин)

### Шаблон отчета об ошибке

```markdown
## Bug Report: [Краткий заголовок]

**Date:** YYYY-MM-DD
**Severity:** critical / high / medium / low
**Component:** [Модуль/Файл]

### Symptom
[Что видит пользователь / сообщение об ошибке]

### Root Cause
[Техническая первопричина]

### Fix
[Что было изменено + почему]

### Affected Files
- `file1.py` — [Изменение]
- `file2.py` — [Изменение]

### Prevention
[Как предотвратить подобный баг в будущем?]
```

### Формат сообщения коммита

```
fix: [Краткое описание исправления]

Cause: [Первопричина в одном предложении]
Fix: [Что было изменено]
Test: [Как проверялось]
```

---

## PyQt6 / Отладка GUI — Распространенные ловушки

> Этот раздел актуален для десктопных GUI-проектов на PyQt6/PySide6.

### Топ-5 ловушек PyQt6

| Ловушка | Проблема | Решение |
|---------|----------|---------|
| **Отключение Signal-Slot** | Сигнал подключен, но обработчик не выполняется | `print` в обработчике, проверка сигнатуры |
| **Потокобезопасность** | Обновление GUI из рабочего потока | `QMetaObject.invokeMethod` или использование сигнала |
| **Каскад макета (Layout)** | Виджет не виден / смещен | `widget.show()`, проверка иерархии layout |
| **Блокировка цикла событий** | Зависание GUI | Перенести долгие операции в QThread |
| **Сборка мусора** | Виджет внезапно исчезает | Сохранять ссылку как `self.widget` |

### Вспомогательные функции отладки PyQt6

```python
# Дамп иерархии виджетов
def dump_widget_tree(widget, indent=0):
    print(" " * indent + f"{widget.__class__.__name__}: {widget.objectName()}")
    for child in widget.findChildren(QWidget):
        if child.parent() == widget:
            dump_widget_tree(child, indent + 2)

# Отладка сигналов
from PyQt6.QtCore import QObject
original_connect = QObject.connect
def debug_connect(self, *args, **kwargs):
    print(f"CONNECT: {self.__class__.__name__} -> {args}")
    return original_connect(self, *args, **kwargs)
```

---

## Быстрая справка

```
ОШИБКА НАЙДЕНА?
     |
     v
[Фаза 1: Быстрые проверки]  ───── Очевидно? -> ИСПРАВИТЬ
     |
     v
[Фаза 2: Диагностика]  ────────── Причина ясна? -> Фаза 4
     |
     v
[Фаза 3: Изолированный тест]  ── Воспроизводимо? -> Фаза 4
     |                                |
     |                           Не воспроизводится?
     |                                |
     |                           Добавить логирование,
     |                           ждать повторения
     v
[Фаза 4: Исправление]  ────────── Минимальное + понятное
     |
     v
[Фаза 5: Верификация]  ───────── Тесты пройдены? -> Фаза 6
     |                                |
     |                           Тесты провалены? -> Назад к Фазе 4
     v
[Фаза 6: Документирование]  ──── Отчет об ошибке + коммит
```

### Правило 20 минут

Если вы застряли через 20 минут:

1. **Измените подход** — Попробуйте другой метод отладки
2. **Метод утенка** — Объясните проблему вслух (или запишите ее)
3. **Сделайте перерыв** — Отодите на 5 минут, вернитесь со свежим взглядом
4. **Обратитесь за помощью** — Спросите коллегу, проверьте Stack Overflow или документацию
5. **Сброс** — `git stash`, начните с чистого листа
