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