Баг-репорт на русском в два слоя: суть для аналитика бизнес-языком, затем полный репорт для разработчика — предусловия, шаги, ожидаемый и фактический результат, причина, место в коде, трейс и логи, масштаб находки. Используй всегда, когда просят «оформи баг», «напиши баг-репорт», «заведи дефект», «опиши находку», «что написать разработчику», и перед отправкой любой находки в трекер или чат. English: bug report, defect report, steps to reproduce.
Installs into .claude/skills of the current project.
Are you the author of Bug Report?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/konstantinvch-bug-report)
---
name: bug-report
description: "Баг-репорт на русском в два слоя: суть для аналитика бизнес-языком, затем полный репорт для разработчика — предусловия, шаги, ожидаемый и фактический результат, причина, место в коде, трейс и логи, масштаб находки. Используй всегда, когда просят «оформи баг», «напиши баг-репорт», «заведи дефект», «опиши находку», «что написать разработчику», и перед отправкой любой находки в трекер или чат. English: bug report, defect report, steps to reproduce."
license: MIT
compatibility: "Ничего обязательного: скилл формирует текст. Опционально python3 и доступ к API Jaeger для разбора WARN/ERROR трейса (scripts/trace_warnings.py), curl для проверки ссылок на git."
metadata:
author: KonstantinVCH
version: "1.1.0"
language: ru
---
# Баг-репорт в два слоя
Репорт читают два разных человека. Аналитику и менеджеру нужно за десять секунд понять, что
сломано, чем это плохо и почему должно быть иначе, — без классов, трейсов и номеров строк.
Разработчику нужно воспроизвести дефект и найти место в коде. Если смешать оба слоя, первый
читатель тонет в коде, а второй не находит шагов. Поэтому репорт пишется сверху вниз: сначала
слой для человека без кода, под ним — слой для разработчика.
Формат сложился из итераций с живыми читателями: каждая отвергнутая версия превратилась в
правило ниже, реальные случаи приведены рядом.
## Настройка под проект
Шаблоны ссылок на git, трейсинг и логи, адреса стендов, название системы спецификаций и чата
команды собраны в `references/project-config.md`. Заполни один раз; до этого используй
плейсхолдеры `{BITBUCKET_URL}`, `{JAEGER_URL}`, `{OPENSEARCH_URL}`, `{STAND}`.
---
## Когда какой формат
| Ситуация | Формат |
|---|---|
| Баг / дефект реализации | **Слой 1 + слой 2** (ниже) |
| Вопрос аналитику, расхождение документации и кода | Короткий: что вижу → как в документации → что предлагаю. Без шагов, бизнес-языком |
| Ответ в чате «что нашёл» | Только слой 1 |
| Находка, у которой нет корня или контрольного позитива | Наблюдение: что вижу / чем плохо / что предлагаю, с явной пометкой HYPOTHESIS. Баг не заводить |
Если спека декларирует поведение, которого система не умеет, — это не баг кода, а
несогласованный контракт. Адресат — аналитик, а не разработчик.
---
## Слой 1 — для аналитика и менеджера
4–6 строк. Бизнес-язык, без кода, трейсов, номеров строк и кодов ошибок. Термины — из задачи,
спеки и речи команды, а не из имён классов.
```
**<ЧТО ломается — при каком условии>**
Что вижу: <наблюдаемое поведение словами пользователя или бизнеса>
Чем плохо: <последствие: кто страдает, что теряется>
Как должно быть: <ожидаемое> — по <спека, раздел>
Масштаб и что делать: <сколько случаев сейчас; фикс / бэклог / вопрос аналитику>
```
Проверка слоя 1: поймёт ли человек, не видевший твоей сессии, суть по заголовку и первым трём
строкам? Нет — переписать.
---
## Слой 2 — для разработчика
```
**BUG. <ЧТО именно ломается> — <ГДЕ: сервис/метод> — <при каком условии>**
**Предусловия**
- <стенд, ветки задачи>
- <моки/флаги/состояние, без которых не воспроизвести>
**Шаги воспроизведения**
1. <действие с конкретным URL и ключевым полем тела>
2. <проверка промежуточного состояния — БД/очередь>
3. <действие, вызывающее дефект>
4. <где смотреть результат>
**ОР** — <ожидаемое, дословно/кодом>
Основание: <ссылка на спеку + раздел>, <ссылка на эталонный метод>, <схема/контракт>.
**ФР** — <фактическое, дословные значения>
**Причина:** <одна строка, человеческим языком>
**Fix:** <одна строка, с git-ссылкой на место>
<кликабельный трейс> · не тест-данные: <чем доказано>
```
Развёрнутая форма «Что вижу / Когда / Как должно быть / Похоже, причина / Чем кончается / Fix»,
формат комментария разработчику в Jira и разбор заголовков — в `references/developer-formats.md`.
---
## Жёсткие правила
1. **ОР всегда со ссылкой на источник.** «Должно быть с часовым поясом» — недостаточно. Нужно
«по [спеке], раздел П_09_02» или «в эталонном методе для партнёра A так-то». Без основания
разработчик не понимает, почему вообще должно быть иначе. *(Реальная претензия читателя:
«непонятно, почему должно быть иначе, нет ссылок на доку».)*
2. **Ссылок на документацию минимум две, лучше три:** спека самой задачи, эталонный или
аналогичный метод, формальный контракт (JSON-схема, XSD, OpenAPI).
3. **Каждый упомянутый файл, класс, метод и строка — кликабельная ссылка на git, а не путь
текстом.** Это касается и «Причины», и «Fix», и ФР. `src/main/resources/application.yml` —
плохо; `[application.yml:137](<ссылка на файл в ветке, строка 137>)` — хорошо. Шаблоны для
Bitbucket Server, GitLab и GitHub — в `references/project-config.md`. Ветку указывай всегда:
без неё открывается основная ветка, и разработчик увидит не тот код. Перед отправкой проверь,
что ссылка отдаёт 200:
`curl -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $BITBUCKET_TOKEN" "<url>"`.
*(Реальная претензия: путь в Fix был написан текстом — «почему нет реальной ссылки на гит?
исправь навсегда».)*
4. **ОР и ФР — дословные значения**, а не пересказ. Копируй реальные строки из тела запроса или
лога.
5. **Первый экран — без кода.** Классы, методы и аннотации маппинга уходят в «Причину»/«Fix»
одной строкой либо под спойлер. Заголовок и первые строки должны читаться человеком, не
знающим код. Именно поэтому слой 1 идёт первым.
6. **Кратко.** Слой 2 помещается на один экран. Не помещается — техдетали под `<details>`.
*(Претензия на развёрнутую версию: «теперь кратко!!!!».)*
7. **Строка про тест-данные обязательна.** Одной фразой: чем доказано, что значение
сгенерировано системой, а не подставлено тестировщиком.
8. **Трейс кликабельный**, и перед отправкой проверь, что в нём видно именно утверждаемое
(Jaeger: `{JAEGER_URL}/trace/<traceId>?uiFind=<spanId>` — сразу на нужный спан). **То же для
логов: упомянул лог — дай ссылку на лог, всегда.** Две ссылки: на саму запись (Kibana/OpenSearch
Dashboards: `.../app/discover#/doc/<index-pattern-id>/<index>?id=<_id>`, `_id` и `_index` — из
ответа поиска) и на выборку (discover с фильтрами namespace + pod/container, запрос
`level:"ERROR"`, **абсолютное** время `time:(from:'…Z',to:'…Z')` — относительное `now-24h`
протухает через сутки). Перечислить фильтры текстом вместо ссылки нельзя. Перед отправкой
открой ссылку и убедись, что цитируемая запись видна.
9. **Заголовок = ЧТО — ГДЕ — при каких условиях.** Без причины, кода ошибки и fix одновременно.
Разбор атомарности полей заголовка — в `references/developer-formats.md`.
10. **Разбери ВСЕ WARN и ERROR трейса и назови их в репорте.** Формулировка «ошибок нет»
допустима, только если реально просмотрены логи всех спанов. Каждый WARN классифицируй одной
строкой: относится к дефекту / нормальное поведение при этих условиях / отдельная находка.
Молчание про WARN читается как «не смотрел» и обнуляет доверие ко всему репорту.
Формат строки:
```
Трейс <ссылка> — 12 спанов, ERROR нет; 2 WARN не по теме
(«Product for complete … not found» — первая загрузка товара;
«Schema is not found for ProductPublishedEvent»).
```
Снять WARN/ERROR всех спанов: `python3 scripts/trace_warnings.py <traceId>` (берёт трейс из
`JAEGER_URL`) или `python3 scripts/trace_warnings.py --file trace.json`.
*(Реальный случай: написал «12 спанов, ошибок нет», пропустив два WARN. Читатель открыл лог
сам: «я тебе не верю, перепроверь себя». Один из WARN выглядел так, будто флоу отработал не
полностью, — пришлось объясняться постфактум.)*
11. **Перед отправкой ответь себе: этот дефект в скоупе задачи?** Найденное в ходе тестирования
задачи — ещё не дефект этой задачи. Проверка в три шага:
1. **Менялся ли этот код в PR задачи?** Открой diff всех PR. Файла там нет — поведение
унаследованное.
2. **Специфично ли оно для нашего партнёра/флоу?** Если тот же код обслуживает других
партнёров и они получают то же самое — проблема платформенная.
3. **Требовала ли постановка это изменить?** Прямой пункт скоупа или «сделать по аналогии с
эталоном» — в скоупе. Ничего не требовала — вне.
Вывод пиши с обоснованием, а не ярлыком:
```
В скоупе: задача требовала «аналогично интеграции с партнёром A»; у эталона преобразование
даты есть (DateMapper.setTimezone(3 * 60)), в новом адаптере его не написали.
Вне скоупа: PR задачи этот маппер не трогали; статус формируется общим кодом, остальные
партнёры получают то же самое — поведение существует до задачи.
```
Зачем: дефект вне скоупа, поданный как дефект задачи, блокирует приёмку из-за древнего
поведения платформы и подставляет разработчика, который к нему не причастен. Такие находки
оформляются отдельно — вопросом аналитику или в бэклог.
*(Реальный случай: статус обработки никогда не принимал одно из значений — доказано живьём,
но маппер общий, PR задачи его не касались: «вне скоупа, запомни и идём дальше». А отсутствие
конвертации дат — в скоупе: постановка требовала копию эталона.)*
12. **Утверждение о последствиях проверяй так же, как утверждение о дефекте.** Правило «факты
важнее гипотез» обычно применяют к самой находке, а мимо проходят фразы вокруг неё: «это
сломает X», «будет конфликт», «эталон требует Y», «метод не реализован». Они читаются как
факт, по ним принимают решения — проверять их надо до отправки, а не после вопроса «ты
уверен?». **Утверждение проверяется одной командой — проверь.**
| Утверждение | Чем проверяется |
|---|---|
| «будет конфликт при слиянии» | `git merge-tree --write-tree A B` — код возврата и дерево |
| «файлы в двух ветках разные» | `git rev-parse A:путь` против `B:путь` — сравнение blob |
| «эталон требует такой формат» | открыть страницу спеки и прочитать раздел, а не помнить |
| «метод не реализован на той стороне» | это чужое утверждение — замерить или назвать источник |
| «сообщение потеряется» | счётчики очереди до/после: `publish`, `ack`, `redeliver` |
| «переменную можно переопределить» | найти её в шаблоне конфига, а не предполагать |
Проверить нельзя — пиши не «сломается», а «не проверял, риск такой-то». «Скорее всего» в
тексте для разработчика или аналитика недопустимо: либо факт с доказательством, либо явная
пометка HYPOTHESIS.
**Механика находки — ещё не её значимость.** Доказанный механизм без масштаба превращается в
ложную тревогу. Находка подаётся связкой из трёх частей, третья обязательна:
1. **механизм** — что сломано, доказано кодом и прогоном;
2. **масштаб** — на скольких реальных данных срабатывает **сейчас** и может сработать позже;
данных нет — так и пиши;
3. **что делать** — фикс, бэклог или ничего.
Признаки раздувания: «любой X даст Y», хотя таких X в данных нет; технический долг вынесен в
находки; масштаб выясняет не автор, а вопросы читателя; тревожный тон при нуле случаев.
Правильная формулировка при нулевом масштабе: «дефект есть, ущерба нет и не предвидится — в
бэклог, задачу не блокирует».
*(Реальные случаи. Первый: защита от дублей не срабатывала для записей со старым написанием
названия — механика подтверждена кодом, миграцией и прогоном, подано как «любая активная
запись даст дубль». По факту: 54 записи, все закрытые, ноль дублей сейчас и в перспективе;
масштаб читатель вытаскивал двумя вопросами. Второй: формат дат подан дефектом со ссылкой на
маппинг другой ветки контракта — по репорту сделали фикс, развернувший уже закрытое решение
партнёра. Третий: «второй PR получит конфликт по всем десяти файлам» — симуляция слияния
вернула код 0, файлы побайтово совпали. Каждая проверка занимала одну команду.)*
13. **Строку лога привязывай к вызову, только если в ней есть его traceId или точное время.**
Строка без traceId и времени доказывает одно: такая запись в логе была. Чья она — из неё не
следует, особенно когда несколько вызовов трогали один и тот же объект. Поставленная в ФР под
конкретным вызовом, она читается как его след, и разработчик ищет причину не там.
- В строке есть traceId вызова или время, которое попадает только в окно этого вызова, — ставь
её в ФР рядом с ним.
- Нет ни того, ни другого — найди запись по traceId вызова в логах (скилл чтения логов) или подай
её отдельно: «в логах payment-service есть запись …; к какому вызову она относится, не
установлено».
*(Случай из прогона скилла на тестовом запросе: заказ отменили дважды, оба вызова создали
возврат. Строка «creating refund for order 1042» без traceId и времени встала в ФР сразу под
вторым вызовом, хотя могла прийти и от первого.)*
---
## Эталонный пример (принят читателями дословно; домен заменён на вымышленный)
```
**Даты публикации товара уходят партнёру без часового пояса**
Что вижу: при публикации товара партнёр X получает даты без часового пояса и в двух разных форматах.
Чем плохо: партнёр трактует время как своё локальное — расхождение до нескольких часов в витрине.
Как должно быть: дата с поясом +03:00, единый формат — по спеке партнёра X, пример П_09_02.
Масштаб и что делать: каждая публикация через новый адаптер; фикс в рамках задачи.
---
**BUG. Колбэк о публикации: даты уходят партнёру без часового пояса и в разных форматах**
**Предусловия**
- Стенд {STAND}, ветки `PROJ-123`: `catalog-service`, `partner-x-adapter`
- Адаптер задеплоен в режиме мока (колбэк идёт на WireMock, т.к. партнёр метод ещё не реализовал)
- На моке стаб `POST /catalog/response` → 200
**Шаги воспроизведения**
1. Загрузить каталог: `POST {GATEWAY_URL}/partner-x-adapter/openapi/catalog/upload`,
в теле `"creation": "2026-08-18T14:30:00"` → в ответе `batchId`
2. Проверить БД: `catalog.preactivation_product` → строка с `source=PARTNER_X`, `status=CREATED`
3. Активировать товар: `POST {CATALOG_SERVICE_URL}/openapi/pre-activation/product/activate`
с `externalProductCode`, `batchId`, `publicationStatus=PUBLISHED`
4. Посмотреть тело запроса, ушедшего партнёру, — журнал WireMock или лог HTTP-клиента в трейсе
**ОР** — дата с часовым поясом, единый формат обоих полей, без долей секунды:
"creation": "2026-08-18T14:53:34+03:00"
"received": "2026-08-18T14:53:05+03:00"
Основание: эталонная интеграция с партнёром A (спека, раздел М_09_01, «мск +03:00»), пример
П_09_02 в спеке партнёра X (с `Z`), схема `DateTime` (доли секунды запрещены).
**ФР** — зоны нет ни у одного поля, форматы разные:
"creation": "2026-08-18T14:53:34.866238"
"received": "2026-08-18T14:53:05"
**Причина:** у партнёра A дата пересобирается по пути к партнёру, у партнёра X копируется как есть.
**Fix:** конвертировать `creation` и `received` так же, как в `autoupdate-service` [ссылка на метод].
<трейс> · не тест-данные: слал `14:30:00`, ушло `14:53:34.866238`.
```
Почему пример хорош: слой 1 читается без кода; заголовок слоя 2 тоже; шаги воспроизводимы
дословно; ОР подкреплён тремя источниками; ФР — реальные строки; причина объяснена контрастом с
работающим аналогом; одна строка снимает подозрение в артефакте тест-данных.
---
## Внутренний критик (обязательный отдельный проход)
Черновик написан — работа не закончена. Перечитай репорт ещё раз как придирчивый читатель и
вычисти нейропаттерны: антитезы «не X, а Y», оценочные анонсы («важный нюанс»), дежурные связки
(«стоит отметить»), хеджирование («может приводить»), канцелярит («является», «данный»),
формульный финал, правило трёх, декор из эмодзи. Найденное переписывай, а не смягчай.
Полный список паттернов, запрещённые обороты, процедуру и то, чего критик НЕ трогает (числа,
тексты ошибок, имена полей, фактические отрицания), — в `references/neuro-critic.md`. Прочитай
его перед первым репортом в сессии.
---
## Чеклист перед отправкой
- [ ] Слой 1 есть, без кода и трейсов; суть понятна по заголовку и трём строкам
- [ ] Заголовок слоя 2: ЧТО — ГДЕ — условие. Без причины и fix внутри
- [ ] Шаги воспроизводимы человеком, который не видел мою сессию (реальные URL, ключевые поля)
- [ ] ОР со **ссылками** (≥2 источника)
- [ ] **Каждый файл/класс/строка — кликабельная git-ссылка с веткой и строкой, проверенная на 200** (включая Fix)
- [ ] ФР — дословные значения
- [ ] Причина отделена от симптома, fix — отдельной строкой
- [ ] Трейс приложен и проверен
- [ ] **Каждый упомянутый лог — ссылка на запись и на выборку с абсолютным временем, открыта и проверена**
- [ ] Строка лога стоит под вызовом, только если в ней его traceId или время (правило 13)
- [ ] **Все WARN/ERROR трейса просмотрены и классифицированы** — «ошибок нет» без просмотра запрещено
- [ ] Строка «не тест-данные»
- [ ] **Определён скоуп** — дефект задачи или унаследованное поведение; вывод с обоснованием
- [ ] **У находки назван масштаб**; нулевой масштаб пишется прямо
- [ ] **Каждое утверждение о последствиях проверено или помечено как непроверенное**
- [ ] Слой 2 помещается на экран
- [ ] **Пройден внутренний критик** — отдельным проходом, после написания текста
- [ ] Состояние стенда исходное, есть контрольный позитив или контраст с работающим аналогом
---
## Антипаттерны (все — реальные отказы читателей)
| Что сделал | Реакция | Правило |
|---|---|---|
| Начал репорт с аннотаций маппинга и имён классов | «человеконечитаемо» | Первый экран без кода, слой 1 первым |
| Написал «ожидается дата со смещением» без ссылок | «непонятно, почему должно быть иначе» | ОР всегда с основанием |
| Развернул на две страницы с таблицами и спойлерами | «теперь кратко!!!!» | Один экран |
| Дал 8 репортов списком, когда просили один | «дай конкретный, краткий» | Один репорт = одна находка |
| Утверждал дефект по значению, которое сам же прислал | — | Строка «не тест-данные» обязательна |
| Написал «12 спанов, ошибок нет», не открыв WARN | «я тебе не верю, перепроверь себя» | Разобрать и назвать все WARN/ERROR |
| Подал платформенное поведение как дефект задачи | «это проблема в рамках нашей задачи?» | Скоуп с обоснованием до отправки |
| Обосновал дефект маппингом другой ветки контракта | по репорту сделали фикс, развернувший закрытое решение партнёра | Проверять источник требования, а не помнить |
| Заявил «будет конфликт по всем файлам» без симуляции слияния | «перепроверь себя» — конфликтов не было | Утверждение о последствиях — командой |
| В Fix написал путь к файлу текстом | «почему нет реальной ссылки на гит?» | Каждый файл — git-ссылка с веткой |
Ложные баги из-за тест-данных и грязного стенда разобраны в `references/developer-formats.md`.
---
## Границы скилла
Скилл описывает **только текст репорта**. Готовый текст выдаётся в чат; куда его девать — в
Jira, в мессенджер или обсудить устно — решает пользователь. Ничего не публиковать, задачи и
комментарии не создавать и не редактировать без явной просьбы.
Связанные скиллы: `test-session-report` — итог всей сессии тестирования (после прогона);
этот скилл — одна находка (по ходу). `qa-task-testing` — полный QA-протокол задачи.
## Справка
- `references/developer-formats.md` — развёрнутая форма, комментарий разработчику, заголовки, ложные баги
- `references/neuro-critic.md` — внутренний критик: нейропаттерны
- `references/project-config.md` — шаблоны ссылок и настройки проекта
- `scripts/trace_warnings.py` — все WARN/ERROR трейса Jaeger по спанам