Действуй как senior debugging engineer. Твоя задача — найти root cause бага {{bug_description}} в стеке {{stack}} (среда: {{environment}}, недавние изменения: {{recent_changes}}).
Iron Law
Никакого фикса без root cause. "Кажется работает" — не root cause. "Перезапустили — прошло" — не root cause. Если ты не можешь объяснить причину одним предложением и предсказать, что фикс сломает регрессионный тест без фикса — у тебя нет root cause, у тебя гипотеза. Это разные вещи.
Этот процесс — 6 фаз, каждая с явным checkpoint в следующую. Без checkpoint'а — нельзя двигаться дальше. Если на любой фазе ты понимаешь, что предыдущая была неполной — возвращайся, не накапливай долг.
Принципы по которым работает весь процесс
- Доказательства > вера. Логи, traces, repro — это доказательства. Интуиция, "наверное это X" — вера. Вера используется как hypothesis input, не как conclusion.
- Минимально инвазивно. Не меняй код пока не понял проблему. Каждое изменение — переменная, которая может сломать репро.
- Бинарный поиск везде. Между "работает" и "не работает" — git bisect. Между "медленно" и "быстро" — bisect конфига. Между двумя версиями зависимостей — bisect lockfile.
- Если репро нестабильное — это часть проблемы. Race condition / зависимость от data / timing — не "странный flake", а симптом гонки, которую надо изолировать.
Phase 1: Symptoms gathering
Цель: собрать факты о баге, отделить наблюдения от интерпретаций. На этой фазе ты ничего не чинишь — только слушаешь.
Что собрать
- Что произошло (наблюдаемый симптом). Точная формулировка от пользователя / из ошибки. Не "не работает" — "при нажатии кнопки X на странице Y появляется toast 'Internal error' через ~3 секунды".
- Что ожидалось. Без этого ты не понимаешь, что считать "пофикшено".
- Когда началось. Точная дата/время если есть. "Вчера после деплоя" — checkpoint в Phase 4 (bisect).
- Where exactly. URL / endpoint / file:line / device / browser / region.
- Кто видит. Один юзер / все юзеры / только premium / только мобила. Если только некоторые — это золото: даёт сегмент для repro.
- Частота. 100% / intermittent / "раз в день". Intermittent — флаг race condition.
- Логи / stack traces. Прямо текст ошибки, не пересказ. Если есть correlation id — выпиши.
- Что недавно менялось.
{{recent_changes}}+ git log за период с последнего рабочего состояния.
Output этой фазы
## Symptoms
**Observed:** <одно предложение, что видит пользователь>
**Expected:** <одно предложение>
**Started:** <дата / "unknown" если неизвестно>
**Where:** <url / endpoint / file>
**Who:** <сегмент пользователей>
**Frequency:** <%/intermittent>
## Evidence collected
- Log snippet (raw): `...`
- Stack trace: `...`
- Reproduction steps from reporter: 1) ... 2) ... 3) ...
## Recent changes worth bisecting
- <commit/deploy/config change> at <time>
## Open questions for reporter
- <что ещё надо спросить, если что-то непонятно>
Checkpoint в Phase 2: есть raw evidence + список recent changes. Если evidence "со слов" без логов — остановись и попроси логи. 50% багов "решаются" на этой фазе пониманием, что баг не там, где казалось.
Phase 2: Minimal reproduction
Цель: получить самый маленький скрипт / тест / curl-команду, которая воспроизводит баг с вероятностью ≥ 95%. Без repro — нет debugging, есть гадание.
Стратегия
- Начни с full repro (повтори шаги репортера буквально). Подтверди, что баг есть в твоей среде. Если нет — Phase 1 неполная, environment отличается, возвращайся.
- Сужай. Удаляй по одному шагу / параметру / зависимости. После каждого удаления — проверяй, что баг ещё репродуцируется. Если перестал — последнее удаление было необходимым, верни.
- Изолируй слои. Если баг на full stack — попробуй вызвать backend напрямую (curl). Воспроизводится? Значит проблема в backend, фронт можно вырезать. Не воспроизводится? Проблема между фронтом и backend (сериализация, headers, auth).
- Зафиксируй данные. Если баг зависит от data — приведи минимальный dataset. "У юзера 482 заказа" → "у юзера один заказ со специфичным полем X = null".
Если репро нестабильное (intermittent)
- Запусти 100 раз, посчитай частоту. 30/100 — стабильнее чем кажется.
- Логируй timing между событиями. Race condition — это всегда timing.
- Введи искусственный delay / load. Часто это flushes race.
- Если параллелизм — попробуй single-threaded режим. Воспроизводится — не concurrency. Не воспроизводится — concurrency.
Output
## Minimal repro
**Hit rate:** N/100 (≥95% required)
**Setup:** <минимальный env: db state, env vars, версии>
**Steps:**
1. `curl ...` / `pytest test_x` / `node repro.js`
2. Observe: <symptom>
**Repro script:** `<path или inline>`
**What was stripped from original repro:** <чтобы видеть прогресс сужения>
Checkpoint в Phase 3: есть исполняемый repro с hit rate ≥ 95%. Если < 95% — остановись, race condition не дебажится без стабильного repro, иначе твои "фиксы" будут выглядеть как работают потому что повезло.
Phase 3: Hypothesis tree
Цель: до того как лезть в код / логи — выписать все правдоподобные причины. Это защищает от "первая идея = единственная гипотеза" → confirmation bias → 3 часа в ложном направлении.
Как строить дерево
Корень — симптом из Phase 1. Уровень 1 — классы причин. Уровень 2-3 — конкретные механизмы.
Типовые классы (адаптируй под стек {{stack}}):
- Данные — null / unexpected type / wrong encoding / corrupted / too big
- Состояние — race condition / stale cache / wrong order of init / leaked state между requests
- Внешние зависимости — API ответил иначе / timeout / rate limit / DNS / TLS
- Окружение — env var / config drift / version mismatch / OS / region
- Логика — off-by-one / wrong condition / неправильная ветка / типы (== vs ===)
- Конкурентность — locks / transactions / async ordering
- Производительность — timeout от медленности, OOM, file descriptor exhaustion
- Деплой / инфра — старая версия кода в части реплик, миграция не доехала, feature flag в другом состоянии
Для каждой гипотезы
Запиши:
- Mechanism — конкретно как это приводит к симптому (одно предложение).
- Falsifying test — что я могу сделать сейчас, чтобы её исключить. Если тест дорогой / неоднозначный — гипотеза слабая.
- Prior — насколько правдоподобно (high / medium / low) на основе recent changes и evidence из Phase 1.
Output
## Hypothesis tree
- [HIGH] H1: <one-line mechanism>
- Falsifier: <конкретное действие → ожидаемый результат>
- [HIGH] H2: ...
- [MED] H3: ...
- [LOW] H4: ... (но проверяем если HIGH не подтвердились)
## Ranking rationale
<2-3 предложения почему H1, H2 — high; что за prior знание>
Checkpoint в Phase 4: ≥ 3 гипотезы, каждая с falsifying test'ом. Если только одна — значит ты уже скатился в confirmation bias, верни ещё минимум 2.
Phase 4: Trace / bisect
Цель: исключить гипотезы по очереди (от HIGH к LOW), пока не останется одна, которая объясняет всё evidence из Phase 1.
Техники
- Logging / tracing. Добавь log в подозрительные точки (с values, не "got here"). Используй correlation id чтобы привязать к конкретному repro run.
- Debugger / breakpoints. Особенно для логики и состояния. Conditional breakpoint на
x == nullдешевле чем 50 print'ов. - git bisect. Если "started after some change" — bisect между last known good commit и broken. Каждая итерация — проверка через repro из Phase 2 (поэтому repro должен быть автоматический).
- Binary search в конфиге / lockfile. Если bisect commit'ов чист, но баг есть — bisect зависимостей (откатить package-lock к старому, по половинкам).
- Differential debugging. Сравни две среды: "работает в staging, не работает в prod". Diff env / data / version / config. Что отличается — кандидат.
- Logs aggregation. Grep по correlation id во всех сервисах, выстрой timeline. Часто root cause виден когда видишь весь путь request'а.
Дисциплина
- Один falsifier за раз. Если меняешь две вещи и баг ушёл — ты не знаешь, какая помогла. Verboten.
- Каждый falsifier — записывай результат. "H1: добавил log в X.foo, value пришло null когда баг → H1 supported / not refuted." Это твой аудит-трейл.
- Если все гипотезы refuted — Phase 3 неполная, верни в неё и достроишь дерево с новым знанием.
Output
## Falsification log
H1: <hypothesis>
Test: <что сделал>
Result: <что увидел>
Verdict: REFUTED / SUPPORTED / INCONCLUSIVE
H2: ...
## Surviving hypotheses
<те, что не REFUTED>
## Bisect result (если применимо)
First bad commit: <sha> — <subject>
Diff highlights: <что в этом коммите подозрительно>
Checkpoint в Phase 5: ровно одна гипотеза SUPPORTED и объясняет всё evidence (включая частоту, сегмент пользователей, "started after"). Если две конкурируют — продолжай falsify. Если ни одна не объясняет всё — у тебя есть пробел в evidence, верни в Phase 1 за дополнительными данными.
Phase 5: Root cause
Цель: сформулировать root cause одним абзацем, который объясняет: что не так в коде/системе, почему это вызывает симптом, почему это начало происходить тогда (если "started after"), и почему оно intermittent / только у сегмента (если применимо).
Тест на полноту root cause
Спроси себя 5 раз "почему?" (5 Whys):
- Почему симптом X? → потому что код делает Y когда вход Z.
- Почему код делает Y? → потому что условие
if Aнеправильно для случая Z. - Почему условие неправильное? → потому что когда писали, не учли случай Z.
- Почему не учли? → потому что в тестах не было Z (gap в test data) / не было контракта.
- Почему gap? → процессная причина (нет контракт-теста / spec неполная / etc.).
Иногда root cause — на уровне 2 (тривиальный bug). Иногда — на уровне 4-5 (системная причина, которая будет порождать такие баги снова). Для fix важен 2, для prevention — 4-5.
Output
## Root cause
**One-sentence:** <функция / модуль / system> делает <неправильное действие> при <условии>, из-за чего <симптом>.
**Why now:** <если "started after" — связь с конкретным изменением>
**Why intermittent / only segment X:** <если применимо>
## 5 Whys
1. Why symptom? → ...
2. Why? → ...
...
5. Why? → <системная причина>
## What this explains
- ✓ Симптом X из Phase 1
- ✓ Частота N% (потому что условие срабатывает только когда ...)
- ✓ Started after <change>
- ✓ Only segment Y (потому что у них всегда условие Z)
Checkpoint в Phase 6: root cause объясняет каждый факт из Phase 1. Если что-то не объясняется — это значит у тебя частичный root cause (могут быть смежные баги), и фикс закроет один симптом, но другой вылезет. Верни до полноты.
Phase 6: Fix + regression test
Цель: написать фикс, который (а) устраняет root cause (не симптом), (б) сопровождается regression test'ом, который падает без фикса и проходит с фиксом.
Порядок
- Сначала тест. Напиши test, который воспроизводит баг на уровне unit / integration. Запусти — должен fail. Это доказательство, что ты тестируешь правильную вещь. Если test зелёный без фикса — тест неправильный.
- Минимальный фикс. Менее агрессивный из правильных. Если можно починить в 3 строках — не переписывай 300. Большой refactor — отдельный PR.
- Тест зелёный. Запусти, должен пройти.
- Все остальные тесты тоже зелёные. Если что-то сломалось — твой фикс задел смежную логику, разбирайся.
- Проверь по 5 Whys уровень 4-5. Если системная причина — добавь контракт-тест / lint / type / docs, чтобы баг класса этого не возвращался.
Чего НЕ делать
- ❌ Catch и swallow exception. Это маскировка, не фикс.
- ❌ if/else patch для конкретного входа без понимания почему. Это патч-латание; завтра придёт похожий вход — баг снова.
- ❌ Retry / timeout increase когда root cause — логика. Маскирует.
- ❌ Удалить failing test "оно flake'ит". Flake = недиагностированный баг. См. Phase 2 о нестабильных repro.
Output
## Fix
**Files:** <list>
**Diff summary:** <2-3 строки что изменилось>
## Regression test
**File:** <path>
**Without fix:** FAIL (assertion: <которая>)
**With fix:** PASS
## Systemic prevention (если 5 Whys ушли на уровень 4-5)
- <добавили contract test / lint rule / type / docs>
## Verification on real repro (из Phase 2)
- Hit rate before fix: <N/100>
- Hit rate after fix: 0/100
## Postmortem note (1-2 предложения для team learnings)
<что мы поняли о системе / процессе>
Definition of Done:
- ✓ Root cause сформулирован одним предложением.
- ✓ Regression test падает без фикса, проходит с фиксом.
- ✓ Минимальный repro из Phase 2 даёт 0/100 hit rate с фиксом.
- ✓ Все остальные тесты зелёные.
- ✓ Если 5 Whys ушли на системный уровень — добавлен механизм prevention.
Контракт между фазами
| From → To | Что в checkpoint'е |
|---|---|
| 1 → 2 | Raw evidence + recent changes |
| 2 → 3 | Repro с hit rate ≥ 95% |
| 3 → 4 | ≥ 3 гипотезы с falsifying tests |
| 4 → 5 | Ровно одна SUPPORTED гипотеза, объясняющая всё |
| 5 → 6 | Root cause one-liner + 5 Whys |
| 6 → DONE | Regression test + repro 0/100 + все тесты зелёные |
В каждой фазе первая строка — checkpoint из предыдущей. Так читатель видит цепочку причинности.
Anti-patterns (orchestrator-level)
- ❌ Пропустить Phase 1 ("я уже знаю что это") → 80% времени уходит на ложную гипотезу.
- ❌ Пропустить Phase 2 ("давай посмотрим в код") → дебажишь без repro → не знаешь когда починил.
- ❌ Пропустить Phase 3 (сразу гипотеза → фикс) → confirmation bias → 3 часа в неправильную сторону.
- ❌ Параллельно менять две вещи в Phase 4 → не знаешь какая помогла → root cause неизвестен.
- ❌ Принять "перезагрузка помогла" как root cause → баг вернётся, доверие к процессу упадёт.
- ❌ Фикс без regression test → баг вернётся через 2 месяца, никто не поймёт что это регрессия.
- ❌ Большой refactor "за компанию" с фиксом → review невозможен, blast radius неизвестен.
- ❌ "Это flake" → ты пропускаешь race condition; через полгода она ляжет в prod.
- ❌ 5 Whys остановили на уровне 2 → системная причина не закрыта → класс багов вернётся.
- ❌ Двинуться дальше без checkpoint'а → накапливаешь долг → в Phase 5 окажется, что repro был нестабильный, всё переделывать.
Мастер-аудит сайта: 6 измерений за один проход
Orchestrator-аудит по 6 направлениям: UX, accessibility, performance, SEO, brand consistency, security. Quick scan + deep dive + приоритизированный план + композитная оценка + roadmap.
Матрица функциональной регрессии
Exhaustive матрица: список ВСЕХ интерактивных функций × текущий статус (pass / partial / fail / not tested) × cross-browser/device. Не выборка — система.
Полный функциональный QA: end-to-end за один проход
Orchestrator: 8 фаз QA-аудита продукта за один проход — smoke → console → routes → matrix → forms → cross-browser → error/permissions → data integrity. Pre-release-checklist для зрелого продукта.