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/CHANGELOG.md +204 -0
- package/README.ar.md +434 -478
- package/README.bn.md +434 -491
- package/README.br.md +434 -520
- package/README.bs.md +431 -500
- package/README.da.md +433 -509
- package/README.de.md +432 -522
- package/README.es.md +428 -517
- package/README.fr.md +426 -520
- package/README.gr.md +433 -517
- package/README.he.md +433 -475
- package/README.it.md +436 -525
- package/README.ja.md +436 -506
- package/README.ko.md +434 -494
- package/README.md +453 -438
- package/README.no.md +435 -509
- package/README.pl.md +433 -511
- package/README.ru.md +434 -515
- package/README.th.md +434 -484
- package/README.tr.md +427 -504
- package/README.uk.md +433 -505
- package/README.vi.md +436 -494
- package/README.zh.md +433 -462
- package/README.zht.md +433 -462
- package/dist/cli.d.mts +716 -110
- package/dist/cli.mjs +9815 -22027
- package/dist/mcp/stdio.mjs +2164 -886
- package/dist/rolldown-runtime-8H4AJuhK.mjs +14 -0
- package/dist/scan-pipeline-C0ka-RmX.mjs +2 -0
- package/dist/scan-pipeline-D3Yk2cef.mjs +15309 -0
- package/package.json +14 -8
package/README.ru.md
CHANGED
|
@@ -1,399 +1,431 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
<img src="assets/readme/
|
|
3
|
+
<img src="assets/readme/hero.svg" alt="Mjölnir. Тесты говорят, что прошло. Mjölnir говорит, чему можно доверять." width="100%" />
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
<br />
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
ломается доверие.
|
|
7
|
+
Mjölnir находит тесты, которые не могут упасть, и пайплайны, которые не могут покраснеть,<br />
|
|
8
|
+
а затем оценивает, насколько можно доверять результату, с доказательством для каждого пункта.
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
[](https://github.com/Sergey-Bar/Mjolnir/actions/workflows/ci.yml)
|
|
13
|
-
[](LICENSE)
|
|
14
|
-
[](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
|
-
|
|
12
|
+
[](https://www.npmjs.com/package/mjolnir-qa)
|
|
13
|
+
[](https://www.npmjs.com/package/mjolnir-qa)
|
|
14
|
+
[](https://github.com/Sergey-Bar/Mjolnir/actions/workflows/ci.yml)
|
|
15
|
+
[](https://codecov.io/gh/Sergey-Bar/Mjolnir)
|
|
16
|
+
[](https://scorecard.dev/viewer/?uri=github.com/Sergey-Bar/Mjolnir)
|
|
17
|
+
[](LICENSE)
|
|
18
|
+
[](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
|
-
<
|
|
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
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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
|
-
|
|
63
|
-
получите:
|
|
112
|
+
`mjolnir explain QA-CI-001` выводит полное досье доверия правила, включая измеренную долю ложных срабатываний и уровень, который эта доля ему обеспечила:
|
|
64
113
|
|
|
65
114
|
```text
|
|
66
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
95
|
-
npx mjolnir-qa@latest
|
|
155
|
+
Docs: mjolnir rules --md (full catalog, this rule included)
|
|
96
156
|
```
|
|
97
157
|
|
|
98
|
-
|
|
99
|
-
|
|
158
|
+
Вот единица ценности: одно место, где CI сообщает об успехе, которого он не заслужил.
|
|
159
|
+
|
|
160
|
+
<br />
|
|
161
|
+
|
|
162
|
+
## Быстрый старт
|
|
100
163
|
|
|
101
164
|
```bash
|
|
102
|
-
npx mjolnir-qa@latest
|
|
165
|
+
npx mjolnir-qa@latest
|
|
103
166
|
```
|
|
104
167
|
|
|
105
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
|
136
|
-
|
|
|
137
|
-
| `mjolnir
|
|
138
|
-
| `mjolnir
|
|
139
|
-
| `mjolnir
|
|
140
|
-
| `mjolnir
|
|
141
|
-
| `mjolnir
|
|
142
|
-
| `mjolnir
|
|
143
|
-
| `mjolnir
|
|
144
|
-
| `mjolnir
|
|
145
|
-
| `mjolnir
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
|
190
|
-
|
|
|
191
|
-
| QA-TEST-
|
|
192
|
-
| QA-
|
|
193
|
-
| QA-
|
|
194
|
-
| QA-
|
|
195
|
-
| QA-
|
|
196
|
-
| QA-
|
|
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
|
-
|
|
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
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
|
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
|
-
|
|
213
|
-
<summary><strong>Playwright 🎭</strong></summary>
|
|
301
|
+
Каждое правило поставляется с фикстурой must-fire **и** фикстурой must-not-fire, а правило, срабатывающее на собственной негативной фикстуре, не может быть выпущено. Это защита от ложных срабатываний; `mjolnir doctor` обеспечивает её в собственном CI этого репозитория.
|
|
214
302
|
|
|
215
|
-
|
|
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
|
-
|
|
305
|
+
`mjolnir doctor:playwright` оценивает каждый локатор по тому, как он находит элемент: так, как это сделал бы пользователь (роль, метка, текст), через явный контракт (`data-testid`) или по структурной случайности (цепочки CSS, XPath). Каждый файл получает оценку от 0 до 100:
|
|
223
306
|
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
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
|
-
|
|
254
|
-
|
|
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
|
-
|
|
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
|
-
|
|
321
|
+
<br />
|
|
265
322
|
|
|
266
|
-
|
|
267
|
-
<summary><strong>C# / .NET — NUnit · xUnit · MSTest 🟣</strong></summary>
|
|
323
|
+
## Оценка надёжности
|
|
268
324
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
329
|
+
<sub>Каждая оценка от 0 до 100, размещённая настоящим `deriveScoreState`. Сгенерировано командой `npm run docs:gauge` и защищено от расхождений в CI.</sub>
|
|
278
330
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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
|
-
|
|
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
|
-
|
|
303
|
-
его **измеренной** частоте ложных срабатываний:
|
|
345
|
+
## Модель доказательств
|
|
304
346
|
|
|
305
|
-
|
|
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
|
-
|
|
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/
|
|
362
|
+
<img src="assets/readme/trust-ladder.svg" alt="Лестница доверия от L0 до L5. L0–L2 получаются чтением кода; для L3–L5 нужен отчёт реального прогона, что отмечено разрывом в лестнице." width="100%" />
|
|
329
363
|
</p>
|
|
330
364
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
365
|
+
| Уровень | Простыми словами | Что для этого нужно |
|
|
366
|
+
| ------- | ------------------- | -------------------------------------------------------- |
|
|
367
|
+
| **L0** | Замечено | Чтение кода |
|
|
368
|
+
| **L1** | Похоже на проблему | Чтение кода: совпал шаблон |
|
|
369
|
+
| **L2** | Доказано в коде | Чтение кода: дефект структурный |
|
|
370
|
+
| **L3** | Файл выполнялся | Отчёт прогона показывает, что файл находки выполнялся |
|
|
371
|
+
| **L4** | Тест выполнялся | Отчёт прогона показывает, что тест находки выполнялся |
|
|
372
|
+
| **L5** | Прогон подтверждает | Собственный результат прогона подтверждает класс дефекта |
|
|
334
373
|
|
|
335
|
-
|
|
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
|
-
|
|
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
|
-
|
|
359
|
-
системе: находки E2 — структурное доказательство; находки E1 —
|
|
360
|
-
корректно позиционированные предупреждения, не формальные доказательства.
|
|
384
|
+
Уровни определяются измеренной долей ложных срабатываний, а не мнением:
|
|
361
385
|
|
|
362
|
-
|
|
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
|
-
|
|
395
|
+
Повышение, понижение и зрелость по языкам: [жизненный цикл правил](https://sergey-bar.github.io/Mjolnir/reference/rule-lifecycle).
|
|
368
396
|
|
|
369
|
-
|
|
370
|
-
локаторы:
|
|
397
|
+
### Почему это не линтер
|
|
371
398
|
|
|
372
|
-
|
|
373
|
-
▚ SELECTOR HEALTH — e2e/checkout.spec.ts
|
|
399
|
+
Линтеры говорят, следует ли код правилам. Mjölnir говорит, можно ли доверять вашей проверке.
|
|
374
400
|
|
|
375
|
-
|
|
376
|
-
|
|
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
|
-
|
|
380
|
-
XPath топят скор — они ломаются на любом DOM-рефакторе, не сообщая,
|
|
381
|
-
какое поведение регрессировало.
|
|
415
|
+
Используйте и ревью с ИИ. Оно улавливает нюансы, намерения и ошибки проектирования, которые не найдёт ни один шаблон. Mjölnir находит то, что ревью с ИИ пропускает, потому что это выглядит намеренным: закоммиченный `.only`, проглоченный код выхода, `continue-on-error` на тестовой джобе. Здесь нужно сканирование, а не рассуждение.
|
|
382
416
|
|
|
383
|
-
|
|
417
|
+
<br />
|
|
384
418
|
|
|
385
|
-
##
|
|
419
|
+
## Анализ прогонов тестов
|
|
386
420
|
|
|
387
|
-
|
|
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
|
-
|
|
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
|
-
|
|
407
|
-
везучий тест. Он помечается `TRUE-FLAKE` независимо от финальной
|
|
408
|
-
зелёной галочки.
|
|
438
|
+
`TRUE-FLAKE` не означает, что тест перезапускался. Это означает, что тест **провалил хотя бы одну попытку, а затем завершился зелёным**: случайный успех, отмеченный независимо от того, что показывает итоговая галочка. `mjolnir triage` превращает эту историю в предложение карантина, а `mjolnir pw-report` подводит итоги прогона. Именно эти отчёты прогонов поднимают находки до уровней доверия L3 и выше.
|
|
409
439
|
|
|
410
|
-
|
|
440
|
+
<br />
|
|
411
441
|
|
|
412
|
-
##
|
|
442
|
+
## Целостность CI
|
|
413
443
|
|
|
414
|
-
|
|
415
|
-
можно ли доверять вашей верификации.
|
|
444
|
+
Тест может проходить, пока окружающий его пайплайн не способен упасть. Mjölnir читает и workflow: `continue-on-error`, `|| true`, коды выхода, которые никогда не передаются дальше, всегда успешные шаги, отчёты, которые используются, но никогда не создаются, и гейты, пропускаемые именно в тех событиях, которые должны блокировать. Каждая находка называет джобу, шаг и строку и несёт свой уровень доказательности.
|
|
416
445
|
|
|
417
|
-
|
|
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
|
-
|
|
427
|
-
|
|
428
|
-
|
|
448
|
+
```bash
|
|
449
|
+
mjolnir ci install
|
|
450
|
+
```
|
|
429
451
|
|
|
430
|
-
|
|
452
|
+
Или добавьте action из Marketplace в уже существующий workflow:
|
|
431
453
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
454
|
+
```yaml
|
|
455
|
+
- uses: Sergey-Bar/Mjolnir@v1
|
|
456
|
+
with:
|
|
457
|
+
scope: changed
|
|
458
|
+
fail-on: error
|
|
459
|
+
```
|
|
437
460
|
|
|
438
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
450
|
-
| ------------------------------------------- | :------------------------------: | :-------------------------------------: |
|
|
451
|
-
| Цена за скан | Токены (растут с размером диффа) | **Ноль** (локально, установлен) |
|
|
452
|
-
| Видит весь сьют + все CI-конфиги | Только PR-дифф, показанный ему | **Всё, каждый раз** |
|
|
453
|
-
| Детерминирован (тот же вход → тот же выход) | ❌ (недетерминирован) | **✅** |
|
|
454
|
-
| Ловит паттерны, дремлющие месяцами | Только если в контексте | **✅** (сканирует все файлы) |
|
|
455
|
-
| Помнит находки между запусками | ❌ (нет памяти между сессиями) | **✅** (baseline + diff) |
|
|
456
|
-
| Запускается без человека | Нужен PR или промпт | **✅** (CI-хук, выполняется за секунды) |
|
|
476
|
+
### Привязка к изменениям ветки
|
|
457
477
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
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
|
-
|
|
484
|
+
Если merge-base определить не удаётся (неглубокий клон, отсоединённый HEAD, цель вне git), находки откатываются к привязке ко всему файлу, **и отчёт об этом сообщает.** Тихий откат был бы ровно тем дефектом, ради поиска которого существует этот инструмент.
|
|
468
485
|
|
|
469
|
-
|
|
470
|
-
никогда блокирующий:
|
|
486
|
+
<br />
|
|
471
487
|
|
|
472
|
-
|
|
473
|
-
mjolnir ci install
|
|
474
|
-
```
|
|
488
|
+
## ИИ-агенты
|
|
475
489
|
|
|
476
|
-
|
|
490
|
+
Находки чего-то стоят, только если на них кто-то реагирует.
|
|
477
491
|
|
|
478
|
-
```
|
|
479
|
-
|
|
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
|
-
|
|
486
|
-
[docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md).
|
|
496
|
+
**ИИ пишет исправление. Mjölnir его проверяет.** Доказательство даёт повторное сканирование, а не собственный отчёт агента об успехе.
|
|
487
497
|
|
|
488
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
"
|
|
517
|
-
|
|
518
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
665
|
-
|
|
666
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
616
|
+
<sub>Создал [Sergey Bar](https://www.linkedin.com/in/sergeybar/) · Лицензия MIT</sub>
|
|
698
617
|
|
|
699
618
|
</div>
|