mjolnir-qa 4.0.0 → 6.0.0

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 DELETED
@@ -1,647 +0,0 @@
1
- <div align="center">
2
-
3
- <img src="assets/readme/hero.svg" alt="Mjölnir. Тесты говорят, что прошло. Mjölnir говорит, чему можно доверять." width="100%" />
4
-
5
- <br />
6
-
7
- Mjölnir находит тесты, которые не могут упасть, и пайплайны, которые не могут покраснеть,<br />
8
- а затем оценивает, насколько можно доверять результату, с доказательством для каждого пункта.
9
-
10
- <br />
11
-
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
-
20
- ```bash
21
- npx mjolnir-qa@3.0.0
22
- ```
23
-
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-26.
32
-
33
- <!-- Source hash: `de04dfb1677b` -->
34
-
35
- </details>
36
-
37
- </div>
38
-
39
- <br />
40
-
41
- ## Release status (English canonical)
42
-
43
- The published line is `3.0.0`; this working tree is the `4.0.0-rc.1`
44
- candidate. `3.0.0` must not be republished or retagged. The M26–M50 program is
45
- tracked in
46
- [`docs/ROADMAP.yaml`](docs/ROADMAP.yaml); provisional capability contracts
47
- are not automatically enabled or certified. Repository-owned checks pass, but
48
- Trust certification remains `NOT_CERTIFIED` until the protected holdout,
49
- real-world, platform/consumer, remote-workflow, support-matrix, and corpus
50
- evidence gates pass. Run `npm run m26:readiness` before treating any candidate
51
- as releasable. This document does not publish a tag or authorize a release.
52
-
53
- > Machine-assisted canonical text. Translate this block before treating it as localized copy.
54
-
55
- ## Зелёная галочка — это заявление, а не доказательство
56
-
57
- Зелёная галочка означает, что пайплайн не упал. Она не означает, что тесты запускались или что они могли упасть. Каждый из этих случаев проходит зелёным:
58
-
59
- - закоммиченный `.only`, из-за которого запустилось 3 теста вместо 900
60
- - `continue-on-error: true` на джобе, которая должна была блокировать
61
- - `|| true` после команды запуска тестов
62
- - тест, который ничего не проверяет или имеет пустое тело
63
- - обёртка с повторами, которая превращает настоящий провал в случайный успех
64
- - отчёт, который workflow загружает, но никогда не создавал
65
- - жёсткий sleep, на котором держится состояние гонки
66
-
67
- Ни один из них не делает пайплайн красным, и каждый на ревью выглядит намеренным. Именно поэтому они и выживают. Вот как Mjölnir читает реальный пример:
68
-
69
- <p align="center">
70
- <img src="assets/readme/scan.svg" alt="CI-workflow демонстрационного репозитория, прочитанный строка за строкой. Mjölnir отмечает каждую находку на указанной строке: правило, что не так, уровень доказательности и измеренную долю ложных срабатываний." width="800" />
71
- </p>
72
-
73
- <sub>Каждая находка, которую демонстрационное сканирование выдало для этого workflow, на указанной строке. Сгенерировано командой `npm run docs:readme-brand` из [`demo-report.json`](assets/readme/demo-report.json) и защищено от расхождений в CI.</sub>
74
-
75
- **Строгий режим.** Самые агрессивные детекции — `.only`, `continue-on-error`, пустые тесты, злоупотребление повторами — живут в карантинном ярусе. Они запускаются только с `--strict` и ограничены серьёзностью `info`: помечают, но никогда не блокируют. Сканирование по умолчанию (`npx mjolnir-qa@3.0.0` без `--strict`) покрывает только основные и расширенные правила. Добавьте `--strict`, когда хотите и консультативный слой.
76
-
77
- Mjölnir читает набор тестов, CI-workflow и, если он есть, отчёт реального прогона. Он не запускает ваши тесты, не устанавливает зависимости и не выполняет сканируемый код. А когда доказательств нет, он так и говорит, вместо того чтобы выдумывать уверенность:
78
-
79
- | Ситуация | Что сообщает Mjölnir |
80
- | ------------------------------------------------------- | ----------------------------------------------------------------------- |
81
- | Объявления тестов не найдены | Оценка `null`, отображается как **UNKNOWN**. Никогда не выдуманные 100. |
82
- | Нет базовой линии или сравнимой ревизии | **UNKNOWN**, с указанием причины. Никогда не предполагаемый 0. |
83
- | Сканирование прервано (лимит времени, нечитаемые файлы) | **PARTIAL**, код выхода `2`. Никогда не выдаётся за чистый результат. |
84
-
85
- <p align="center">
86
- <img src="assets/readme/how-it-works.svg" alt="Как работает Mjölnir. Он статически читает набор тестов и CI-пайплайн, а также отчёт реального прогона, если он есть. Каждую находку он взвешивает по уровню доказательности и уровню доверия, причём только реальный прогон может достичь L3–L5, и выдаёт находки, оценку надёжности и CI-гейт с замороженными кодами выхода. В цикле агента ИИ пишет исправление, а Mjölnir сканирует повторно, чтобы его доказать." width="880" />
87
- </p>
88
-
89
- <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>
90
-
91
- <br />
92
-
93
- ## Посмотреть в работе
94
-
95
- Реальное сканирование [`examples/demo-repo`](examples/demo-repo), небольшого набора тестов Playwright с CI-workflow. Вот куда ушли его баллы:
96
-
97
- <p align="center">
98
- <img src="assets/readme/terminal-hero.svg" alt="Разбивка вычетов Mjölnir: WORTHINESS 80/100 WORTHY, оценка по категориям, блок вычетов по серьёзности и список FIX THIS FIRST" width="520" />
99
- </p>
100
-
101
- <sub>Сгенерировано командой `npm run docs:hero` из реального сканирования и защищено от расхождений в CI. Полный отчёт `--verbose` того же сканирования — [`demo.svg`](assets/readme/demo.svg) (`npm run docs:demo`).</sub>
102
-
103
- <details>
104
- <summary><strong>Смотреть</strong> — сканирование, исправление, которое оно выводит, и повторное сканирование, которое его доказывает</summary>
105
-
106
- <br />
107
-
108
- <p align="center">
109
- <a href="assets/video/mjolnir-demo.mp4">
110
- <img src="assets/video/mjolnir-demo-poster.png" alt="Кадр демонстрационной записи: npx mjolnir-qa@3.0.0 сканирует демонстрационный репозиторий в окне терминала" width="900" />
111
- </a>
112
- </p>
113
-
114
- <sub>Отрисовано кадр за кадром из реального сканирования командой `npm run docs:video`; никогда не записывалось с экрана. Выберите кадр, чтобы открыть [`mjolnir-demo.mp4`](assets/video/mjolnir-demo.mp4).</sub>
115
-
116
- </details>
117
-
118
- ### Одна находка крупным планом
119
-
120
- Каждая находка отвечает на четыре вопроса: где она, насколько Mjölnir уверен, как часто правило ошибается и как это исправить.
121
-
122
- <p align="center">
123
- <img src="assets/readme/finding-anatomy.svg" alt="Первая находка демонстрационного сканирования, ровно так, как её выводит терминал, с отмеченными четырьмя частями: где, насколько уверенно, как часто правило ошибается, и исправление." width="100%" />
124
- </p>
125
-
126
- `mjolnir explain QA-CI-001` выводит полное досье доверия правила, включая измеренную долю ложных срабатываний и уровень, который эта доля ему обеспечила:
127
-
128
- ```text
129
- ▍ QA-CI-001 — continue-on-error masks a failing verification gate
130
-
131
- Severity: error
132
- Confidence: high
133
- Tier: quarantine
134
- Evidence: E2
135
- QA impact: False-green risk (FALSE-GREEN)
136
- Measured FP: 11% (19 hand-classified corpus verdicts)
137
- FP risk: low (author estimate)
138
- Languages: yaml
139
- Frameworks: github-actions, azure-pipelines
140
-
141
- WHAT WAS FOUND (real detector output, not a mockup)
142
- Job `security-scan` runs a verification gate under `continue-on-error: true`.
143
-
144
- WHY IT MATTERS
145
- This job can fail every day and CI will still show green. The checkmark on
146
- this workflow cannot be trusted.
147
-
148
- HOW TO FIX
149
- Remove continue-on-error, or scope it to individual non-blocking steps only.
150
-
151
- Example from this rule's own must-fire fixture: QA-CI-001/must-fire/masked.yml
152
-
153
- WHAT WOULD CHANGE THE VERDICT
154
- - a run report next to the scan target (mjolnir.report.json or test-results/)
155
- corroborating this file lifts its findings to L3–L5
156
- - a documented suppression (mjolnir.config.json) lowers the finding count
157
- without claiming correctness
158
- - quarantine findings run only under --strict and are advisory (E0) — they can
159
- never gate CI
160
-
161
- NEXT ACTION
162
- Fix the first occurrence, then re-run: `mjolnir --scope changed`. Every
163
- occurrence of this rule is listed in the scan output.
164
-
165
- HOW TO VERIFY THE FIX
166
- Re-run `mjolnir` on the changed file(s) — this finding should no longer
167
- appear. `mjolnir --scope changed` scopes the check to just what you touched.
168
-
169
- Docs: mjolnir rules --md (full catalog, this rule included)
170
- ```
171
-
172
- Вот единица ценности: одно место, где CI сообщает об успехе, которого он не заслужил.
173
-
174
- <br />
175
-
176
- ## Быстрый старт
177
-
178
- ```bash
179
- npx mjolnir-qa@3.0.0
180
- ```
181
-
182
- Он сканирует текущий каталог и выводит Trust Report: что найдено, насколько этому можно доверять, почему и что делать дальше. Он завершается с кодом `0`, если на уровне гейта или выше ничего не найдено.
183
-
184
- В CI сканируйте только то, что внесла ветка, чтобы унаследованный набор тестов не утопил ваш первый pull request:
185
-
186
- ```bash
187
- npx mjolnir-qa@3.0.0 --scope changed
188
- ```
189
-
190
- `mjolnir ci install` записывает это как workflow GitHub Actions с [action](https://github.com/Sergey-Bar/Mjolnir#readme), закреплённым на мажорном теге `v3` (или просто `npx` с `--no-action`). Он остаётся рекомендательным, пока вы не решите, что он должен блокировать.
191
-
192
- | Команда | Что делает |
193
- | --------------------------------------------- | ----------------------------------------------------------- |
194
- | `mjolnir` | Trust Report: вердикт, уверенность, следующее действие |
195
- | `mjolnir --scope changed` | Только то, что внесла ваша ветка (вариант для CI) |
196
- | `mjolnir ci install` | Создаёт рекомендательный workflow для PR (на основе action) |
197
- | `mjolnir business-case` | ROI estimate: projected savings per finding |
198
- | `mjolnir release-report` | Release readiness: GO, CONDITIONAL GO, or NO-GO |
199
- | `mjolnir release-trust` | 12-dimension release assurance verdict |
200
- | `mjolnir report` | Generate a Playwright-compatible report |
201
- | `mjolnir trend` | Record, show, or diff local quality snapshots |
202
- | `mjolnir policy` | Initialize, validate, or check policy gates |
203
- | `mjolnir quarantine` | Review deterministic proposals (prototype) |
204
- | `mjolnir analyze --cross-file` | Bounded cross-file analysis |
205
- | `mjolnir ci-adapter github .` | Generate CI templates for supported providers |
206
- | `mjolnir dashboard` | Generate a self-contained quality dashboard |
207
- | `mjolnir exec-report` | Executive KPIs and recommendations (advisory) |
208
- | `mjolnir enterprise` | Self-hosted templates (prototype) |
209
- | `mjolnir maturity` | Assess maturity or display maturity levels |
210
- | `mjolnir mutation tests/mutation-report.json` | Analyze mutation reports; never promotes trust |
211
- | `mjolnir mcp` | Read-only MCP tools over stdio |
212
- | `mjolnir explain QA-CI-001` | Что, почему и как исправить, плюс измеренная доля FP |
213
- | `mjolnir why src/a.spec.ts:42` | Почему отмечена именно эта строка. Никогда не блокирует. |
214
- | `mjolnir forensics ./test-results/` | Доказательства из реального прогона |
215
- | `mjolnir trust-report` | Самодостаточный Trust Artifact (md + json) |
216
- | `mjolnir handoff` | План исправлений для агента-программиста |
217
- | `mjolnir --json` / `--format sarif` | Машиночитаемый вывод, GitHub Code Scanning |
218
- | `mjolnir --format codequality` | Отчёт GitLab Code Quality (артефакт виджета MR) |
219
- | `mjolnir --strict` | Также запускает правила уровня quarantine (выше риск FP) |
220
-
221
- <details>
222
- <summary><strong>Все остальные команды</strong> — разбор нестабильных тестов, отчёты, контроль</summary>
223
-
224
- <br />
225
-
226
- | Команда | Что делает |
227
- | ----------------------------------- | ------------------------------------------------------------------------------------- |
228
- | `mjolnir --classic` | Баннер оценки из времён до Trust Report |
229
- | `mjolnir explain verdict` | Почему вердикт сохранённого сканирования именно такой |
230
- | `mjolnir triage ./test-results/` | Пошаговый разбор. Каждая строка заканчивается следующим действием. |
231
- | `mjolnir pw-report ./test-results/` | Сводка прогона Playwright: повторы, нестабильные тесты, самые медленные |
232
- | `mjolnir doctor:playwright` | Глубокое сканирование только для Playwright плюс Selector Health Score |
233
- | `mjolnir fix --dry-run` / `fix` | Безопасные автоисправления, каждое пересканируется, чтобы доказать, что оно сработало |
234
- | `mjolnir baseline` / `diff` | Снимок находок, затем отчёт только о новых или ухудшившихся |
235
- | `mjolnir impact --since <ref>` | Что внёс и что исправил коммит |
236
- | `mjolnir summary` | Аннотации CI и сводка шага на основе отчёта |
237
- | `mjolnir pr-comment` | Комментарий к PR в пределах изменений, в формате Markdown |
238
- | `mjolnir debt` | Реестр тестового долга с моделью затрат |
239
- | `mjolnir handover` | Карта набора тестов для нового QA-инженера |
240
- | `mjolnir init` | Определяет фреймворки, выводит чек-лист настройки |
241
- | `mjolnir suppressions` | Список подавленных находок для контроля |
242
- | `mjolnir rules --unmeasured` | Правила, работающие на допущении, а не на измерении |
243
- | `mjolnir rules --md` | Полный каталог правил (JSON или Markdown) |
244
- | `mjolnir doctor` | Самоаудит собственной базы правил Mjölnir |
245
- | `mjolnir create-rule <ID>` | Создаёт заготовку нового правила и его фикстур |
246
- | `mjolnir stats` | Локальные счётчики всех замеченных исправлений |
247
- | `mjolnir badge` | JSON для эндпоинта shields.io и фрагмент кода |
248
- | `mjolnir --cache` | Инкрементальные пересканирования через локальный кэш вердиктов |
249
- | `mjolnir --format mermaid` | Диаграмма архитектуры тестов для комментария к PR |
250
-
251
- `mjolnir help <command>` выводит использование, примеры и следующий шаг для любой из них.
252
-
253
- </details>
254
-
255
- Требуется **Node.js ≥ 22.18** на Windows, macOS или Linux. Предпочитаете глобальную установку? `npm i -g mjolnir-qa`. Минимальная версия задаётся инструментами сборки (tsdown ориентирован на неё, а пайплайн релизов прогоняет на ней smoke-тесты); зависимостям времени выполнения большего не нужно.
256
-
257
- <br />
258
-
259
- ## Что находит Mjölnir
260
-
261
- <p align="center">
262
- <img src="assets/readme/stack.svg" alt="Работает с вашим стеком: языки, тестовые фреймворки и CI-системы, которые покрывают его правила, по данным реестра правил." width="100%" />
263
- </p>
264
-
265
- **79 правил** в четырёх семействах — гигиена тестов, качество тестов, Playwright и целостность CI — для TypeScript и JavaScript, Python, Java, C# и YAML GitHub Actions. Они охватывают Playwright во всех четырёх привязках, а также pytest, JUnit, TestNG, NUnit, xUnit, MSTest, Jest, Vitest и Mocha, с начальным покрытием Cypress и Selenium. Девять из них, чтобы показать общий вид:
266
-
267
- | ID | Правило | Серьёзность | Уровень |
268
- | ------------ | ----------------------------------------------------------------------- | ----------- | ---------- |
269
- | QA-CI-001 | `continue-on-error` маскирует падающий гейт проверки | error | quarantine |
270
- | QA-CI-009 | Код выхода тестов не передаётся дальше (`\|` без pipefail, цепочки `;`) | error | extended |
271
- | QA-TEST-001 | Закоммичен сфокусированный тест (`.only`, `fit`) | error | quarantine |
272
- | QA-TEST-003 | Тест без утверждений | error | quarantine |
273
- | QA-TQUAL-009 | Утверждение на promise без await | error | quarantine |
274
- | QA-PW-002 | Утверждение на локаторе без await | error | core |
275
- | QA-PW-004 | Хрупкие селекторы CSS/XPath | warning | quarantine |
276
- | QA-PY-002 | Пропущенный тест (`skip`, нестрогий `xfail`) | warning | core |
277
- | QA-CS-103 | Тестовый метод без утверждений | error | core |
278
-
279
- Полный каталог генерируется из реестра и никогда не ведётся вручную: `mjolnir rules --md`, [`docs/rules/`](docs/rules/) или [руководство о том, что он проверяет](https://sergey-bar.github.io/Mjolnir/guide/what-it-checks).
280
-
281
- <details>
282
- <summary><strong>Все правила, упомянутые в этом README</strong>, в одной таблице</summary>
283
-
284
- <br />
285
-
286
- > Правила `quarantine` запускаются только с `--strict` и никогда не блокируют (их уровень ограничен info). Показана серьёзность, заданная автором.
287
-
288
- | ID | Семейство | Правило | Серьёзность | Уровень |
289
- | ------------ | ---------- | ------------------------------------------------------------------ | ----------- | ---------- |
290
- | QA-TEST-001 | Гигиена | Закоммичен сфокусированный тест (`.only`, `fit`) | error | quarantine |
291
- | QA-TEST-002 | Гигиена | Пропущенный тест. Без отслеживаемой причины повышается до `error`. | warning | quarantine |
292
- | QA-TEST-003 | Гигиена | Тест без утверждений | error | quarantine |
293
- | QA-TEST-004 | Гигиена | Жёсткий sleep (`waitForTimeout`, `sleep()`, `delay()`) | warning | extended |
294
- | QA-TEST-006 | Гигиена | Злоупотребление повторами, скрывающее нестабильность | warning | quarantine |
295
- | QA-TEST-010 | Гигиена | Пустое тело теста | error | quarantine |
296
- | QA-TQUAL-002 | Качество | Тавтологическое утверждение | error | quarantine |
297
- | QA-TQUAL-009 | Качество | Утверждение на promise без await | error | quarantine |
298
- | QA-TQUAL-011 | Качество | Закомментированные тесты | warning | extended |
299
- | QA-PW-002 | Playwright | Утверждение на локаторе без await | error | core |
300
- | QA-PW-003 | Playwright | Закоммичен `page.pause()` / `test.only()` | error | core |
301
- | QA-PW-004 | Playwright | Хрупкие селекторы CSS/XPath | warning | quarantine |
302
- | QA-PW-123 | Playwright | Жёстко заданные URL окружений | warning | quarantine |
303
- | QA-PW-140 | Playwright | Скриншот без `maxDiffPixelRatio` | warning | core |
304
- | QA-CI-001 | CI | `continue-on-error` маскирует падающий гейт | error | quarantine |
305
- | QA-CI-002 | CI | `\|\| true` проглатывает коды выхода | error | extended |
306
- | QA-CI-005 | CI | Отчёт используется, но никогда не создаётся | error | quarantine |
307
- | QA-CI-007 | CI | Обёртки с повторами вокруг тестов | warning | extended |
308
- | QA-CI-008 | CI | Всегда успешный шаг маскирует падения | error | quarantine |
309
- | QA-CI-009 | CI | Код выхода не передаётся дальше (`\|` без pipefail, цепочки `;`) | error | extended |
310
- | QA-CI-010 | CI | Тесты пропускаются там, где должны блокировать | error | quarantine |
311
- | QA-PY-002 | Python | Пропущенный тест (`skip`, нестрогий `xfail`) | warning | core |
312
- | QA-PY-003 | Python | Тестовая функция без утверждений | error | quarantine |
313
- | QA-PY-005 | Python | `time.sleep()` в тестах | warning | extended |
314
- | QA-PY-012 | Python | Тавтологическое утверждение | error | quarantine |
315
- | QA-JV-101 | Java | Отключённый тест (`@Disabled`) | warning | core |
316
- | QA-JV-102 | Java | Жёсткий sleep (`Thread.sleep()`) | warning | extended |
317
- | QA-JV-103 | Java | Тестовый метод без утверждений | error | extended |
318
- | QA-JV-105 | Java | Жёсткий sleep через `waitForTimeout()` в Playwright | warning | core |
319
- | QA-JV-106 | Java | Хрупкий селектор вместо локатора по роли | warning | quarantine |
320
- | QA-CS-101 | C# | Пропущенный тест (`[Ignore]`, `[Fact(Skip=)]`) | warning | core |
321
- | QA-CS-102 | C# | Жёсткий sleep (`Thread.Sleep` / `Task.Delay`) | warning | core |
322
- | QA-CS-103 | C# | Тестовый метод без утверждений | error | core |
323
- | QA-CS-105 | C# | Жёсткий sleep через `WaitForTimeoutAsync()` | warning | extended |
324
- | QA-CS-106 | C# | Хрупкий селектор вместо локатора по роли | warning | quarantine |
325
-
326
- Для Python также есть QA-PY-001…012 (гигиена pytest) и QA-PY-101…108 (Playwright для Python). У Cypress и Selenium есть стартовые наборы по три правила.
327
-
328
- </details>
329
-
330
- Каждое правило поставляется с фикстурой must-fire **и** фикстурой must-not-fire, а правило, срабатывающее на собственной негативной фикстуре, не может быть выпущено. Это защита от ложных срабатываний; `mjolnir doctor` обеспечивает её в собственном CI этого репозитория.
331
-
332
- ### Selector Health Score
333
-
334
- `mjolnir doctor:playwright` оценивает каждый локатор по тому, как он находит элемент: так, как это сделал бы пользователь (роль, метка, текст), через явный контракт (`data-testid`) или по структурной случайности (цепочки CSS, XPath). Каждый файл получает оценку от 0 до 100:
335
-
336
- ```text
337
- ▍ SELECTOR HEALTH
338
-
339
- e2e/login.spec.ts
340
- [█████████████░░░░░░░] 65 / 100
341
- role/text: 1 · testid: 0 · plain-css: 0 · css-chains: 1 ⚠ · xpath: 0
342
-
343
- e2e/checkout.spec.ts
344
- [██████████████████░░] 88 / 100
345
- role/text: 4 · testid: 1 · plain-css: 0 · css-chains: 1 ⚠ · xpath: 0
346
- ```
347
-
348
- Это измеряет **устойчивость, а не корректность**. `.btn.btn-primary > div:nth-child(2)` проходит сегодня и будет проходить, пока кто-нибудь не тронет разметку. Низкая оценка никогда не утверждает, что тест сломан, — только что он зависит от разметки, сохранять которую никто не обещал.
349
-
350
- <br />
351
-
352
- ## Оценка надёжности
353
-
354
- <p align="center">
355
- <img src="assets/readme/score-gauge.svg" alt="Шкала надёжности от 0 до 100 с маркером, проходящим по каждой оценке: UNWORTHY ниже 50, NEEDS WORK от 50 до 79, WORTHY от 80 до 99, FORGED при 100" width="720" />
356
- </p>
357
-
358
- <sub>Каждая оценка от 0 до 100, размещённая настоящим `deriveScoreState`. Сгенерировано командой `npm run docs:gauge` и защищено от расхождений в CI.</sub>
359
-
360
- | Оценка | Вердикт |
361
- | --------- | ----------------------------------------- |
362
- | `0 – 49` | **UNWORTHY** |
363
- | `50 – 79` | **NEEDS WORK** |
364
- | `80 – 99` | **WORTHY** |
365
- | `100` | **FORGED** |
366
- | `null` | **UNKNOWN**: объявления тестов не найдены |
367
-
368
- **Как она вычисляется.** Серьёзность задаёт базовый вычет (`error −8`, `warning −3`, `info −1`), а уровень доказательности его уменьшает: E2 учитывается полностью, E1 наполовину (с округлением вниз), E0 не учитывается. Сумма нормируется по охвату набора, то есть вычеты на одно объявление теста, а не на файл. Терминал выводит те же уменьшенные числа, что использовала оценка; скрытой второй модели нет. Подробности: [docs/SCORING.md](docs/SCORING.md) и [руководство по оценке](https://sergey-bar.github.io/Mjolnir/guide/scoring).
369
-
370
- **Чего не означает 100.** Это не значит, что программа корректна, набор тестов достаточен или продукт свободен от дефектов. Это значит только одно: **ни одно из правил, проверенных Mjölnir, не дало вычета в этом сканировании и при этой модели доказательств.**
371
-
372
- <br />
373
-
374
- ## Модель доказательств
375
-
376
- Каждая находка несёт две метки: насколько уверен Mjölnir и насколько далеко находка проверена. В этом разница между инструментом, который сообщает о шаблонах, и инструментом, от которого можно ставить в зависимость релиз.
377
-
378
- **Насколько уверенно — уровень доказательности.**
379
-
380
- | Уровень | Название | Значение | Вычет |
381
- | ------- | -------------------------------- | ----------------------------------------------------- | -------- |
382
- | **E2** | Детерминированное доказательство | Дефект присутствует в коде в том виде, как он написан | Полный |
383
- | **E1** | Доказательство по шаблону | Совпал шаблон, тесно связанный с дефектом | Половина |
384
- | **E0** | Наблюдение | Стоит знать. Не утверждение, что что-то не так. | Ноль |
385
-
386
- Уверенность в обнаружении — это не сила доказательства. Правило может быть уверено, что нашло то, что искало, и всё равно смотреть на эвристику. Находки E1 предназначены для того, чтобы их читали и оценивали, а не применяли вслепую, и эта граница отмечена на находке в терминале, в JSON и в передаче агенту.
387
-
388
- **Насколько далеко проверено — уровень доверия.** Большинство находок получено чтением вашего кода. Дайте Mjölnir отчёт реального прогона тестов, и он сможет подтвердить, что код действительно выполнялся.
389
-
390
- <p align="center">
391
- <img src="assets/readme/trust-ladder.svg" alt="Лестница доверия от L0 до L5. L0–L2 получаются чтением кода; для L3–L5 нужен отчёт реального прогона, что отмечено разрывом в лестнице." width="100%" />
392
- </p>
393
-
394
- | Уровень | Простыми словами | Что для этого нужно |
395
- | ------- | ------------------- | -------------------------------------------------------- |
396
- | **L0** | Замечено | Чтение кода |
397
- | **L1** | Похоже на проблему | Чтение кода: совпал шаблон |
398
- | **L2** | Доказано в коде | Чтение кода: дефект структурный |
399
- | **L3** | Файл выполнялся | Отчёт прогона показывает, что файл находки выполнялся |
400
- | **L4** | Тест выполнялся | Отчёт прогона показывает, что тест находки выполнялся |
401
- | **L5** | Прогон подтверждает | Собственный результат прогона подтверждает класс дефекта |
402
-
403
- Статическое сканирование останавливается на L2. Только отчёт реального прогона (Playwright JSON, Jest или Vitest JSON, JUnit XML) может поднять находку до L3 и выше, поэтому находка, которую ни разу не видели в работе, никогда не может утверждать обратное. Определения: [docs/TERMINOLOGY.md](docs/TERMINOLOGY.md).
404
-
405
- ### Сколько из этого измерено
406
-
407
- **У 74 из 79 правил доля ложных срабатываний измерена на реальном OSS-коде** (не менее 10 вручную классифицированных находок на каждое; см. [docs/FP-AUDIT.md](docs/FP-AUDIT.md)). Остальные 5 опираются на оценку автора и прямо говорят об этом, правило за правилом, в `mjolnir explain`. `mjolnir rules --unmeasured` перечисляет их, а подвал каждого сканирования сообщает, сколько из действительно _сработавших_ правил измерено.
408
-
409
- Показатели остаются публичными, даже когда они плохие. QA-TEST-001 (закоммиченный `.only`) плохо проходит аудит на реальных репозиториях и поэтому находится в quarantine. Актуальное значение для каждого правила, включая QA-PW-141, есть в аудите.
410
-
411
- ### Уровни доверия правил
412
-
413
- Уровни определяются измеренной долей ложных срабатываний, а не мнением:
414
-
415
- | Уровень | Измеренная FP | Поведение |
416
- | -------------- | -------------------------- | ---------------------------------------------------------------- |
417
- | **core** | ≤ 10% | Отчёт по умолчанию, блокирует |
418
- | **extended** | ≤ 30% | Отчёт по умолчанию, пониженная уверенность |
419
- | **quarantine** | > 30% или явно объявленное | Только `--strict`, ограничено уровнем info, никогда не блокирует |
420
- | _не измерено_ | n < 10 | Не может быть повышено до core, пока не измерено |
421
-
422
- Полосы FP могут только понизить уровень — они никогда не повышают правило из `quarantine`, если оно было туда явно объявлено. Явно помещённое в карантин правило остаётся в quarantine независимо от измеренного уровня FP.
423
-
424
- Повышение, понижение и зрелость по языкам: [жизненный цикл правил](https://sergey-bar.github.io/Mjolnir/reference/rule-lifecycle).
425
-
426
- ### Почему это не линтер
427
-
428
- Линтеры говорят, следует ли код правилам. Mjölnir говорит, можно ли доверять вашей проверке.
429
-
430
- | | Линтеры (ESLint, SonarQube) | Инструменты покрытия | Код-ревью с ИИ | **Mjölnir** |
431
- | ----------------------------------------------------------------- | :-------------------------: | :------------------: | :------------: | :-----------------: |
432
- | Оценивает **систему проверки**, а не код продукта | Нет | Нет | Нет | Да |
433
- | Целостность CI-workflow (`continue-on-error`, `\|\| true`) | Нет | Нет | только diff | Да |
434
- | Оценивает устойчивость локаторов Playwright (Selector Health) | Нет | Нет | Нет | Да |
435
- | Читает реальные данные прогонов для вердиктов `TRUE-FLAKE` | Нет | Нет | Нет | Да |
436
- | Публикует измеренную долю ложных срабатываний для каждого правила | Нет | Нет | Нет | Да |
437
- | Отмечает тесты без утверждений | Да\* | Нет | иногда | Да |
438
- | Находит жёсткие sleep (`waitForTimeout`, `time.sleep`) | Да\* | Нет | иногда | Да |
439
- | Детерминированность (одинаковый вход — одинаковый выход) | Да | Да | Нет | Да |
440
- | Стоимость сканирования | бесплатно | бесплатно | токены | **ноль** (локально) |
441
-
442
- <sub>\*Покрывается `eslint-plugin-jest` и `eslint-plugin-playwright` (`expect-expect`, `no-wait-for-timeout`), а также собственными правилами SonarQube для утверждений. Столбцы описывают поведение по умолчанию при проверке наборов тестов; плагины, платные тарифы и собственные правила меняют некоторые ответы. Это сводка позиционирования, а не бенчмарк.</sub>
443
-
444
- Используйте и ревью с ИИ. Оно улавливает нюансы, намерения и ошибки проектирования, которые не найдёт ни один шаблон. Mjölnir находит то, что ревью с ИИ пропускает, потому что это выглядит намеренным: закоммиченный `.only`, проглоченный код выхода, `continue-on-error` на тестовой джобе. Здесь нужно сканирование, а не рассуждение.
445
-
446
- <br />
447
-
448
- ## Анализ прогонов тестов
449
-
450
- Статический анализ рассуждает о коде, который никогда не выполнялся. Анализ прогонов читает то, что произошло на самом деле: Playwright JSON, Jest JSON, Vitest JSON и JUnit XML от любого раннера.
451
-
452
- ```bash
453
- mjolnir forensics ./test-results/
454
- ```
455
-
456
- ```text
457
- ▍ FLAKINESS LEADERBOARD
458
-
459
- 3 tests · 1 failed · 1 flaky · 1 retried
460
-
461
- TRUE-FLAKE completes checkout with saved card (e2e/checkout.spec.ts)
462
- ████████████████████ 6.0s · 2 attempts
463
- FAILING declines an expired card (e2e/checkout.spec.ts)
464
- ████░░░░░░░░░░░░░░░░ 1.1s · 1 attempt
465
- ```
466
-
467
- `TRUE-FLAKE` не означает, что тест перезапускался. Это означает, что тест **провалил хотя бы одну попытку, а затем завершился зелёным**: случайный успех, отмеченный независимо от того, что показывает итоговая галочка. `mjolnir triage` превращает эту историю в предложение карантина, а `mjolnir pw-report` подводит итоги прогона. Именно эти отчёты прогонов поднимают находки до уровней доверия L3 и выше.
468
-
469
- <br />
470
-
471
- ## Целостность CI
472
-
473
- Тест может проходить, пока окружающий его пайплайн не способен упасть. Mjölnir читает и workflow: `continue-on-error`, `|| true`, коды выхода, которые никогда не передаются дальше, всегда успешные шаги, отчёты, которые используются, но никогда не создаются, и гейты, пропускаемые именно в тех событиях, которые должны блокировать. Каждая находка называет джобу, шаг и строку и несёт свой уровень доказательности.
474
-
475
- Создайте workflow для PR, по умолчанию рекомендательный:
476
-
477
- ```bash
478
- mjolnir ci install
479
- ```
480
-
481
- Или добавьте action из Marketplace в уже существующий workflow:
482
-
483
- ```yaml
484
- - uses: Sergey-Bar/Mjolnir@v3
485
- with:
486
- scope: changed
487
- fail-on: error
488
- ```
489
-
490
- Закрепите `@v3`, чтобы следовать мажорной линии, или точный тег (`@v0.5.32`) для воспроизводимого гейта. [docs/DISTRIBUTION-KIT.md](docs/DISTRIBUTION-KIT.md) описывает Marketplace, Smithery и реестры MCP.
491
-
492
- Чтобы отправить находки в GitHub Code Scanning, загрузите SARIF (требуется `security-events: write` на уровне workflow или job):
493
-
494
- ```yaml
495
- - run: npx mjolnir-qa@3.0.0 --format sarif > mjolnir.sarif
496
- continue-on-error: true
497
- - uses: github/codeql-action/upload-sarif@v3
498
- if: ${{ !cancelled() }}
499
- with:
500
- sarif_file: mjolnir.sarif
501
- ```
502
-
503
- В GitLab `--format codequality` записывает отчёт Code Quality, который читают виджет MR и аннотации diff ([docs/GITLAB-CI.md](docs/GITLAB-CI.md)). Настройка редактора и пайплайна: [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md).
504
-
505
- ### Привязка к изменениям ветки
506
-
507
- ```bash
508
- npx mjolnir-qa@3.0.0 --scope changed
509
- ```
510
-
511
- Находки привязываются к строкам, добавленным вашей веткой, относительно **merge-base**. Область — тот же набор файлов, что находит полное сканирование (спецификации TS/JS и конфигурации адаптеров, `test_*.py`, `*Test.java`, `*Tests.cs`, `.github/workflows/*.yml`), плюс незакоммиченные и неотслеживаемые изменения, поэтому это работает ещё до коммита. База определяется в порядке `main → master → origin/main → origin/master → origin/HEAD`; её можно переопределить через `--base <ref>`.
512
-
513
- Если merge-base определить не удаётся (неглубокий клон, отсоединённый HEAD, цель вне git), находки откатываются к привязке ко всему файлу, **и отчёт об этом сообщает.** Тихий откат был бы ровно тем дефектом, ради поиска которого существует этот инструмент.
514
-
515
- <br />
516
-
517
- ## ИИ-агенты
518
-
519
- Находки чего-то стоят, только если на них кто-то реагирует.
520
-
521
- ```text
522
- SCAN → EVIDENCE → HANDOFF → AGENT → RE-SCAN → PROOF
523
- ```
524
-
525
- **ИИ пишет исправление. Mjölnir его проверяет.** Доказательство даёт повторное сканирование, а не собственный отчёт агента об успехе.
526
-
527
- | Команда | Что получает агент |
528
- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
529
- | `mjolnir mcp` | Сервер [MCP](https://modelcontextprotocol.io) через stdio. `scan`, `explain` и `diff` становятся вызываемыми инструментами. |
530
- | `mjolnir handoff` | Сохранённый отчёт `--json` превращается в детерминированный план в Markdown: что обнаружено, граница доказательности для каждой находки, что **не** должно меняться и как это проверить. |
531
- | `mjolnir install` | Записывает в места для агентов, которые уже есть в вашем репозитории (`.claude/`, `.cursor/`, `.kilo/`, `AGENTS.md`), чтобы агент пересканировал код, прежде чем заявить, что закончил. |
532
-
533
- Добавьте его в клиент, у которого есть собственный CLI:
534
-
535
- ```bash
536
- claude mcp add mjolnir -- npx -y mjolnir-qa@3.0.0 mcp
537
- ```
538
-
539
- Или в любой клиент, принимающий блок `mcpServers`:
540
-
541
- ```json
542
- {
543
- "mcpServers": {
544
- "mjolnir": { "command": "npx", "args": ["-y", "mjolnir-qa@3.0.0", "mcp"] }
545
- }
546
- }
547
- ```
548
-
549
- **Ограничитель важнее удобства.** Каждая находка в передаче несёт свою границу. **E2** говорит _детерминированно: проверьте место и примените исправление_. **E1** говорит _ТРЕБУЕТСЯ ПОДТВЕРЖДЕНИЕ: одно наблюдение не доказывает дефект_. Агент, который вслепую исправляет E1, подавляет правило или редактирует правило, чтобы поднять оценку, делает ровно то, ради поиска чего существует этот инструмент, поэтому передача говорит об этом прямо в промпте, рядом с находкой.
550
-
551
- <br />
552
-
553
- ## Доверие и безопасность
554
-
555
- **Локально, без телеметрии.** Нигде в `src/` нет ни одного API с сетевыми возможностями (`fetch`, `http`, `https`, `net`, `dns`, `dgram`, WebSocket), а [`privacy-network-isolation.spec.ts`](tests/contract/privacy-network-isolation.spec.ts) проваливает сборку, если такой появится. Он также запрещает `eval` и `new Function`. Сканирование недоверенного кода никогда его не выполняет: статический анализ читает исходный текст, а анализ прогонов разбирает файлы отчётов, которые уже есть на диске.
556
-
557
- Две оговорки: сам `npx` скачивает пакет до того, как что-либо запустится, а гарантия распространяется на `src/`, но не на сторонние плагины.
558
-
559
- **Плагины не изолированы в песочнице.** JS-плагины (`mjolnir-rules/*.mjs` или npm-пакеты, перечисленные в `"plugins"`) работают с полными привилегиями Node — та же модель доверия, что и у плагинов ESLint или Vitest. Их загрузка включается явно **для каждого сканирования**: без `--enable-plugins` (или `MJOLNIR_ENABLE_PLUGINS=1`) их исходники никогда не загружаются, а сообщение в stderr перечисляет пропущенное. JSON-манифесты правил не выполняют код, а префиксы ID core-правил зарезервированы, чтобы плагин не мог выдать себя за одно из них. Сообщайте об уязвимостях через [SECURITY.md](SECURITY.md).
560
-
561
- **Он проверяет сам себя.** Движок доверия к проверкам ничего не стоит, если он сам не поддаётся проверке. Каждый прогон CI сканирует этот репозиторий сборкой, созданной тем же прогоном. Гейт падает при любой находке уровня error, а также при **частичном** сканировании или **упавшем правиле**, потому что обрезанное самосканирование, которое ни о чём не сообщает, — это и есть ложный зелёный, ради поиска которого существует этот проект. `mjolnir doctor` в том же прогоне заново проверяет базу правил (защита фикстур, честность уровней, лимит уровня core), а проверка с результатом INCONCLUSIVE падает точно так же, как проваленная. Оба отчёта загружаются как артефакты сборки.
562
-
563
- ### Коды выхода и машинный контракт
564
-
565
- Заморожены, чтобы на них можно было строить логику CI:
566
-
567
- | Код выхода | Значение |
568
- | ---------- | ---------------------------------------------------------------------------------------- |
569
- | `0` | Чисто: нет находок на уровне гейта или выше |
570
- | `1` | Есть находки на уровне гейта или выше |
571
- | `2` | Частичное сканирование (исчерпан лимит времени, нечитаемые файлы). Никогда не блокирует. |
572
- | `10` | Ошибка использования (неверный флаг, нет цели) |
573
- | `20` | Внутренняя ошибка |
574
-
575
- `2` намеренно отличается от `0`: сканирование, которое не завершилось, не «ничего не нашло». Оно просто не закончило искать.
576
-
577
- Всё, что потребляет машина (результаты инструментов MCP, `--json`, SARIF 2.1), берётся из одного канонического результата по версионированной схеме, **расширяемой только добавлением** (`schemaVersion: 1`, `contractVersion: 1`), поэтому ни одному потребителю не нужно восстанавливать смысл из отрисованного текста. См. [машинный контракт](docs/machine-contract.md). ID правил (`QA-<FAMILY>-NNN`) неизменны после выпуска и никогда не используются повторно.
578
-
579
- <br />
580
-
581
- ## Чего Mjölnir сказать не может
582
-
583
- - **Он не запускает ваши тесты.** Чистое сканирование — это не проходящий набор тестов.
584
- - **Он не может сказать, что утверждение _неверно_.** `expect(total).toBe(41)` выглядит здоровым. Mjölnir находит тесты, которые _не могут упасть_, и пайплайны, которые _не могут покраснеть_, а не тесты, проверяющие не то.
585
- - **Он не доказывает бизнес-корректность.** Ничто здесь не говорит, что ваш продукт делает то, чего требовало требование.
586
- - **100 — не доказательство хорошего набора тестов.** Покрывает ли ваш набор реальные риски — отдельный вопрос, и этот инструмент на него не отвечает.
587
- - **5 из 79 правил опираются на оценку**, а не на измеренную долю. Каждое из них говорит об этом в своей находке.
588
- - **E1 — это не E2.** Эвристические находки стоит читать, но не стоит применять вслепую.
589
- - **Пустой репозиторий получает `null`, никогда не 100.**
590
- - **Файл с именем `*.spec.ts` без объявлений тестов не считается покрытием.** Репозиторий, где единственные spec-файлы содержат импорты или типы (ноль вызовов `it`/`test`), получает `null`, а не 100.
591
-
592
- <br />
593
-
594
- ## Документация
595
-
596
- Полный сайт документации находится по адресу <https://sergey-bar.github.io/Mjolnir/>.
597
-
598
- | Документ | Что внутри |
599
- | ------------------------------------------------------ | --------------------------------------------------------------------- |
600
- | [docs/SCORING.md](docs/SCORING.md) | Нормирование оценки и взвешивание доказательств |
601
- | [docs/TERMINOLOGY.md](docs/TERMINOLOGY.md) | Канонический словарь: одно слово на понятие |
602
- | [docs/FP-AUDIT.md](docs/FP-AUDIT.md) | Измеренные доли ложных срабатываний и методика |
603
- | [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md) | Состояния правил, уровни, подавление, вывод из употребления |
604
- | [docs/VERSIONING.md](docs/VERSIONING.md) | Политика semver, замороженные интерфейсы, цикл вывода из употребления |
605
- | [docs/machine-contract.md](docs/machine-contract.md) | Канонический машиночитаемый результат |
606
- | [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md) | Вывод SARIF и настройка редактора или CI |
607
- | [docs/GITLAB-CI.md](docs/GITLAB-CI.md) | GitLab: отчёт Code Quality, рецепт для MR, гейт |
608
- | [docs/rules/](docs/rules/) | Сгенерированный каталог по правилам |
609
- | [CONTRIBUTING.md](CONTRIBUTING.md) | Среда разработки и процесс внесения изменений |
610
- | [SUPPORT.md](SUPPORT.md) | Где спросить, сообщить о проблеме и получить помощь |
611
- | [SECURITY.md](SECURITY.md) | Сообщение об уязвимостях |
612
- | [CHANGELOG.md](CHANGELOG.md) | История релизов |
613
-
614
- ### Статус
615
-
616
- **Версия 1.** JSON-схема и коды выхода — замороженные контракты. У TypeScript и Python самое широкое измеренное покрытие. Java и C# новее; оценивайте их по [таблице зрелости](https://sergey-bar.github.io/Mjolnir/reference/rule-lifecycle). Что будет дальше, без выдуманных дат: [публичная дорожная карта](https://sergey-bar.github.io/Mjolnir/reference/roadmap).
617
-
618
- ### Участие в разработке
619
-
620
- Новые правила — самый простой первый вклад. Одна команда создаёт заготовку правила с фикстурами must-fire **и** must-not-fire. Сгенерированное правило намеренно проваливает собственные фикстуры, пока не написано настоящее обнаружение, потому что выпущенная заглушка — это правило, которое никто не измерял:
621
-
622
- ```bash
623
- mjolnir create-rule QA-PW-140 --title "Screenshot without diff bound"
624
- ```
625
-
626
- Среда разработки, команды постоянных гейтов, а также законы anti-creep и защиты фикстур описаны в [CONTRIBUTING.md](CONTRIBUTING.md).
627
-
628
- <br />
629
-
630
- <div align="center">
631
-
632
- <img src="assets/readme/closing.svg" alt="Запустите его на своём репозитории." width="100%" />
633
-
634
- ```bash
635
- npx mjolnir-qa@3.0.0
636
- ```
637
-
638
- [Читать руководство](https://sergey-bar.github.io/Mjolnir/guide/getting-started) · [Сайт документации](https://sergey-bar.github.io/Mjolnir/) · [npm](https://www.npmjs.com/package/mjolnir-qa)
639
-
640
- <br />
641
-
642
- Не спрашивайте, прошли ли тесты.<br />
643
- Спросите, доказывают ли доказательства, что они заслуживают доверия.
644
-
645
- <sub>Создал [Sergey Bar](https://www.linkedin.com/in/sergeybar/) · Лицензия MIT</sub>
646
-
647
- </div>