350 lines
20 KiB
Markdown
350 lines
20 KiB
Markdown
# 03. Quality Gates (QG)
|
||
|
||
**Назначение:** превратить «согласовать у Иванова» в машинно-проверяемые ворота между этапами. Без зелёного QG задача физически не может уйти на следующий этап — git hook и CI этого не позволят.
|
||
|
||
---
|
||
|
||
## Простым языком
|
||
|
||
Quality Gate — это шлагбаум на выходе с каждого этапа. Шлагбаум открывает не человек, а робот, который проверяет:
|
||
|
||
- лежат ли все нужные файлы там, где они должны лежать;
|
||
- проходят ли они формальную проверку (валидный YAML, заполненные секции, ссылки на оригиналы);
|
||
- зелёный ли CI;
|
||
- поставил ли кто-то нужный «штамп» (reaction `:approved:` в Plane от пользователя с правом утверждения).
|
||
|
||
Если что-то не так — робот не пускает дальше и возвращает задачу с конкретным списком замечаний. Никакого «договорились в чате», никакого «потом доделаем».
|
||
|
||
Это и есть главная защита от того, что агент **сам себе** поставит галочку «готово».
|
||
|
||
---
|
||
|
||
## Принципы QG
|
||
|
||
1. **Всё машинно-проверяемо.** Если критерий нельзя проверить скриптом или линтером — это не QG, а пожелание.
|
||
2. **Каждое QG имеет владельца.** «Кто чинит, если QG красный» — однозначно (см. таблицу ниже).
|
||
3. **QG не пропускается.** Нет режима «давайте этот раз без проверки». Если действительно есть исключительный случай — заводится отдельная процедура `qg-override` с явным человеческим approve и записью в audit-лог.
|
||
4. **Reactions — это допустимая форма подписи.** `:approved:` от пользователя с ролью `Stakeholder` в комментарии Plane или PR — валидный «штамп». Их собирает CI через API.
|
||
5. **Обратная совместимость не оправдание.** Если изменение требует апдейта `CLAUDE.md`, миграции, новой переменной окружения — она часть QG, не «потом».
|
||
6. **Время на QG ограничено.** Если QG висит красный больше SLA — эскалация в Plane.
|
||
|
||
---
|
||
|
||
## Сводная таблица всех QG
|
||
|
||
| QG | Между этапами | Чья ответственность | Чем проверяется | SLA до устранения |
|
||
|----|---------------|--------------------|-----------------|------------------|
|
||
| QG-0 | Inception → Analysis | Webhook handler | `plane-webhook-validator` | n/a (синхронно) |
|
||
| QG-1 | Analysis → Architecture | Analyst | `lint-spec.sh` + reaction-checker | 24h |
|
||
| QG-2 | Architecture → Design/Dev | Architect | `lint-adr.sh` + req-coverage | 24h |
|
||
| QG-3 | Design → Development | Designer | `lint-design.sh` + token-check | 24h |
|
||
| QG-4 | Development → Review | Developer | CI: lint+type+unit+integration+build | 8h |
|
||
| QG-5 | Review → Test | Reviewer | GitHub/Gitea API: approve + 0 unresolved | 4h |
|
||
| QG-6 | Test → Deploy | Tester | CI на preview: e2e + visual + a11y + perf | 8h |
|
||
| QG-7 | Deploy (test) → Deploy (prom) | Deployer/CI | smoke + healthcheck + user approval | 4h |
|
||
| QG-final | Done | Deployer/CI | uptime 10min + user `:approved:` финала | 1h |
|
||
|
||
---
|
||
|
||
## QG-0: Постановка → Анализ
|
||
|
||
**Что проверяет:** валидность Work Item в Plane.
|
||
|
||
**Технически:**
|
||
- `title` существует, длина 5–80 символов
|
||
- `description` существует, ≥3 предложений (≥150 символов)
|
||
- `project` валиден (есть в Plane)
|
||
- `priority` ∈ {low, medium, high, urgent}
|
||
- (опционально) `labels` соответствуют известному словарю (`area:*`, `type:*`)
|
||
|
||
**Реализация:** Plane webhook → `scripts/plane-webhook-validator.py`. При успехе — создаются ветка и подзадачи. При неуспехе — Work Item получает комментарий «не хватает X», статус `blocked`.
|
||
|
||
**Кто чинит:** заказчик (человек) дополняет Work Item.
|
||
|
||
---
|
||
|
||
## QG-1: Анализ → Архитектура
|
||
|
||
**Что проверяет:** артефакты этапа Анализа полны и согласованы.
|
||
|
||
**Машинные проверки (`scripts/lint-spec.sh` + `scripts/lint-test-plan.sh`):**
|
||
|
||
Обязательные файлы существуют:
|
||
- `docs/work-items/<id>/01-brd.md`
|
||
- `docs/work-items/<id>/02-trz.md`
|
||
- `docs/work-items/<id>/03-acceptance-criteria.md`
|
||
- `docs/work-items/<id>/04-test-plan.yaml`
|
||
|
||
Frontmatter валиден:
|
||
- `type` соответствует имени файла (`type: brd` для `01-brd.md` и т.д.)
|
||
- `plane_id` совпадает с папкой
|
||
- `status: approved` для всех
|
||
|
||
Семантические проверки:
|
||
- В `02-trz.md` каждое `REQ-` встречается ≥1 раз
|
||
- В `03-acceptance-criteria.md` для каждого `REQ-F-*` есть хотя бы один `AC-` со ссылкой `[REQ-F-N]`
|
||
- В `04-test-plan.yaml` для каждого `AC-` есть хотя бы один `TC-` (поле `coverage`)
|
||
- Все ссылки из frontmatter `related:` указывают на существующие файлы
|
||
|
||
Бизнес-проверки (от человека):
|
||
- На подзадаче «Анализ» в Plane стоит reaction `:approved:` от пользователя с ролью `Stakeholder`
|
||
|
||
**Реализация:** GitHub Action job `qg-analysis`, триггер — push в ветку `feature/<id>-*` ИЛИ комментарий в Plane с подписью.
|
||
|
||
**Что делать если красный:** агент-Analyst исправляет, делает новый коммит. CI пере-проверяет.
|
||
|
||
---
|
||
|
||
## QG-2: Архитектура → (Дизайн или Разработка)
|
||
|
||
**Что проверяет:** архитектурные решения зафиксированы и покрывают требования.
|
||
|
||
**Машинные проверки (`scripts/lint-adr.sh` + `scripts/req-coverage.py`):**
|
||
|
||
ADR-проверки:
|
||
- В `docs/work-items/<id>/06-adr/` есть хотя бы один файл `adr-NNNN-*.md`
|
||
- Каждый ADR имеет валидный frontmatter (`adr_id`, `status`, `date`, `authors`)
|
||
- Каждый ADR имеет секции: `## Context`, `## Decision`, `## Alternatives considered`, `## Consequences`
|
||
- `superseded_by` (если есть) указывает на существующий ADR
|
||
|
||
Покрытие требований:
|
||
- Скрипт `req-coverage.py` собирает все `REQ-` из ТЗ и проверяет, что для каждого:
|
||
- либо есть упоминание в ADR данной задачи,
|
||
- либо есть явная пометка в `06-adr/no-decision-needed.md` со списком таких REQ.
|
||
- Если есть «голые» REQ — QG красный.
|
||
|
||
Диаграммы:
|
||
- Все `.mmd` в `docs/architecture/` рендерятся без ошибок (Mermaid CLI).
|
||
|
||
UI-флаг:
|
||
- Если в ТЗ `ui_affected: true`, обязателен файл `09-ui-requirements.md`. Если `false` — Designer-этап автозакрывается с лейблом `skip:not-applicable`.
|
||
|
||
Инфраструктура:
|
||
- Если `07-infra-requirements.md` упоминает новые сервисы/переменные — `.env.example` и `docker-compose.yml` уже обновлены (CI проверяет diff).
|
||
|
||
**Реализация:** GitHub Action job `qg-architecture`. При зелёном — лейбл PR меняется на `stage:design` или `stage:dev` (в зависимости от UI-флага).
|
||
|
||
**Что делать если красный:** Architect добавляет недостающие ADR / уточнения.
|
||
|
||
---
|
||
|
||
## QG-3: Дизайн → Разработка (опциональный)
|
||
|
||
**Что проверяет:** дизайн полный и соответствует UI-требованиям.
|
||
|
||
**Машинные проверки (`scripts/lint-design.sh`):**
|
||
|
||
Файлы:
|
||
- `docs/work-items/<id>/11-design/wireframes.md`
|
||
- `docs/work-items/<id>/11-design/mockups.md`
|
||
- `docs/work-items/<id>/11-design/states.md`
|
||
- `docs/work-items/<id>/11-design/a11y.md`
|
||
|
||
Покрытие:
|
||
- Каждое UI-требование из `09-ui-requirements.md` упомянуто в `mockups.md` (по ID).
|
||
|
||
Состояния:
|
||
- В `states.md` для каждого экрана описаны минимум: `loading`, `empty`, `error`, `success`. Если какое-то состояние неприменимо — явная пометка `not-applicable: <причина>`.
|
||
|
||
Дизайн-токены:
|
||
- Линтер парсит `mockups.md` (если есть встроенные стили) и проверяет, что цвета/шрифты — только из `docs/design/design-tokens.json`. Любой произвольный hex/font — fail.
|
||
|
||
A11y чек-лист:
|
||
- В `a11y.md` все обязательные пункты отмечены (контраст, ARIA, клавиатурная навигация, focus order).
|
||
|
||
Бизнес-approve:
|
||
- Reaction `:approved:` от стейкхолдера на подзадаче «Дизайн» в Plane.
|
||
|
||
**Реализация:** GitHub Action job `qg-design`. Запускается, только если этап не `skip:not-applicable`.
|
||
|
||
**Что делать если красный:** Designer дорабатывает.
|
||
|
||
---
|
||
|
||
## QG-4: Разработка → Code Review
|
||
|
||
**Что проверяет:** код, собирается, тесты зелёные, документация обновлена.
|
||
|
||
**Машинные проверки (CI pipeline `ci.yml`):**
|
||
|
||
Сборка и линт:
|
||
- `make lint` — все линтеры (eslint, ruff, mypy, тип-чекеры) — без ошибок
|
||
- `make build` — успешная сборка
|
||
- Никаких новых TODO/FIXME в diff (linter `no-new-todos.sh`)
|
||
|
||
Тесты:
|
||
- `make test` — все тесты зелёные
|
||
- Покрытие: новый код имеет coverage ≥ 80% (`check-coverage.sh`)
|
||
- Coverage delta всего проекта ≥ 0% (`coverage-delta.sh` сравнивает с main)
|
||
|
||
Безопасность:
|
||
- `trivy` (контейнер): нет критичных CVE
|
||
- `bandit` (Python) или `npm audit` (JS): нет критичных
|
||
- secret-scan (gitleaks): нет утечек
|
||
|
||
Документация:
|
||
- `CHANGELOG.md` обновлён (есть запись для этой задачи)
|
||
- Если есть API-изменения — `docs/api/openapi.yaml` обновлён
|
||
- `CLAUDE.md` актуален (если изменился стек или команды)
|
||
|
||
PR-правила:
|
||
- Заполнен PR template (`.github/PULL_REQUEST_TEMPLATE.md`):
|
||
- ссылка на ТЗ ✓
|
||
- чек-лист DoD заполнен ✓
|
||
- заметка о breaking changes (даже если их нет — явное «нет») ✓
|
||
- Лейбл `stage:dev` стоит
|
||
- Размер PR ≤ 1500 строк diff (если больше — предупреждение, но не блокировка)
|
||
|
||
**Реализация:** GitHub Action `ci.yml` — обязательная проверка на PR.
|
||
|
||
**Что делать если красный:** Developer чинит.
|
||
|
||
---
|
||
|
||
## QG-5: Code Review → Test
|
||
|
||
**Что проверяет:** ревью прошло, нет открытых вопросов.
|
||
|
||
**Машинные проверки (Forge API через `scripts/check-review.sh`):**
|
||
|
||
- В PR хотя бы 1 review со статусом `APPROVED`
|
||
- Reviewer ≠ Developer (проверка через автора коммитов и автора review)
|
||
- 0 review-комментариев в статусе `unresolved`
|
||
- В `docs/work-items/<id>/12-review.md` есть запись с вердиктом `approved`
|
||
- Frontmatter `12-review.md` содержит:
|
||
- `reviewer_findings`: список (P0/P1 = blocker; P2/P3 — допустимы и описаны)
|
||
- `compliance_with_trz: true`
|
||
- `compliance_with_adr: true`
|
||
|
||
Если Reviewer-агент даёт `request-changes` — PR возвращается в `stage:dev`.
|
||
|
||
**Реализация:** GitHub Action `qg-review` запускается на event `pull_request_review`.
|
||
|
||
**Что делать если красный:** Developer вносит правки.
|
||
|
||
---
|
||
|
||
## QG-6: Тестирование → Внедрение
|
||
|
||
**Что проверяет:** полный регресс на preview-окружении, включая UI.
|
||
|
||
**Машинные проверки (CI workflow `preview.yml` + `qg-test.yml`):**
|
||
|
||
Окружение:
|
||
- Preview-окружение поднялось из текущей ветки (Docker Compose в CI)
|
||
- Healthcheck preview-сервиса зелёный
|
||
|
||
Функциональные тесты:
|
||
- Все unit/integration ещё раз зелёные
|
||
- Все e2e (Playwright) зелёные
|
||
- Все TC из `04-test-plan.yaml` запущены (по `automation.tool` и `automation.file`)
|
||
|
||
UI-тесты:
|
||
- Visual regression: 0 нерассмотренных diff'ов (либо явное обновление baseline в коммите)
|
||
- a11y (axe-core): 0 нарушений уровня A и AA
|
||
- Cross-browser: e2e прошли в Chromium, Firefox, WebKit
|
||
|
||
Производительность (если есть NFR в ТЗ):
|
||
- p95 latency не превышает порог из ТЗ
|
||
- Lighthouse score (для UI) ≥ согласованного
|
||
|
||
Безопасность:
|
||
- Trivy / npm audit на собранном образе — нет критичных
|
||
- Базовая OWASP-проверка через ZAP baseline (если применимо)
|
||
|
||
Артефакты:
|
||
- `docs/work-items/<id>/13-test-report.md` создан, frontmatter `verdict: pass`
|
||
- Скриншоты сохранены в `13-test-report/screenshots/`
|
||
- Логи CI прикреплены к PR
|
||
|
||
Баги:
|
||
- Если найдены — заведены в Plane с лейблом `bug:found-by-qa`, привязаны к Work Item parent
|
||
|
||
**Реализация:** GitHub Action `qg-test.yml`, триггер — лейбл `stage:test`.
|
||
|
||
**Что делать если красный:** Tester заводит баги, PR возвращается в `stage:dev`. После фикса — снова QG-4 → QG-5 → QG-6.
|
||
|
||
---
|
||
|
||
## QG-7: Внедрение в test → Внедрение в prom
|
||
|
||
**Что проверяет:** деплой в test прошёл корректно, smoke на test зелёный, есть человеческий approve.
|
||
|
||
**Машинные проверки (`deploy-test.yml` + `qg-deploy-test.sh`):**
|
||
|
||
Деплой:
|
||
- merge в `main` выполнен (squash или rebase согласно проекту)
|
||
- tag `v<X.Y.Z>` создан (semver на основе типа commit'а)
|
||
- CI задеплоил в test-окружение без ошибок
|
||
- Healthcheck test-окружения зелёный 5 минут после деплоя
|
||
- Smoke-тесты на test зелёные (минимальный набор из `tests/smoke/`)
|
||
|
||
Approve:
|
||
- В Plane на подзадаче «Внедрение» стоит reaction `:approved:` от пользователя с ролью `Stakeholder` (deployment approval)
|
||
|
||
**Реализация:** GitHub Action `deploy-test.yml`, далее ждёт approval-event из Plane.
|
||
|
||
**Что делать если красный:** Deployer-агент анализирует deploy log, при тривиальной проблеме — фикс и retry. При нетривиальной — эскалация (issue в Plane, лейбл `incident`).
|
||
|
||
---
|
||
|
||
## QG-final: prom → Done
|
||
|
||
**Что проверяет:** prom стабилен после деплоя.
|
||
|
||
**Машинные проверки (`deploy-prom.yml` + `qg-final.sh`):**
|
||
|
||
Деплой:
|
||
- CI задеплоил в prom без ошибок
|
||
- Healthcheck prom-окружения зелёный 10 минут после деплоя
|
||
- Smoke-тесты на prom зелёные
|
||
- Метрики: error rate, latency не выросли больше чем на согласованный порог за 10-минутное окно
|
||
- Нет открытых алёртов в Prometheus/Grafana (новых, привязанных по времени к деплою)
|
||
|
||
Финальный approve:
|
||
- В Plane на Work Item стоит reaction `:approved:` от стейкхолдера (financial close)
|
||
|
||
При выполнении — Work Item автоматически закрывается, статус `Done`.
|
||
|
||
---
|
||
|
||
## Override-процедура (исключения)
|
||
|
||
В исключительных случаях (например, hotfix во время инцидента) можно пропустить QG. Для этого:
|
||
|
||
1. В Plane создаётся отдельный Work Item типа `qg-override` с:
|
||
- `parent` = Work Item с проблемой
|
||
- `description` = причина override и список пропускаемых QG
|
||
- reaction `:approved:` от пользователя с ролью `Owner` workspace
|
||
2. Override логируется в `docs/operations/qg-overrides.log` (CI-скрипт пишет автоматически)
|
||
3. После инцидента — обязательная ретроспектива и закрытие override-Work Item с заполненным `13-test-report.md` (т.е. техдолг учтён)
|
||
|
||
> Override — не способ работать «быстрее». Это аварийный выход. Использование override чаще, чем 1 раз в месяц, — сигнал, что процесс сломан.
|
||
|
||
---
|
||
|
||
## Метрики QG (для дашборда)
|
||
|
||
Снимаются автоматически из CI и Plane:
|
||
|
||
- **QG pass-rate first try** — % случаев, когда QG прошёл с первой попытки. Цель: ≥80%.
|
||
- **Время простоя в красном QG** — медиана и p95. Цель: p95 ≤ SLA.
|
||
- **QG retry count** — сколько раз задача возвращалась на тот же этап. Цель: ≤2 для P1+ задач.
|
||
- **Override count** — количество QG-override в месяц. Цель: ≤1.
|
||
- **Время от Inception до Done** (lead time) — DORA метрика.
|
||
|
||
Эти метрики пишутся CI в Prometheus и визуализируются в Grafana (или отдельный простой дашборд на Plotly/Streamlit).
|
||
|
||
---
|
||
|
||
## Чем эта схема отличается от «обычного DoD-чек-листа»
|
||
|
||
В типичной команде «Definition of Done» — это галочки в Confluence, которые ставит человек: «тесты написал ✓», «доку обновил ✓». Проблема: галочки ставит сам исполнитель, перед лицом дедлайна.
|
||
|
||
Здесь:
|
||
- Галочка = результат автоматической проверки.
|
||
- Кто ставит галочку — не имеет права изменить условия проверки в текущей задаче.
|
||
- Reactions от человека — лимитированы только бизнес-approve (когда машина не может проверить); технические QG — целиком машинные.
|
||
|
||
Это и есть **«ворота, которые нельзя забыть»**.
|