mjolnir-qa 1.0.9 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ru.md CHANGED
@@ -1,399 +1,431 @@
1
1
  <div align="center">
2
2
 
3
- <img src="assets/readme/logo.png" alt="Mjölnir — Verification Trust Engine" width="800" />
3
+ <img src="assets/readme/hero.svg" alt="Mjölnir. Тесты говорят, что прошло. Mjölnir говорит, чему можно доверять." width="100%" />
4
4
 
5
- ### Ваши тесты вам лгут. Мы это докажем.
5
+ <br />
6
6
 
7
- **Verification Trust Engine для QA.** Mjölnir проверяет тест-сьюты и
8
- CI-пайплайны, выдаёт показатель достойности и показывает точно, где
9
- ломается доверие.
7
+ Mjölnir находит тесты, которые не могут упасть, и пайплайны, которые не могут покраснеть,<br />
8
+ а затем оценивает, насколько можно доверять результату, с доказательством для каждого пункта.
10
9
 
11
- [![npm](https://img.shields.io/npm/v/mjolnir-qa.svg?style=flat-square&color=C19A34&labelColor=0A1119)](https://www.npmjs.com/package/mjolnir-qa)
12
- [![ci](https://img.shields.io/github/actions/workflow/status/Sergey-Bar/Mjolnir/ci.yml?branch=main&style=flat-square&label=ci&labelColor=0A1119)](https://github.com/Sergey-Bar/Mjolnir/actions/workflows/ci.yml)
13
- [![license](https://img.shields.io/badge/license-MIT-C19A34.svg?style=flat-square&labelColor=0A1119)](LICENSE)
14
- [![node](https://img.shields.io/badge/node-%E2%89%A5%2022.18-37ABBD.svg?style=flat-square&labelColor=0A1119)](https://nodejs.org)
15
-
16
- [English](README.md) | [简体中文](README.zh.md) | [繁體中文](README.zht.md) | [한국어](README.ko.md) | [Deutsch](README.de.md) | [Español](README.es.md) | [Français](README.fr.md) | [Italiano](README.it.md) | [Dansk](README.da.md) | [日本語](README.ja.md) | [Polski](README.pl.md) | Русский | [Norsk](README.no.md) | [Português (Brasil)](README.br.md) | [ไทย](README.th.md) | [Türkçe](README.tr.md) | [Українська](README.uk.md) | [বাংলা](README.bn.md) | [Ελληνικά](README.gr.md) | [Tiếng Việt](README.vi.md) | [עברית](README.he.md) | [العربية](README.ar.md) | [Bosanski](README.bs.md)
10
+ <br />
17
11
 
18
- > 🤖 Machine-assisted translation. The [English README](README.md) is canonical. Last synced: 2026-09-08.
12
+ [![npm](https://img.shields.io/npm/v/mjolnir-qa.svg?style=flat-square&color=1F6F7C&labelColor=0A1119)](https://www.npmjs.com/package/mjolnir-qa)
13
+ [![downloads](https://img.shields.io/npm/dm/mjolnir-qa.svg?style=flat-square&color=1F6F7C&labelColor=0A1119)](https://www.npmjs.com/package/mjolnir-qa)
14
+ [![ci](https://img.shields.io/github/actions/workflow/status/Sergey-Bar/Mjolnir/ci.yml?branch=main&style=flat-square&label=ci&labelColor=0A1119)](https://github.com/Sergey-Bar/Mjolnir/actions/workflows/ci.yml)
15
+ [![coverage](https://img.shields.io/codecov/c/github/Sergey-Bar/Mjolnir?style=flat-square&color=1F6F7C&labelColor=0A1119&label=coverage)](https://codecov.io/gh/Sergey-Bar/Mjolnir)
16
+ [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/Sergey-Bar/Mjolnir/badge)](https://scorecard.dev/viewer/?uri=github.com/Sergey-Bar/Mjolnir)
17
+ [![license](https://img.shields.io/badge/license-MIT-1F6F7C.svg?style=flat-square&labelColor=0A1119)](LICENSE)
18
+ [![node](https://img.shields.io/badge/node-%E2%89%A5%2022.18-1F6F7C.svg?style=flat-square&labelColor=0A1119)](https://nodejs.org)
19
19
 
20
20
  ```bash
21
21
  npx mjolnir-qa@latest
22
22
  ```
23
23
 
24
- **Достойны ли доверия ваши тесты?**
24
+ [Посмотреть в работе](#посмотреть-в-работе) · [Быстрый старт](#быстрый-старт) · [Что находит](#что-находит-mjölnir) · [Оценка](#оценка-надёжности) · [Доказательства](#модель-доказательств) · [Анализ прогонов](#анализ-прогонов-тестов) · [CI](#целостность-ci) · [Агенты](#ии-агенты) · [Безопасность](#доверие-и-безопасность) · [Ограничения](#чего-mjölnir-сказать-не-может) · [Документация](#документация)
25
+
26
+ <details>
27
+ <summary>Читать на другом языке — 22 перевода</summary>
28
+
29
+ [English](README.md) | [简体中文](README.zh.md) | [繁體中文](README.zht.md) | [한국어](README.ko.md) | [Deutsch](README.de.md) | [Español](README.es.md) | [Français](README.fr.md) | [Italiano](README.it.md) | [Dansk](README.da.md) | [日本語](README.ja.md) | [Polski](README.pl.md) | Русский | [Norsk](README.no.md) | [Português (Brasil)](README.br.md) | [ไทย](README.th.md) | [Türkçe](README.tr.md) | [Українська](README.uk.md) | [বাংলা](README.bn.md) | [Ελληνικά](README.gr.md) | [Tiếng Việt](README.vi.md) | [עברית](README.he.md) | [العربية](README.ar.md) | [Bosanski](README.bs.md)
30
+
31
+ > 🤖 Machine-assisted translation. The [English README](README.md) is canonical. Last synced: 2026-09-15.
32
+
33
+ <!-- Source hash: 3541b09e8d04 -->
25
34
 
26
- [Посмотрите в деле](#-посмотрите-в-деле) ·
27
- [Быстрый старт](#-быстрый-старт) ·
28
- [Что он проверяет](#-что-проверяет-mjölnir) ·
29
- [Скоринг](#как-работает-скор) ·
30
- [CI](#-интеграция-ci) · [Конфигурация](#конфигурация) ·
31
- [Документация](#-документация)
35
+ </details>
32
36
 
33
37
  </div>
34
38
 
35
- ---
39
+ <br />
40
+
41
+ ## Зелёная галочка — это заявление, а не доказательство
42
+
43
+ Зелёная галочка означает, что пайплайн не упал. Она не означает, что тесты запускались или что они могли упасть. Каждый из этих случаев проходит зелёным:
44
+
45
+ - закоммиченный `.only`, из-за которого запустилось 3 теста вместо 900
46
+ - `continue-on-error: true` на джобе, которая должна была блокировать
47
+ - `|| true` после команды запуска тестов
48
+ - тест, который ничего не проверяет или имеет пустое тело
49
+ - обёртка с повторами, которая превращает настоящий провал в случайный успех
50
+ - отчёт, который workflow загружает, но никогда не создавал
51
+ - жёсткий sleep, на котором держится состояние гонки
52
+
53
+ Ни один из них не делает пайплайн красным, и каждый на ревью выглядит намеренным. Именно поэтому они и выживают. Вот как Mjölnir читает реальный пример:
54
+
55
+ <p align="center">
56
+ <img src="assets/readme/scan.svg" alt="CI-workflow демонстрационного репозитория, прочитанный строка за строкой. Mjölnir отмечает каждую находку на указанной строке: правило, что не так, уровень доказательности и измеренную долю ложных срабатываний." width="800" />
57
+ </p>
58
+
59
+ <sub>Каждая находка, которую демонстрационное сканирование выдало для этого workflow, на указанной строке. Сгенерировано командой `npm run docs:readme-brand` из [`demo-report.json`](assets/readme/demo-report.json) и защищено от расхождений в CI.</sub>
60
+
61
+ **Строгий режим.** Самые агрессивные детекции — `.only`, `continue-on-error`, пустые тесты, злоупотребление повторами — живут в карантинном ярусе. Они запускаются только с `--strict` и ограничены серьёзностью `info`: помечают, но никогда не блокируют. Сканирование по умолчанию (`npx mjolnir-qa@latest` без `--strict`) покрывает только основные и расширенные правила. Добавьте `--strict`, когда хотите и консультативный слой.
62
+
63
+ Mjölnir читает набор тестов, CI-workflow и, если он есть, отчёт реального прогона. Он не запускает ваши тесты, не устанавливает зависимости и не выполняет сканируемый код. А когда доказательств нет, он так и говорит, вместо того чтобы выдумывать уверенность:
64
+
65
+ | Ситуация | Что сообщает Mjölnir |
66
+ | ------------------------------------------------------- | ----------------------------------------------------------------------- |
67
+ | Объявления тестов не найдены | Оценка `null`, отображается как **UNKNOWN**. Никогда не выдуманные 100. |
68
+ | Нет базовой линии или сравнимой ревизии | **UNKNOWN**, с указанием причины. Никогда не предполагаемый 0. |
69
+ | Сканирование прервано (лимит времени, нечитаемые файлы) | **PARTIAL**, код выхода `2`. Никогда не выдаётся за чистый результат. |
70
+
71
+ <p align="center">
72
+ <img src="assets/readme/how-it-works.svg" alt="Как работает Mjölnir. Он статически читает набор тестов и CI-пайплайн, а также отчёт реального прогона, если он есть. Каждую находку он взвешивает по уровню доказательности и уровню доверия, причём только реальный прогон может достичь L3–L5, и выдаёт находки, оценку надёжности и CI-гейт с замороженными кодами выхода. В цикле агента ИИ пишет исправление, а Mjölnir сканирует повторно, чтобы его доказать." width="880" />
73
+ </p>
74
+
75
+ <sub>Сделано для этой страницы и показано в масштабе 1:1. Сгенерировано командой `npm run docs:readme-brand` и защищено от расхождений в CI; оценка, счётчики и ID правила берутся из [`script.demo.json`](assets/video/script.demo.json), [`demo-report.json`](assets/readme/demo-report.json) и реестра правил, никогда не вводятся вручную. То же изображение в виде постера: [`architecture.svg`](assets/readme/architecture.svg).</sub>
76
+
77
+ <br />
78
+
79
+ ## Посмотреть в работе
80
+
81
+ Реальное сканирование [`examples/demo-repo`](examples/demo-repo), небольшого набора тестов Playwright с CI-workflow. Вот куда ушли его баллы:
82
+
83
+ <p align="center">
84
+ <img src="assets/readme/terminal-hero.svg" alt="Разбивка вычетов Mjölnir: WORTHINESS 80/100 WORTHY, оценка по категориям, блок вычетов по серьёзности и список FIX THIS FIRST" width="520" />
85
+ </p>
86
+
87
+ <sub>Сгенерировано командой `npm run docs:hero` из реального сканирования и защищено от расхождений в CI. Полный отчёт `--verbose` того же сканирования — [`demo.svg`](assets/readme/demo.svg) (`npm run docs:demo`).</sub>
88
+
89
+ <details>
90
+ <summary><strong>Смотреть</strong> — сканирование, исправление, которое оно выводит, и повторное сканирование, которое его доказывает</summary>
36
91
 
37
- ## 🎬 Посмотрите в деле
92
+ <br />
38
93
 
39
94
  <p align="center">
40
- <img src="assets/readme/demo.svg" alt="Полный --verbose-отчёт Mjölnir по демо-репозиторию: WORTHINESS 75/100 NEEDS WORK, разбивка диагностик по категориям, список FIX THIS FIRST и каждая находка с ID правила и номером строки — CI, Playwright, тест-гигиена и Python-правила" width="900" />
95
+ <a href="assets/video/mjolnir-demo.mp4">
96
+ <img src="assets/video/mjolnir-demo-poster.png" alt="Кадр демонстрационной записи: npx mjolnir-qa@latest сканирует демонстрационный репозиторий в окне терминала" width="900" />
97
+ </a>
41
98
  </p>
42
99
 
43
- <sub>Полный вывод `npx mjolnir-qa ./examples/demo-repo --verbose`,
44
- отрендеренный настоящим репортером — ничего не урезано. Перегенерируется
45
- командой `npm run docs:demo`;
46
- [`tests/demo-asset-reproducibility.spec.ts`](tests/demo-asset-reproducibility.spec.ts)
47
- роняет CI, если артефакт разошёлся с тем, что печатает инструмент.</sub>
100
+ <sub>Отрисовано кадр за кадром из реального сканирования командой `npm run docs:video`; никогда не записывалось с экрана. Выберите кадр, чтобы открыть [`mjolnir-demo.mp4`](assets/video/mjolnir-demo.mp4).</sub>
101
+
102
+ </details>
48
103
 
49
- **Что только что произошло:**
104
+ ### Одна находка крупным планом
50
105
 
51
- 1. Mjölnir нашёл Playwright-спеки, его конфигурацию, CI-workflow и
52
- Python-тестовый файл — четыре языка/формата за один проход.
53
- 2. Он нашёл улики, ослабляющие доверие к сьюту — `continue-on-error`
54
- в маскировке job, `|| true`, глотающий exit-код, жёсткие sleep'ы,
55
- хрупкий селектор, захардкоженные staging-URL, ожидание
56
- `networkidle`.
57
- 3. Каждую он превратил в конкретную находку с ID правила, местом и
58
- фиксом — и в единый скор, по которому можно гейтить PR.
106
+ Каждая находка отвечает на четыре вопроса: где она, насколько Mjölnir уверен, как часто правило ошибается и как это исправить.
59
107
 
60
- ### Одна находка вблизи
108
+ <p align="center">
109
+ <img src="assets/readme/finding-anatomy.svg" alt="Первая находка демонстрационного сканирования, ровно так, как её выводит терминал, с отмеченными четырьмя частями: где, насколько уверенно, как часто правило ошибается, и исправление." width="100%" />
110
+ </p>
61
111
 
62
- Запустите `mjolnir explain QA-CI-001` на первой находке выше — и
63
- получите:
112
+ `mjolnir explain QA-CI-001` выводит полное досье доверия правила, включая измеренную долю ложных срабатываний и уровень, который эта доля ему обеспечила:
64
113
 
65
114
  ```text
66
- ▚ QA-CI-001 — continue-on-error masks a failing verification gate
115
+ ▍ QA-CI-001 — continue-on-error masks a failing verification gate
67
116
 
68
117
  Severity: error
69
118
  Confidence: high
119
+ Tier: quarantine
70
120
  Evidence: E2
71
- Measured FP: not yet measured — this rule ships on assumption (see docs/FP-AUDIT.md)
121
+ QA impact: False-green risk (FALSE-GREEN)
122
+ Measured FP: 11% (19 hand-classified corpus verdicts)
123
+ FP risk: low (author estimate)
124
+ Languages: yaml
125
+ Frameworks: github-actions, azure-pipelines
72
126
 
73
127
  WHAT WAS FOUND (real detector output, not a mockup)
74
128
  Job `security-scan` runs a verification gate under `continue-on-error: true`.
75
129
 
76
130
  WHY IT MATTERS
77
- This job can fail every day and CI will still show green. The checkmark
78
- on this workflow cannot be trusted.
131
+ This job can fail every day and CI will still show green. The checkmark on
132
+ this workflow cannot be trusted.
79
133
 
80
134
  HOW TO FIX
81
135
  Remove continue-on-error, or scope it to individual non-blocking steps only.
82
- ```
83
136
 
84
- Вот единица ценности: не придирка к стилю, а место, где ваш CI
85
- сообщает, что что-то прошло, хотя это не так.
137
+ Example from this rule's own must-fire fixture: QA-CI-001/must-fire/masked.yml
86
138
 
87
- ---
139
+ WHAT WOULD CHANGE THE VERDICT
140
+ - a run report next to the scan target (mjolnir.report.json or test-results/)
141
+ corroborating this file lifts its findings to L3–L5
142
+ - a documented suppression (mjolnir.config.json) lowers the finding count
143
+ without claiming correctness
144
+ - quarantine findings run only under --strict and are advisory (E0) — they can
145
+ never gate CI
88
146
 
89
- ## ⚡ Быстрый старт
147
+ NEXT ACTION
148
+ Fix the first occurrence, then re-run: `mjolnir --scope changed`. Every
149
+ occurrence of this rule is listed in the scan output.
90
150
 
91
- Запустите на репозитории — получите полный отчёт и показатель
92
- достойности:
151
+ HOW TO VERIFY THE FIX
152
+ Re-run `mjolnir` on the changed file(s) — this finding should no longer
153
+ appear. `mjolnir --scope changed` scopes the check to just what you touched.
93
154
 
94
- ```bash
95
- npx mjolnir-qa@latest
155
+ Docs: mjolnir rules --md (full catalog, this rule included)
96
156
  ```
97
157
 
98
- **В CI продукт — одна команда.** Она сканирует только затронутое
99
- веткой и завершается с ненулевым кодом при новых проблемах:
158
+ Вот единица ценности: одно место, где CI сообщает об успехе, которого он не заслужил.
159
+
160
+ <br />
161
+
162
+ ## Быстрый старт
100
163
 
101
164
  ```bash
102
- npx mjolnir-qa@latest --scope changed
165
+ npx mjolnir-qa@latest
103
166
  ```
104
167
 
105
- Встройте это в PR-check — `mjolnir ci install` пишет workflow — и
106
- готово. Всё остальное опционально.
168
+ Он сканирует текущий каталог и выводит Trust Report: что найдено, насколько этому можно доверять, почему и что делать дальше. Он завершается с кодом `0`, если на уровне гейта или выше ничего не найдено.
107
169
 
108
- | Команда | Что делает |
109
- | ----------------------------------- | ---------------------------------------------------- |
110
- | `mjolnir` | Скан всего репо + показатель достойности |
111
- | `mjolnir --scope changed` | Только то, что принесла ваша ветка — CI-режим |
112
- | `mjolnir ci install` | Генерирует рекомендательный PR-workflow |
113
- | `mjolnir explain QA-CI-001` | Что / почему / фикс + измеренный FP-рейт для правила |
114
- | `mjolnir rules --unmeasured` | Правила, работающие по допущению, а не по измерению |
115
- | `mjolnir --json` / `--format sarif` | Машинночитаемо / GitHub Code Scanning |
116
- | `mjolnir --strict` | Также правила tier-а quarantine (выше риск FP) |
117
-
118
- <details>
119
- <summary><strong>Когда что-то флакует</strong></summary>
170
+ В CI сканируйте только то, что внесла ветка, чтобы унаследованный набор тестов не утопил ваш первый pull request:
120
171
 
121
- | Команда | Что делает |
122
- | ----------------------------------- | ------------------------------------------------------------- |
123
- | `mjolnir forensics ./test-results/` | Реальные данные прогонов → вердикты `TRUE-FLAKE`, `FLAKY.md` |
124
- | `mjolnir triage ./test-results/` | Предложение карантина из истории выполнения |
125
- | `mjolnir pw-report ./test-results/` | Сводка прогона Playwright — ретраи / флейки / самые медленные |
126
- | `mjolnir doctor:playwright` | Глубокий скан только Playwright + Selector Health Score |
172
+ ```bash
173
+ npx mjolnir-qa@latest --scope changed
174
+ ```
127
175
 
128
- </details>
176
+ `mjolnir ci install` записывает это как workflow GitHub Actions с [action](https://github.com/Sergey-Bar/Mjolnir#readme), закреплённым на мажорном теге `v1` (или просто `npx` с `--no-action`). Он остаётся рекомендательным, пока вы не решите, что он должен блокировать.
177
+
178
+ | Команда | Что делает |
179
+ | ----------------------------------- | ----------------------------------------------------------- |
180
+ | `mjolnir` | Trust Report: вердикт, уверенность, следующее действие |
181
+ | `mjolnir --scope changed` | Только то, что внесла ваша ветка (вариант для CI) |
182
+ | `mjolnir ci install` | Создаёт рекомендательный workflow для PR (на основе action) |
183
+ | `mjolnir explain QA-CI-001` | Что, почему и как исправить, плюс измеренная доля FP |
184
+ | `mjolnir why src/a.spec.ts:42` | Почему отмечена именно эта строка. Никогда не блокирует. |
185
+ | `mjolnir forensics ./test-results/` | Доказательства из реального прогона |
186
+ | `mjolnir trust-report` | Самодостаточный Trust Artifact (md + json) |
187
+ | `mjolnir handoff` | План исправлений для агента-программиста |
188
+ | `mjolnir --json` / `--format sarif` | Машиночитаемый вывод, GitHub Code Scanning |
189
+ | `mjolnir --format codequality` | Отчёт GitLab Code Quality (артефакт виджета MR) |
190
+ | `mjolnir --strict` | Также запускает правила уровня quarantine (выше риск FP) |
129
191
 
130
192
  <details>
131
- <summary><strong>По случаю / отчёты</strong></summary>
132
-
133
- | Команда | Что делает |
134
- | ------------------------------- | ----------------------------------------------------- |
135
- | `mjolnir fix --dry-run` / `fix` | Безопасные автофиксы с доказательством |
136
- | `mjolnir baseline` / `diff` | Снимок находок, затем отчёт только новых/ухудшившихся |
137
- | `mjolnir impact --since <ref>` | Что изменилось с момента раннего коммита |
138
- | `mjolnir debt` | Реестр тестового долга с моделью стоимости |
139
- | `mjolnir handover` | Карта онбординга сьюта для нового QA |
140
- | `mjolnir stats` | Локальные накопленные счётчики увиденных фиксов |
141
- | `mjolnir badge` | JSON shields.io-эндпоинта + сниппет |
142
- | `mjolnir rules --md` | Полный каталог правил (JSON или Markdown) |
143
- | `mjolnir doctor` | Самоаудит собственной базы правил Mjölnir |
144
- | `mjolnir create-rule <ID>` | Скаффолд нового правила + фикстур |
145
- | `mjolnir --format mermaid` | Диаграмма тестовой архитектуры для комментария к PR |
193
+ <summary><strong>Все остальные команды</strong> — разбор нестабильных тестов, отчёты, контроль</summary>
194
+
195
+ <br />
196
+
197
+ | Команда | Что делает |
198
+ | ----------------------------------- | ------------------------------------------------------------------------------------- |
199
+ | `mjolnir --classic` | Баннер оценки из времён до Trust Report |
200
+ | `mjolnir explain verdict` | Почему вердикт сохранённого сканирования именно такой |
201
+ | `mjolnir triage ./test-results/` | Пошаговый разбор. Каждая строка заканчивается следующим действием. |
202
+ | `mjolnir pw-report ./test-results/` | Сводка прогона Playwright: повторы, нестабильные тесты, самые медленные |
203
+ | `mjolnir doctor:playwright` | Глубокое сканирование только для Playwright плюс Selector Health Score |
204
+ | `mjolnir fix --dry-run` / `fix` | Безопасные автоисправления, каждое пересканируется, чтобы доказать, что оно сработало |
205
+ | `mjolnir baseline` / `diff` | Снимок находок, затем отчёт только о новых или ухудшившихся |
206
+ | `mjolnir impact --since <ref>` | Что внёс и что исправил коммит |
207
+ | `mjolnir summary` | Аннотации CI и сводка шага на основе отчёта |
208
+ | `mjolnir pr-comment` | Комментарий к PR в пределах изменений, в формате Markdown |
209
+ | `mjolnir debt` | Реестр тестового долга с моделью затрат |
210
+ | `mjolnir handover` | Карта набора тестов для нового QA-инженера |
211
+ | `mjolnir init` | Определяет фреймворки, выводит чек-лист настройки |
212
+ | `mjolnir suppressions` | Список подавленных находок для контроля |
213
+ | `mjolnir rules --unmeasured` | Правила, работающие на допущении, а не на измерении |
214
+ | `mjolnir rules --md` | Полный каталог правил (JSON или Markdown) |
215
+ | `mjolnir doctor` | Самоаудит собственной базы правил Mjölnir |
216
+ | `mjolnir create-rule <ID>` | Создаёт заготовку нового правила и его фикстур |
217
+ | `mjolnir stats` | Локальные счётчики всех замеченных исправлений |
218
+ | `mjolnir badge` | JSON для эндпоинта shields.io и фрагмент кода |
219
+ | `mjolnir --cache` | Инкрементальные пересканирования через локальный кэш вердиктов |
220
+ | `mjolnir --format mermaid` | Диаграмма архитектуры тестов для комментария к PR |
221
+
222
+ `mjolnir help <command>` выводит использование, примеры и следующий шаг для любой из них.
146
223
 
147
224
  </details>
148
225
 
149
- Установите глобально вместо `npx`, если так удобнее:
150
- `npm i -g mjolnir-qa`. Требуется Node.js ≥ 22.18. Работает на Windows,
151
- macOS и Linux.
152
-
153
- ---
154
-
155
- ## 👥 Для кого это?
156
-
157
- - **QA / SDET**, владеющие e2e- или интеграционной сьютой и которым
158
- нужны доказательства, что сьют действительно заслуживает зелёную
159
- галочку, которую он выдаёт.
160
- - **Платформенные / DevEx-команды**, отвечающие за целостность CI и
161
- release-gates — те, для кого `continue-on-error` никогда не должен
162
- молча перекрашивать красный пайплайн в зелёный.
163
- - **OSS-мейнтейнеры**, которым нужен дешёвый, всегда включённый
164
- верификационный гейт, работающий локально и в CI без сетевых
165
- вызовов.
166
-
167
- ---
226
+ Требуется **Node.js ≥ 22.18** на Windows, macOS или Linux. Предпочитаете глобальную установку? `npm i -g mjolnir-qa`. Минимальная версия задаётся инструментами сборки (tsdown ориентирован на неё, а пайплайн релизов прогоняет на ней smoke-тесты); зависимостям времени выполнения большего не нужно.
168
227
 
169
- ## 🔨 Что проверяет Mjölnir
228
+ <br />
170
229
 
171
- | | |
172
- | --- | ----------------------------------------------------------------------------------------------------------------------------------- |
173
- | ⚖️ | **Показатель достойности** — одно число, прозрачная таблица вычетов, никакого чёрного ящика |
174
- | 🎭 | **Selector Health Score** — оценивает ваши Playwright-локаторы, а не только pass-rate |
175
- | 🔬 | **Runtime-криминалистика** — читает реальные данные прогонов Playwright/JUnit и ловит `TRUE-FLAKE`, а не только статические догадки |
176
- | 🚨 | **Правила целостности CI** — ловит `continue-on-error`, `\|\| true` и другие трюки с ложным зелёным |
177
- | 🐍 | **Все четыре Playwright-биндинга** — TypeScript, Python, Java, C#/.NET — плюс pytest, JUnit/TestNG и CI-workflows |
178
- | 🔒 | **Local-first** — ноль сетевых вызовов при сканировании, ноль телеметрии, работа за секунды |
230
+ ## Что находит Mjölnir
179
231
 
180
- ### Правила
232
+ <p align="center">
233
+ <img src="assets/readme/stack.svg" alt="Работает с вашим стеком: языки, тестовые фреймворки и CI-системы, которые покрывают его правила, по данным реестра правил." width="100%" />
234
+ </p>
181
235
 
182
- Каждое правило поставляется с must-fire- **и** must-not-fire-фикстурами.
183
- Правило, срабатывающее на собственной негативной фикстуре, не может
184
- выйти — это фаервол ложных срабатываний.
236
+ **79 правил** в четырёх семействах — гигиена тестов, качество тестов, Playwright и целостность CI — для TypeScript и JavaScript, Python, Java, C# и YAML GitHub Actions. Они охватывают Playwright во всех четырёх привязках, а также pytest, JUnit, TestNG, NUnit, xUnit, MSTest, Jest, Vitest и Mocha, с начальным покрытием Cypress и Selenium. Девять из них, чтобы показать общий вид:
185
237
 
186
- <details>
187
- <summary><strong>Тест-гигиена</strong></summary>
188
-
189
- | ID | Правило | Severity |
190
- | ----------- | ------------------------------------------------------ | -------- |
191
- | QA-TEST-001 | Закоммичен сфокусированный тест (`.only`, `fit`) | error |
192
- | QA-TEST-002 | Пропущен тест без обоснования | error |
193
- | QA-TEST-002 | Пропущен тест с учтённым обоснованием | warning |
194
- | QA-TEST-003 | Тест без ассертов | error |
195
- | QA-TEST-004 | Жёсткий sleep (`waitForTimeout`, `sleep()`, `delay()`) | warning |
196
- | QA-TEST-006 | Злоупотребление ретраями, скрывающее флакость | warning |
197
- | QA-TEST-010 | Пустое тело теста | error |
238
+ | ID | Правило | Серьёзность | Уровень |
239
+ | ------------ | ----------------------------------------------------------------------- | ----------- | ---------- |
240
+ | QA-CI-001 | `continue-on-error` маскирует падающий гейт проверки | error | quarantine |
241
+ | QA-CI-009 | Код выхода тестов не передаётся дальше (`\|` без pipefail, цепочки `;`) | error | extended |
242
+ | QA-TEST-001 | Закоммичен сфокусированный тест (`.only`, `fit`) | error | quarantine |
243
+ | QA-TEST-003 | Тест без утверждений | error | quarantine |
244
+ | QA-TQUAL-009 | Утверждение на promise без await | error | quarantine |
245
+ | QA-PW-002 | Утверждение на локаторе без await | error | core |
246
+ | QA-PW-004 | Хрупкие селекторы CSS/XPath | warning | quarantine |
247
+ | QA-PY-002 | Пропущенный тест (`skip`, нестрогий `xfail`) | warning | core |
248
+ | QA-CS-103 | Тестовый метод без утверждений | error | core |
198
249
 
199
- </details>
250
+ Полный каталог генерируется из реестра и никогда не ведётся вручную: `mjolnir rules --md`, [`docs/rules/`](docs/rules/) или [руководство о том, что он проверяет](https://sergey-bar.github.io/Mjolnir/guide/what-it-checks).
200
251
 
201
252
  <details>
202
- <summary><strong>Качество тестов</strong></summary>
203
-
204
- | ID | Правило | Severity |
205
- | ------------ | ----------------------------- | -------- |
206
- | QA-TQUAL-002 | Тавтологический ассерт | error |
207
- | QA-TQUAL-009 | Ассерт не-awaitнутого promise | error |
208
- | QA-TQUAL-011 | Закомментированные тесты | warning |
253
+ <summary><strong>Все правила, упомянутые в этом README</strong>, в одной таблице</summary>
254
+
255
+ <br />
256
+
257
+ > Правила `quarantine` запускаются только с `--strict` и никогда не блокируют (их уровень ограничен info). Показана серьёзность, заданная автором.
258
+
259
+ | ID | Семейство | Правило | Серьёзность | Уровень |
260
+ | ------------ | ---------- | ------------------------------------------------------------------ | ----------- | ---------- |
261
+ | QA-TEST-001 | Гигиена | Закоммичен сфокусированный тест (`.only`, `fit`) | error | quarantine |
262
+ | QA-TEST-002 | Гигиена | Пропущенный тест. Без отслеживаемой причины повышается до `error`. | warning | quarantine |
263
+ | QA-TEST-003 | Гигиена | Тест без утверждений | error | quarantine |
264
+ | QA-TEST-004 | Гигиена | Жёсткий sleep (`waitForTimeout`, `sleep()`, `delay()`) | warning | extended |
265
+ | QA-TEST-006 | Гигиена | Злоупотребление повторами, скрывающее нестабильность | warning | quarantine |
266
+ | QA-TEST-010 | Гигиена | Пустое тело теста | error | quarantine |
267
+ | QA-TQUAL-002 | Качество | Тавтологическое утверждение | error | quarantine |
268
+ | QA-TQUAL-009 | Качество | Утверждение на promise без await | error | quarantine |
269
+ | QA-TQUAL-011 | Качество | Закомментированные тесты | warning | extended |
270
+ | QA-PW-002 | Playwright | Утверждение на локаторе без await | error | core |
271
+ | QA-PW-003 | Playwright | Закоммичен `page.pause()` / `test.only()` | error | core |
272
+ | QA-PW-004 | Playwright | Хрупкие селекторы CSS/XPath | warning | quarantine |
273
+ | QA-PW-123 | Playwright | Жёстко заданные URL окружений | warning | quarantine |
274
+ | QA-PW-140 | Playwright | Скриншот без `maxDiffPixelRatio` | warning | core |
275
+ | QA-CI-001 | CI | `continue-on-error` маскирует падающий гейт | error | quarantine |
276
+ | QA-CI-002 | CI | `\|\| true` проглатывает коды выхода | error | extended |
277
+ | QA-CI-005 | CI | Отчёт используется, но никогда не создаётся | error | quarantine |
278
+ | QA-CI-007 | CI | Обёртки с повторами вокруг тестов | warning | extended |
279
+ | QA-CI-008 | CI | Всегда успешный шаг маскирует падения | error | quarantine |
280
+ | QA-CI-009 | CI | Код выхода не передаётся дальше (`\|` без pipefail, цепочки `;`) | error | extended |
281
+ | QA-CI-010 | CI | Тесты пропускаются там, где должны блокировать | error | quarantine |
282
+ | QA-PY-002 | Python | Пропущенный тест (`skip`, нестрогий `xfail`) | warning | core |
283
+ | QA-PY-003 | Python | Тестовая функция без утверждений | error | quarantine |
284
+ | QA-PY-005 | Python | `time.sleep()` в тестах | warning | extended |
285
+ | QA-PY-012 | Python | Тавтологическое утверждение | error | quarantine |
286
+ | QA-JV-101 | Java | Отключённый тест (`@Disabled`) | warning | core |
287
+ | QA-JV-102 | Java | Жёсткий sleep (`Thread.sleep()`) | warning | extended |
288
+ | QA-JV-103 | Java | Тестовый метод без утверждений | error | extended |
289
+ | QA-JV-105 | Java | Жёсткий sleep через `waitForTimeout()` в Playwright | warning | core |
290
+ | QA-JV-106 | Java | Хрупкий селектор вместо локатора по роли | warning | quarantine |
291
+ | QA-CS-101 | C# | Пропущенный тест (`[Ignore]`, `[Fact(Skip=)]`) | warning | core |
292
+ | QA-CS-102 | C# | Жёсткий sleep (`Thread.Sleep` / `Task.Delay`) | warning | core |
293
+ | QA-CS-103 | C# | Тестовый метод без утверждений | error | core |
294
+ | QA-CS-105 | C# | Жёсткий sleep через `WaitForTimeoutAsync()` | warning | extended |
295
+ | QA-CS-106 | C# | Хрупкий селектор вместо локатора по роли | warning | quarantine |
296
+
297
+ Для Python также есть QA-PY-001…012 (гигиена pytest) и QA-PY-101…108 (Playwright для Python). У Cypress и Selenium есть стартовые наборы по три правила.
209
298
 
210
299
  </details>
211
300
 
212
- <details>
213
- <summary><strong>Playwright 🎭</strong></summary>
301
+ Каждое правило поставляется с фикстурой must-fire **и** фикстурой must-not-fire, а правило, срабатывающее на собственной негативной фикстуре, не может быть выпущено. Это защита от ложных срабатываний; `mjolnir doctor` обеспечивает её в собственном CI этого репозитория.
214
302
 
215
- | ID | Правило | Severity |
216
- | --------- | ---------------------------------------- | -------- |
217
- | QA-PW-002 | Ассерт локатора без await | error |
218
- | QA-PW-003 | `page.pause()` / `test.only()` в коммите | error |
219
- | QA-PW-004 | Хрупкие CSS/XPath-селекторы | warning |
220
- | QA-PW-123 | Захардкоженные URL окружений | warning |
303
+ ### Selector Health Score
221
304
 
222
- </details>
305
+ `mjolnir doctor:playwright` оценивает каждый локатор по тому, как он находит элемент: так, как это сделал бы пользователь (роль, метка, текст), через явный контракт (`data-testid`) или по структурной случайности (цепочки CSS, XPath). Каждый файл получает оценку от 0 до 100:
223
306
 
224
- <details>
225
- <summary><strong>Целостность CI</strong></summary>
226
-
227
- | ID | Правило | Severity |
228
- | --------- | ----------------------------------------------------------------- | -------- |
229
- | QA-CI-001 | `continue-on-error` маскирует падения | error |
230
- | QA-CI-002 | `\|\| true` глотает exit-коды | error |
231
- | QA-CI-005 | Отчёт потребляется, но никогда не генерируется | error |
232
- | QA-CI-007 | Retry-обёртки вокруг тестов | warning |
233
- | QA-CI-008 | Всегда успешный шаг маскирует падения | error |
234
- | QA-CI-009 | Exit-код теста не прокидывается (`\|` без pipefail, цепочки `;`) | error |
235
- | QA-CI-010 | Тесты пропускаются там, где должны блокировать (skip-on-PR-гарды) | error |
236
-
237
- </details>
238
-
239
- <details>
240
- <summary><strong>Python / pytest 🐍</strong></summary>
241
-
242
- | ID | Правило | Severity |
243
- | --------- | -------------------------------------------- | -------- |
244
- | QA-PY-002 | Пропущенный тест (`skip`, нестрогий `xfail`) | warning |
245
- | QA-PY-003 | Тестовая функция без ассертов | error |
246
- | QA-PY-005 | `time.sleep()` в тестах | warning |
247
- | QA-PY-012 | Тавтологический ассерт | error |
248
-
249
- Всего 20 Python-правил (QA-PY-001…012 гигиена pytest + QA-PY-101…108 Playwright-Python).
307
+ ```text
308
+ ▍ SELECTOR HEALTH
250
309
 
251
- </details>
310
+ e2e/login.spec.ts
311
+ [█████████████░░░░░░░] 65 / 100
312
+ role/text: 1 · testid: 0 · plain-css: 0 · css-chains: 1 ⚠ · xpath: 0
252
313
 
253
- <details>
254
- <summary><strong>Java / JUnit · TestNG ☕</strong></summary>
314
+ e2e/checkout.spec.ts
315
+ [██████████████████░░] 88 / 100
316
+ role/text: 4 · testid: 1 · plain-css: 0 · css-chains: 1 ⚠ · xpath: 0
317
+ ```
255
318
 
256
- | ID | Правило | Severity |
257
- | --------- | ------------------------------------------- | -------- |
258
- | QA-JV-101 | Отключённый тест (`@Disabled`) | warning |
259
- | QA-JV-102 | Жёсткий sleep (`Thread.sleep()`) | warning |
260
- | QA-JV-103 | Тестовый метод без ассертов | error |
261
- | QA-JV-105 | Жёсткий sleep Playwright `waitForTimeout()` | warning |
262
- | QA-JV-106 | Хрупкий селектор вместо role-локатора | warning |
319
+ Это измеряет **устойчивость, а не корректность**. `.btn.btn-primary > div:nth-child(2)` проходит сегодня и будет проходить, пока кто-нибудь не тронет разметку. Низкая оценка никогда не утверждает, что тест сломан, — только что он зависит от разметки, сохранять которую никто не обещал.
263
320
 
264
- </details>
321
+ <br />
265
322
 
266
- <details>
267
- <summary><strong>C# / .NET — NUnit · xUnit · MSTest 🟣</strong></summary>
323
+ ## Оценка надёжности
268
324
 
269
- | ID | Правило | Severity |
270
- | --------- | ---------------------------------------------- | -------- |
271
- | QA-CS-101 | Пропущенный тест (`[Ignore]`, `[Fact(Skip=)]`) | warning |
272
- | QA-CS-102 | Жёсткий sleep (`Thread.Sleep` / `Task.Delay`) | warning |
273
- | QA-CS-103 | Тестовый метод без ассертов | error |
274
- | QA-CS-105 | Жёсткий sleep `WaitForTimeoutAsync()` | warning |
275
- | QA-CS-106 | Хрупкий селектор вместо role-локатора | warning |
325
+ <p align="center">
326
+ <img src="assets/readme/score-gauge.svg" alt="Шкала надёжности от 0 до 100 с маркером, проходящим по каждой оценке: UNWORTHY ниже 50, NEEDS WORK от 50 до 79, WORTHY от 80 до 99, FORGED при 100" width="720" />
327
+ </p>
276
328
 
277
- </details>
329
+ <sub>Каждая оценка от 0 до 100, размещённая настоящим `deriveScoreState`. Сгенерировано командой `npm run docs:gauge` и защищено от расхождений в CI.</sub>
278
330
 
279
- > Полный живой каталог — каждое правило с tier, confidence, риском
280
- > ложных срабатываний и доступностью автофикса — генерируется из
281
- > реестра:
282
- >
283
- > ```bash
284
- > mjolnir rules --md
285
- > ```
286
- >
287
- > Страницы по правилам лежат в [`docs/rules/`](docs/rules/).
331
+ | Оценка | Вердикт |
332
+ | --------- | ----------------------------------------- |
333
+ | `0 – 49` | **UNWORTHY** |
334
+ | `50 – 79` | **NEEDS WORK** |
335
+ | `80 – 99` | **WORTHY** |
336
+ | `100` | **FORGED** |
337
+ | `null` | **UNKNOWN**: объявления тестов не найдены |
288
338
 
289
- ### Сколько из этого измерено
339
+ **Как она вычисляется.** Серьёзность задаёт базовый вычет (`error −8`, `warning −3`, `info −1`), а уровень доказательности его уменьшает: E2 учитывается полностью, E1 наполовину (с округлением вниз), E0 не учитывается. Сумма нормируется по охвату набора, то есть вычеты на одно объявление теста, а не на файл. Терминал выводит те же уменьшенные числа, что использовала оценка; скрытой второй модели нет. Подробности: [docs/SCORING.md](docs/SCORING.md) и [руководство по оценке](https://sergey-bar.github.io/Mjolnir/guide/scoring).
290
340
 
291
- **78 из 99 правил несут ложную положительную частоту, измеренную на
292
- реальном OSS-коде** (по ≥ 10 вручную классифицированных находок на
293
- правило; см. [docs/FP-AUDIT.md](docs/FP-AUDIT.md)). Остальные 21
294
- выходят на оценке автора. Футер каждого скана говорит, сколько из
295
- _сработавших_ правил измерены; `mjolnir rules --unmeasured` перечисляет
296
- неизмеренные; страница `mjolnir explain` каждого правила указывает её
297
- аудируется на 95 % и за это отправлен в карантин. Увеличивать это
298
- число — постоянная работа проекта.
341
+ **Чего не означает 100.** Это не значит, что программа корректна, набор тестов достаточен или продукт свободен от дефектов. Это значит только одно: **ни одно из правил, проверенных Mjölnir, не дало вычета в этом сканировании и при этой модели доказательств.**
299
342
 
300
- ### Тиры правил и зрелость языков
343
+ <br />
301
344
 
302
- Каждое правило — `core`, `extended` или `quarantine`, назначенный по
303
- его **измеренной** частоте ложных срабатываний:
345
+ ## Модель доказательств
304
346
 
305
- | Tier | Значение | Скан по умолчанию | `--strict` |
306
- | ------------ | -------------------------------------- | :---------------: | :--------: |
307
- | `core` | ≤ 10 % измеренных FP | ✅ | ✅ |
308
- | `extended` | ≤ 30 % измеренных FP | ✅ | ✅ |
309
- | `quarantine` | выше 30 % или ещё не измерено (n < 10) | ❌ | ✅ |
347
+ Каждая находка несёт две метки: насколько уверен Mjölnir и насколько далеко находка проверена. В этом разница между инструментом, который сообщает о шаблонах, и инструментом, от которого можно ставить в зависимость релиз.
310
348
 
311
- | Язык | Адаптер | Охват сегодня |
312
- | --------------- | --------------- | -------------------------------------------------------------- |
313
- | TypeScript / JS | AST компилятора | самый широкий, самый измеренный — в основном `core`/`extended` |
314
- | Python / pytest | Regex-слой | широкий, проверен корпусом — в основном `core`/`extended` |
315
- | Java | Regex-слой | новее — в основном `extended`/`quarantine` |
316
- | C# / .NET | Regex-слой | новее — в основном `extended`/`quarantine` |
349
+ **Насколько уверенно — уровень доказательности.**
317
350
 
318
- У TypeScript и Python самый широкий измеренный охват. Java и C#
319
- вышли, задокументированы и остаются за пределами головного числа, пока
320
- реальный сьют-потребитель (не собственные тесты биндинг-библиотеки) не
321
- будет проаудирован.
351
+ | Уровень | Название | Значение | Вычет |
352
+ | ------- | -------------------------------- | ----------------------------------------------------- | -------- |
353
+ | **E2** | Детерминированное доказательство | Дефект присутствует в коде в том виде, как он написан | Полный |
354
+ | **E1** | Доказательство по шаблону | Совпал шаблон, тесно связанный с дефектом | Половина |
355
+ | **E0** | Наблюдение | Стоит знать. Не утверждение, что что-то не так. | Ноль |
322
356
 
323
- ---
357
+ Уверенность в обнаружении — это не сила доказательства. Правило может быть уверено, что нашло то, что искало, и всё равно смотреть на эвристику. Находки E1 предназначены для того, чтобы их читали и оценивали, а не применяли вслепую, и эта граница отмечена на находке в терминале, в JSON и в передаче агенту.
324
358
 
325
- ## Как работает скор
359
+ **Насколько далеко проверено — уровень доверия.** Большинство находок получено чтением вашего кода. Дайте Mjölnir отчёт реального прогона тестов, и он сможет подтвердить, что код действительно выполнялся.
326
360
 
327
361
  <p align="center">
328
- <img src="assets/readme/terminal-hero.svg" alt="Терминальный вывод Mjölnir — WORTHINESS 75/100 NEEDS WORK, разбивка диагностик по категориям и список FIX THIS FIRST" width="820" />
362
+ <img src="assets/readme/trust-ladder.svg" alt="Лестница доверия от L0 до L5. L0–L2 получаются чтением кода; для L3–L5 нужен отчёт реального прогона, что отмечено разрывом в лестнице." width="100%" />
329
363
  </p>
330
364
 
331
- <sub>Перегенерируется командой `npm run docs:hero`;
332
- [`tests/hero-asset-reproducibility.spec.ts`](tests/hero-asset-reproducibility.spec.ts)
333
- роняет CI, если артефакт разошёлся с тем, что печатает репортер.</sub>
365
+ | Уровень | Простыми словами | Что для этого нужно |
366
+ | ------- | ------------------- | -------------------------------------------------------- |
367
+ | **L0** | Замечено | Чтение кода |
368
+ | **L1** | Похоже на проблему | Чтение кода: совпал шаблон |
369
+ | **L2** | Доказано в коде | Чтение кода: дефект структурный |
370
+ | **L3** | Файл выполнялся | Отчёт прогона показывает, что файл находки выполнялся |
371
+ | **L4** | Тест выполнялся | Отчёт прогона показывает, что тест находки выполнялся |
372
+ | **L5** | Прогон подтверждает | Собственный результат прогона подтверждает класс дефекта |
334
373
 
335
- Скор прозрачен: **error −8, warning −3, info −1**, затем нормировка на
336
- экспозицию сьюта (вычеты на объявление теста). Вычеты, взвешенные по
337
- уликам, означают, что слабые сигналы стоят дешевле. Терминал показывает
338
- те же со скидкой числа, что использует скор — никакого чёрного ящика.
339
- Полная методика: [docs/SCORING.md](docs/SCORING.md).
374
+ Статическое сканирование останавливается на L2. Только отчёт реального прогона (Playwright JSON, Jest или Vitest JSON, JUnit XML) может поднять находку до L3 и выше, поэтому находка, которую ни разу не видели в работе, никогда не может утверждать обратное. Определения: [docs/TERMINOLOGY.md](docs/TERMINOLOGY.md).
340
375
 
341
- **Вердикты**
376
+ ### Сколько из этого измерено
342
377
 
343
- | Score | Вердикт |
344
- | ------- | ---------------- |
345
- | ≥ 80 | ✓ **WORTHY** |
346
- | 50 – 79 | ⚠ **NEEDS WORK** |
347
- | < 50 | ✖ **UNWORTHY** |
378
+ **У 74 из 79 правил доля ложных срабатываний измерена на реальном OSS-коде** (не менее 10 вручную классифицированных находок на каждое; см. [docs/FP-AUDIT.md](docs/FP-AUDIT.md)). Остальные 5 опираются на оценку автора и прямо говорят об этом, правило за правилом, в `mjolnir explain`. `mjolnir rules --unmeasured` перечисляет их, а подвал каждого сканирования сообщает, сколько из действительно _сработавших_ правил измерено.
348
379
 
349
- **Уровни улик** — каждая находка несёт один; он задаёт вес находки в
350
- скоре:
380
+ Показатели остаются публичными, даже когда они плохие. QA-TEST-001 (закоммиченный `.only`) плохо проходит аудит на реальных репозиториях и поэтому находится в quarantine. Актуальное значение для каждого правила, включая QA-PW-141, есть в аудите.
351
381
 
352
- | Уровень | Значение | Влияние на скор | Пример |
353
- | ------- | ------------------------ | ------------------ | ---------------------------------------------------------------- |
354
- | E2 | Детерминированный дефект | Полный вычет | `.only` в коммите — структурно доказуемо |
355
- | E1 | Эвристический паттерн | Половинный вычет | Найденный regex'ом `sleep()` — сильный сигнал, не доказательство |
356
- | E0 | Наблюдение | Ноль (только info) | Репортится, но никогда не гейтит CI и не вычитает |
382
+ ### Уровни доверия правил
357
383
 
358
- Большинство правил — **E1**. Слоган «we prove it» отсылает к этой
359
- системе: находки E2 — структурное доказательство; находки E1 —
360
- корректно позиционированные предупреждения, не формальные доказательства.
384
+ Уровни определяются измеренной долей ложных срабатываний, а не мнением:
361
385
 
362
- Пустой репозиторий получает `null`, никогда фейковую сотню — см.
363
- [Модель доверия](#модель-доверия).
386
+ | Уровень | Измеренная FP | Поведение |
387
+ | -------------- | -------------------------- | ---------------------------------------------------------------- |
388
+ | **core** | ≤ 10% | Отчёт по умолчанию, блокирует |
389
+ | **extended** | ≤ 30% | Отчёт по умолчанию, пониженная уверенность |
390
+ | **quarantine** | > 30% или явно объявленное | Только `--strict`, ограничено уровнем info, никогда не блокирует |
391
+ | _не измерено_ | n < 10 | Не может быть повышено до core, пока не измерено |
364
392
 
365
- ---
393
+ Полосы FP могут только понизить уровень — они никогда не повышают правило из `quarantine`, если оно было туда явно объявлено. Явно помещённое в карантин правило остаётся в quarantine независимо от измеренного уровня FP.
366
394
 
367
- ## 🎭 Selector Health Score
395
+ Повышение, понижение и зрелость по языкам: [жизненный цикл правил](https://sergey-bar.github.io/Mjolnir/reference/rule-lifecycle).
368
396
 
369
- Главная метрика для Playwright-сьютов — насколько устойчивы ваши
370
- локаторы:
397
+ ### Почему это не линтер
371
398
 
372
- ```text
373
- ▚ SELECTOR HEALTH — e2e/checkout.spec.ts
399
+ Линтеры говорят, следует ли код правилам. Mjölnir говорит, можно ли доверять вашей проверке.
374
400
 
375
- [█████████████████░░░] 83 / 100
376
- role/text: 2 · testid: 1 · css-chains: 1 ⚠ · xpath: 0
377
- ```
401
+ | | Линтеры (ESLint, SonarQube) | Инструменты покрытия | Код-ревью с ИИ | **Mjölnir** |
402
+ | ----------------------------------------------------------------- | :-------------------------: | :------------------: | :------------: | :-----------------: |
403
+ | Оценивает **систему проверки**, а не код продукта | Нет | Нет | Нет | Да |
404
+ | Целостность CI-workflow (`continue-on-error`, `\|\| true`) | Нет | Нет | только diff | Да |
405
+ | Оценивает устойчивость локаторов Playwright (Selector Health) | Нет | Нет | Нет | Да |
406
+ | Читает реальные данные прогонов для вердиктов `TRUE-FLAKE` | Нет | Нет | Нет | Да |
407
+ | Публикует измеренную долю ложных срабатываний для каждого правила | Нет | Нет | Нет | Да |
408
+ | Отмечает тесты без утверждений | Да\* | Нет | иногда | Да |
409
+ | Находит жёсткие sleep (`waitForTimeout`, `time.sleep`) | Да\* | Нет | иногда | Да |
410
+ | Детерминированность (одинаковый вход — одинаковый выход) | Да | Да | Нет | Да |
411
+ | Стоимость сканирования | бесплатно | бесплатно | токены | **ноль** (локально) |
412
+
413
+ <sub>\*Покрывается `eslint-plugin-jest` и `eslint-plugin-playwright` (`expect-expect`, `no-wait-for-timeout`), а также собственными правилами SonarQube для утверждений. Столбцы описывают поведение по умолчанию при проверке наборов тестов; плагины, платные тарифы и собственные правила меняют некоторые ответы. Это сводка позиционирования, а не бенчмарк.</sub>
378
414
 
379
- Локаторы на основе ролей получают полный балл. Цепочки CSS-классов и
380
- XPath топят скор — они ломаются на любом DOM-рефакторе, не сообщая,
381
- какое поведение регрессировало.
415
+ Используйте и ревью с ИИ. Оно улавливает нюансы, намерения и ошибки проектирования, которые не найдёт ни один шаблон. Mjölnir находит то, что ревью с ИИ пропускает, потому что это выглядит намеренным: закоммиченный `.only`, проглоченный код выхода, `continue-on-error` на тестовой джобе. Здесь нужно сканирование, а не рассуждение.
382
416
 
383
- ---
417
+ <br />
384
418
 
385
- ## 🔬 Runtime-улики
419
+ ## Анализ прогонов тестов
386
420
 
387
- Статическое детектирование флакости — гадание. Mjölnir читает **реальные
388
- данные выполнения** — JSON-репорты Playwright и XML JUnit от любого
389
- раннера:
421
+ Статический анализ рассуждает о коде, который никогда не выполнялся. Анализ прогонов читает то, что произошло на самом деле: Playwright JSON, Jest JSON, Vitest JSON и JUnit XML от любого раннера.
390
422
 
391
423
  ```bash
392
424
  mjolnir forensics ./test-results/
393
425
  ```
394
426
 
395
427
  ```text
396
- ▚ FLAKINESS LEADERBOARD
428
+ ▍ FLAKINESS LEADERBOARD
397
429
 
398
430
  3 tests · 1 failed · 1 flaky · 1 retried
399
431
 
@@ -403,297 +435,184 @@ FAILING declines an expired card (e2e/checkout.spec.ts)
403
435
  ████░░░░░░░░░░░░░░░░ 1.1s · 1 attempt
404
436
  ```
405
437
 
406
- Тест, проходящий только с попытки ≥ 2, — не проходящий тест; это
407
- везучий тест. Он помечается `TRUE-FLAKE` независимо от финальной
408
- зелёной галочки.
438
+ `TRUE-FLAKE` не означает, что тест перезапускался. Это означает, что тест **провалил хотя бы одну попытку, а затем завершился зелёным**: случайный успех, отмеченный независимо от того, что показывает итоговая галочка. `mjolnir triage` превращает эту историю в предложение карантина, а `mjolnir pw-report` подводит итоги прогона. Именно эти отчёты прогонов поднимают находки до уровней доверия L3 и выше.
409
439
 
410
- ---
440
+ <br />
411
441
 
412
- ## ⚡ Mjölnir — не ещё один линтер
442
+ ## Целостность CI
413
443
 
414
- Линтеры говорят, соответствует ли код правилам. Mjölnir говорит,
415
- можно ли доверять вашей верификации.
444
+ Тест может проходить, пока окружающий его пайплайн не способен упасть. Mjölnir читает и workflow: `continue-on-error`, `|| true`, коды выхода, которые никогда не передаются дальше, всегда успешные шаги, отчёты, которые используются, но никогда не создаются, и гейты, пропускаемые именно в тех событиях, которые должны блокировать. Каждая находка называет джобу, шаг и строку и несёт свой уровень доказательности.
416
445
 
417
- | | ESLint / SonarQube | Coverage-инструменты | Ручное ревью | **Mjölnir** |
418
- | ------------------------------------------------------------- | :----------------: | :------------------: | :----------: | :---------: |
419
- | Целостность CI-workflow (`continue-on-error`, `\|\| true`) | ❌ | ❌ | редко | ✅ |
420
- | Кросс-язык (TS, Python, Java, C#) из одного инструмента | ❌ | ❌ | ❌ | ✅ |
421
- | Оценивает устойчивость Playwright-локаторов (Selector Health) | ❌ | ❌ | редко | ✅ |
422
- | Помечает тесты без настоящих ассертов | ✅ (плагин)\* | ❌ | иногда | ✅ |
423
- | Ловит жёсткие sleep'ы (`waitForTimeout`, `time.sleep`) | ✅ (плагин)\* | ❌ | иногда | ✅ |
424
- | Работает за секунды, ноль сетевых вызовов при скане | ✅ | ✅ | — | ✅ |
446
+ Создайте workflow для PR, по умолчанию рекомендательный:
425
447
 
426
- \*`eslint-plugin-jest` (`expect-expect`) и `eslint-plugin-playwright`
427
- (`expect-expect`, `no-wait-for-timeout`) покрывают это для своих
428
- фреймворков.
448
+ ```bash
449
+ mjolnir ci install
450
+ ```
429
451
 
430
- **Runtime-анализ** — отдельная категория рядом со статическим линтингом:
452
+ Или добавьте action из Marketplace в уже существующий workflow:
431
453
 
432
- | | Playwright retry reporter | Allure / ReportPortal | **Mjölnir forensics** |
433
- | ---------------------------------------------------------- | :-----------------------: | :-------------------: | :-------------------: |
434
- | Читает реальные данные прогонов для вердиктов `TRUE-FLAKE` | частично\* | частично (тег) | ✅ |
435
- | Отчёт флей-триажа из истории выполнения | ❌ | ✅ | ✅ |
436
- | Интегрируется со статическим скором достойности | ❌ | ❌ | ✅ |
454
+ ```yaml
455
+ - uses: Sergey-Bar/Mjolnir@v1
456
+ with:
457
+ scope: changed
458
+ fail-on: error
459
+ ```
437
460
 
438
- \*Playwright отслеживает ретраи внутри, но не выдаёт самостоятельного
439
- отчёта о флакости с вердиктными метками.
461
+ Закрепите `@v1`, чтобы следовать мажорной линии, или точный тег (`@v0.5.32`) для воспроизводимого гейта. [docs/DISTRIBUTION-KIT.md](docs/DISTRIBUTION-KIT.md) описывает Marketplace, Smithery и реестры MCP.
440
462
 
441
- ---
463
+ Чтобы отправить находки в GitHub Code Scanning, загрузите SARIF (требуется `security-events: write` на уровне workflow или job):
442
464
 
443
- ## 🤖 Почему бы не использовать просто AI-код-ревью?
465
+ ```yaml
466
+ - run: npx mjolnir-qa@latest --format sarif > mjolnir.sarif
467
+ continue-on-error: true
468
+ - uses: github/codeql-action/upload-sarif@v3
469
+ if: ${{ !cancelled() }}
470
+ with:
471
+ sarif_file: mjolnir.sarif
472
+ ```
444
473
 
445
- Другая проблема, другой слой. AI-ревью может заметить подозрительное
446
- изменение теста в диффе; оно не доказывает, что система верификации в
447
- целом заслуживает доверия — и видит только показанный ему дифф.
474
+ В GitLab `--format codequality` записывает отчёт Code Quality, который читают виджет MR и аннотации diff ([docs/GITLAB-CI.md](docs/GITLAB-CI.md)). Настройка редактора и пайплайна: [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md).
448
475
 
449
- | | AI-код-ревью (Copilot и пр.) | **Mjölnir** |
450
- | ------------------------------------------- | :------------------------------: | :-------------------------------------: |
451
- | Цена за скан | Токены (растут с размером диффа) | **Ноль** (локально, установлен) |
452
- | Видит весь сьют + все CI-конфиги | Только PR-дифф, показанный ему | **Всё, каждый раз** |
453
- | Детерминирован (тот же вход → тот же выход) | ❌ (недетерминирован) | **✅** |
454
- | Ловит паттерны, дремлющие месяцами | Только если в контексте | **✅** (сканирует все файлы) |
455
- | Помнит находки между запусками | ❌ (нет памяти между сессиями) | **✅** (baseline + diff) |
456
- | Запускается без человека | Нужен PR или промпт | **✅** (CI-хук, выполняется за секунды) |
476
+ ### Привязка к изменениям ветки
457
477
 
458
- **Используйте оба.** AI ловит нюанс, замысел и дизайнерские изъяны,
459
- которые не найдёт ни один regex. Mjölnir ловит структурные паттерны,
460
- которые AI упускает, потому что те выглядят «намеренными» —
461
- закоммиченный `.only`, проглоченный exit-код, `continue-on-error` на
462
- тестовом job. Это не баги, требующие рассуждений; это факты, требующие
463
- сканирования.
478
+ ```bash
479
+ npx mjolnir-qa@latest --scope changed
480
+ ```
464
481
 
465
- ---
482
+ Находки привязываются к строкам, добавленным вашей веткой, относительно **merge-base**. Область — тот же набор файлов, что находит полное сканирование (спецификации TS/JS и конфигурации адаптеров, `test_*.py`, `*Test.java`, `*Tests.cs`, `.github/workflows/*.yml`), плюс незакоммиченные и неотслеживаемые изменения, поэтому это работает ещё до коммита. База определяется в порядке `main → master → origin/main → origin/master → origin/HEAD`; её можно переопределить через `--base <ref>`.
466
483
 
467
- ## 🤖 Интеграция CI
484
+ Если merge-base определить не удаётся (неглубокий клон, отсоединённый HEAD, цель вне git), находки откатываются к привязке ко всему файлу, **и отчёт об этом сообщает.** Тихий откат был бы ровно тем дефектом, ради поиска которого существует этот инструмент.
468
485
 
469
- Одна команда генерирует PR-workflow — по умолчанию рекомендательный,
470
- никогда блокирующий:
486
+ <br />
471
487
 
472
- ```bash
473
- mjolnir ci install
474
- ```
488
+ ## ИИ-агенты
475
489
 
476
- Или подключите нативно к GitHub Code Scanning через SARIF:
490
+ Находки чего-то стоят, только если на них кто-то реагирует.
477
491
 
478
- ```yaml
479
- - run: npx mjolnir-qa@latest --format sarif > mjolnir.sarif
480
- - uses: github/codeql-action/upload-sarif@v3
481
- with:
482
- sarif_file: mjolnir.sarif
492
+ ```text
493
+ SCAN → EVIDENCE → HANDOFF → AGENT → RE-SCAN → PROOF
483
494
  ```
484
495
 
485
- Настройка редактора и пайплайна для SARIF:
486
- [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md).
496
+ **ИИ пишет исправление. Mjölnir его проверяет.** Доказательство даёт повторное сканирование, а не собственный отчёт агента об успехе.
487
497
 
488
- ### Охват по изменённому scope
498
+ | Команда | Что получает агент |
499
+ | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
500
+ | `mjolnir mcp` | Сервер [MCP](https://modelcontextprotocol.io) через stdio. `scan`, `explain` и `diff` становятся вызываемыми инструментами. |
501
+ | `mjolnir handoff` | Сохранённый отчёт `--json` превращается в детерминированный план в Markdown: что обнаружено, граница доказательности для каждой находки, что **не** должно меняться и как это проверить. |
502
+ | `mjolnir install` | Записывает в места для агентов, которые уже есть в вашем репозитории (`.claude/`, `.cursor/`, `.kilo/`, `AGENTS.md`), чтобы агент пересканировал код, прежде чем заявить, что закончил. |
489
503
 
490
- `--scope changed` атрибутирует находки строкам, добавленным в вашей
491
- ветке относительно merge-base с `main`. Он покрывает тестовые файлы
492
- (`*.spec.*`, `*.test.*`), плюс workflow-файлы GitHub и конфигурации
493
- Playwright в диффе. Когда merge-base не разрешается — shallow clone,
494
- detached HEAD, не-git-цель, другой дефолтный ветка — он честно
495
- деградирует: находки возвращаются к атрибуции на весь файл, и отчёт об
496
- этом говорит. Переопределите базовую ref через `--base <ref>`.
504
+ Добавьте его в клиент, у которого есть собственный CLI:
497
505
 
498
- ---
499
-
500
- ## Конфигурация
501
-
502
- Mjölnir — zero-config. Опциональный `mjolnir.config.json` (или
503
- `.mjolnir.json`) в корне репо подстраивает severity, гейтинг и scope —
504
- он никогда не меняет семантику детекции.
506
+ ```bash
507
+ claude mcp add mjolnir -- npx -y mjolnir-qa@latest mcp
508
+ ```
505
509
 
506
- | Key | Тип | Действие |
507
- | ------------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
508
- | `exclude` | `string[]` | Дополнительные ignore-глобы (подмножество gitignore), поверх встроенных дефолтов |
509
- | `gate` | `"advisory" \| "error" \| "warning"` | Какие severity завершают процесс ненулевым кодом (по умолчанию `error`; `advisory` никогда не блокирует) |
510
- | `severityOverrides` | `{ "<RULE-ID>": severity }` | Переранжирует находки правила для вашего репо |
511
- | `ignore` | `IgnoreEntry[]` | Подавляет находки — **`reason` обязателен**; записи истекают через 90 дней (явная дата `expires`, либо время последнего изменения файла конфига для записей без неё) |
512
- | `plugins` | `string[]` | Сторонние пакеты правил (см. [Модель доверия](#модель-доверия)) |
510
+ Или в любой клиент, принимающий блок `mcpServers`:
513
511
 
514
512
  ```json
515
513
  {
516
- "gate": "error",
517
- "exclude": ["legacy/**"],
518
- "severityOverrides": { "QA-PW-141": "warning" },
519
- "ignore": [
520
- {
521
- "ruleId": "QA-TEST-004",
522
- "files": ["e2e/legacy-login.spec.ts"],
523
- "reason": "Third-party widget needs a settle delay; tracked in JIRA-4821",
524
- "expires": "2026-12-31"
525
- }
526
- ]
514
+ "mcpServers": {
515
+ "mjolnir": { "command": "npx", "args": ["-y", "mjolnir-qa@latest", "mcp"] }
516
+ }
527
517
  }
528
518
  ```
529
519
 
530
- - **`.mjolnirignore`** — простой файл в стиле gitignore для исключений
531
- путей, тот же диалект, что `exclude`. Используйте его для
532
- машинного шума; используйте `exclude`, когда список должен жить в
533
- версионном контроле вместе с остальной конфигурацией.
534
- - **CLI-переопределения** — `--strict` (включить правила карантина),
535
- `--width <cols>` и `--ascii` / `--no-ascii` (терминальный рендер),
536
- `--tone blunt` (более резкие сообщения), `--max-duration <sec>`
537
- (ограниченный частичный скан).
538
- - Подавление правил и жизненный цикл депрекации:
539
- [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md).
540
-
541
- Записи `ignore` также питают отдельную команду `mjolnir suppressions`,
542
- которая перечисляет текущие подавления и время истечения каждой записи.
543
-
544
- ---
545
-
546
- ## 📐 Коды выхода и контракты
547
-
548
- Заморожены — безопасно строить на них CI-логику:
549
-
550
- | Код выхода | Значение |
551
- | ---------- | --------------------------------------------------------------------------------- |
552
- | `0` | Чисто — нет находок на уровне гейта или выше |
553
- | `1` | Находки на уровне гейта или выше |
554
- | `2` | Частичный скан (исчерпан бюджет времени, нечитаемые файлы) — никогда не блокирует |
555
- | `10` | Ошибка использования (плохой флаг, отсутствие цели) |
556
- | `20` | Внутренняя ошибка |
557
-
558
- JSON/SARIF-отчёт — `schemaVersion: 1`. ID правил
559
- (`QA-<FAMILY>-NNN`) неизменны после выхода и никогда не используются
560
- повторно.
561
-
562
- ---
563
-
564
- ## Модель доверия
565
-
566
- - **Local-first** — ноль сетевых вызовов во время сканирования. Никогда.
567
- Ноль телеметрии.
568
- - **Никаких ложных доказательств** — мы скорее скажем «неизвестно», чем
569
- «проверено». Пустое репо получает `score: null`, никогда фейковую
570
- сотню.
571
- - **Частичная честность** — если анализ оборван, вывод об этом говорит.
572
- Никогда «complete», когда это не так.
573
- - **FP-фаервол** — детекция работает на очищенном от комментариев и
574
- строк представлении кода (правила TypeScript используют AST
575
- компилятора): паттерн внутри прозаического комментария или
576
- док-примера-строки — это документация, а не находка.
577
- - **Измерено, а не заявлено** — в головные тиры выходят только правила
578
- с частотой ложных срабатываний из реального OSS-кода (см.
579
- [Сколько из этого измерено](#сколько-из-этого-измерено)); футер скана
580
- и `mjolnir rules --unmeasured` скажут, какие какие.
581
- - **Доверие к плагинам и ворота исполнения** — плагины — это npm-пакеты,
582
- объявленные в
583
- `"plugins"`; JS-модули живут в `mjolnir-rules/*.mjs`.
584
- **Песочницы нет**: код плагина работает с полными
585
- привилегиями Node, та же модель доверия, что у плагинов ESLint или
586
- Vitest. Именно поэтому исполнение кода — **opt-in при каждом скане**:
587
- передайте `--enable-plugins` (или задайте
588
- `MJOLNIR_ENABLE_PLUGINS=1`), иначе источники НЕ загружаются —
589
- громкое уведомление в stderr точно перечисляет пропущенное. Сканирование
590
- недоверенного кода никогда его не исполняет. JSON-манифесты правил
591
- (`mjolnir-rules/*.json`) не затронуты: они декларируют regex-паттерны
592
- и по конструкции не исполняют код. Префиксы ID основных правил
593
- зарезервированы и отвергаются от
594
- плагинов и внешних правил против подмены.
595
- - **Workspace-локальные внешние правила** (фолдерные, ноль сети) —
596
- каталог `mjolnir-rules/` рядом с целью скана загружает собственные
597
- правила: JSON-файлы декларируют regex-паттерны (код не исполняется),
598
- модули `.mjs`/`.js` экспортируют `rules` (полное доверие Node, как у
599
- плагинов). Внешние правила несут те же trust-метаданные, что и core;
600
- они никогда не могут выйти в core-тире (core требует измеренной
601
- FP-частоты из corpus-сайдкара — заявленный `tier: "core"` зажимается
602
- до `extended`), соблюдают тировые лимиты и проверяются на дрейф:
603
- `mjolnir rules --md --external` рендерит каталог из загруженных
604
- файлов (происхождение `external`), а генератор матрицы принимает
605
- `--external <root>`.
606
-
607
- ---
608
-
609
- ## 🏗️ Архитектура
520
+ **Ограничитель важнее удобства.** Каждая находка в передаче несёт свою границу. **E2** говорит _детерминированно: проверьте место и примените исправление_. **E1** говорит _ТРЕБУЕТСЯ ПОДТВЕРЖДЕНИЕ: одно наблюдение не доказывает дефект_. Агент, который вслепую исправляет E1, подавляет правило или редактирует правило, чтобы поднять оценку, делает ровно то, ради поиска чего существует этот инструмент, поэтому передача говорит об этом прямо в промпте, рядом с находкой.
610
521
 
611
- <details>
612
- <summary>Развернуть дерево</summary>
522
+ <br />
613
523
 
614
- ```
615
- mjolnir/
616
- ├── src/
617
- │ ├── engine/ # LanguageAdapter interface + rule runner
618
- │ ├── adapters/ # typescript · python · java · csharp · github-actions
619
- │ ├── rules/ # rules across 8 families + the measured-FP table
620
- │ ├── playwright/ # Selector Health Score engine
621
- │ ├── discovery/ # workspace, frameworks, ignore resolution
622
- │ ├── scope/ # git merge-base changed-scope engine
623
- │ ├── scorer/ # transparent deduction table + prioritization
624
- │ ├── reporter/ # terminal · JSON · SARIF 2.1 · Mermaid
625
- │ ├── forensics/ # run-data ingestion · flake verdicts · triage
626
- │ ├── config/ # mjolnir.config.json + suppressions
627
- │ ├── plugins/ # third-party rule loading (no sandbox)
628
- │ └── commands/ # every subcommand
629
- └── tests/
630
- ├── fixtures/ # must-fire / must-not-fire per rule
631
- └── golden/ # frozen score regression locks
632
- ```
524
+ ## Доверие и безопасность
633
525
 
634
- </details>
526
+ **Локально, без телеметрии.** Нигде в `src/` нет ни одного API с сетевыми возможностями (`fetch`, `http`, `https`, `net`, `dns`, `dgram`, WebSocket), а [`privacy-network-isolation.spec.ts`](tests/contract/privacy-network-isolation.spec.ts) проваливает сборку, если такой появится. Он также запрещает `eval` и `new Function`. Сканирование недоверенного кода никогда его не выполняет: статический анализ читает исходный текст, а анализ прогонов разбирает файлы отчётов, которые уже есть на диске.
527
+
528
+ Две оговорки: сам `npx` скачивает пакет до того, как что-либо запустится, а гарантия распространяется на `src/`, но не на сторонние плагины.
529
+
530
+ **Плагины не изолированы в песочнице.** JS-плагины (`mjolnir-rules/*.mjs` или npm-пакеты, перечисленные в `"plugins"`) работают с полными привилегиями Node — та же модель доверия, что и у плагинов ESLint или Vitest. Их загрузка включается явно **для каждого сканирования**: без `--enable-plugins` (или `MJOLNIR_ENABLE_PLUGINS=1`) их исходники никогда не загружаются, а сообщение в stderr перечисляет пропущенное. JSON-манифесты правил не выполняют код, а префиксы ID core-правил зарезервированы, чтобы плагин не мог выдать себя за одно из них. Сообщайте об уязвимостях через [SECURITY.md](SECURITY.md).
531
+
532
+ **Он проверяет сам себя.** Движок доверия к проверкам ничего не стоит, если он сам не поддаётся проверке. Каждый прогон CI сканирует этот репозиторий сборкой, созданной тем же прогоном. Гейт падает при любой находке уровня error, а также при **частичном** сканировании или **упавшем правиле**, потому что обрезанное самосканирование, которое ни о чём не сообщает, — это и есть ложный зелёный, ради поиска которого существует этот проект. `mjolnir doctor` в том же прогоне заново проверяет базу правил (защита фикстур, честность уровней, лимит уровня core), а проверка с результатом INCONCLUSIVE падает точно так же, как проваленная. Оба отчёта загружаются как артефакты сборки.
533
+
534
+ ### Коды выхода и машинный контракт
635
535
 
636
- - **Правила — чистые функции** — `(SourceFileContext) → Finding[]`, без
637
- I/O, без глобалов. Новый экосистем = один адаптер + его правила.
638
- - **TypeScript/Playwright использует AST компилятора** (ts-morph).
639
- Python, Java и C# работают на общем regex-слое с маскированием
640
- комментариев и строк.
641
- - Слой tree-sitter WASM AST для Java и C# существует и является
642
- следующим шагом точности — он ещё не подключён к синхронному
643
- скан-пайплайну.
536
+ Заморожены, чтобы на них можно было строить логику CI:
644
537
 
645
- ---
538
+ | Код выхода | Значение |
539
+ | ---------- | ---------------------------------------------------------------------------------------- |
540
+ | `0` | Чисто: нет находок на уровне гейта или выше |
541
+ | `1` | Есть находки на уровне гейта или выше |
542
+ | `2` | Частичное сканирование (исчерпан лимит времени, нечитаемые файлы). Никогда не блокирует. |
543
+ | `10` | Ошибка использования (неверный флаг, нет цели) |
544
+ | `20` | Внутренняя ошибка |
646
545
 
647
- ## 📚 Документация
546
+ `2` намеренно отличается от `0`: сканирование, которое не завершилось, не «ничего не нашло». Оно просто не закончило искать.
648
547
 
649
- | Документ | Что внутри |
650
- | ------------------------------------------------------ | ------------------------------------------------- |
651
- | [docs/SCORING.md](docs/SCORING.md) | Нормировка скора + взвешивание по уликам |
652
- | [docs/FP-AUDIT.md](docs/FP-AUDIT.md) | Измеренные частоты ложных срабатываний + методика |
653
- | [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md) | Состояния правил, подавление, депрекация |
654
- | [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md) | SARIF-вывод + настройка редактора/CI |
655
- | [docs/rules/](docs/rules/) | Сгенерированный каталог по правилам |
656
- | [CONTRIBUTING.md](CONTRIBUTING.md) | Dev-сетап + процесс контрибуции |
657
- | [CHANGELOG.md](CHANGELOG.md) | История релизов |
658
- | [SECURITY.md](SECURITY.md) | Сообщение об уязвимостях |
548
+ Всё, что потребляет машина (результаты инструментов MCP, `--json`, SARIF 2.1), берётся из одного канонического результата по версионированной схеме, **расширяемой только добавлением** (`schemaVersion: 1`, `contractVersion: 1`), поэтому ни одному потребителю не нужно восстанавливать смысл из отрисованного текста. См. [машинный контракт](docs/machine-contract.md). ID правил (`QA-<FAMILY>-NNN`) неизменны после выпуска и никогда не используются повторно.
659
549
 
660
- ---
550
+ <br />
661
551
 
662
- ## 📈 Статус
552
+ ## Чего Mjölnir сказать не может
663
553
 
664
- **v0.5.x · открытая бета.** JSON-схема и коды выхода — замороженные
665
- контракты. TypeScript и Python имеют самый широкий измеренный охват;
666
- Java и C# новее — читайте о них в
667
- [таблице тиров](#тиры-правил-и-зрелость-языков).
554
+ - **Он не запускает ваши тесты.** Чистое сканирование — это не проходящий набор тестов.
555
+ - **Он не может сказать, что утверждение _неверно_.** `expect(total).toBe(41)` выглядит здоровым. Mjölnir находит тесты, которые _не могут упасть_, и пайплайны, которые _не могут покраснеть_, а не тесты, проверяющие не то.
556
+ - **Он не доказывает бизнес-корректность.** Ничто здесь не говорит, что ваш продукт делает то, чего требовало требование.
557
+ - **100 — не доказательство хорошего набора тестов.** Покрывает ли ваш набор реальные риски — отдельный вопрос, и этот инструмент на него не отвечает.
558
+ - **5 из 79 правил опираются на оценку**, а не на измеренную долю. Каждое из них говорит об этом в своей находке.
559
+ - **E1 — это не E2.** Эвристические находки стоит читать, но не стоит применять вслепую.
560
+ - **Пустой репозиторий получает `null`, никогда не 100.**
561
+ - **Файл с именем `*.spec.ts` без объявлений тестов не считается покрытием.** Репозиторий, где единственные spec-файлы содержат импорты или типы (ноль вызовов `it`/`test`), получает `null`, а не 100.
668
562
 
669
- ---
563
+ <br />
670
564
 
671
- ## 🤝 Участие в проекте
565
+ ## Документация
672
566
 
673
- Новые правила — самый простой первый вклад: одна команда скаффолдит
674
- правило и его must-fire- **и** must-not-fire-фикстуры (сгенерированное
675
- правление намеренно падает на фикстурах, пока вы не реализуете реальную
676
- детекцию — стаб не может выйти):
567
+ Полный сайт документации находится по адресу <https://sergey-bar.github.io/Mjolnir/>.
568
+
569
+ | Документ | Что внутри |
570
+ | ------------------------------------------------------ | --------------------------------------------------------------------- |
571
+ | [docs/SCORING.md](docs/SCORING.md) | Нормирование оценки и взвешивание доказательств |
572
+ | [docs/TERMINOLOGY.md](docs/TERMINOLOGY.md) | Канонический словарь: одно слово на понятие |
573
+ | [docs/FP-AUDIT.md](docs/FP-AUDIT.md) | Измеренные доли ложных срабатываний и методика |
574
+ | [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md) | Состояния правил, уровни, подавление, вывод из употребления |
575
+ | [docs/VERSIONING.md](docs/VERSIONING.md) | Политика semver, замороженные интерфейсы, цикл вывода из употребления |
576
+ | [docs/machine-contract.md](docs/machine-contract.md) | Канонический машиночитаемый результат |
577
+ | [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md) | Вывод SARIF и настройка редактора или CI |
578
+ | [docs/GITLAB-CI.md](docs/GITLAB-CI.md) | GitLab: отчёт Code Quality, рецепт для MR, гейт |
579
+ | [docs/rules/](docs/rules/) | Сгенерированный каталог по правилам |
580
+ | [CONTRIBUTING.md](CONTRIBUTING.md) | Среда разработки и процесс внесения изменений |
581
+ | [SUPPORT.md](SUPPORT.md) | Где спросить, сообщить о проблеме и получить помощь |
582
+ | [SECURITY.md](SECURITY.md) | Сообщение об уязвимостях |
583
+ | [CHANGELOG.md](CHANGELOG.md) | История релизов |
584
+
585
+ ### Статус
586
+
587
+ **Версия 1.** JSON-схема и коды выхода — замороженные контракты. У TypeScript и Python самое широкое измеренное покрытие. Java и C# новее; оценивайте их по [таблице зрелости](https://sergey-bar.github.io/Mjolnir/reference/rule-lifecycle). Что будет дальше, без выдуманных дат: [публичная дорожная карта](https://sergey-bar.github.io/Mjolnir/reference/roadmap).
588
+
589
+ ### Участие в разработке
590
+
591
+ Новые правила — самый простой первый вклад. Одна команда создаёт заготовку правила с фикстурами must-fire **и** must-not-fire. Сгенерированное правило намеренно проваливает собственные фикстуры, пока не написано настоящее обнаружение, потому что выпущенная заглушка — это правило, которое никто не измерял:
677
592
 
678
593
  ```bash
679
594
  mjolnir create-rule QA-PW-140 --title "Screenshot without diff bound"
680
595
  ```
681
596
 
682
- Полный dev-сетап, команды постоянного гейта и законы anti-creep /
683
- фикстурного фаервола — в [CONTRIBUTING.md](CONTRIBUTING.md).
597
+ Среда разработки, команды постоянных гейтов, а также законы anti-creep и защиты фикстур описаны в [CONTRIBUTING.md](CONTRIBUTING.md).
684
598
 
685
- ---
599
+ <br />
686
600
 
687
601
  <div align="center">
688
602
 
689
- **Перестаньте выкатывать тесты, которым нельзя доверять.**
603
+ <img src="assets/readme/closing.svg" alt="Запустите его на своём репозитории." width="100%" />
690
604
 
691
605
  ```bash
692
606
  npx mjolnir-qa@latest
693
607
  ```
694
608
 
695
- **Star ⭐ · Watch 👀 · Contribute 🤝**
609
+ [Читать руководство](https://sergey-bar.github.io/Mjolnir/guide/getting-started) · [Сайт документации](https://sergey-bar.github.io/Mjolnir/) · [npm](https://www.npmjs.com/package/mjolnir-qa)
610
+
611
+ <br />
612
+
613
+ Не спрашивайте, прошли ли тесты.<br />
614
+ Спросите, доказывают ли доказательства, что они заслуживают доверия.
696
615
 
697
- Создано [Сергеем Баром](https://www.linkedin.com/in/sergeybar/)
616
+ <sub>Создал [Sergey Bar](https://www.linkedin.com/in/sergeybar/) · Лицензия MIT</sub>
698
617
 
699
618
  </div>