mjolnir-qa 1.0.9 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +204 -0
- package/README.ar.md +434 -478
- package/README.bn.md +434 -491
- package/README.br.md +434 -520
- package/README.bs.md +431 -500
- package/README.da.md +433 -509
- package/README.de.md +432 -522
- package/README.es.md +428 -517
- package/README.fr.md +426 -520
- package/README.gr.md +433 -517
- package/README.he.md +433 -475
- package/README.it.md +436 -525
- package/README.ja.md +436 -506
- package/README.ko.md +434 -494
- package/README.md +453 -438
- package/README.no.md +435 -509
- package/README.pl.md +433 -511
- package/README.ru.md +434 -515
- package/README.th.md +434 -484
- package/README.tr.md +427 -504
- package/README.uk.md +433 -505
- package/README.vi.md +436 -494
- package/README.zh.md +433 -462
- package/README.zht.md +433 -462
- package/dist/cli.d.mts +716 -110
- package/dist/cli.mjs +9815 -22027
- package/dist/mcp/stdio.mjs +2164 -886
- package/dist/rolldown-runtime-8H4AJuhK.mjs +14 -0
- package/dist/scan-pipeline-C0ka-RmX.mjs +2 -0
- package/dist/scan-pipeline-D3Yk2cef.mjs +15309 -0
- package/package.json +14 -8
package/README.zht.md
CHANGED
|
@@ -1,379 +1,431 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
<img src="assets/readme/
|
|
3
|
+
<img src="assets/readme/hero.svg" alt="Mjölnir。測試告訴你什麼通過了。Mjölnir 告訴你什麼值得信任。" width="100%" />
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
<br />
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
Mjölnir 找出不可能失敗的測試和不可能變紅的流水線,<br />
|
|
8
|
+
再評估結果可信到什麼程度,每一分都附有證據。
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
[](https://github.com/Sergey-Bar/Mjolnir/actions/workflows/ci.yml)
|
|
12
|
-
[](LICENSE)
|
|
13
|
-
[](https://nodejs.org)
|
|
14
|
-
|
|
15
|
-
[English](README.md) | [简体中文](README.zh.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.uk.md) | [বাংলা](README.bn.md) | [Ελληνικά](README.gr.md) | [Tiếng Việt](README.vi.md) | [עברית](README.he.md) | [العربية](README.ar.md) | [Bosanski](README.bs.md)
|
|
10
|
+
<br />
|
|
16
11
|
|
|
17
|
-
|
|
12
|
+
[](https://www.npmjs.com/package/mjolnir-qa)
|
|
13
|
+
[](https://www.npmjs.com/package/mjolnir-qa)
|
|
14
|
+
[](https://github.com/Sergey-Bar/Mjolnir/actions/workflows/ci.yml)
|
|
15
|
+
[](https://codecov.io/gh/Sergey-Bar/Mjolnir)
|
|
16
|
+
[](https://scorecard.dev/viewer/?uri=github.com/Sergey-Bar/Mjolnir)
|
|
17
|
+
[](LICENSE)
|
|
18
|
+
[](https://nodejs.org)
|
|
18
19
|
|
|
19
20
|
```bash
|
|
20
21
|
npx mjolnir-qa@latest
|
|
21
22
|
```
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
[實際效果](#實際效果) · [快速開始](#快速開始) · [能發現什麼](#mjölnir-能發現什麼) · [評分](#可信度評分) · [證據](#證據模型) · [執行鑑識](#執行時鑑識) · [CI](#ci-完整性) · [代理](#ai-代理) · [安全](#信任與安全) · [局限](#mjölnir-無法告訴你的事) · [文件](#文件)
|
|
25
|
+
|
|
26
|
+
<details>
|
|
27
|
+
<summary>閱讀其他語言版本 — 22 種譯文</summary>
|
|
28
|
+
|
|
29
|
+
[English](README.md) | [简体中文](README.zh.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.uk.md) | [বাংলা](README.bn.md) | [Ελληνικά](README.gr.md) | [Tiếng Việt](README.vi.md) | [עברית](README.he.md) | [العربية](README.ar.md) | [Bosanski](README.bs.md)
|
|
30
|
+
|
|
31
|
+
> 🤖 Machine-assisted translation. The [English README](README.md) is canonical. Last synced: 2026-09-15.
|
|
32
|
+
|
|
33
|
+
<!-- Source hash: 3541b09e8d04 -->
|
|
24
34
|
|
|
25
|
-
|
|
26
|
-
[快速上手](#-快速上手) ·
|
|
27
|
-
[它檢查什麼](#-mjölnir-檢查什麼) ·
|
|
28
|
-
[評分](#評分如何運作) ·
|
|
29
|
-
[CI](#-ci-整合) · [設定](#設定) ·
|
|
30
|
-
[文件](#-文件)
|
|
35
|
+
</details>
|
|
31
36
|
|
|
32
37
|
</div>
|
|
33
38
|
|
|
34
|
-
|
|
39
|
+
<br />
|
|
40
|
+
|
|
41
|
+
## 綠色勾號是一種聲明,而不是證明
|
|
42
|
+
|
|
43
|
+
綠色勾號只說明流水線沒有失敗。它並不說明測試真的執行了,也不說明它們有可能失敗。以下每一種情況都會顯示為綠色:
|
|
44
|
+
|
|
45
|
+
- 提交進儲存庫的 `.only`,只執行了 3 個測試而不是 900 個
|
|
46
|
+
- 本應攔截的 job 上寫著 `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` 嚴重性:只標記,從不攔截。預設掃描(不帶 `--strict` 的 `npx mjolnir-qa@latest`)僅涵蓋核心和擴充規則。需要諮詢層時,加上 `--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 關卡。在代理循環中,AI 撰寫修正,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
|
+
## 實際效果
|
|
35
80
|
|
|
36
|
-
|
|
81
|
+
對 [`examples/demo-repo`](examples/demo-repo) 的一次真實掃描,這是一個附帶 CI workflow 的小型 Playwright 套件。它的分數都扣在了這裡:
|
|
37
82
|
|
|
38
83
|
<p align="center">
|
|
39
|
-
<img src="assets/readme/
|
|
84
|
+
<img src="assets/readme/terminal-hero.svg" alt="Mjölnir 的扣分明細:WORTHINESS 80/100 WORTHY、依類別的評分、依嚴重程度的扣分框,以及 FIX THIS FIRST 清單" width="520" />
|
|
40
85
|
</p>
|
|
41
86
|
|
|
42
|
-
<sub
|
|
43
|
-
reporter 渲染——毫無刪減。以 `npm run docs:demo` 重新產生;
|
|
44
|
-
[`tests/demo-asset-reproducibility.spec.ts`](tests/demo-asset-reproducibility.spec.ts)
|
|
45
|
-
會在產物與工具實際印出的內容發生偏移時讓 CI 失敗。</sub>
|
|
87
|
+
<sub>由 `npm run docs:hero` 根據一次真實掃描產生,並在 CI 中鎖定以防漂移。同一次掃描的完整 `--verbose` 報告是 [`demo.svg`](assets/readme/demo.svg)(`npm run docs:demo`)。</sub>
|
|
46
88
|
|
|
47
|
-
|
|
89
|
+
<details>
|
|
90
|
+
<summary><strong>觀看示範</strong> — 一次掃描、它給出的修正,以及證明修正有效的重新掃描</summary>
|
|
91
|
+
|
|
92
|
+
<br />
|
|
48
93
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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>
|
|
99
|
+
|
|
100
|
+
<sub>由 `npm run docs:video` 根據一次真實掃描逐格算繪;從不錄製螢幕。點選畫面即可開啟 [`mjolnir-demo.mp4`](assets/video/mjolnir-demo.mp4)。</sub>
|
|
101
|
+
|
|
102
|
+
</details>
|
|
56
103
|
|
|
57
104
|
### 近看一項發現
|
|
58
105
|
|
|
59
|
-
|
|
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` 會印出一條規則完整的信任檔案,包括它的實測誤報率,以及該誤報率為它贏得的等級:
|
|
60
113
|
|
|
61
114
|
```text
|
|
62
|
-
|
|
115
|
+
▍ QA-CI-001 — continue-on-error masks a failing verification gate
|
|
63
116
|
|
|
64
117
|
Severity: error
|
|
65
118
|
Confidence: high
|
|
119
|
+
Tier: quarantine
|
|
66
120
|
Evidence: E2
|
|
67
|
-
|
|
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
|
|
68
126
|
|
|
69
127
|
WHAT WAS FOUND (real detector output, not a mockup)
|
|
70
128
|
Job `security-scan` runs a verification gate under `continue-on-error: true`.
|
|
71
129
|
|
|
72
130
|
WHY IT MATTERS
|
|
73
|
-
This job can fail every day and CI will still show green. The checkmark
|
|
74
|
-
|
|
131
|
+
This job can fail every day and CI will still show green. The checkmark on
|
|
132
|
+
this workflow cannot be trusted.
|
|
75
133
|
|
|
76
134
|
HOW TO FIX
|
|
77
135
|
Remove continue-on-error, or scope it to individual non-blocking steps only.
|
|
78
|
-
```
|
|
79
136
|
|
|
80
|
-
|
|
81
|
-
實際上卻沒通過的那個位置。
|
|
137
|
+
Example from this rule's own must-fire fixture: QA-CI-001/must-fire/masked.yml
|
|
82
138
|
|
|
83
|
-
|
|
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
|
|
84
146
|
|
|
85
|
-
|
|
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.
|
|
86
150
|
|
|
87
|
-
|
|
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.
|
|
88
154
|
|
|
89
|
-
|
|
90
|
-
npx mjolnir-qa@latest
|
|
155
|
+
Docs: mjolnir rules --md (full catalog, this rule included)
|
|
91
156
|
```
|
|
92
157
|
|
|
93
|
-
|
|
94
|
-
|
|
158
|
+
這就是價值的基本單位:CI 回報了一次它並未贏得的通過。
|
|
159
|
+
|
|
160
|
+
<br />
|
|
161
|
+
|
|
162
|
+
## 快速開始
|
|
95
163
|
|
|
96
164
|
```bash
|
|
97
|
-
npx mjolnir-qa@latest
|
|
165
|
+
npx mjolnir-qa@latest
|
|
98
166
|
```
|
|
99
167
|
|
|
100
|
-
|
|
101
|
-
其餘一切都是選用的。
|
|
168
|
+
它掃描目前的目錄並印出 Trust Report:發現了什麼、你能在多大程度上信任它、原因,以及下一步該做什麼。當關卡及以上級別沒有任何發現時,它以 `0` 結束。
|
|
102
169
|
|
|
103
|
-
|
|
104
|
-
| ----------------------------------- | ------------------------------------------------- |
|
|
105
|
-
| `mjolnir` | 全儲存庫掃描 + 可信度評分 |
|
|
106
|
-
| `mjolnir --scope changed` | 只看你分支引入的內容——CI 形態 |
|
|
107
|
-
| `mjolnir ci install` | 產生建議性的 PR 工作流程 |
|
|
108
|
-
| `mjolnir explain QA-CI-001` | 是什麼 / 為什麼 / 如何修復 + 單一規則的實測 FP 率 |
|
|
109
|
-
| `mjolnir rules --unmeasured` | 列出按假設而非測量運作的規則 |
|
|
110
|
-
| `mjolnir --json` / `--format sarif` | 機器可讀 / GitHub Code Scanning |
|
|
111
|
-
| `mjolnir --strict` | 同時執行隔離層(quarantine)規則(FP 風險較高) |
|
|
112
|
-
|
|
113
|
-
<details>
|
|
114
|
-
<summary><strong>當某個測試不穩定時</strong></summary>
|
|
170
|
+
在 CI 中,只掃描分支引入的內容,這樣舊有的測試套件就不會淹沒你的第一個 pull request:
|
|
115
171
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
| `mjolnir triage ./test-results/` | 依執行歷史提出隔離建議 |
|
|
120
|
-
| `mjolnir pw-report ./test-results/` | Playwright 執行摘要——重試 / 不穩定 / 最慢 |
|
|
121
|
-
| `mjolnir doctor:playwright` | 僅 Playwright 的深度掃描 + Selector Health Score |
|
|
172
|
+
```bash
|
|
173
|
+
npx mjolnir-qa@latest --scope changed
|
|
174
|
+
```
|
|
122
175
|
|
|
123
|
-
|
|
176
|
+
`mjolnir ci install` 會把它寫成一個 GitHub Actions workflow,使用固定在 `v1` 主版本標籤上的 [action](https://github.com/Sergey-Bar/Mjolnir#readme)(或使用 `--no-action` 改用一般的 `npx`)。在你決定讓它攔截之前,它始終只是建議性的。
|
|
177
|
+
|
|
178
|
+
| 指令 | 作用 |
|
|
179
|
+
| ----------------------------------- | --------------------------------------------- |
|
|
180
|
+
| `mjolnir` | Trust Report:結論、信心程度、下一步行動 |
|
|
181
|
+
| `mjolnir --scope changed` | 只檢查你的分支引入的內容(CI 用法) |
|
|
182
|
+
| `mjolnir ci install` | 產生建議性的 PR workflow(基於 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 風險較高) |
|
|
124
191
|
|
|
125
192
|
<details>
|
|
126
|
-
<summary><strong
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
|
131
|
-
|
|
|
132
|
-
| `mjolnir
|
|
133
|
-
| `mjolnir
|
|
134
|
-
| `mjolnir
|
|
135
|
-
| `mjolnir
|
|
136
|
-
| `mjolnir
|
|
137
|
-
| `mjolnir
|
|
138
|
-
| `mjolnir
|
|
139
|
-
| `mjolnir
|
|
140
|
-
| `mjolnir
|
|
193
|
+
<summary><strong>其他所有指令</strong> — 不穩定測試分類、報告、治理</summary>
|
|
194
|
+
|
|
195
|
+
<br />
|
|
196
|
+
|
|
197
|
+
| 指令 | 作用 |
|
|
198
|
+
| ----------------------------------- | -------------------------------------------------------- |
|
|
199
|
+
| `mjolnir --classic` | Trust Report 之前的評分橫幅樣式 |
|
|
200
|
+
| `mjolnir explain verdict` | 解釋已儲存掃描的結論為何如此 |
|
|
201
|
+
| `mjolnir triage ./test-results/` | 引導式分類。每一列都以下一步行動作結。 |
|
|
202
|
+
| `mjolnir pw-report ./test-results/` | Playwright 執行摘要:重試、不穩定測試、最慢的測試 |
|
|
203
|
+
| `mjolnir doctor:playwright` | 僅針對 Playwright 的深度掃描,外加 Selector Health Score |
|
|
204
|
+
| `mjolnir fix --dry-run` / `fix` | 安全的自動修正,每一項都會重新掃描以證明修正生效 |
|
|
205
|
+
| `mjolnir baseline` / `diff` | 為發現建立快照,之後只回報新增或惡化的 |
|
|
206
|
+
| `mjolnir impact --since <ref>` | 某次提交引入並解決了什麼 |
|
|
207
|
+
| `mjolnir summary` | 根據報告產生 CI 註記和 step 摘要 |
|
|
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>` | 為新規則及其 fixtures 產生骨架 |
|
|
217
|
+
| `mjolnir stats` | 本機記錄的歷來修正計數 |
|
|
218
|
+
| `mjolnir badge` | shields.io 端點 JSON 與程式碼片段 |
|
|
219
|
+
| `mjolnir --cache` | 借助本機結論快取進行增量重新掃描 |
|
|
220
|
+
| `mjolnir --format mermaid` | 用於 PR 留言的測試架構圖 |
|
|
221
|
+
|
|
222
|
+
`mjolnir help <command>` 會印出其中任一指令的用法、範例和下一步。
|
|
141
223
|
|
|
142
224
|
</details>
|
|
143
225
|
|
|
144
|
-
|
|
145
|
-
需要 Node.js ≥ 22.18。支援 Windows、macOS 與 Linux。
|
|
146
|
-
|
|
147
|
-
---
|
|
148
|
-
|
|
149
|
-
## 👥 這是為誰而做?
|
|
150
|
-
|
|
151
|
-
- **QA / SDET**——擁有 e2e 或整合測試套件,需要證據證明套件確實配得上
|
|
152
|
-
它產出的綠色勾勾。
|
|
153
|
-
- **平台 / DevEx 團隊**——負責 CI 完整性與發布門檻;他們在乎
|
|
154
|
-
`continue-on-error` 絕不能悄悄把紅色管線塗成綠色。
|
|
155
|
-
- **OSS 維護者**——想要一個便宜、常駐開啟、可在本機與 CI 執行且零
|
|
156
|
-
網路呼叫的驗證門檻。
|
|
157
|
-
|
|
158
|
-
---
|
|
226
|
+
需要 Windows、macOS 或 Linux 上的 **Node.js ≥ 22.18**。想全域安裝?`npm i -g mjolnir-qa`。這個最低版本來自建置工具鏈(tsdown 以它為目標,發佈流水線也針對它做冒煙測試);執行時相依套件對版本沒有更高要求。
|
|
159
227
|
|
|
160
|
-
|
|
228
|
+
<br />
|
|
161
229
|
|
|
162
|
-
|
|
163
|
-
| --- | --------------------------------------------------------------------------------------------------------- |
|
|
164
|
-
| ⚖️ | **可信度評分**——一個數字、透明的扣分表、沒有黑箱 |
|
|
165
|
-
| 🎭 | **Selector Health Score**——為你的 Playwright 定位器評級,而不只是通過率 |
|
|
166
|
-
| 🔬 | **執行期鑑識**——讀取真實的 Playwright/JUnit 執行資料來捕捉 `TRUE-FLAKE`,而不只是靜態猜測 |
|
|
167
|
-
| 🚨 | **CI 完整性規則**——抓出 `continue-on-error`、`\|\| true` 等假綠花招 |
|
|
168
|
-
| 🐍 | **全部四種 Playwright 綁定**——TypeScript、Python、Java、C#/.NET——外加 pytest、JUnit/TestNG 與 CI 工作流程 |
|
|
169
|
-
| 🔒 | **本機優先**——掃描時零網路呼叫、零遙測、數秒內完成 |
|
|
230
|
+
## Mjölnir 能發現什麼
|
|
170
231
|
|
|
171
|
-
|
|
232
|
+
<p align="center">
|
|
233
|
+
<img src="assets/readme/stack.svg" alt="適用你的技術堆疊:規則所涵蓋的語言、測試框架和 CI 系統,資料來自規則登錄表。" width="100%" />
|
|
234
|
+
</p>
|
|
172
235
|
|
|
173
|
-
|
|
174
|
-
固定樣本。會觸發自身負樣本的規則不能發布——這就是假陽性防火牆。
|
|
236
|
+
**79 條規則**,分為四個類別:測試衛生、測試品質、Playwright 和 CI 完整性,涵蓋 TypeScript 與 JavaScript、Python、Java、C# 以及 GitHub Actions YAML。它們涵蓋 Playwright 的全部四種語言繫結,以及 pytest、JUnit、TestNG、NUnit、xUnit、MSTest、Jest、Vitest 和 Mocha,並為 Cypress 和 Selenium 提供入門級涵蓋。以下列出其中九條,以呈現大致樣貌:
|
|
175
237
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
|
180
|
-
|
|
|
181
|
-
| QA-TEST-
|
|
182
|
-
| QA-
|
|
183
|
-
| QA-
|
|
184
|
-
| QA-
|
|
185
|
-
| QA-
|
|
186
|
-
| QA-
|
|
187
|
-
| 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 | 未 await 的 promise 斷言 | error | quarantine |
|
|
245
|
+
| QA-PW-002 | 未 await 的 locator 斷言 | error | core |
|
|
246
|
+
| QA-PW-004 | 脆弱的 CSS/XPath 選擇器 | warning | quarantine |
|
|
247
|
+
| QA-PY-002 | 被略過的測試(`skip`、非嚴格的 `xfail`) | warning | core |
|
|
248
|
+
| QA-CS-103 | 沒有斷言的測試方法 | error | core |
|
|
188
249
|
|
|
189
|
-
|
|
250
|
+
完整目錄由登錄表自動產生,從不手動維護:`mjolnir rules --md`、[`docs/rules/`](docs/rules/),或 [檢查項目指南](https://sergey-bar.github.io/Mjolnir/guide/what-it-checks)。
|
|
190
251
|
|
|
191
252
|
<details>
|
|
192
|
-
<summary><strong
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
|
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 | 衛生 | 硬等待(`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 | 品質 | 未 await 的 promise 斷言 | error | quarantine |
|
|
269
|
+
| QA-TQUAL-011 | 品質 | 被註解掉的測試 | warning | extended |
|
|
270
|
+
| QA-PW-002 | Playwright | 未 await 的 locator 斷言 | 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 | 總是成功的 step 掩蓋了失敗 | 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 | 硬等待(`Thread.sleep()`) | warning | extended |
|
|
288
|
+
| QA-JV-103 | Java | 沒有斷言的測試方法 | error | extended |
|
|
289
|
+
| QA-JV-105 | Java | Playwright `waitForTimeout()` 硬等待 | warning | core |
|
|
290
|
+
| QA-JV-106 | Java | 使用脆弱選擇器而非基於角色的 locator | warning | quarantine |
|
|
291
|
+
| QA-CS-101 | C# | 被略過的測試(`[Ignore]`、`[Fact(Skip=)]`) | warning | core |
|
|
292
|
+
| QA-CS-102 | C# | 硬等待(`Thread.Sleep` / `Task.Delay`) | warning | core |
|
|
293
|
+
| QA-CS-103 | C# | 沒有斷言的測試方法 | error | core |
|
|
294
|
+
| QA-CS-105 | C# | `WaitForTimeoutAsync()` 硬等待 | warning | extended |
|
|
295
|
+
| QA-CS-106 | C# | 使用脆弱選擇器而非基於角色的 locator | warning | quarantine |
|
|
296
|
+
|
|
297
|
+
Python 另外提供 QA-PY-001…012(pytest 衛生)和 QA-PY-101…108(Python 版 Playwright)。Cypress 和 Selenium 各有一套三條規則的入門集。
|
|
199
298
|
|
|
200
299
|
</details>
|
|
201
300
|
|
|
202
|
-
|
|
203
|
-
<summary><strong>Playwright 🎭</strong></summary>
|
|
301
|
+
每條規則都附帶 must-fire **和** must-not-fire 兩類 fixture,在自己的負向 fixture 上觸發的規則不能發佈。這就是誤報防火牆;`mjolnir doctor` 在本儲存庫自己的 CI 中強制執行它。
|
|
204
302
|
|
|
205
|
-
|
|
206
|
-
| --------- | ------------------------------------- | -------- |
|
|
207
|
-
| QA-PW-002 | 未 await 的 locator 斷言 | error |
|
|
208
|
-
| QA-PW-003 | 提交了 `page.pause()` / `test.only()` | error |
|
|
209
|
-
| QA-PW-004 | 脆弱的 CSS/XPath 選擇器 | warning |
|
|
210
|
-
| QA-PW-123 | 寫死的環境 URL | warning |
|
|
303
|
+
### Selector Health Score
|
|
211
304
|
|
|
212
|
-
|
|
305
|
+
`mjolnir doctor:playwright` 依每個 locator 找到元素的方式為其評分:像使用者那樣尋找(角色、標籤、文字)、透過明確的契約(`data-testid`),還是仰賴結構上的偶然(CSS 串接、XPath)。每個檔案得到 0 到 100 的分數:
|
|
213
306
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
| ID | 規則 | Severity |
|
|
218
|
-
| --------- | -------------------------------------------------- | -------- |
|
|
219
|
-
| QA-CI-001 | `continue-on-error` 掩蓋失敗 | error |
|
|
220
|
-
| QA-CI-002 | `\|\| true` 吞掉結束碼 | error |
|
|
221
|
-
| QA-CI-005 | 消費報告卻從不產生報告 | error |
|
|
222
|
-
| QA-CI-007 | 包在測試外面的重試包裝 | warning |
|
|
223
|
-
| QA-CI-008 | 永遠成功的步驟掩蓋失敗 | error |
|
|
224
|
-
| QA-CI-009 | 測試結束碼未被傳遞(`\|` 沒有 pipefail、`;` 串接) | error |
|
|
225
|
-
| QA-CI-010 | 在必須攔截的地方跳過測試(skip-on-PR 防護) | error |
|
|
226
|
-
|
|
227
|
-
</details>
|
|
228
|
-
|
|
229
|
-
<details>
|
|
230
|
-
<summary><strong>Python / pytest 🐍</strong></summary>
|
|
231
|
-
|
|
232
|
-
| ID | 規則 | Severity |
|
|
233
|
-
| --------- | ------------------------------------ | -------- |
|
|
234
|
-
| QA-PY-002 | 跳過的測試(`skip`、非嚴格 `xfail`) | warning |
|
|
235
|
-
| QA-PY-003 | 無斷言的測試函式 | error |
|
|
236
|
-
| QA-PY-005 | 測試中的 `time.sleep()` | warning |
|
|
237
|
-
| QA-PY-012 | 同義反覆的斷言 | error |
|
|
238
|
-
|
|
239
|
-
共 20 條 Python 規則(QA-PY-001…012 pytest 衛生 + QA-PY-101…108 Playwright-Python)。
|
|
307
|
+
```text
|
|
308
|
+
▍ SELECTOR HEALTH
|
|
240
309
|
|
|
241
|
-
|
|
310
|
+
e2e/login.spec.ts
|
|
311
|
+
[█████████████░░░░░░░] 65 / 100
|
|
312
|
+
role/text: 1 · testid: 0 · plain-css: 0 · css-chains: 1 ⚠ · xpath: 0
|
|
242
313
|
|
|
243
|
-
|
|
244
|
-
|
|
314
|
+
e2e/checkout.spec.ts
|
|
315
|
+
[██████████████████░░] 88 / 100
|
|
316
|
+
role/text: 4 · testid: 1 · plain-css: 0 · css-chains: 1 ⚠ · xpath: 0
|
|
317
|
+
```
|
|
245
318
|
|
|
246
|
-
|
|
247
|
-
| --------- | ---------------------------------------- | -------- |
|
|
248
|
-
| QA-JV-101 | 被停用的測試(`@Disabled`) | warning |
|
|
249
|
-
| QA-JV-102 | 硬式 sleep(`Thread.sleep()`) | warning |
|
|
250
|
-
| QA-JV-103 | 無斷言的測試方法 | error |
|
|
251
|
-
| QA-JV-105 | Playwright 硬式 sleep `waitForTimeout()` | warning |
|
|
252
|
-
| QA-JV-106 | 脆弱選擇器取代 role 定位器 | warning |
|
|
319
|
+
這衡量的是**韌性,而非正確性**。`.btn.btn-primary > div:nth-child(2)` 今天能通過,並會一直通過,直到有人改動標記結構。低分從不聲稱測試壞了,只說明它依賴於沒有人承諾保留的標記結構。
|
|
253
320
|
|
|
254
|
-
|
|
321
|
+
<br />
|
|
255
322
|
|
|
256
|
-
|
|
257
|
-
<summary><strong>C# / .NET — NUnit · xUnit · MSTest 🟣</strong></summary>
|
|
323
|
+
## 可信度評分
|
|
258
324
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
| QA-CS-102 | 硬式 sleep(`Thread.Sleep` / `Task.Delay`) | warning |
|
|
263
|
-
| QA-CS-103 | 無斷言的測試方法 | error |
|
|
264
|
-
| QA-CS-105 | 硬式 sleep `WaitForTimeoutAsync()` | warning |
|
|
265
|
-
| QA-CS-106 | 脆弱選擇器取代 role 定位器 | warning |
|
|
325
|
+
<p align="center">
|
|
326
|
+
<img src="assets/readme/score-gauge.svg" alt="0 到 100 的可信度刻度,指針掃過每一個分數:低於 50 為 UNWORTHY,50 到 79 為 NEEDS WORK,80 到 99 為 WORTHY,100 為 FORGED" width="720" />
|
|
327
|
+
</p>
|
|
266
328
|
|
|
267
|
-
|
|
329
|
+
<sub>0 到 100 的每一個分數,都由真實的 `deriveScoreState` 定位。由 `npm run docs:gauge` 產生,並在 CI 中鎖定以防漂移。</sub>
|
|
268
330
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
> 每條規則的頁面位於 [`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**:找不到測試宣告 |
|
|
277
338
|
|
|
278
|
-
|
|
339
|
+
**計算方式**。嚴重程度決定基礎扣分(`error −8`、`warning −3`、`info −1`),證據等級再對其打折:E2 全額扣分,E1 扣一半(無條件捨去),E0 不扣分。總扣分依套件規模正規化,也就是以每個測試宣告計算,而不是以檔案計算。終端機印出的就是評分所用的同一組折後數字;不存在隱藏的第二套模型。詳情:[docs/SCORING.md](docs/SCORING.md) 和 [評分指南](https://sergey-bar.github.io/Mjolnir/guide/scoring)。
|
|
279
340
|
|
|
280
|
-
**
|
|
281
|
-
人工分類的發現;見 [docs/FP-AUDIT.md](docs/FP-AUDIT.md))。其餘 21 條按
|
|
282
|
-
作者的估計發布。每次掃描的頁尾都會告訴你,_觸發過的_ 規則中有多少經過
|
|
283
|
-
測量;`mjolnir rules --unmeasured` 列出未測量的;每條規則的
|
|
284
|
-
`mjolnir explain` 頁面都聲明其狀態。即使數字難看我們也照樣公布——
|
|
285
|
-
持續性工作。
|
|
341
|
+
**100 分不代表什麼**。它不代表軟體是正確的,不代表測試套件是充分的,也不代表產品沒有缺陷。它只代表一件事:**在本次掃描和這套證據模型下,Mjölnir 評估的規則都沒有產生扣分。**
|
|
286
342
|
|
|
287
|
-
|
|
343
|
+
<br />
|
|
288
344
|
|
|
289
|
-
|
|
290
|
-
分配:
|
|
345
|
+
## 證據模型
|
|
291
346
|
|
|
292
|
-
|
|
293
|
-
| ------------ | ------------------------------ | :------: | :--------: |
|
|
294
|
-
| `core` | 實測 FP ≤ 10 % | ✅ | ✅ |
|
|
295
|
-
| `extended` | 實測 FP ≤ 30 % | ✅ | ✅ |
|
|
296
|
-
| `quarantine` | 高於 30%,或尚未測量(n < 10) | ❌ | ✅ |
|
|
347
|
+
每一項發現都帶有兩個標籤:Mjölnir 有多確定,以及這項發現被查證到什麼程度。這正是只會回報模式的工具,與可以用來把關發佈的工具之間的差別。
|
|
297
348
|
|
|
298
|
-
|
|
299
|
-
| --------------- | ------------ | -------------------------------------------- |
|
|
300
|
-
| TypeScript / JS | 編譯器 AST | 最廣、測量最多——主要為 `core`/`extended` |
|
|
301
|
-
| Python / pytest | 正規表達式層 | 廣泛、經語料庫稽核——主要為 `core`/`extended` |
|
|
302
|
-
| Java | 正規表達式層 | 較新——主要為 `extended`/`quarantine` |
|
|
303
|
-
| C# / .NET | 正規表達式層 | 較新——主要為 `extended`/`quarantine` |
|
|
349
|
+
**有多確定 — 證據等級。**
|
|
304
350
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
351
|
+
| 等級 | 名稱 | 含義 | 扣分 |
|
|
352
|
+
| ------ | ---------- | ------------------------------ | ---- |
|
|
353
|
+
| **E2** | 確定性證明 | 缺陷就存在於程式碼現有的寫法中 | 全額 |
|
|
354
|
+
| **E1** | 模式證據 | 比對到與缺陷高度相關的模式 | 一半 |
|
|
355
|
+
| **E0** | 觀察 | 值得知道。並不聲稱有任何問題。 | 零 |
|
|
308
356
|
|
|
309
|
-
|
|
357
|
+
偵測的信心程度不等於證明的強度。一條規則可以確定自己比對到了要找的東西,但它看到的仍可能只是啟發式結果。E1 發現是用來閱讀和判斷的,絕不能盲目套用;這條界線會標註在終端機、JSON 以及交給代理的交接內容中的每一項發現上。
|
|
310
358
|
|
|
311
|
-
|
|
359
|
+
**查證到什麼程度 — 信任等級**。大多數發現來自閱讀你的程式碼。把一次真實測試執行的報告交給 Mjölnir,它就能確認程式碼確實執行過。
|
|
312
360
|
|
|
313
361
|
<p align="center">
|
|
314
|
-
<img src="assets/readme/
|
|
362
|
+
<img src="assets/readme/trust-ladder.svg" alt="從 L0 到 L5 的信任階梯。L0 到 L2 來自閱讀程式碼;L3 到 L5 需要真實的執行報告,階梯上的斷口標示了這一點。" width="100%" />
|
|
315
363
|
</p>
|
|
316
364
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
365
|
+
| 等級 | 白話解釋 | 所需條件 |
|
|
366
|
+
| ------ | ------------------ | ---------------------------------- |
|
|
367
|
+
| **L0** | 已記錄 | 閱讀程式碼 |
|
|
368
|
+
| **L1** | 看起來像是問題 | 閱讀程式碼:比對到模式 |
|
|
369
|
+
| **L2** | 在程式碼中得到證明 | 閱讀程式碼:缺陷是結構性的 |
|
|
370
|
+
| **L3** | 檔案執行過 | 執行報告顯示發現所在的檔案被執行過 |
|
|
371
|
+
| **L4** | 測試執行過 | 執行報告顯示發現所在的測試被執行過 |
|
|
372
|
+
| **L5** | 執行結果吻合 | 執行本身的結果證實了該缺陷類別 |
|
|
320
373
|
|
|
321
|
-
|
|
322
|
-
(每筆測試宣告的扣分)正規化。按證據加權的扣分意味著弱訊號代價更低。
|
|
323
|
-
終端機顯示的正是評分所用的同一批折後數字——沒有黑箱。完整方法:
|
|
324
|
-
[docs/SCORING.md](docs/SCORING.md)。
|
|
374
|
+
靜態掃描止步於 L2。只有真實的執行報告(Playwright JSON、Jest 或 Vitest JSON、JUnit XML)才能把發現提升到 L3 或更高,因此從未被觀察到執行過的發現,永遠不能聲稱它執行過。定義:[docs/TERMINOLOGY.md](docs/TERMINOLOGY.md)。
|
|
325
375
|
|
|
326
|
-
|
|
376
|
+
### 其中有多少經過實測
|
|
327
377
|
|
|
328
|
-
|
|
329
|
-
| ------- | ---------------- |
|
|
330
|
-
| ≥ 80 | ✓ **WORTHY** |
|
|
331
|
-
| 50 – 79 | ⚠ **NEEDS WORK** |
|
|
332
|
-
| < 50 | ✖ **UNWORTHY** |
|
|
378
|
+
**79 條規則中有 74 條的誤報率是在真實開源程式碼上測得的**(每條至少 10 個人工分類的發現;見 [docs/FP-AUDIT.md](docs/FP-AUDIT.md))。其餘 5 條基於作者的估計發佈,並在 `mjolnir explain` 中逐條註明。`mjolnir rules --unmeasured` 會列出它們,每次掃描的頁尾也會回報實際*觸發*的規則中有多少經過實測。
|
|
333
379
|
|
|
334
|
-
|
|
380
|
+
即使誤報率很差也照樣公開。QA-TEST-001(提交進儲存庫的 `.only`)在真實儲存庫上的稽核結果很差,因此被放在 quarantine。每條規則(包括 QA-PW-141)的最新數字都在稽核報告裡。
|
|
335
381
|
|
|
336
|
-
|
|
337
|
-
| ---- | ---------- | ------------ | ---------------------------------------------- |
|
|
338
|
-
| E2 | 確定性缺陷 | 全額扣分 | 提交了 `.only`——結構上可證明 |
|
|
339
|
-
| E1 | 啟發式模式 | 一半扣分 | 正規表達式匹配到 `sleep()`——訊號強烈,但非證明 |
|
|
340
|
-
| E0 | 觀察 | 零(僅提示) | 只報告,從不為 CI 設門檻,也不扣分 |
|
|
382
|
+
### 規則信任等級
|
|
341
383
|
|
|
342
|
-
|
|
343
|
-
結構性證明;E1 發現是位置恰當的警告,不是形式化證明。
|
|
384
|
+
等級由實測誤報率決定,而不是憑主觀判斷:
|
|
344
385
|
|
|
345
|
-
|
|
346
|
-
|
|
386
|
+
| 等級 | 實測 FP | 行為 |
|
|
387
|
+
| -------------- | ------------------ | --------------------------------------------- |
|
|
388
|
+
| **core** | ≤ 10% | 預設報告,會攔截 |
|
|
389
|
+
| **extended** | ≤ 30% | 預設報告,信心較低 |
|
|
390
|
+
| **quarantine** | > 30% 或被明確宣告 | 僅在 `--strict` 下執行,上限為 info,從不攔截 |
|
|
391
|
+
| _未實測_ | n < 10 | 實測之前不能晉升為 core |
|
|
347
392
|
|
|
348
|
-
|
|
393
|
+
FP 帶只能降級一個層級 — 如果規則被明確宣告在 `quarantine` 中,它們永遠不會將其提升出去。被明確置於 quarantine 的規則無論其測量的 FP 率如何都保持在 quarantine 中。
|
|
349
394
|
|
|
350
|
-
|
|
395
|
+
晉升、降級以及各語言的成熟度:[規則生命週期](https://sergey-bar.github.io/Mjolnir/reference/rule-lifecycle)。
|
|
351
396
|
|
|
352
|
-
|
|
397
|
+
### 為什麼這不是 linter
|
|
353
398
|
|
|
354
|
-
|
|
355
|
-
▚ SELECTOR HEALTH — e2e/checkout.spec.ts
|
|
399
|
+
Linter 告訴你程式碼是否遵循規則。Mjölnir 告訴你你的驗證是否值得信任。
|
|
356
400
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
401
|
+
| | Linter(ESLint、SonarQube) | 覆蓋率工具 | AI 程式碼審查 | **Mjölnir** |
|
|
402
|
+
| ------------------------------------------------------ | :-------------------------: | :--------: | :-----------: | :----------------: |
|
|
403
|
+
| 評估的是**驗證體系**,而不是產品程式碼 | 否 | 否 | 否 | 是 |
|
|
404
|
+
| CI workflow 完整性(`continue-on-error`、`\|\| true`) | 否 | 否 | 僅限 diff | 是 |
|
|
405
|
+
| 評估 Playwright locator 的韌性(Selector Health) | 否 | 否 | 否 | 是 |
|
|
406
|
+
| 讀取真實執行資料得出 `TRUE-FLAKE` 結論 | 否 | 否 | 否 | 是 |
|
|
407
|
+
| 公布每條規則的實測誤報率 | 否 | 否 | 否 | 是 |
|
|
408
|
+
| 標記沒有斷言的測試 | 是\* | 否 | 有時 | 是 |
|
|
409
|
+
| 捕捉硬等待(`waitForTimeout`、`time.sleep`) | 是\* | 否 | 有時 | 是 |
|
|
410
|
+
| 確定性(相同輸入,相同輸出) | 是 | 是 | 否 | 是 |
|
|
411
|
+
| 每次掃描的成本 | 免費 | 免費 | token | **零**(本機執行) |
|
|
360
412
|
|
|
361
|
-
|
|
362
|
-
重構時都會斷,卻不會告訴你是哪個行為回歸了。
|
|
413
|
+
<sub>\*由 `eslint-plugin-jest` 和 `eslint-plugin-playwright`(`expect-expect`、`no-wait-for-timeout`)以及 SonarQube 內建的斷言規則涵蓋。各欄描述的是驗證測試套件時的預設行為;外掛、付費方案和自訂規則會改變其中部分答案。這是一份定位概覽,而不是基準測試。</sub>
|
|
363
414
|
|
|
364
|
-
|
|
415
|
+
也請使用 AI 審查。它能捕捉到任何模式都發現不了的細微差異、意圖和設計缺陷。而 Mjölnir 能捕捉到 AI 審查因為看起來是刻意為之而忽略的東西:提交進儲存庫的 `.only`、被吞掉的結束碼、測試 job 上的 `continue-on-error`。這些需要的是掃描,而不是推理。
|
|
365
416
|
|
|
366
|
-
|
|
417
|
+
<br />
|
|
367
418
|
|
|
368
|
-
|
|
369
|
-
|
|
419
|
+
## 執行時鑑識
|
|
420
|
+
|
|
421
|
+
靜態分析是對從未執行過的程式碼進行推理。鑑識讀取的是實際發生的事情:來自任何執行器的 Playwright JSON、Jest JSON、Vitest JSON 和 JUnit XML。
|
|
370
422
|
|
|
371
423
|
```bash
|
|
372
424
|
mjolnir forensics ./test-results/
|
|
373
425
|
```
|
|
374
426
|
|
|
375
427
|
```text
|
|
376
|
-
|
|
428
|
+
▍ FLAKINESS LEADERBOARD
|
|
377
429
|
|
|
378
430
|
3 tests · 1 failed · 1 flaky · 1 retried
|
|
379
431
|
|
|
@@ -383,265 +435,184 @@ FAILING declines an expired card (e2e/checkout.spec.ts)
|
|
|
383
435
|
████░░░░░░░░░░░░░░░░ 1.1s · 1 attempt
|
|
384
436
|
```
|
|
385
437
|
|
|
386
|
-
|
|
387
|
-
|
|
438
|
+
`TRUE-FLAKE` 並不是說測試被重試過。它的意思是該測試**至少有一次嘗試失敗,隨後以綠色結束**:這是一次僥倖通過,無論最終的勾號怎麼顯示都會被標記出來。`mjolnir triage` 會把這段歷史轉換成隔離建議,`mjolnir pw-report` 則彙整一次執行。正是這些執行報告,把發現提升到 L3 及以上的信任等級。
|
|
439
|
+
|
|
440
|
+
<br />
|
|
388
441
|
|
|
389
|
-
|
|
442
|
+
## CI 完整性
|
|
443
|
+
|
|
444
|
+
測試可以通過,而包住它的流水線卻不可能失敗。Mjölnir 同樣讀取 workflow:`continue-on-error`、`|| true`、從不傳遞的結束碼、總是成功的 step、被使用卻從未產生的報告,以及恰恰在應當攔截的事件上被略過的關卡。每一項發現都會指明 job、step 和行號,並帶有自己的證據等級。
|
|
445
|
+
|
|
446
|
+
產生 PR workflow,預設為建議性的:
|
|
447
|
+
|
|
448
|
+
```bash
|
|
449
|
+
mjolnir ci install
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
或者把 Marketplace 上的 action 加到你現有的 workflow 中:
|
|
453
|
+
|
|
454
|
+
```yaml
|
|
455
|
+
- uses: Sergey-Bar/Mjolnir@v1
|
|
456
|
+
with:
|
|
457
|
+
scope: changed
|
|
458
|
+
fail-on: error
|
|
459
|
+
```
|
|
390
460
|
|
|
391
|
-
|
|
461
|
+
固定 `@v1` 以跟隨主版本線,或固定一個確切的標籤(`@v0.5.32`)以獲得可重現的關卡。[docs/DISTRIBUTION-KIT.md](docs/DISTRIBUTION-KIT.md) 介紹了 Marketplace、Smithery 和各個 MCP 登錄表。
|
|
392
462
|
|
|
393
|
-
|
|
463
|
+
要把發現送進 GitHub Code Scanning,上傳 SARIF(需要在 workflow 或 job 範圍內設定 `security-events: write`):
|
|
394
464
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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
|
+
```
|
|
403
473
|
|
|
404
|
-
|
|
405
|
-
`eslint-plugin-playwright`(`expect-expect`、`no-wait-for-timeout`)
|
|
406
|
-
為其各自框架涵蓋了這些。
|
|
474
|
+
在 GitLab 上,`--format codequality` 會寫出 MR 元件和 diff 註記所讀取的 Code Quality 報告([docs/GITLAB-CI.md](docs/GITLAB-CI.md))。編輯器和流水線設定:[docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md)。
|
|
407
475
|
|
|
408
|
-
|
|
476
|
+
### 變更範圍歸因
|
|
409
477
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
| 基於執行歷史的不穩定分診報告 | ❌ | ✅ | ✅ |
|
|
414
|
-
| 與靜態可信度評分整合 | ❌ | ❌ | ✅ |
|
|
478
|
+
```bash
|
|
479
|
+
npx mjolnir-qa@latest --scope changed
|
|
480
|
+
```
|
|
415
481
|
|
|
416
|
-
|
|
482
|
+
發現會歸因到你的分支新增的行,以 **merge-base** 為基準計算。範圍與完整掃描發現的檔案集合相同(TS/JS spec 和轉接器設定、`test_*.py`、`*Test.java`、`*Tests.cs`、`.github/workflows/*.yml`),再加上未提交和未追蹤的變更,所以在你提交之前就能使用。基準依 `main → master → origin/main → origin/master → origin/HEAD` 的順序解析;可以用 `--base <ref>` 覆寫。
|
|
417
483
|
|
|
418
|
-
|
|
484
|
+
當無法解析 merge-base 時(淺層複製、分離的 HEAD、不在 git 中的目標),發現會退回到以整個檔案歸因,**而且報告會明確說明這一點**。無聲的退回正是這個工具要捕捉的那類缺陷。
|
|
419
485
|
|
|
420
|
-
|
|
486
|
+
<br />
|
|
421
487
|
|
|
422
|
-
|
|
423
|
-
證明整個驗證系統值得信任——而且它只看到你展示給它的 diff。
|
|
488
|
+
## AI 代理
|
|
424
489
|
|
|
425
|
-
|
|
426
|
-
| ----------------------------- | :-------------------------: | :-------------------------------: |
|
|
427
|
-
| 每次掃描成本 | Token(隨 diff 大小成長) | **零**(本機、已安裝) |
|
|
428
|
-
| 看到整個套件 + 所有 CI 設定 | 只有你展示的 PR diff | **每次都是全部** |
|
|
429
|
-
| 確定性(相同輸入 → 相同輸出) | ❌(非確定性) | **✅** |
|
|
430
|
-
| 抓出沉睡數月的模式 | 只在其進入上下文時 | **✅**(掃描所有檔案) |
|
|
431
|
-
| 跨執行記住發現 | ❌(工作階段之間沒有記憶) | **✅**(baseline + diff) |
|
|
432
|
-
| 無人觸發也能執行 | 需要 PR 或提示詞 | **✅**(CI 掛鉤,數秒內執行完成) |
|
|
490
|
+
只有當某個東西據此採取行動時,發現才有價值。
|
|
433
491
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
這些不是需要推理的 bug;它們是需要掃描的事實。
|
|
492
|
+
```text
|
|
493
|
+
SCAN → EVIDENCE → HANDOFF → AGENT → RE-SCAN → PROOF
|
|
494
|
+
```
|
|
438
495
|
|
|
439
|
-
|
|
496
|
+
**AI 撰寫修正。Mjölnir 驗證它**。證明來自重新掃描,而絕不是代理自己回報的成功。
|
|
440
497
|
|
|
441
|
-
|
|
498
|
+
| 指令 | 代理得到什麼 |
|
|
499
|
+
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
500
|
+
| `mjolnir mcp` | 一個基於 stdio 的 [MCP](https://modelcontextprotocol.io) 伺服器。`scan`、`explain` 和 `diff` 都成為可呼叫的工具。 |
|
|
501
|
+
| `mjolnir handoff` | 儲存下來的 `--json` 報告會變成一份確定性的 Markdown 計畫:偵測到了什麼、每項發現的證據界線、哪些東西**不能**改動,以及如何驗證。 |
|
|
502
|
+
| `mjolnir install` | 寫入你的儲存庫中已有的代理設定位置(`.claude/`、`.cursor/`、`.kilo/`、`AGENTS.md`),這樣代理在聲稱完成之前會重新掃描。 |
|
|
442
503
|
|
|
443
|
-
|
|
504
|
+
加入自帶 CLI 的用戶端:
|
|
444
505
|
|
|
445
506
|
```bash
|
|
446
|
-
mjolnir
|
|
507
|
+
claude mcp add mjolnir -- npx -y mjolnir-qa@latest mcp
|
|
447
508
|
```
|
|
448
509
|
|
|
449
|
-
|
|
510
|
+
或者加入任何接受 `mcpServers` 設定區塊的用戶端:
|
|
450
511
|
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
512
|
+
```json
|
|
513
|
+
{
|
|
514
|
+
"mcpServers": {
|
|
515
|
+
"mjolnir": { "command": "npx", "args": ["-y", "mjolnir-qa@latest", "mcp"] }
|
|
516
|
+
}
|
|
517
|
+
}
|
|
456
518
|
```
|
|
457
519
|
|
|
458
|
-
|
|
520
|
+
**護欄比便利更重要**。交接中的每一項發現都帶有它的界線。**E2** 表示 _確定性:檢查位置並套用修正_。**E1** 表示 _需要確認:僅憑觀察不能證明缺陷_。一個盲目修正 E1、抑制規則或修改規則來拉高分數的代理,所做的正是這個工具要捕捉的事情,因此交接內容會在提示詞中、緊鄰這項發現寫明這一點。
|
|
459
521
|
|
|
460
|
-
|
|
522
|
+
<br />
|
|
461
523
|
|
|
462
|
-
|
|
463
|
-
涵蓋測試檔(`*.spec.*`、`*.test.*`),以及 diff 中的 GitHub 工作流程
|
|
464
|
-
檔與 Playwright 設定。當合併基無法解析——淺層複製、detached HEAD、
|
|
465
|
-
非 git 目標、預設分支不同——它會誠實地降級:發現回退到整檔歸因,
|
|
466
|
-
報告會說明這一點。用 `--base <ref>` 覆寫基準參照。
|
|
524
|
+
## 信任與安全
|
|
467
525
|
|
|
468
|
-
|
|
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`。掃描不受信任的程式碼時從不執行它:靜態分析讀取原始碼文字,鑑識解析磁碟上已存在的報告檔案。
|
|
469
527
|
|
|
470
|
-
|
|
528
|
+
兩點說明:`npx` 本身會在任何程式碼執行之前下載套件;而這項保證涵蓋的是 `src/`,不包括第三方外掛。
|
|
471
529
|
|
|
472
|
-
|
|
473
|
-
`.mjolnir.json`)可以微調嚴重度、門檻與範圍——它從不改變偵測語義。
|
|
530
|
+
**外掛不在沙箱中執行**。JS 外掛(`mjolnir-rules/*.mjs`,或在 `"plugins"` 下列出的 npm 套件)以完整的 Node 權限執行,與 ESLint 或 Vitest 外掛的信任模型相同。載入它們需要**在每次掃描時**明確啟用:沒有 `--enable-plugins`(或 `MJOLNIR_ENABLE_PLUGINS=1`)時,它們的原始碼永遠不會被載入,stderr 上的提示會列出被略過的內容。JSON 規則清單不執行任何程式碼,核心規則 ID 前綴是保留的,因此外掛無法冒充核心規則。請透過 [SECURITY.md](SECURITY.md) 回報漏洞。
|
|
474
531
|
|
|
475
|
-
|
|
476
|
-
| ------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
|
|
477
|
-
| `exclude` | `string[]` | 額外的忽略 glob(gitignore 子集),疊加在內建預設之上 |
|
|
478
|
-
| `gate` | `"advisory" \| "error" \| "warning"` | 哪些嚴重度以非零碼結束(預設 `error`;`advisory` 絕不阻塞) |
|
|
479
|
-
| `severityOverrides` | `{ "<RULE-ID>": severity }` | 為你的儲存庫重新排列某條規則的發現 |
|
|
480
|
-
| `ignore` | `IgnoreEntry[]` | 壓制發現——**`reason` 必填**;條目 90 天後過期(明確的 `expires` 日期,或未註明時以設定檔的最後修改時間為準) |
|
|
481
|
-
| `plugins` | `string[]` | 第三方規則套件(見[信任模型](#信任模型)) |
|
|
532
|
+
**它會檢查自己**。一個驗證信任引擎,只有自身可被驗證才站得住腳。每次 CI 執行都會用同一次執行產出的建置來掃描本儲存庫。只要出現任何 error 等級的發現,關卡就會失敗;遇到**部分**掃描或**當掉的規則**時同樣失敗,因為一次被截斷、什麼都沒回報的自我掃描,正是這個專案要捕捉的虛假綠燈。`mjolnir doctor` 會在同一次執行中重新稽核規則庫(fixture 防火牆、等級的誠實性、core 等級上限),結果為 INCONCLUSIVE 的檢查與失敗的檢查同樣判定為失敗。兩份報告都會作為建置產物上傳。
|
|
482
533
|
|
|
483
|
-
|
|
484
|
-
{
|
|
485
|
-
"gate": "error",
|
|
486
|
-
"exclude": ["legacy/**"],
|
|
487
|
-
"severityOverrides": { "QA-PW-141": "warning" },
|
|
488
|
-
"ignore": [
|
|
489
|
-
{
|
|
490
|
-
"ruleId": "QA-TEST-004",
|
|
491
|
-
"files": ["e2e/legacy-login.spec.ts"],
|
|
492
|
-
"reason": "Third-party widget needs a settle delay; tracked in JIRA-4821",
|
|
493
|
-
"expires": "2026-12-31"
|
|
494
|
-
}
|
|
495
|
-
]
|
|
496
|
-
}
|
|
497
|
-
```
|
|
534
|
+
### 結束碼與機器契約
|
|
498
535
|
|
|
499
|
-
|
|
500
|
-
`exclude` 同一語法。機器層級的雜訊用它;當清單應當與其餘設定一起進入
|
|
501
|
-
版本控制時用 `exclude`。
|
|
502
|
-
- **CLI 覆寫**——`--strict`(包含隔離層規則)、`--width <cols>` 與
|
|
503
|
-
`--ascii` / `--no-ascii`(終端機渲染)、`--tone blunt`(更生硬的措辭)、
|
|
504
|
-
`--max-duration <sec>`(限時部分掃描)。
|
|
505
|
-
- 規則壓制與棄用生命週期:[docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md)。
|
|
506
|
-
|
|
507
|
-
`ignore` 條目也為獨立命令 `mjolnir suppressions` 提供資料,該命令列出
|
|
508
|
-
目前被壓制的項目以及每一條的過期時間。
|
|
509
|
-
|
|
510
|
-
---
|
|
511
|
-
|
|
512
|
-
## 📐 結束碼與契約
|
|
513
|
-
|
|
514
|
-
凍結——可以放心在其上建構 CI 邏輯:
|
|
515
|
-
|
|
516
|
-
| 結束碼 | 意義 |
|
|
517
|
-
| ------ | ---------------------------------------------- |
|
|
518
|
-
| `0` | 乾淨——沒有達到或超過門檻的發現 |
|
|
519
|
-
| `1` | 存在達到或超過門檻的發現 |
|
|
520
|
-
| `2` | 部分掃描(時間預算用盡、檔案不可讀)——絕不阻塞 |
|
|
521
|
-
| `10` | 用法錯誤(錯誤的旗標、缺少目標) |
|
|
522
|
-
| `20` | 內部錯誤 |
|
|
523
|
-
|
|
524
|
-
JSON/SARIF 報告為 `schemaVersion: 1`。規則 ID(`QA-<FAMILY>-NNN`)一經
|
|
525
|
-
發布即不可變,且絕不重複使用。
|
|
526
|
-
|
|
527
|
-
---
|
|
528
|
-
|
|
529
|
-
## 信任模型
|
|
530
|
-
|
|
531
|
-
- **本機優先**——掃描期間零網路呼叫。任何時候都是。零遙測。
|
|
532
|
-
- **不做虛假證明**——我們寧可說「未知」也不說「已驗證」。空儲存庫得到
|
|
533
|
-
`score: null`,絕不是虛假的 100。
|
|
534
|
-
- **部分誠實**——如果分析被截斷,輸出會說明。絕不會在未完成時聲稱
|
|
535
|
-
「complete」。
|
|
536
|
-
- **假陽性防火牆**——偵測在去除註解/字串的程式碼視圖上運行
|
|
537
|
-
(TypeScript 規則使用編譯器 AST):出現在散文註解或文件範例字串中的
|
|
538
|
-
模式是文件,不是發現。
|
|
539
|
-
- **測量,而非斷言**——只有具有來自真實 OSS 程式碼的假陽性率的規則才
|
|
540
|
-
進入主打層級(見[這些規則中有多少經過測量](#這些規則中有多少經過測量));
|
|
541
|
-
掃描頁尾與 `mjolnir rules --unmeasured` 會告訴你哪條是哪條。
|
|
542
|
-
- **外掛信任與執行閘門**——外掛是在 `"plugins"` 下宣告的 npm 套件;
|
|
543
|
-
JS 模組位於 `mjolnir-rules/*.mjs`。**沒有沙箱**:外掛程式碼以完整
|
|
544
|
-
Node 權限執行,與 ESLint 或 Vitest 外掛相同的信任模型。正因如此,
|
|
545
|
-
程式碼執行**在每次掃描時都是選擇性的**:傳入 `--enable-plugins`(或
|
|
546
|
-
設定 `MJOLNIR_ENABLE_PLUGINS=1`),否則這些來源不會被載入——一條
|
|
547
|
-
醒目的 stderr 提示會準確列出被跳過的內容。掃描不可信的程式碼絕不會
|
|
548
|
-
執行它。JSON 規則清單(`mjolnir-rules/*.json`)不受影響:它們宣告
|
|
549
|
-
正規表示式模式,按設計不執行任何程式碼。核心規則 ID 前綴是保留的,
|
|
550
|
-
外掛與外部規則若使用將被拒絕以防偽冒。
|
|
551
|
-
- **工作區本機外部規則**(基於資料夾、零網路)——掃描目標旁的
|
|
552
|
-
`mjolnir-rules/` 目錄可載入自訂規則:JSON 檔宣告正規表達式模式(不執行
|
|
553
|
-
程式碼),`.mjs`/`.js` 模組匯出 `rules`(完整 Node 信任,同外掛)。外部
|
|
554
|
-
規則攜帶與核心相同的信任中繼資料;它們絕不能進入核心層級(核心要求
|
|
555
|
-
來自語料庫側檔的實測 FP 率——宣告的 `tier: "core"` 會被壓到
|
|
556
|
-
`extended`),遵守層級上限,並做漂移檢查:`mjolnir rules --md --external`
|
|
557
|
-
從載入的檔案渲染目錄(來源 `external`),矩陣產生器接受 `--external <root>`。
|
|
558
|
-
|
|
559
|
-
---
|
|
560
|
-
|
|
561
|
-
## 🏗️ 架構
|
|
536
|
+
已凍結,你可以放心地在其上建立 CI 邏輯:
|
|
562
537
|
|
|
563
|
-
|
|
564
|
-
|
|
538
|
+
| 結束碼 | 含義 |
|
|
539
|
+
| ------ | -------------------------------------------------- |
|
|
540
|
+
| `0` | 乾淨:關卡及以上級別沒有發現 |
|
|
541
|
+
| `1` | 關卡及以上級別存在發現 |
|
|
542
|
+
| `2` | 部分掃描(時間預算用盡、檔案無法讀取)。從不攔截。 |
|
|
543
|
+
| `10` | 用法錯誤(參數錯誤、缺少目標) |
|
|
544
|
+
| `20` | 內部錯誤 |
|
|
565
545
|
|
|
566
|
-
|
|
567
|
-
mjolnir/
|
|
568
|
-
├── src/
|
|
569
|
-
│ ├── engine/ # LanguageAdapter interface + rule runner
|
|
570
|
-
│ ├── adapters/ # typescript · python · java · csharp · github-actions
|
|
571
|
-
│ ├── rules/ # rules across 8 families + the measured-FP table
|
|
572
|
-
│ ├── playwright/ # Selector Health Score engine
|
|
573
|
-
│ ├── discovery/ # workspace, frameworks, ignore resolution
|
|
574
|
-
│ ├── scope/ # git merge-base changed-scope engine
|
|
575
|
-
│ ├── scorer/ # transparent deduction table + prioritization
|
|
576
|
-
│ ├── reporter/ # terminal · JSON · SARIF 2.1 · Mermaid
|
|
577
|
-
│ ├── forensics/ # run-data ingestion · flake verdicts · triage
|
|
578
|
-
│ ├── config/ # mjolnir.config.json + suppressions
|
|
579
|
-
│ ├── plugins/ # third-party rule loading (no sandbox)
|
|
580
|
-
│ └── commands/ # every subcommand
|
|
581
|
-
└── tests/
|
|
582
|
-
├── fixtures/ # must-fire / must-not-fire per rule
|
|
583
|
-
└── golden/ # frozen score regression locks
|
|
584
|
-
```
|
|
546
|
+
`2` 被刻意區別於 `0`:一次沒有完成的掃描並不是「什麼都沒發現」,它只是還沒找完。
|
|
585
547
|
|
|
586
|
-
|
|
548
|
+
機器使用的一切(MCP 工具結果、`--json`、SARIF 2.1)都來自同一個標準結果,遵循帶版本號且**只做增量擴充**的 schema(`schemaVersion: 1`、`contractVersion: 1`),因此任何使用者都無需從算繪後的文字中重建含義。參見 [機器契約](docs/machine-contract.md)。規則 ID(`QA-<FAMILY>-NNN`)一經發佈即不可更改,也絕不重複使用。
|
|
587
549
|
|
|
588
|
-
|
|
589
|
-
全域狀態。增加一個生態系 = 一個介接器 + 它的規則。
|
|
590
|
-
- **TypeScript/Playwright 使用編譯器 AST**(ts-morph)。Python、Java 與
|
|
591
|
-
C# 執行在共用的、遮蔽註解/字串的正規表達式層上。
|
|
592
|
-
- 針對 Java 與 C# 的 tree-sitter WASM AST 層已存在,是下一步的精度
|
|
593
|
-
提升——尚未接入同步掃描管線。
|
|
550
|
+
<br />
|
|
594
551
|
|
|
595
|
-
|
|
552
|
+
## Mjölnir 無法告訴你的事
|
|
596
553
|
|
|
597
|
-
|
|
554
|
+
- **它不會執行你的測試**。掃描乾淨不等於測試套件通過。
|
|
555
|
+
- **它無法告訴你某個斷言是*錯誤的***。`expect(total).toBe(41)` 看起來很健康。Mjölnir 找的是*不可能失敗*的測試和*不可能變紅*的流水線,而不是檢查了錯誤內容的測試。
|
|
556
|
+
- **它不能證明業務正確性**。這裡沒有任何東西能說明你的產品做到了需求的要求。
|
|
557
|
+
- **100 分不能證明測試套件好**。你的套件是否涵蓋了真實風險是另一個問題,這個工具不回答它。
|
|
558
|
+
- **79 條規則中有 5 條基於估計發佈**,而不是實測的誤報率。每一條都會在自己的發現中註明。
|
|
559
|
+
- **E1 不是 E2**。啟發式發現值得閱讀,但不值得盲目套用。
|
|
560
|
+
- **空儲存庫的得分是 `null`,絕不是 100。**
|
|
561
|
+
- **名為 `*.spec.ts` 卻沒有測試宣告的檔案不算涵蓋**。如果一個儲存庫僅有的 spec 檔案裡只有匯入或型別(`it`/`test` 呼叫為零),它的得分是 `null`,而不是 100。
|
|
598
562
|
|
|
599
|
-
|
|
600
|
-
| ------------------------------------------------------ | --------------------------- |
|
|
601
|
-
| [docs/SCORING.md](docs/SCORING.md) | 分數正規化 + 證據加權 |
|
|
602
|
-
| [docs/FP-AUDIT.md](docs/FP-AUDIT.md) | 實測假陽性率 + 方法 |
|
|
603
|
-
| [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md) | 規則狀態、壓制、棄用 |
|
|
604
|
-
| [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md) | SARIF 輸出 + 編輯器/CI 設定 |
|
|
605
|
-
| [docs/rules/](docs/rules/) | 產生的逐規則目錄 |
|
|
606
|
-
| [CONTRIBUTING.md](CONTRIBUTING.md) | 開發環境 + 貢獻流程 |
|
|
607
|
-
| [CHANGELOG.md](CHANGELOG.md) | 發布歷史 |
|
|
608
|
-
| [SECURITY.md](SECURITY.md) | 漏洞回報 |
|
|
563
|
+
<br />
|
|
609
564
|
|
|
610
|
-
|
|
565
|
+
## 文件
|
|
611
566
|
|
|
612
|
-
|
|
567
|
+
完整的文件網站位於 <https://sergey-bar.github.io/Mjolnir/>。
|
|
613
568
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
[
|
|
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) | 版本歷史 |
|
|
617
584
|
|
|
618
|
-
|
|
585
|
+
### 狀態
|
|
619
586
|
|
|
620
|
-
|
|
587
|
+
**版本 1**。JSON schema 和結束碼是凍結的契約。TypeScript 和 Python 擁有最廣的實測涵蓋。Java 和 C# 較新;請參照 [成熟度表](https://sergey-bar.github.io/Mjolnir/reference/rule-lifecycle) 來理解它們。接下來的計畫,不捏造日期:[公開路線圖](https://sergey-bar.github.io/Mjolnir/reference/roadmap)。
|
|
621
588
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
589
|
+
### 參與貢獻
|
|
590
|
+
|
|
591
|
+
新規則是最容易上手的第一份貢獻。一條指令就能為規則產生骨架,連同它的 must-fire **和** must-not-fire fixture。產生的規則在寫出真正的偵測邏輯之前,會刻意在自己的 fixture 上失敗,因為一個被發佈出去的空殼,就是一條沒人量測過的規則:
|
|
625
592
|
|
|
626
593
|
```bash
|
|
627
594
|
mjolnir create-rule QA-PW-140 --title "Screenshot without diff bound"
|
|
628
595
|
```
|
|
629
596
|
|
|
630
|
-
|
|
631
|
-
[CONTRIBUTING.md](CONTRIBUTING.md)。
|
|
597
|
+
開發環境建置、常駐關卡指令,以及 anti-creep 和 fixture 防火牆兩條法則,都在 [CONTRIBUTING.md](CONTRIBUTING.md) 中。
|
|
632
598
|
|
|
633
|
-
|
|
599
|
+
<br />
|
|
634
600
|
|
|
635
601
|
<div align="center">
|
|
636
602
|
|
|
637
|
-
|
|
603
|
+
<img src="assets/readme/closing.svg" alt="在你的儲存庫上執行它。" width="100%" />
|
|
638
604
|
|
|
639
605
|
```bash
|
|
640
606
|
npx mjolnir-qa@latest
|
|
641
607
|
```
|
|
642
608
|
|
|
643
|
-
|
|
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
|
+
要問證據能否證明它們值得信任。
|
|
644
615
|
|
|
645
|
-
|
|
616
|
+
<sub>由 [Sergey Bar](https://www.linkedin.com/in/sergeybar/) 打造 · MIT 授權</sub>
|
|
646
617
|
|
|
647
618
|
</div>
|