mjolnir-qa 1.0.8 → 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/CHANGELOG.md +208 -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 +416 -426
- 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 +590 -111
- package/dist/cli.mjs +9937 -8951
- package/dist/mcp/stdio.mjs +1904 -816
- package/dist/rolldown-runtime-8H4AJuhK.mjs +14 -0
- package/package.json +7 -4
package/README.vi.md
CHANGED
|
@@ -1,391 +1,431 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
<img src="assets/readme/
|
|
3
|
+
<img src="assets/readme/hero.svg" alt="Mjölnir. Test cho bạn biết cái gì đã qua. Mjölnir cho bạn biết cái gì đáng tin." width="100%" />
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
<br />
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
niềm tin gãy.
|
|
7
|
+
Mjölnir tìm ra những test không thể thất bại và những pipeline không thể chuyển đỏ,<br />
|
|
8
|
+
rồi chấm điểm mức độ đáng tin của kết quả, kèm bằng chứng cho từng điểm.
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
[](https://github.com/Sergey-Bar/Mjolnir/actions/workflows/ci.yml)
|
|
13
|
-
[](LICENSE)
|
|
14
|
-
[](https://nodejs.org)
|
|
15
|
-
|
|
16
|
-
[English](README.md) | [简体中文](README.zh.md) | [繁體中文](README.zht.md) | [한국어](README.ko.md) | [Deutsch](README.de.md) | [Español](README.es.md) | [Français](README.fr.md) | [Italiano](README.it.md) | [Dansk](README.da.md) | [日本語](README.ja.md) | [Polski](README.pl.md) | [Русский](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.he.md) | [العربية](README.ar.md) | [Bosanski](README.bs.md)
|
|
10
|
+
<br />
|
|
17
11
|
|
|
18
|
-
|
|
12
|
+
[](https://www.npmjs.com/package/mjolnir-qa)
|
|
13
|
+
[](https://www.npmjs.com/package/mjolnir-qa)
|
|
14
|
+
[](https://github.com/Sergey-Bar/Mjolnir/actions/workflows/ci.yml)
|
|
15
|
+
[](https://codecov.io/gh/Sergey-Bar/Mjolnir)
|
|
16
|
+
[](https://scorecard.dev/viewer/?uri=github.com/Sergey-Bar/Mjolnir)
|
|
17
|
+
[](LICENSE)
|
|
18
|
+
[](https://nodejs.org)
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
21
|
npx mjolnir-qa@latest
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
[Xem cách hoạt động](#xem-cách-hoạt-động) · [Bắt đầu nhanh](#bắt-đầu-nhanh) · [Phát hiện gì](#mjölnir-phát-hiện-gì) · [Điểm](#điểm-đáng-tin) · [Bằng chứng](#mô-hình-bằng-chứng) · [Phân tích lần chạy](#phân-tích-pháp-chứng-lúc-chạy) · [CI](#tính-toàn-vẹn-ci) · [Tác tử](#tác-tử-ai) · [Bảo mật](#tin-cậy-và-bảo-mật) · [Giới hạn](#những-điều-mjölnir-không-thể-cho-bạn-biết) · [Tài liệu](#tài-liệu)
|
|
25
|
+
|
|
26
|
+
<details>
|
|
27
|
+
<summary>Đọc bằng ngôn ngữ khác — 22 bản dịch</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.uk.md) | [বাংলা](README.bn.md) | [Ελληνικά](README.gr.md) | Tiếng Việt | [עברית](README.he.md) | [العربية](README.ar.md) | [Bosanski](README.bs.md)
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
[Tài liệu](#-tài-liệu)
|
|
31
|
+
> 🤖 Machine-assisted translation. The [English README](README.md) is canonical. Last synced: 2026-09-15.
|
|
32
|
+
|
|
33
|
+
<!-- Source hash: 3541b09e8d04 -->
|
|
34
|
+
|
|
35
|
+
</details>
|
|
32
36
|
|
|
33
37
|
</div>
|
|
34
38
|
|
|
35
|
-
|
|
39
|
+
<br />
|
|
40
|
+
|
|
41
|
+
## Dấu tích xanh là một lời khẳng định, không phải bằng chứng
|
|
42
|
+
|
|
43
|
+
Dấu tích xanh nghĩa là pipeline không thất bại. Nó không có nghĩa là test đã chạy, hay test đã có thể thất bại. Mỗi trường hợp dưới đây đều qua với màu xanh:
|
|
44
|
+
|
|
45
|
+
- một `.only` bị commit khiến chỉ 3 test chạy thay vì 900
|
|
46
|
+
- `continue-on-error: true` trên job lẽ ra phải chặn
|
|
47
|
+
- `|| true` phía sau lệnh chạy test
|
|
48
|
+
- một test không khẳng định gì, hoặc có thân rỗng
|
|
49
|
+
- một lớp bọc thử lại biến thất bại thật thành lần qua may mắn
|
|
50
|
+
- một báo cáo mà workflow tải lên nhưng chưa từng được tạo ra
|
|
51
|
+
- một lệnh sleep cố định đang níu giữ một race condition
|
|
52
|
+
|
|
53
|
+
Không cái nào khiến pipeline chuyển đỏ, và cái nào trông cũng có vẻ cố ý khi review. Đó là lý do chúng sống sót. Đây là Mjölnir đang đọc một ví dụ thật:
|
|
54
|
+
|
|
55
|
+
<p align="center">
|
|
56
|
+
<img src="assets/readme/scan.svg" alt="Workflow CI của kho demo, đọc từng dòng. Mjölnir đánh dấu mỗi phát hiện tại dòng nó báo cáo, kèm quy tắc, điều sai, mức bằng chứng và tỷ lệ dương tính giả đã đo." width="800" />
|
|
57
|
+
</p>
|
|
58
|
+
|
|
59
|
+
<sub>Mọi phát hiện mà lần quét demo báo cáo cho workflow này, tại dòng được báo cáo. Được tạo bởi `npm run docs:readme-brand` từ [`demo-report.json`](assets/readme/demo-report.json) và được khóa chống sai lệch trong CI.</sub>
|
|
60
|
+
|
|
61
|
+
**Chế độ nghiêm ngặt.** Các phát hiện hung hăng nhất — `.only`, `continue-on-error`, kiểm tra trống, lạm dụng thử lại — nằm ở tầng cách ly. Chúng chỉ chạy với `--strict` và bị giới hạn ở mức nghiêm trọng `info`: chúng đánh dấu, không bao giờ chặn. Quét mặc định (`npx mjolnir-qa@latest` không có `--strict`) chỉ bao gồm các quy tắc cốt lõi và mở rộng. Thêm `--strict` khi bạn cũng muốn lớp tư vấn.
|
|
62
|
+
|
|
63
|
+
Mjölnir đọc bộ test, các workflow CI và, nếu bạn có, báo cáo của một lần chạy thật. Nó không chạy test của bạn, không cài dependency và không thực thi mã mà nó quét. Khi không có bằng chứng, nó nói thẳng như vậy thay vì bịa ra sự tự tin:
|
|
64
|
+
|
|
65
|
+
| Tình huống | Mjölnir báo cáo gì |
|
|
66
|
+
| --------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
67
|
+
| Không tìm thấy khai báo test | Điểm `null`, hiển thị là **UNKNOWN**. Không bao giờ là một con số 100 bịa ra. |
|
|
68
|
+
| Không có baseline hay phiên bản để so sánh | **UNKNOWN**, kèm lý do. Không bao giờ giả định là 0. |
|
|
69
|
+
| Lần quét bị cắt ngang (hết thời gian, tệp không đọc được) | **PARTIAL**, mã thoát `2`. Không bao giờ được trình bày là sạch. |
|
|
70
|
+
|
|
71
|
+
<p align="center">
|
|
72
|
+
<img src="assets/readme/how-it-works.svg" alt="Cách Mjölnir hoạt động. Nó đọc tĩnh bộ test và pipeline CI, cùng với báo cáo của một lần chạy thật khi có. Nó cân mỗi phát hiện theo mức bằng chứng và mức tin cậy, trong đó chỉ lần chạy thật mới đạt được L3 đến L5, rồi tạo ra các phát hiện, điểm đáng tin và một cổng CI dựa trên mã thoát đã đóng băng. Trong vòng lặp của tác tử, AI viết bản sửa và Mjölnir quét lại để chứng minh nó." width="880" />
|
|
73
|
+
</p>
|
|
74
|
+
|
|
75
|
+
<sub>Được dựng riêng cho trang này và hiển thị ở tỷ lệ 1:1. Được tạo bởi `npm run docs:readme-brand` và được khóa chống sai lệch trong CI; điểm, số đếm và ID quy tắc đến từ [`script.demo.json`](assets/video/script.demo.json), [`demo-report.json`](assets/readme/demo-report.json) và sổ đăng ký quy tắc, không bao giờ gõ tay. Cùng bức hình ở dạng poster: [`architecture.svg`](assets/readme/architecture.svg).</sub>
|
|
76
|
+
|
|
77
|
+
<br />
|
|
78
|
+
|
|
79
|
+
## Xem cách hoạt động
|
|
80
|
+
|
|
81
|
+
Một lần quét thật trên [`examples/demo-repo`](examples/demo-repo), một bộ test Playwright nhỏ có workflow CI. Đây là nơi điểm của nó bị trừ:
|
|
82
|
+
|
|
83
|
+
<p align="center">
|
|
84
|
+
<img src="assets/readme/terminal-hero.svg" alt="Bảng phân tích trừ điểm của Mjölnir: WORTHINESS 75/100 NEEDS WORK, điểm theo từng hạng mục, ô trừ điểm theo mức nghiêm trọng và danh sách FIX THIS FIRST" width="520" />
|
|
85
|
+
</p>
|
|
86
|
+
|
|
87
|
+
<sub>Được tạo bởi `npm run docs:hero` từ một lần quét thật và được khóa chống sai lệch trong CI. Báo cáo `--verbose` đầy đủ của cùng lần quét là [`demo.svg`](assets/readme/demo.svg) (`npm run docs:demo`).</sub>
|
|
88
|
+
|
|
89
|
+
<details>
|
|
90
|
+
<summary><strong>Xem video</strong> — một lần quét, bản sửa mà nó in ra, và lần quét lại chứng minh bản sửa đó</summary>
|
|
36
91
|
|
|
37
|
-
|
|
92
|
+
<br />
|
|
38
93
|
|
|
39
94
|
<p align="center">
|
|
40
|
-
<
|
|
95
|
+
<a href="assets/video/mjolnir-demo.mp4">
|
|
96
|
+
<img src="assets/video/mjolnir-demo-poster.png" alt="Một khung hình từ bản ghi demo: npx mjolnir-qa@latest đang quét kho demo trong cửa sổ terminal" width="900" />
|
|
97
|
+
</a>
|
|
41
98
|
</p>
|
|
42
99
|
|
|
43
|
-
<sub
|
|
44
|
-
render từ reporter thật — không lược bỏ gì. Tạo lại bằng
|
|
45
|
-
`npm run docs:demo`;
|
|
46
|
-
[`tests/demo-asset-reproducibility.spec.ts`](tests/demo-asset-reproducibility.spec.ts)
|
|
47
|
-
khiến CI fail nếu sản phẩm lệch khỏi những gì công cụ in ra.</sub>
|
|
100
|
+
<sub>Được dựng từng khung hình từ một lần quét thật bằng `npm run docs:video`; không bao giờ quay màn hình. Chọn khung hình để mở [`mjolnir-demo.mp4`](assets/video/mjolnir-demo.mp4).</sub>
|
|
48
101
|
|
|
49
|
-
|
|
102
|
+
</details>
|
|
50
103
|
|
|
51
|
-
|
|
52
|
-
một file kiểm thử Python — bốn ngôn ngữ/định dạng, một lượt chạy.
|
|
53
|
-
2. Nó tìm thấy bằng chứng làm suy giảm niềm tin vào suite — một
|
|
54
|
-
`continue-on-error` che giấu job, một `|| true` nuốt exit code,
|
|
55
|
-
sleep cứng, selector giòn, URL staging hardcode, chờ `networkidle`.
|
|
56
|
-
3. Nó biến từng cái thành finding cụ thể với rule ID, vị trí và cách
|
|
57
|
-
sửa — và một điểm duy nhất để gate một PR.
|
|
104
|
+
### Cận cảnh một phát hiện
|
|
58
105
|
|
|
59
|
-
|
|
106
|
+
Mỗi phát hiện trả lời bốn câu hỏi: nó ở đâu, Mjölnir chắc chắn đến mức nào, quy tắc sai thường xuyên đến đâu, và cách sửa.
|
|
60
107
|
|
|
61
|
-
|
|
62
|
-
được:
|
|
108
|
+
<p align="center">
|
|
109
|
+
<img src="assets/readme/finding-anatomy.svg" alt="Phát hiện đầu tiên của lần quét demo, đúng như terminal in ra, với bốn phần được đánh dấu: ở đâu, chắc chắn đến mức nào, quy tắc sai thường xuyên đến đâu, và bản sửa." width="100%" />
|
|
110
|
+
</p>
|
|
111
|
+
|
|
112
|
+
`mjolnir explain QA-CI-001` in ra toàn bộ hồ sơ tin cậy của một quy tắc, gồm cả tỷ lệ dương tính giả đã đo và cấp mà tỷ lệ đó mang lại cho nó:
|
|
63
113
|
|
|
64
114
|
```text
|
|
65
|
-
|
|
115
|
+
▍ QA-CI-001 — continue-on-error masks a failing verification gate
|
|
66
116
|
|
|
67
117
|
Severity: error
|
|
68
118
|
Confidence: high
|
|
119
|
+
Tier: quarantine
|
|
69
120
|
Evidence: E2
|
|
70
|
-
|
|
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
|
|
71
126
|
|
|
72
127
|
WHAT WAS FOUND (real detector output, not a mockup)
|
|
73
128
|
Job `security-scan` runs a verification gate under `continue-on-error: true`.
|
|
74
129
|
|
|
75
130
|
WHY IT MATTERS
|
|
76
|
-
This job can fail every day and CI will still show green. The checkmark
|
|
77
|
-
|
|
131
|
+
This job can fail every day and CI will still show green. The checkmark on
|
|
132
|
+
this workflow cannot be trusted.
|
|
78
133
|
|
|
79
134
|
HOW TO FIX
|
|
80
135
|
Remove continue-on-error, or scope it to individual non-blocking steps only.
|
|
81
|
-
```
|
|
82
136
|
|
|
83
|
-
|
|
84
|
-
bạn nói rằng điều gì đó đã pass khi thực tế chưa pass.
|
|
137
|
+
Example from this rule's own must-fire fixture: QA-CI-001/must-fire/masked.yml
|
|
85
138
|
|
|
86
|
-
|
|
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
|
|
87
146
|
|
|
88
|
-
|
|
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.
|
|
89
150
|
|
|
90
|
-
|
|
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.
|
|
91
154
|
|
|
92
|
-
|
|
93
|
-
npx mjolnir-qa@latest
|
|
155
|
+
Docs: mjolnir rules --md (full catalog, this rule included)
|
|
94
156
|
```
|
|
95
157
|
|
|
96
|
-
|
|
97
|
-
|
|
158
|
+
Đó là đơn vị giá trị: một chỗ mà CI báo cáo một lần qua mà nó không xứng đáng có.
|
|
159
|
+
|
|
160
|
+
<br />
|
|
161
|
+
|
|
162
|
+
## Bắt đầu nhanh
|
|
98
163
|
|
|
99
164
|
```bash
|
|
100
|
-
npx mjolnir-qa@latest
|
|
165
|
+
npx mjolnir-qa@latest
|
|
101
166
|
```
|
|
102
167
|
|
|
103
|
-
|
|
104
|
-
và xong. Mọi thứ còn lại là tuỳ chọn.
|
|
105
|
-
|
|
106
|
-
| Lệnh | Nó làm gì |
|
|
107
|
-
| ----------------------------------- | ---------------------------------------------------------- |
|
|
108
|
-
| `mjolnir` | Quét toàn repo + điểm độ đáng tin |
|
|
109
|
-
| `mjolnir --scope changed` | Chỉ những gì branch bạn đưa vào — dạng CI |
|
|
110
|
-
| `mjolnir ci install` | Sinh workflow PR kiểu tư vấn |
|
|
111
|
-
| `mjolnir explain QA-CI-001` | Gì / tại sao / cách sửa + tỷ lệ FP đo được của một quy tắc |
|
|
112
|
-
| `mjolnir rules --unmeasured` | Các quy tắc chạy bằng giả định, không phải đo đạc |
|
|
113
|
-
| `mjolnir --json` / `--format sarif` | Đọc được bằng máy / GitHub Code Scanning |
|
|
114
|
-
| `mjolnir --strict` | Chạy thêm các quy tắc tier quarantine (rủi ro FP cao hơn) |
|
|
168
|
+
Nó quét thư mục hiện tại và in ra Trust Report: nó tìm thấy gì, bạn có thể tin đến mức nào, vì sao, và bước tiếp theo là gì. Nó thoát với `0` khi không tìm thấy gì ở mức cổng hoặc cao hơn.
|
|
115
169
|
|
|
116
|
-
|
|
117
|
-
<summary><strong>Khi có gì đó flaky</strong></summary>
|
|
170
|
+
Trong CI, chỉ quét những gì nhánh đưa vào, để một bộ test cũ không nhấn chìm pull request đầu tiên của bạn:
|
|
118
171
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
| `mjolnir triage ./test-results/` | Đề xuất cách ly từ lịch sử thực thi |
|
|
123
|
-
| `mjolnir pw-report ./test-results/` | Tóm tắt lần chạy Playwright — retry / flake / chậm nhất |
|
|
124
|
-
| `mjolnir doctor:playwright` | Quét sâu riêng Playwright + Selector Health Score |
|
|
172
|
+
```bash
|
|
173
|
+
npx mjolnir-qa@latest --scope changed
|
|
174
|
+
```
|
|
125
175
|
|
|
126
|
-
|
|
176
|
+
`mjolnir ci install` ghi điều đó thành một workflow GitHub Actions, dùng [action](https://github.com/Sergey-Bar/Mjolnir#readme) được ghim vào tag chính `v1` (hoặc `npx` thuần với `--no-action`). Nó chỉ mang tính tư vấn cho đến khi bạn quyết định nó nên chặn.
|
|
177
|
+
|
|
178
|
+
| Lệnh | Chức năng |
|
|
179
|
+
| ----------------------------------- | ------------------------------------------------------ |
|
|
180
|
+
| `mjolnir` | Trust Report: kết luận, độ tin, hành động tiếp theo |
|
|
181
|
+
| `mjolnir --scope changed` | Chỉ những gì nhánh của bạn đưa vào (dạng dùng cho CI) |
|
|
182
|
+
| `mjolnir ci install` | Tạo workflow PR mang tính tư vấn (dựa trên action) |
|
|
183
|
+
| `mjolnir explain QA-CI-001` | Cái gì, vì sao và cách sửa, kèm tỷ lệ FP đã đo |
|
|
184
|
+
| `mjolnir why src/a.spec.ts:42` | Vì sao chính dòng này bị đánh dấu. Không bao giờ chặn. |
|
|
185
|
+
| `mjolnir forensics ./test-results/` | Bằng chứng runtime từ một lần chạy thật |
|
|
186
|
+
| `mjolnir trust-report` | Trust Artifact độc lập (md + json) |
|
|
187
|
+
| `mjolnir handoff` | Kế hoạch khắc phục cho tác tử lập trình |
|
|
188
|
+
| `mjolnir --json` / `--format sarif` | Đầu ra máy đọc được, GitHub Code Scanning |
|
|
189
|
+
| `mjolnir --format codequality` | Báo cáo GitLab Code Quality (artifact cho widget MR) |
|
|
190
|
+
| `mjolnir --strict` | Chạy cả các quy tắc cấp quarantine (rủi ro FP cao hơn) |
|
|
127
191
|
|
|
128
192
|
<details>
|
|
129
|
-
<summary><strong>
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
|
134
|
-
|
|
|
135
|
-
| `mjolnir
|
|
136
|
-
| `mjolnir
|
|
137
|
-
| `mjolnir
|
|
138
|
-
| `mjolnir
|
|
139
|
-
| `mjolnir
|
|
140
|
-
| `mjolnir
|
|
141
|
-
| `mjolnir
|
|
142
|
-
| `mjolnir
|
|
143
|
-
| `mjolnir
|
|
193
|
+
<summary><strong>Mọi lệnh khác</strong> — phân loại test chập chờn, báo cáo, quản trị</summary>
|
|
194
|
+
|
|
195
|
+
<br />
|
|
196
|
+
|
|
197
|
+
| Lệnh | Chức năng |
|
|
198
|
+
| ----------------------------------- | --------------------------------------------------------------------------- |
|
|
199
|
+
| `mjolnir --classic` | Giao diện banner điểm từ trước khi có Trust Report |
|
|
200
|
+
| `mjolnir explain verdict` | Vì sao kết luận của lần quét đã lưu lại như vậy |
|
|
201
|
+
| `mjolnir triage ./test-results/` | Phân loại có hướng dẫn. Mỗi hàng kết thúc bằng một hành động tiếp theo. |
|
|
202
|
+
| `mjolnir pw-report ./test-results/` | Tóm tắt lần chạy Playwright: số lần thử lại, test chập chờn, test chậm nhất |
|
|
203
|
+
| `mjolnir doctor:playwright` | Quét sâu chỉ dành cho Playwright kèm Selector Health Score |
|
|
204
|
+
| `mjolnir fix --dry-run` / `fix` | Tự động sửa an toàn, mỗi bản sửa được quét lại để chứng minh nó có hiệu lực |
|
|
205
|
+
| `mjolnir baseline` / `diff` | Chụp nhanh các phát hiện, sau đó chỉ báo cáo cái mới hoặc tệ hơn |
|
|
206
|
+
| `mjolnir impact --since <ref>` | Một commit đã đưa vào và giải quyết những gì |
|
|
207
|
+
| `mjolnir summary` | Chú thích CI và tóm tắt step từ một báo cáo |
|
|
208
|
+
| `mjolnir pr-comment` | Một bình luận PR có phạm vi, dạng Markdown |
|
|
209
|
+
| `mjolnir debt` | Sổ nợ test kèm mô hình chi phí |
|
|
210
|
+
| `mjolnir handover` | Bản đồ làm quen bộ test cho kỹ sư QA mới |
|
|
211
|
+
| `mjolnir init` | Phát hiện framework, in danh sách kiểm tra thiết lập |
|
|
212
|
+
| `mjolnir suppressions` | Liệt kê các phát hiện bị chặn, phục vụ quản trị |
|
|
213
|
+
| `mjolnir rules --unmeasured` | Những quy tắc chạy dựa trên giả định, không phải đo lường |
|
|
214
|
+
| `mjolnir rules --md` | Danh mục quy tắc đầy đủ (JSON hoặc Markdown) |
|
|
215
|
+
| `mjolnir doctor` | Tự kiểm toán cơ sở quy tắc của chính Mjölnir |
|
|
216
|
+
| `mjolnir create-rule <ID>` | Tạo khung cho một quy tắc mới và các fixture của nó |
|
|
217
|
+
| `mjolnir stats` | Bộ đếm cục bộ mọi bản sửa từng thấy |
|
|
218
|
+
| `mjolnir badge` | JSON cho endpoint shields.io và đoạn mã |
|
|
219
|
+
| `mjolnir --cache` | Quét lại tăng dần qua bộ đệm kết luận cục bộ |
|
|
220
|
+
| `mjolnir --format mermaid` | Sơ đồ kiến trúc test cho một bình luận PR |
|
|
221
|
+
|
|
222
|
+
`mjolnir help <command>` in cách dùng, ví dụ và bước tiếp theo cho bất kỳ lệnh nào.
|
|
144
223
|
|
|
145
224
|
</details>
|
|
146
225
|
|
|
147
|
-
|
|
148
|
-
Yêu cầu Node.js ≥ 22.18. Chạy trên Windows, macOS và Linux.
|
|
149
|
-
|
|
150
|
-
---
|
|
226
|
+
Yêu cầu **Node.js ≥ 22.18** trên Windows, macOS hoặc Linux. Muốn cài toàn cục? `npm i -g mjolnir-qa`. Mức tối thiểu này đến từ chuỗi công cụ build (tsdown nhắm tới nó và pipeline phát hành chạy smoke test trên nó); các dependency lúc chạy không cần gì hơn.
|
|
151
227
|
|
|
152
|
-
|
|
228
|
+
<br />
|
|
153
229
|
|
|
154
|
-
|
|
155
|
-
suite thực sự xứng đáng với dấu xanh nó tạo ra.
|
|
156
|
-
- **Nhóm Platform / DevEx** chịu trách nhiệm về tính toàn vẹn CI và các
|
|
157
|
-
release gate — những người không muốn một `continue-on-error` lặng lẽ
|
|
158
|
-
tô đỏ thành xanh cho cả pipeline.
|
|
159
|
-
- **Người duy trì OSS** muốn một gate kiểm chứng rẻ, luôn bật, chạy cả
|
|
160
|
-
cục bộ và trong CI với 0 lệnh gọi mạng.
|
|
230
|
+
## Mjölnir phát hiện gì
|
|
161
231
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
232
|
+
<p align="center">
|
|
233
|
+
<img src="assets/readme/stack.svg" alt="Hoạt động với stack của bạn: các ngôn ngữ, framework test và hệ thống CI mà các quy tắc bao phủ, lấy từ sổ đăng ký quy tắc." width="100%" />
|
|
234
|
+
</p>
|
|
165
235
|
|
|
166
|
-
|
|
167
|
-
| --- | -------------------------------------------------------------------------------------------------------------- |
|
|
168
|
-
| ⚖️ | **Điểm độ đáng tin** — một con số, bảng trừ minh bạch, không hộp đen |
|
|
169
|
-
| 🎭 | **Selector Health Score** — chấm locator Playwright của bạn, không chỉ tỉ lệ pass |
|
|
170
|
-
| 🔬 | **Pháp y runtime** — đọc dữ liệu chạy thật của Playwright/JUnit để bắt `TRUE-FLAKE`, không chỉ phỏng đoán tĩnh |
|
|
171
|
-
| 🚨 | **Quy tắc toàn vẹn CI** — bắt `continue-on-error`, `\|\| true` và các mẹo xanh giả khác |
|
|
172
|
-
| 🐍 | **Cả bốn binding Playwright** — TypeScript, Python, Java, C#/.NET — cộng pytest, JUnit/TestNG và CI workflows |
|
|
173
|
-
| 🔒 | **Local-first** — 0 lệnh gọi mạng khi quét, 0 telemetry, chạy trong vài giây |
|
|
236
|
+
**79 quy tắc** trong bốn nhóm — vệ sinh test, chất lượng test, Playwright và tính toàn vẹn CI — cho TypeScript và JavaScript, Python, Java, C# và YAML của GitHub Actions. Chúng bao phủ Playwright ở cả bốn binding, cùng pytest, JUnit, TestNG, NUnit, xUnit, MSTest, Jest, Vitest và Mocha, với mức hỗ trợ ban đầu cho Cypress và Selenium. Chín quy tắc trong số đó, để bạn hình dung:
|
|
174
237
|
|
|
175
|
-
|
|
238
|
+
| ID | Quy tắc | Mức nghiêm trọng | Cấp |
|
|
239
|
+
| ------------ | -------------------------------------------------------------------------- | ---------------- | ---------- |
|
|
240
|
+
| QA-CI-001 | `continue-on-error` che giấu một cổng xác minh đang thất bại | error | quarantine |
|
|
241
|
+
| QA-CI-009 | Mã thoát của test không được truyền đi (`\|` không có pipefail, chuỗi `;`) | error | extended |
|
|
242
|
+
| QA-TEST-001 | Commit test bị focus (`.only`, `fit`) | error | quarantine |
|
|
243
|
+
| QA-TEST-003 | Test không có assertion | error | quarantine |
|
|
244
|
+
| QA-TQUAL-009 | Assertion trên promise không được await | error | quarantine |
|
|
245
|
+
| QA-PW-002 | Assertion trên locator không được await | error | core |
|
|
246
|
+
| QA-PW-004 | Selector CSS/XPath dễ vỡ | warning | quarantine |
|
|
247
|
+
| QA-PY-002 | Test bị bỏ qua (`skip`, `xfail` không nghiêm ngặt) | warning | core |
|
|
248
|
+
| QA-CS-103 | Phương thức test không có assertion | error | core |
|
|
176
249
|
|
|
177
|
-
|
|
178
|
-
bắn vào chính fixture âm của nó thì không thể ship — đó là bức tường
|
|
179
|
-
lửa false positive.
|
|
250
|
+
Danh mục đầy đủ được tạo từ sổ đăng ký, không bao giờ duy trì thủ công: `mjolnir rules --md`, [`docs/rules/`](docs/rules/), hoặc [hướng dẫn về những gì nó kiểm tra](https://sergey-bar.github.io/Mjolnir/guide/what-it-checks).
|
|
180
251
|
|
|
181
252
|
<details>
|
|
182
|
-
<summary><strong>
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
|
189
|
-
|
|
|
190
|
-
| QA-TEST-
|
|
191
|
-
| QA-TEST-
|
|
192
|
-
| QA-TEST-
|
|
253
|
+
<summary><strong>Mọi quy tắc được nhắc đến trong README này</strong>, trong một bảng</summary>
|
|
254
|
+
|
|
255
|
+
<br />
|
|
256
|
+
|
|
257
|
+
> Quy tắc `quarantine` chỉ chạy khi có `--strict` và không bao giờ chặn (bị giới hạn ở info). Mức nghiêm trọng hiển thị là mức do tác giả đặt.
|
|
258
|
+
|
|
259
|
+
| ID | Nhóm | Quy tắc | Mức nghiêm trọng | Cấp |
|
|
260
|
+
| ------------ | ---------- | ------------------------------------------------------------------ | ---------------- | ---------- |
|
|
261
|
+
| QA-TEST-001 | Vệ sinh | Commit test bị focus (`.only`, `fit`) | error | quarantine |
|
|
262
|
+
| QA-TEST-002 | Vệ sinh | Test bị bỏ qua. Nâng lên `error` nếu không có lý do được theo dõi. | warning | quarantine |
|
|
263
|
+
| QA-TEST-003 | Vệ sinh | Test không có assertion | error | quarantine |
|
|
264
|
+
| QA-TEST-004 | Vệ sinh | Sleep cố định (`waitForTimeout`, `sleep()`, `delay()`) | warning | extended |
|
|
265
|
+
| QA-TEST-006 | Vệ sinh | Lạm dụng thử lại để che giấu sự chập chờn | warning | quarantine |
|
|
266
|
+
| QA-TEST-010 | Vệ sinh | Thân test rỗng | error | quarantine |
|
|
267
|
+
| QA-TQUAL-002 | Chất lượng | Assertion hằng đúng | error | quarantine |
|
|
268
|
+
| QA-TQUAL-009 | Chất lượng | Assertion trên promise không được await | error | quarantine |
|
|
269
|
+
| QA-TQUAL-011 | Chất lượng | Test bị comment lại | warning | extended |
|
|
270
|
+
| QA-PW-002 | Playwright | Assertion trên locator không được await | error | core |
|
|
271
|
+
| QA-PW-003 | Playwright | Commit `page.pause()` / `test.only()` | error | core |
|
|
272
|
+
| QA-PW-004 | Playwright | Selector CSS/XPath dễ vỡ | warning | quarantine |
|
|
273
|
+
| QA-PW-123 | Playwright | URL môi trường bị viết cứng | warning | quarantine |
|
|
274
|
+
| QA-PW-140 | Playwright | Chụp màn hình không có `maxDiffPixelRatio` | warning | core |
|
|
275
|
+
| QA-CI-001 | CI | `continue-on-error` che giấu một cổng đang thất bại | error | quarantine |
|
|
276
|
+
| QA-CI-002 | CI | `\|\| true` nuốt mất mã thoát | error | extended |
|
|
277
|
+
| QA-CI-005 | CI | Báo cáo được dùng nhưng chưa từng được tạo | error | quarantine |
|
|
278
|
+
| QA-CI-007 | CI | Lớp bọc thử lại quanh test | warning | extended |
|
|
279
|
+
| QA-CI-008 | CI | Step luôn thành công che giấu thất bại | error | quarantine |
|
|
280
|
+
| QA-CI-009 | CI | Mã thoát không được truyền đi (`\|` không có pipefail, chuỗi `;`) | error | extended |
|
|
281
|
+
| QA-CI-010 | CI | Test bị bỏ qua ở nơi chúng phải chặn | error | quarantine |
|
|
282
|
+
| QA-PY-002 | Python | Test bị bỏ qua (`skip`, `xfail` không nghiêm ngặt) | warning | core |
|
|
283
|
+
| QA-PY-003 | Python | Hàm test không có assertion | error | quarantine |
|
|
284
|
+
| QA-PY-005 | Python | `time.sleep()` trong test | warning | extended |
|
|
285
|
+
| QA-PY-012 | Python | Assertion hằng đúng | error | quarantine |
|
|
286
|
+
| QA-JV-101 | Java | Test bị vô hiệu hóa (`@Disabled`) | warning | core |
|
|
287
|
+
| QA-JV-102 | Java | Sleep cố định (`Thread.sleep()`) | warning | extended |
|
|
288
|
+
| QA-JV-103 | Java | Phương thức test không có assertion | error | extended |
|
|
289
|
+
| QA-JV-105 | Java | Sleep cố định bằng `waitForTimeout()` của Playwright | warning | core |
|
|
290
|
+
| QA-JV-106 | Java | Selector dễ vỡ thay vì locator theo vai trò | warning | quarantine |
|
|
291
|
+
| QA-CS-101 | C# | Test bị bỏ qua (`[Ignore]`, `[Fact(Skip=)]`) | warning | core |
|
|
292
|
+
| QA-CS-102 | C# | Sleep cố định (`Thread.Sleep` / `Task.Delay`) | warning | core |
|
|
293
|
+
| QA-CS-103 | C# | Phương thức test không có assertion | error | core |
|
|
294
|
+
| QA-CS-105 | C# | Sleep cố định bằng `WaitForTimeoutAsync()` | warning | extended |
|
|
295
|
+
| QA-CS-106 | C# | Selector dễ vỡ thay vì locator theo vai trò | warning | quarantine |
|
|
296
|
+
|
|
297
|
+
Python còn có QA-PY-001…012 (vệ sinh pytest) và QA-PY-101…108 (Playwright cho Python). Cypress và Selenium mỗi bên có một bộ khởi đầu gồm ba quy tắc.
|
|
193
298
|
|
|
194
299
|
</details>
|
|
195
300
|
|
|
196
|
-
|
|
197
|
-
<summary><strong>Chất lượng kiểm thử</strong></summary>
|
|
301
|
+
Mỗi quy tắc được phát hành cùng một fixture must-fire **và** một fixture must-not-fire, và quy tắc nào kích hoạt trên chính fixture âm của nó thì không thể phát hành. Đó là tường lửa chống dương tính giả; `mjolnir doctor` thực thi nó trong CI của chính kho này.
|
|
198
302
|
|
|
199
|
-
|
|
200
|
-
| ------------ | --------------------------------------- | -------- |
|
|
201
|
-
| QA-TQUAL-002 | Assertion đồng nghĩa lặp (tautological) | error |
|
|
202
|
-
| QA-TQUAL-009 | Assertion của promise không await | error |
|
|
203
|
-
| QA-TQUAL-011 | Kiểm thử bị comment | warning |
|
|
303
|
+
### Selector Health Score
|
|
204
304
|
|
|
205
|
-
|
|
305
|
+
`mjolnir doctor:playwright` chấm điểm mỗi locator theo cách nó tìm phần tử: theo cách người dùng tìm (vai trò, nhãn, văn bản), qua một hợp đồng rõ ràng (`data-testid`), hay nhờ một sự tình cờ về cấu trúc (chuỗi CSS, XPath). Mỗi tệp nhận điểm từ 0 đến 100:
|
|
206
306
|
|
|
207
|
-
|
|
208
|
-
|
|
307
|
+
```text
|
|
308
|
+
▍ SELECTOR HEALTH
|
|
209
309
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
| QA-PW-003 | `page.pause()` / `test.only()` bị commit | error |
|
|
214
|
-
| QA-PW-004 | Selector CSS/XPath giòn | warning |
|
|
215
|
-
| QA-PW-123 | URL môi trường hardcode | warning |
|
|
310
|
+
e2e/login.spec.ts
|
|
311
|
+
[█████████████░░░░░░░] 65 / 100
|
|
312
|
+
role/text: 1 · testid: 0 · plain-css: 0 · css-chains: 1 ⚠ · xpath: 0
|
|
216
313
|
|
|
217
|
-
|
|
314
|
+
e2e/checkout.spec.ts
|
|
315
|
+
[█████████████████░░░] 86 / 100
|
|
316
|
+
role/text: 3 · testid: 1 · plain-css: 0 · css-chains: 1 ⚠ · xpath: 0
|
|
317
|
+
```
|
|
218
318
|
|
|
219
|
-
|
|
220
|
-
<summary><strong>Toàn vẹn CI</strong></summary>
|
|
221
|
-
|
|
222
|
-
| ID | Quy tắc | Severity |
|
|
223
|
-
| --------- | ------------------------------------------------------------------------- | -------- |
|
|
224
|
-
| QA-CI-001 | `continue-on-error` che giấu thất bại | error |
|
|
225
|
-
| QA-CI-002 | `\|\| true` nuốt exit code | error |
|
|
226
|
-
| QA-CI-005 | Báo cáo được tiêu thụ nhưng không bao giờ sinh ra | error |
|
|
227
|
-
| QA-CI-007 | Wrapper retry quanh kiểm thử | warning |
|
|
228
|
-
| QA-CI-008 | Step luôn thành công che giấu thất bại | error |
|
|
229
|
-
| QA-CI-009 | Exit code của kiểm thử không được truyền (`\|` không pipefail, chuỗi `;`) | error |
|
|
230
|
-
| QA-CI-010 | Kiểm thử bị bỏ qua ở nơi phải chặn (guard skip-on-PR) | error |
|
|
319
|
+
Điều này đo **độ bền, không phải độ đúng**. `.btn.btn-primary > div:nth-child(2)` qua hôm nay và sẽ tiếp tục qua cho đến khi ai đó động vào markup. Điểm thấp không bao giờ khẳng định test bị hỏng, chỉ nói rằng nó phụ thuộc vào markup mà không ai hứa giữ nguyên.
|
|
231
320
|
|
|
232
|
-
|
|
321
|
+
<br />
|
|
233
322
|
|
|
234
|
-
|
|
235
|
-
<summary><strong>Python / pytest 🐍</strong></summary>
|
|
323
|
+
## Điểm đáng tin
|
|
236
324
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
| QA-PY-003 | Hàm kiểm thử không có assertion | error |
|
|
241
|
-
| QA-PY-005 | `time.sleep()` trong kiểm thử | warning |
|
|
242
|
-
| QA-PY-012 | Assertion tautological | error |
|
|
325
|
+
<p align="center">
|
|
326
|
+
<img src="assets/readme/score-gauge.svg" alt="Thang điểm đáng tin từ 0 đến 100, với một con trỏ quét qua mọi mức điểm: UNWORTHY dưới 50, NEEDS WORK từ 50 đến 79, WORTHY từ 80 đến 99, FORGED ở 100" width="720" />
|
|
327
|
+
</p>
|
|
243
328
|
|
|
244
|
-
|
|
329
|
+
<sub>Mọi mức điểm từ 0 đến 100, được đặt vị trí bởi `deriveScoreState` thật. Được tạo bởi `npm run docs:gauge` và được khóa chống sai lệch trong CI.</sub>
|
|
245
330
|
|
|
246
|
-
|
|
331
|
+
| Điểm | Kết luận |
|
|
332
|
+
| --------- | ----------------------------------------- |
|
|
333
|
+
| `0 – 49` | **UNWORTHY** |
|
|
334
|
+
| `50 – 79` | **NEEDS WORK** |
|
|
335
|
+
| `80 – 99` | **WORTHY** |
|
|
336
|
+
| `100` | **FORGED** |
|
|
337
|
+
| `null` | **UNKNOWN**: không tìm thấy khai báo test |
|
|
247
338
|
|
|
248
|
-
|
|
249
|
-
<summary><strong>Java / JUnit · TestNG ☕</strong></summary>
|
|
339
|
+
**Cách tính.** Mức nghiêm trọng đặt ra mức trừ cơ bản (`error −8`, `warning −3`, `info −1`) và mức bằng chứng chiết khấu nó: E2 trừ đủ, E1 trừ một nửa (làm tròn xuống), E0 không trừ. Tổng được chuẩn hóa theo quy mô bộ test, tức là trừ theo từng khai báo test chứ không theo tệp. Terminal in ra đúng những con số đã chiết khấu mà điểm đã dùng; không có mô hình thứ hai nào ẩn giấu. Chi tiết: [docs/SCORING.md](docs/SCORING.md) và [hướng dẫn chấm điểm](https://sergey-bar.github.io/Mjolnir/guide/scoring).
|
|
250
340
|
|
|
251
|
-
|
|
252
|
-
| --------- | ---------------------------------------- | -------- |
|
|
253
|
-
| QA-JV-101 | Kiểm thử bị tắt (`@Disabled`) | warning |
|
|
254
|
-
| QA-JV-102 | Sleep cứng (`Thread.sleep()`) | warning |
|
|
255
|
-
| QA-JV-103 | Phương thức kiểm thử không có assertion | error |
|
|
256
|
-
| QA-JV-105 | Sleep cứng Playwright `waitForTimeout()` | warning |
|
|
257
|
-
| QA-JV-106 | Selector giòn thay vì role locator | warning |
|
|
341
|
+
**Điều mà 100 không có nghĩa.** Nó không có nghĩa là phần mềm đúng, bộ test đầy đủ, hay sản phẩm không có lỗi. Nó chỉ có nghĩa một điều: **không quy tắc nào mà Mjölnir đánh giá tạo ra mức trừ điểm trong lần quét này và với mô hình bằng chứng này.**
|
|
258
342
|
|
|
259
|
-
|
|
343
|
+
<br />
|
|
260
344
|
|
|
261
|
-
|
|
262
|
-
<summary><strong>C# / .NET — NUnit · xUnit · MSTest 🟣</strong></summary>
|
|
345
|
+
## Mô hình bằng chứng
|
|
263
346
|
|
|
264
|
-
|
|
265
|
-
| --------- | ------------------------------------------------ | -------- |
|
|
266
|
-
| QA-CS-101 | Kiểm thử bị bỏ qua (`[Ignore]`, `[Fact(Skip=)]`) | warning |
|
|
267
|
-
| QA-CS-102 | Sleep cứng (`Thread.Sleep` / `Task.Delay`) | warning |
|
|
268
|
-
| QA-CS-103 | Phương thức kiểm thử không có assertion | error |
|
|
269
|
-
| QA-CS-105 | Sleep cứng `WaitForTimeoutAsync()` | warning |
|
|
270
|
-
| QA-CS-106 | Selector giòn thay vì role locator | warning |
|
|
347
|
+
Mỗi phát hiện mang hai nhãn: Mjölnir chắc chắn đến mức nào, và phát hiện đã được kiểm chứng đến đâu. Đó là khác biệt giữa một công cụ báo cáo mẫu và một công cụ bạn có thể dùng làm cổng cho một bản phát hành.
|
|
271
348
|
|
|
272
|
-
|
|
349
|
+
**Chắc chắn đến mức nào — mức bằng chứng.**
|
|
273
350
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
### Bao nhiêu đã được đo
|
|
284
|
-
|
|
285
|
-
**78 trong 99 quy tắc mang tỷ lệ false positive được đo trên mã OSS
|
|
286
|
-
thật** (≥ 10 finding được phân loại tay mỗi quy tắc; xem
|
|
287
|
-
[docs/FP-AUDIT.md](docs/FP-AUDIT.md)). 21 quy tắc còn lại ra mắt trên
|
|
288
|
-
ước lượng của tác giả. Chân mỗi bản quét cho biết bao nhiêu quy tắc
|
|
289
|
-
_đã bắn_ được đo; `mjolnir rules --unmeasured` liệt kê những quy tắc
|
|
290
|
-
chưa đo; trang `mjolnir explain` của từng quy tắc nêu trạng thái. Chúng
|
|
291
|
-
và bị cách ly vì thế. Mở rộng con số đó là công việc liên tục của
|
|
292
|
-
dự án.
|
|
293
|
-
|
|
294
|
-
### Tier quy tắc và độ trưởng thành theo ngôn ngữ
|
|
295
|
-
|
|
296
|
-
Mỗi quy tắc là `core`, `extended` hoặc `quarantine`, phân theo tỷ lệ
|
|
297
|
-
false positive **được đo**:
|
|
298
|
-
|
|
299
|
-
| Tier | Ý nghĩa | Quét mặc định | `--strict` |
|
|
300
|
-
| ------------ | -------------------------------- | :-----------: | :--------: |
|
|
301
|
-
| `core` | ≤ 10 % FP đo được | ✅ | ✅ |
|
|
302
|
-
| `extended` | ≤ 30 % FP đo được | ✅ | ✅ |
|
|
303
|
-
| `quarantine` | trên 30 %, hoặc chưa đo (n < 10) | ❌ | ✅ |
|
|
304
|
-
|
|
305
|
-
| Ngôn ngữ | Adapter | Độ phủ hiện nay |
|
|
306
|
-
| --------------- | ---------------- | ---------------------------------------------------------- |
|
|
307
|
-
| TypeScript / JS | AST bộ biên dịch | rộng nhất, đo nhiều nhất — chủ yếu `core`/`extended` |
|
|
308
|
-
| Python / pytest | Lớp regex | rộng, đã kiểm toán trên corpus — chủ yếu `core`/`extended` |
|
|
309
|
-
| Java | Lớp regex | mới hơn — chủ yếu `extended`/`quarantine` |
|
|
310
|
-
| C# / .NET | Lớp regex | mới hơn — chủ yếu `extended`/`quarantine` |
|
|
311
|
-
|
|
312
|
-
TypeScript và Python có độ phủ đo được rộng nhất. Java và C# đã ship,
|
|
313
|
-
có tài liệu và ở ngoài con số tiêu đề cho đến khi một suite người dùng
|
|
314
|
-
thật (không phải chính các kiểm thử của thư viện binding) được kiểm
|
|
315
|
-
toán.
|
|
316
|
-
|
|
317
|
-
---
|
|
318
|
-
|
|
319
|
-
## Cách thức chấm điểm
|
|
351
|
+
| Mức | Tên | Ý nghĩa | Trừ điểm |
|
|
352
|
+
| ------ | ------------------- | ------------------------------------------------ | -------- |
|
|
353
|
+
| **E2** | Chứng minh tất định | Lỗi hiện diện trong mã đúng như nó được viết | Đủ |
|
|
354
|
+
| **E1** | Bằng chứng theo mẫu | Một mẫu gắn chặt với lỗi đã khớp | Một nửa |
|
|
355
|
+
| **E0** | Quan sát | Đáng biết. Không phải khẳng định rằng có gì sai. | Không |
|
|
356
|
+
|
|
357
|
+
Độ tin trong một lần phát hiện không phải là sức mạnh của chứng minh. Một quy tắc có thể chắc chắn rằng nó đã khớp đúng thứ nó tìm mà vẫn chỉ đang nhìn vào một phép suy đoán. Phát hiện E1 là để đọc và cân nhắc, không bao giờ áp dụng mù quáng, và ranh giới đó được đóng dấu trên phát hiện trong terminal, trong JSON và trong phần bàn giao cho tác tử.
|
|
358
|
+
|
|
359
|
+
**Đã kiểm chứng đến đâu — mức tin cậy.** Phần lớn phát hiện đến từ việc đọc mã của bạn. Đưa cho Mjölnir báo cáo của một lần chạy test thật và nó có thể xác nhận rằng mã thực sự đã chạy.
|
|
320
360
|
|
|
321
361
|
<p align="center">
|
|
322
|
-
<img src="assets/readme/
|
|
362
|
+
<img src="assets/readme/trust-ladder.svg" alt="Nấc thang tin cậy từ L0 đến L5. L0 đến L2 đến từ việc đọc mã; L3 đến L5 cần một báo cáo chạy thật, được đánh dấu bằng một chỗ đứt trên nấc thang." width="100%" />
|
|
323
363
|
</p>
|
|
324
364
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
365
|
+
| Mức | Nói đơn giản | Cần gì |
|
|
366
|
+
| ------ | ---------------------- | --------------------------------------------------------- |
|
|
367
|
+
| **L0** | Đã ghi nhận | Đọc mã |
|
|
368
|
+
| **L1** | Trông giống vấn đề | Đọc mã: một mẫu đã khớp |
|
|
369
|
+
| **L2** | Đã chứng minh trong mã | Đọc mã: lỗi mang tính cấu trúc |
|
|
370
|
+
| **L3** | Tệp đã chạy | Báo cáo chạy cho thấy tệp của phát hiện đã được thực thi |
|
|
371
|
+
| **L4** | Test đã chạy | Báo cáo chạy cho thấy test của phát hiện đã được thực thi |
|
|
372
|
+
| **L5** | Lần chạy xác nhận | Chính kết quả của lần chạy xác nhận loại lỗi |
|
|
328
373
|
|
|
329
|
-
|
|
330
|
-
theo độ phơi của suite (trừ trên mỗi khai báo kiểm thử). Các khoản trừ
|
|
331
|
-
được cân theo bằng chứng nghĩa là tín hiệu yếu tốn ít hơn. Terminal
|
|
332
|
-
hiện những con số đã chiết khấu chính mà điểm số dùng — không hộp đen.
|
|
333
|
-
Phương pháp đầy đủ: [docs/SCORING.md](docs/SCORING.md).
|
|
374
|
+
Quét tĩnh dừng ở L2. Chỉ một báo cáo chạy thật (Playwright JSON, Jest hoặc Vitest JSON, JUnit XML) mới có thể nâng một phát hiện lên L3 hoặc cao hơn, nên một phát hiện chưa từng được thấy chạy sẽ không bao giờ có thể khẳng định là nó đã chạy. Định nghĩa: [docs/TERMINOLOGY.md](docs/TERMINOLOGY.md).
|
|
334
375
|
|
|
335
|
-
|
|
376
|
+
### Bao nhiêu phần trong số này đã được đo
|
|
336
377
|
|
|
337
|
-
|
|
338
|
-
| ------- | ---------------- |
|
|
339
|
-
| ≥ 80 | ✓ **WORTHY** |
|
|
340
|
-
| 50 – 79 | ⚠ **NEEDS WORK** |
|
|
341
|
-
| < 50 | ✖ **UNWORTHY** |
|
|
378
|
+
**74 trên 79 quy tắc có tỷ lệ dương tính giả được đo trên mã OSS thật** (mỗi quy tắc ít nhất 10 phát hiện được phân loại thủ công; xem [docs/FP-AUDIT.md](docs/FP-AUDIT.md)). 5 quy tắc còn lại phát hành dựa trên ước tính của tác giả và nói rõ điều đó, từng quy tắc một, trong `mjolnir explain`. `mjolnir rules --unmeasured` liệt kê chúng, và phần chân của mỗi lần quét cho biết bao nhiêu quy tắc thực sự _đã kích hoạt_ đã được đo.
|
|
342
379
|
|
|
343
|
-
|
|
344
|
-
trong điểm:
|
|
380
|
+
Các tỷ lệ vẫn công khai kể cả khi chúng tệ. QA-TEST-001 (một `.only` bị commit) cho kết quả kiểm toán kém trên các kho thật và vì thế nằm trong quarantine. Con số hiện tại của mọi quy tắc, kể cả QA-PW-141, nằm trong báo cáo kiểm toán.
|
|
345
381
|
|
|
346
|
-
|
|
347
|
-
| --- | --------------------------- | ---------------- | ---------------------------------------------------------- |
|
|
348
|
-
| E2 | Lỗi tất yếu (deterministic) | Trừ đủ | `.only` bị commit — chứng minh được về cấu trúc |
|
|
349
|
-
| E1 | Mẫu heuristic | Trừ nửa | `sleep()` khớp regex — tín hiệu mạnh, chưa phải bằng chứng |
|
|
350
|
-
| E0 | Quan sát | Không (chỉ info) | Được báo nhưng không bao giờ gate CI hay trừ |
|
|
382
|
+
### Cấp tin cậy của quy tắc
|
|
351
383
|
|
|
352
|
-
|
|
353
|
-
finding E2 là bằng chứng cấu trúc; finding E1 là cảnh báo được đặt đúng
|
|
354
|
-
vị trí, không phải chứng minh hình thức.
|
|
384
|
+
Các cấp theo tỷ lệ dương tính giả đã đo, không theo ý kiến:
|
|
355
385
|
|
|
356
|
-
|
|
357
|
-
|
|
386
|
+
| Cấp | FP đã đo | Hành vi |
|
|
387
|
+
| -------------- | -------------------------------- | ------------------------------------------------------- |
|
|
388
|
+
| **core** | ≤ 10% | Báo cáo mặc định, có chặn |
|
|
389
|
+
| **extended** | ≤ 30% | Báo cáo mặc định, độ tin thấp hơn |
|
|
390
|
+
| **quarantine** | > 30% hoặc được khai báo rõ ràng | Chỉ với `--strict`, giới hạn ở info, không bao giờ chặn |
|
|
391
|
+
| _chưa đo_ | n < 10 | Không thể nâng lên core cho đến khi được đo |
|
|
358
392
|
|
|
359
|
-
|
|
393
|
+
Dải FP chỉ có thể hạ cấp một bậc — chúng không bao giờ nâng cấp một quy tắc ra khỏi `quarantine` nếu nó đã được khai báo rõ ràng ở đó. Một quy tắc bị quarantined rõ ràng vẫn ở quarantine bất kể tỷ lệ FP đã đo được.
|
|
360
394
|
|
|
361
|
-
|
|
395
|
+
Nâng cấp, hạ cấp và độ trưởng thành theo ngôn ngữ: [vòng đời quy tắc](https://sergey-bar.github.io/Mjolnir/reference/rule-lifecycle).
|
|
362
396
|
|
|
363
|
-
|
|
397
|
+
### Vì sao đây không phải một linter
|
|
364
398
|
|
|
365
|
-
|
|
366
|
-
▚ SELECTOR HEALTH — e2e/checkout.spec.ts
|
|
399
|
+
Linter cho bạn biết mã có tuân theo quy tắc hay không. Mjölnir cho bạn biết việc xác minh của bạn có đáng tin hay không.
|
|
367
400
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
401
|
+
| | Linter (ESLint, SonarQube) | Công cụ đo độ phủ | Review mã bằng AI | **Mjölnir** |
|
|
402
|
+
| ---------------------------------------------------------------- | :------------------------: | :---------------: | :---------------: | :---------------------: |
|
|
403
|
+
| Chấm điểm **hệ thống xác minh**, không phải mã sản phẩm | Không | Không | Không | Có |
|
|
404
|
+
| Tính toàn vẹn của workflow CI (`continue-on-error`, `\|\| true`) | Không | Không | chỉ phần diff | Có |
|
|
405
|
+
| Chấm độ bền của locator Playwright (Selector Health) | Không | Không | Không | Có |
|
|
406
|
+
| Đọc dữ liệu chạy thật để đưa ra kết luận `TRUE-FLAKE` | Không | Không | Không | Có |
|
|
407
|
+
| Công bố tỷ lệ dương tính giả đã đo cho từng quy tắc | Không | Không | Không | Có |
|
|
408
|
+
| Đánh dấu test không có assertion | Có\* | Không | đôi khi | Có |
|
|
409
|
+
| Bắt các sleep cố định (`waitForTimeout`, `time.sleep`) | Có\* | Không | đôi khi | Có |
|
|
410
|
+
| Tất định (cùng đầu vào, cùng đầu ra) | Có | Có | Không | Có |
|
|
411
|
+
| Chi phí mỗi lần quét | miễn phí | miễn phí | token | **bằng không** (cục bộ) |
|
|
412
|
+
|
|
413
|
+
<sub>\*Được bao phủ bởi `eslint-plugin-jest` và `eslint-plugin-playwright` (`expect-expect`, `no-wait-for-timeout`) và bởi các quy tắc assertion riêng của SonarQube. Các cột mô tả hành vi mặc định khi xác minh bộ test; plugin, gói trả phí và quy tắc tùy chỉnh sẽ làm thay đổi một số câu trả lời. Đây là bản tóm tắt định vị, không phải benchmark.</sub>
|
|
371
414
|
|
|
372
|
-
|
|
373
|
-
— chúng vỡ với mọi lần refactor DOM mà không nói cho bạn biết hành vi
|
|
374
|
-
nào đã thoái trào.
|
|
415
|
+
Hãy dùng cả review bằng AI. Nó nắm bắt sắc thái, ý định và lỗi thiết kế mà không mẫu nào tìm được. Mjölnir bắt được những gì review bằng AI bỏ sót vì trông có vẻ cố ý: một `.only` bị commit, một mã thoát bị nuốt, một `continue-on-error` trên job test. Những thứ đó cần quét, không cần suy luận.
|
|
375
416
|
|
|
376
|
-
|
|
417
|
+
<br />
|
|
377
418
|
|
|
378
|
-
##
|
|
419
|
+
## Phân tích pháp chứng lúc chạy
|
|
379
420
|
|
|
380
|
-
|
|
381
|
-
thật** — báo cáo JSON Playwright và XML JUnit từ runner bất kỳ:
|
|
421
|
+
Phân tích tĩnh suy luận về mã chưa từng chạy. Phân tích pháp chứng đọc những gì thực sự đã xảy ra: Playwright JSON, Jest JSON, Vitest JSON và JUnit XML từ bất kỳ runner nào.
|
|
382
422
|
|
|
383
423
|
```bash
|
|
384
424
|
mjolnir forensics ./test-results/
|
|
385
425
|
```
|
|
386
426
|
|
|
387
427
|
```text
|
|
388
|
-
|
|
428
|
+
▍ FLAKINESS LEADERBOARD
|
|
389
429
|
|
|
390
430
|
3 tests · 1 failed · 1 flaky · 1 retried
|
|
391
431
|
|
|
@@ -395,282 +435,184 @@ FAILING declines an expired card (e2e/checkout.spec.ts)
|
|
|
395
435
|
████░░░░░░░░░░░░░░░░ 1.1s · 1 attempt
|
|
396
436
|
```
|
|
397
437
|
|
|
398
|
-
|
|
399
|
-
|
|
438
|
+
`TRUE-FLAKE` không có nghĩa là test đã được thử lại. Nó có nghĩa là test **đã thất bại ít nhất một lần thử rồi kết thúc với màu xanh**: một lần qua may mắn, bị đánh dấu bất kể dấu tích cuối cùng nói gì. `mjolnir triage` biến lịch sử đó thành một đề xuất cách ly, và `mjolnir pw-report` tóm tắt một lần chạy. Chính những báo cáo chạy này là thứ nâng phát hiện lên mức tin cậy L3 trở lên.
|
|
439
|
+
|
|
440
|
+
<br />
|
|
441
|
+
|
|
442
|
+
## Tính toàn vẹn CI
|
|
400
443
|
|
|
401
|
-
|
|
444
|
+
Một test có thể qua trong khi pipeline bao quanh nó không thể thất bại. Mjölnir cũng đọc các workflow: `continue-on-error`, `|| true`, mã thoát không bao giờ được truyền đi, step luôn thành công, báo cáo được dùng nhưng chưa từng được tạo, và cổng bị bỏ qua đúng ở những sự kiện lẽ ra phải chặn. Mỗi phát hiện nêu tên job, step và dòng, và mang mức bằng chứng riêng.
|
|
402
445
|
|
|
403
|
-
|
|
446
|
+
Tạo workflow PR, mặc định mang tính tư vấn:
|
|
447
|
+
|
|
448
|
+
```bash
|
|
449
|
+
mjolnir ci install
|
|
450
|
+
```
|
|
404
451
|
|
|
405
|
-
|
|
406
|
-
sự kiểm chứng của bạn có thể được tin không.
|
|
452
|
+
Hoặc thêm action trên Marketplace vào một workflow bạn đã có:
|
|
407
453
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
454
|
+
```yaml
|
|
455
|
+
- uses: Sergey-Bar/Mjolnir@v1
|
|
456
|
+
with:
|
|
457
|
+
scope: changed
|
|
458
|
+
fail-on: error
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
Ghim `@v1` để theo dòng phiên bản chính, hoặc một tag chính xác (`@v0.5.32`) để có cổng tái lập được. [docs/DISTRIBUTION-KIT.md](docs/DISTRIBUTION-KIT.md) đề cập đến Marketplace, Smithery và các sổ đăng ký MCP.
|
|
462
|
+
|
|
463
|
+
Để đưa phát hiện vào GitHub Code Scanning, hãy tải lên SARIF (yêu cầu `security-events: write` ở phạm vi workflow hoặc job):
|
|
464
|
+
|
|
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
|
+
```
|
|
416
473
|
|
|
417
|
-
|
|
418
|
-
(`expect-expect`, `no-wait-for-timeout`) phủ các điểm đó cho framework
|
|
419
|
-
tương ứng.
|
|
474
|
+
Trên GitLab, `--format codequality` ghi báo cáo Code Quality mà widget MR và chú thích diff đọc ([docs/GITLAB-CI.md](docs/GITLAB-CI.md)). Thiết lập trình soạn thảo và pipeline: [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md).
|
|
420
475
|
|
|
421
|
-
|
|
476
|
+
### Quy trách nhiệm theo phạm vi thay đổi
|
|
422
477
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
| Báo cáo triage flake từ lịch sử thực thi | ❌ | ✅ | ✅ |
|
|
427
|
-
| Tích hợp với điểm độ đáng tin tĩnh | ❌ | ❌ | ✅ |
|
|
478
|
+
```bash
|
|
479
|
+
npx mjolnir-qa@latest --scope changed
|
|
480
|
+
```
|
|
428
481
|
|
|
429
|
-
|
|
430
|
-
độc lập với nhãn phán quyết.
|
|
482
|
+
Phát hiện được quy về các dòng mà nhánh của bạn đã thêm, đo so với **merge-base**. Phạm vi là cùng tập tệp mà một lần quét đầy đủ phát hiện (spec TS/JS và cấu hình adapter, `test_*.py`, `*Test.java`, `*Tests.cs`, `.github/workflows/*.yml`), cộng thêm các thay đổi chưa commit và chưa theo dõi, nên nó hoạt động cả trước khi bạn commit. Nhánh gốc được xác định theo thứ tự `main → master → origin/main → origin/master → origin/HEAD`; ghi đè bằng `--base <ref>`.
|
|
431
483
|
|
|
432
|
-
|
|
484
|
+
Khi không xác định được merge-base (clone nông, HEAD tách rời, mục tiêu nằm ngoài git), phát hiện sẽ quay về quy cho toàn bộ tệp **và báo cáo nói rõ điều đó.** Một sự quay về âm thầm sẽ chính là loại lỗi mà công cụ này tồn tại để bắt.
|
|
433
485
|
|
|
434
|
-
|
|
486
|
+
<br />
|
|
435
487
|
|
|
436
|
-
|
|
437
|
-
nghi ngờ trong diff; nó không chứng minh hệ thống kiểm chứng như một
|
|
438
|
-
toàn thể đáng tin — và nó chỉ thấy diff bạn cho nó xem.
|
|
488
|
+
## Tác tử AI
|
|
439
489
|
|
|
440
|
-
|
|
441
|
-
| ----------------------------------- | :--------------------------------: | :-----------------------------------: |
|
|
442
|
-
| Chi phí mỗi lần quét | Token (scale theo kích thước diff) | **Zero** (cục bộ, đã cài) |
|
|
443
|
-
| Thấy cả suite + mọi cấu hình CI | Chỉ diff PR bạn cho xem | **Mọi thứ, mỗi lần** |
|
|
444
|
-
| Tất định (cùng input → cùng output) | ❌ (không tất định) | **✅** |
|
|
445
|
-
| Bắt mẫu nằm im hàng tháng | Chỉ khi có trong ngữ cảnh | **✅** (quét mọi file) |
|
|
446
|
-
| Nhớ finding giữa các lần chạy | ❌ (không trí nhớ giữa các phiên) | **✅** (baseline + diff) |
|
|
447
|
-
| Chạy không cần người kích hoạt | Cần PR hoặc prompt | **✅** (hook CI, chạy trong vài giây) |
|
|
490
|
+
Phát hiện chỉ có giá trị nếu có thứ gì đó hành động dựa trên chúng.
|
|
448
491
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
`continue-on-error` trên job kiểm thử. Đó không phải bug cần suy luận;
|
|
453
|
-
đó là sự thật cần quét.
|
|
492
|
+
```text
|
|
493
|
+
SCAN → EVIDENCE → HANDOFF → AGENT → RE-SCAN → PROOF
|
|
494
|
+
```
|
|
454
495
|
|
|
455
|
-
|
|
496
|
+
**AI viết bản sửa. Mjölnir xác minh nó.** Bằng chứng đến từ lần quét lại, không bao giờ đến từ báo cáo thành công của chính tác tử.
|
|
456
497
|
|
|
457
|
-
|
|
498
|
+
| Lệnh | Tác tử nhận được gì |
|
|
499
|
+
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
500
|
+
| `mjolnir mcp` | Một máy chủ [MCP](https://modelcontextprotocol.io) qua stdio. `scan`, `explain` và `diff` trở thành công cụ có thể gọi. |
|
|
501
|
+
| `mjolnir handoff` | Một báo cáo `--json` đã lưu trở thành một kế hoạch Markdown tất định: đã phát hiện gì, ranh giới bằng chứng của từng phát hiện, những gì **không** được thay đổi, và cách xác minh. |
|
|
502
|
+
| `mjolnir install` | Ghi vào các vị trí dành cho tác tử mà kho của bạn đã có (`.claude/`, `.cursor/`, `.kilo/`, `AGENTS.md`) để tác tử quét lại trước khi khẳng định là đã xong. |
|
|
458
503
|
|
|
459
|
-
|
|
504
|
+
Thêm vào một client có CLI riêng:
|
|
460
505
|
|
|
461
506
|
```bash
|
|
462
|
-
mjolnir
|
|
507
|
+
claude mcp add mjolnir -- npx -y mjolnir-qa@latest mcp
|
|
463
508
|
```
|
|
464
509
|
|
|
465
|
-
Hoặc
|
|
510
|
+
Hoặc vào bất kỳ client nào nhận khối `mcpServers`:
|
|
466
511
|
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
512
|
+
```json
|
|
513
|
+
{
|
|
514
|
+
"mcpServers": {
|
|
515
|
+
"mjolnir": { "command": "npx", "args": ["-y", "mjolnir-qa@latest", "mcp"] }
|
|
516
|
+
}
|
|
517
|
+
}
|
|
472
518
|
```
|
|
473
519
|
|
|
474
|
-
|
|
475
|
-
[docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md).
|
|
520
|
+
**Lan can bảo vệ quan trọng hơn sự tiện lợi.** Mỗi phát hiện trong phần bàn giao mang ranh giới của nó. **E2** nói _tất định: kiểm tra vị trí và áp dụng bản sửa_. **E1** nói _CẦN XÁC NHẬN: chỉ riêng quan sát không chứng minh được lỗi_. Một tác tử sửa E1 một cách mù quáng, chặn một quy tắc, hoặc sửa một quy tắc để nâng điểm đang làm đúng điều mà công cụ này tồn tại để bắt, nên phần bàn giao nói rõ điều đó trong prompt, ngay cạnh phát hiện.
|
|
476
521
|
|
|
477
|
-
|
|
522
|
+
<br />
|
|
478
523
|
|
|
479
|
-
|
|
480
|
-
so với merge-base với `main`. Nó phủ các file kiểm thử (`*.spec.*`,
|
|
481
|
-
`*.test.*`) cùng file workflow GitHub và cấu hình Playwright trong
|
|
482
|
-
diff. Khi không resolve được merge-base — shallow clone, detached HEAD,
|
|
483
|
-
đích không phải git, branch mặc định khác — nó thoái tr honoured: finding
|
|
484
|
-
quay về gán cho toàn file và báo cáo nói rõ. Ghi đè ref gốc bằng
|
|
485
|
-
`--base <ref>`.
|
|
524
|
+
## Tin cậy và bảo mật
|
|
486
525
|
|
|
487
|
-
|
|
526
|
+
**Ưu tiên cục bộ, không thu thập dữ liệu sử dụng.** Không có API nào có khả năng truy cập mạng (`fetch`, `http`, `https`, `net`, `dns`, `dgram`, WebSocket) tồn tại ở bất kỳ đâu trong `src/`, và [`privacy-network-isolation.spec.ts`](tests/contract/privacy-network-isolation.spec.ts) sẽ làm build thất bại nếu có một cái xuất hiện. Nó cũng cấm `eval` và `new Function`. Quét mã không đáng tin không bao giờ thực thi nó: phân tích tĩnh đọc văn bản nguồn, còn phân tích pháp chứng phân tích các tệp báo cáo đã có sẵn trên đĩa.
|
|
488
527
|
|
|
489
|
-
|
|
528
|
+
Hai lưu ý: bản thân `npx` tải gói xuống trước khi bất cứ thứ gì chạy, và cam kết này áp dụng cho `src/`, không bao gồm plugin của bên thứ ba.
|
|
490
529
|
|
|
491
|
-
|
|
492
|
-
`.mjolnir.json`) ở gốc repo tinh chỉnh severity, gating và scope —
|
|
493
|
-
không bao giờ đổi ngữ nghĩa phát hiện.
|
|
530
|
+
**Plugin không chạy trong sandbox.** Plugin JS (`mjolnir-rules/*.mjs`, hoặc các gói npm liệt kê dưới `"plugins"`) chạy với toàn quyền của Node, cùng mô hình tin cậy như plugin của ESLint hay Vitest. Việc tải chúng phải được bật **cho từng lần quét**: không có `--enable-plugins` (hoặc `MJOLNIR_ENABLE_PLUGINS=1`) thì mã nguồn của chúng không bao giờ được tải, và một thông báo trên stderr liệt kê những gì đã bị bỏ qua. Manifest quy tắc dạng JSON không thực thi mã, và các tiền tố ID của quy tắc core được giữ riêng để plugin không thể mạo danh chúng. Báo cáo lỗ hổng qua [SECURITY.md](SECURITY.md).
|
|
494
531
|
|
|
495
|
-
|
|
496
|
-
| ------------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
497
|
-
| `exclude` | `string[]` | Glob bỏ qua bổ sung (tập con gitignore), chồng lên mặc định sẵn có |
|
|
498
|
-
| `gate` | `"advisory" \| "error" \| "warning"` | Severity nào thoát khác 0 (mặc định `error`; `advisory` không bao giờ chặn) |
|
|
499
|
-
| `severityOverrides` | `{ "<RULE-ID>": severity }` | Xếp lại hạng finding của một quy tắc cho repo của bạn |
|
|
500
|
-
| `ignore` | `IgnoreEntry[]` | Nuốt finding — **`reason` bắt buộc**; mục hết hạn sau 90 ngày (ngày `expires` tường minh, hoặc thời gian sửa lần cuối của file config cho mục không ghi) |
|
|
501
|
-
| `plugins` | `string[]` | Gói quy tắc bên thứ ba (xem [Mô hình niềm tin](#mô-hình-niềm-tin)) |
|
|
532
|
+
**Nó tự chạy trên chính mình.** Một công cụ tin cậy xác minh chẳng có chỗ đứng nếu chính nó không thể được xác minh. Mỗi lần chạy CI đều quét kho này bằng bản build mà chính lần chạy đó tạo ra. Cổng thất bại với bất kỳ phát hiện nào ở mức error, và cả khi lần quét là **một phần** hoặc có **quy tắc bị sập**, vì một lần tự quét bị cắt cụt mà không báo cáo gì chính là màu xanh giả mà dự án này tồn tại để bắt. `mjolnir doctor` kiểm toán lại cơ sở quy tắc trong cùng lần chạy (tường lửa fixture, tính trung thực của các cấp, trần của cấp core), và một kiểm tra có kết quả INCONCLUSIVE sẽ thất bại y như một kiểm tra thất bại. Cả hai báo cáo được tải lên dưới dạng artifact của build.
|
|
502
533
|
|
|
503
|
-
|
|
504
|
-
{
|
|
505
|
-
"gate": "error",
|
|
506
|
-
"exclude": ["legacy/**"],
|
|
507
|
-
"severityOverrides": { "QA-PW-141": "warning" },
|
|
508
|
-
"ignore": [
|
|
509
|
-
{
|
|
510
|
-
"ruleId": "QA-TEST-004",
|
|
511
|
-
"files": ["e2e/legacy-login.spec.ts"],
|
|
512
|
-
"reason": "Third-party widget needs a settle delay; tracked in JIRA-4821",
|
|
513
|
-
"expires": "2026-12-31"
|
|
514
|
-
}
|
|
515
|
-
]
|
|
516
|
-
}
|
|
517
|
-
```
|
|
534
|
+
### Mã thoát và hợp đồng máy
|
|
518
535
|
|
|
519
|
-
|
|
520
|
-
dẫn, cùng ngữ pháp với `exclude`. Dùng nó cho nhiễu riêng máy; dùng
|
|
521
|
-
`exclude` khi danh sách thuộc version control cùng phần còn lại của
|
|
522
|
-
cấu hình.
|
|
523
|
-
- **CLI overrides** — `--strict` (gồm quy tắc cách ly), `--width <cols>`
|
|
524
|
-
và `--ascii` / `--no-ascii` (render terminal), `--tone blunt`
|
|
525
|
-
(thông điệp khô hơn), `--max-duration <sec>` (quét một phần giới hạn).
|
|
526
|
-
- Nuốt quy tắc và vòng đời deprecated: [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md).
|
|
527
|
-
|
|
528
|
-
Mục `ignore` cũng nuôi lệnh độc lập `mjolnir suppressions`, liệt kê thứ
|
|
529
|
-
đang bị nuốt và từng mục hết hạn khi nào.
|
|
530
|
-
|
|
531
|
-
---
|
|
532
|
-
|
|
533
|
-
## 📐 Mã thoát & hợp đồng
|
|
534
|
-
|
|
535
|
-
Đóng băng — an toàn để xây logic CI trên đó:
|
|
536
|
-
|
|
537
|
-
| Mã thoát | Ý nghĩa |
|
|
538
|
-
| -------- | --------------------------------------------------------------------------------- |
|
|
539
|
-
| `0` | Sạch — không finding ở hoặc trên gate |
|
|
540
|
-
| `1` | Có finding ở hoặc trên gate |
|
|
541
|
-
| `2` | Quét một phần (hết ngân sách thời gian, file không đọc được) — không bao giờ chặn |
|
|
542
|
-
| `10` | Lỗi sử dụng (flag sai, thiếu đích) |
|
|
543
|
-
| `20` | Lỗi nội bộ |
|
|
544
|
-
|
|
545
|
-
Báo cáo JSON/SARIF là `schemaVersion: 1`. Rule ID (`QA-<FAMILY>-NNN`)
|
|
546
|
-
bất biến sau khi ra mắt và không bao giờ tái sử dụng.
|
|
547
|
-
|
|
548
|
-
---
|
|
549
|
-
|
|
550
|
-
## Mô hình niềm tin
|
|
551
|
-
|
|
552
|
-
- **Local-first** — 0 lệnh gọi mạng trong lúc quét. Bao giờ vậy. 0
|
|
553
|
-
telemetry.
|
|
554
|
-
- **Không bằng chứng giả** — thà nói «chưa biết» hơn là «đã kiểm chứng».
|
|
555
|
-
Repo rỗng nhận `score: null`, không bao giờ 100 giả.
|
|
556
|
-
- **Trung thực một phần** — nếu phân tích bị cắt ngắn, đầu ra nói vậy.
|
|
557
|
-
Không bao giờ «complete» khi chưa phải.
|
|
558
|
-
- **Tường lửa FP** — phát hiện chạy trên cái nhìn mã không comment/
|
|
559
|
-
chuỗi (quy tắc TypeScript dùng AST bộ biên dịch): một mẫu trong comment
|
|
560
|
-
văn xuôi hoặc chuỗi ví dụ tài liệu là tài liệu, không phải finding.
|
|
561
|
-
- **Đã đo, không phải khẳng định** — chỉ quy tắc có tỷ lệ false positive
|
|
562
|
-
từ mã OSS thật mới ra tier tiêu đề (xem
|
|
563
|
-
[Bao nhiêu đã được đo](#bao-nhiêu-đã-được-đo)); chân bản quét và
|
|
564
|
-
`mjolnir rules --unmeasured` cho biết cái nào là cái nào.
|
|
565
|
-
- **Niềm tin plugin và cổng thực thi** — plugin là gói npm khai báo trong
|
|
566
|
-
`"plugins"`; module JS nằm trong `mjolnir-rules/*.mjs`.
|
|
567
|
-
**Không sandbox**: mã plugin chạy với đầy đủ đặc quyền Node, cùng mô
|
|
568
|
-
hình tin cậy như plugin ESLint hay Vitest. Vì vậy việc thực thi mã là
|
|
569
|
-
**opt-in trong mỗi lần quét**: truyền `--enable-plugins` (hoặc đặt
|
|
570
|
-
`MJOLNIR_ENABLE_PLUGINS=1`), nếu không nguồn sẽ KHÔNG được nạp — một
|
|
571
|
-
thông báo stderr rõ ràng liệt kê chính xác điều gì bị bỏ qua. Quét mã
|
|
572
|
-
không đáng tin cậy không bao giờ chạy nó. JSON rule manifest
|
|
573
|
-
(`mjolnir-rules/*.json`) không bị ảnh hưởng: khai báo regex pattern và
|
|
574
|
-
không thực thi mã theo thiết kế. Tiền tố rule ID core được
|
|
575
|
-
bảo lưu và từ chối khỏi plugin và quy tắc ngoài để chống giả danh.
|
|
576
|
-
- **Quy tắc ngoài cục bộ theo workspace** (theo thư mục, 0 mạng) — một
|
|
577
|
-
thư mục `mjolnir-rules/` cạnh đích quét nạp quy tắc riêng: file JSON
|
|
578
|
-
khai báo mẫu regex (không chạy mã), module `.mjs`/`.js` export
|
|
579
|
-
`rules` (tin cậy Node đầy đủ, như plugin). Quy tắc ngoài mang cùng
|
|
580
|
-
metadata tin cậy với core; không bao giờ vào được tier core (core cần
|
|
581
|
-
tỷ lệ FP đo từ sidecar corpus — `tier: "core"` khai báo bị kẹp về
|
|
582
|
-
`extended`), tuân trần tier và được kiểm tra trôi: `mjolnir rules --md
|
|
583
|
-
--external` render danh mục từ các file đã nạp (nguồn gốc `external`),
|
|
584
|
-
và bộ sinh ma trận nhận `--external <root>`.
|
|
585
|
-
|
|
586
|
-
---
|
|
587
|
-
|
|
588
|
-
## 🏗️ Kiến trúc
|
|
536
|
+
Đã đóng băng, nên bạn có thể xây logic CI dựa trên chúng:
|
|
589
537
|
|
|
590
|
-
|
|
591
|
-
|
|
538
|
+
| Mã thoát | Ý nghĩa |
|
|
539
|
+
| -------- | ---------------------------------------------------------------------- |
|
|
540
|
+
| `0` | Sạch: không có phát hiện ở mức cổng hoặc cao hơn |
|
|
541
|
+
| `1` | Có phát hiện ở mức cổng hoặc cao hơn |
|
|
542
|
+
| `2` | Quét một phần (hết thời gian, tệp không đọc được). Không bao giờ chặn. |
|
|
543
|
+
| `10` | Lỗi sử dụng (cờ sai, thiếu mục tiêu) |
|
|
544
|
+
| `20` | Lỗi nội bộ |
|
|
592
545
|
|
|
593
|
-
|
|
594
|
-
mjolnir/
|
|
595
|
-
├── src/
|
|
596
|
-
│ ├── engine/ # LanguageAdapter interface + rule runner
|
|
597
|
-
│ ├── adapters/ # typescript · python · java · csharp · github-actions
|
|
598
|
-
│ ├── rules/ # rules across 8 families + the measured-FP table
|
|
599
|
-
│ ├── playwright/ # Selector Health Score engine
|
|
600
|
-
│ ├── discovery/ # workspace, frameworks, ignore resolution
|
|
601
|
-
│ ├── scope/ # git merge-base changed-scope engine
|
|
602
|
-
│ ├── scorer/ # transparent deduction table + prioritization
|
|
603
|
-
│ ├── reporter/ # terminal · JSON · SARIF 2.1 · Mermaid
|
|
604
|
-
│ ├── forensics/ # run-data ingestion · flake verdicts · triage
|
|
605
|
-
│ ├── config/ # mjolnir.config.json + suppressions
|
|
606
|
-
│ ├── plugins/ # third-party rule loading (no sandbox)
|
|
607
|
-
│ └── commands/ # every subcommand
|
|
608
|
-
└── tests/
|
|
609
|
-
├── fixtures/ # must-fire / must-not-fire per rule
|
|
610
|
-
└── golden/ # frozen score regression locks
|
|
611
|
-
```
|
|
546
|
+
`2` được cố ý tách biệt khỏi `0`: một lần quét chưa hoàn tất không phải là "không tìm thấy gì". Nó chỉ chưa tìm xong.
|
|
612
547
|
|
|
613
|
-
|
|
548
|
+
Mọi thứ mà máy tiêu thụ (kết quả công cụ MCP, `--json`, SARIF 2.1) đều đến từ một kết quả chuẩn duy nhất theo một schema có phiên bản và **chỉ mở rộng bằng cách bổ sung** (`schemaVersion: 1`, `contractVersion: 1`), nên không bên tiêu thụ nào phải dựng lại ý nghĩa từ văn bản đã hiển thị. Xem [hợp đồng máy](docs/machine-contract.md). ID quy tắc (`QA-<FAMILY>-NNN`) không thể thay đổi sau khi phát hành và không bao giờ được dùng lại.
|
|
614
549
|
|
|
615
|
-
|
|
616
|
-
I/O, không biến toàn cục. Thêm một hệ sinh thái = một adapter + quy
|
|
617
|
-
tắc của nó.
|
|
618
|
-
- **TypeScript/Playwright dùng AST bộ biên dịch** (ts-morph). Python,
|
|
619
|
-
Java và C# chạy trên lớp regex chung có che comment/chuỗi.
|
|
620
|
-
- Lớp AST tree-sitter WASM cho Java và C# đã tồn tại và là bước chính
|
|
621
|
-
xác kế tiếp — chưa được cắm vào pipeline quét đồng bộ.
|
|
550
|
+
<br />
|
|
622
551
|
|
|
623
|
-
|
|
552
|
+
## Những điều Mjölnir không thể cho bạn biết
|
|
624
553
|
|
|
625
|
-
|
|
554
|
+
- **Nó không chạy test của bạn.** Một lần quét sạch không phải là một bộ test đang qua.
|
|
555
|
+
- **Nó không thể cho bạn biết một assertion là _sai_.** `expect(total).toBe(41)` trông vẫn khỏe mạnh. Mjölnir tìm những test _không thể thất bại_ và những pipeline _không thể chuyển đỏ_, không phải những test kiểm tra sai thứ.
|
|
556
|
+
- **Nó không chứng minh tính đúng đắn nghiệp vụ.** Không có gì ở đây nói rằng sản phẩm của bạn làm đúng điều mà yêu cầu đặt ra.
|
|
557
|
+
- **Điểm 100 không phải bằng chứng của một bộ test tốt.** Bộ test của bạn có bao phủ rủi ro thực tế hay không là một câu hỏi khác, và công cụ này không trả lời câu hỏi đó.
|
|
558
|
+
- **5 trên 79 quy tắc phát hành dựa trên ước tính**, không phải tỷ lệ đã đo. Mỗi quy tắc đều nói rõ điều đó trên phát hiện của chính nó.
|
|
559
|
+
- **E1 không phải E2.** Phát hiện theo suy đoán đáng để đọc, không đáng để áp dụng mù quáng.
|
|
560
|
+
- **Một kho rỗng nhận điểm `null`, không bao giờ là 100.**
|
|
561
|
+
- **Một tệp tên `*.spec.ts` không có khai báo test không được tính là độ phủ.** Một kho mà các tệp spec duy nhất chỉ chứa import hoặc kiểu (không có lời gọi `it`/`test` nào) nhận điểm `null`, không phải 100.
|
|
626
562
|
|
|
627
|
-
|
|
628
|
-
| ------------------------------------------------------ | ------------------------------------------ |
|
|
629
|
-
| [docs/SCORING.md](docs/SCORING.md) | Chuẩn hoá điểm + cân bằng chứng |
|
|
630
|
-
| [docs/FP-AUDIT.md](docs/FP-AUDIT.md) | Tỷ lệ false positive đo được + phương pháp |
|
|
631
|
-
| [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md) | Trạng thái quy tắc, nuốt, deprecation |
|
|
632
|
-
| [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md) | Đầu ra SARIF + cấu hình editor/CI |
|
|
633
|
-
| [docs/rules/](docs/rules/) | Danh mục sinh tự động theo quy tắc |
|
|
634
|
-
| [CONTRIBUTING.md](CONTRIBUTING.md) | Cài đặt dev + quy trình đóng góp |
|
|
635
|
-
| [CHANGELOG.md](CHANGELOG.md) | Lịch sử phát hành |
|
|
636
|
-
| [SECURITY.md](SECURITY.md) | Báo cáo lỗ hổng |
|
|
563
|
+
<br />
|
|
637
564
|
|
|
638
|
-
|
|
565
|
+
## Tài liệu
|
|
639
566
|
|
|
640
|
-
|
|
567
|
+
Trang tài liệu đầy đủ ở <https://sergey-bar.github.io/Mjolnir/>.
|
|
641
568
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
[
|
|
569
|
+
| Tài liệu | Nội dung |
|
|
570
|
+
| ------------------------------------------------------ | ----------------------------------------------------------- |
|
|
571
|
+
| [docs/SCORING.md](docs/SCORING.md) | Chuẩn hóa điểm và trọng số bằng chứng |
|
|
572
|
+
| [docs/TERMINOLOGY.md](docs/TERMINOLOGY.md) | Bộ từ vựng chuẩn: một từ cho một khái niệm |
|
|
573
|
+
| [docs/FP-AUDIT.md](docs/FP-AUDIT.md) | Tỷ lệ dương tính giả đã đo và phương pháp |
|
|
574
|
+
| [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md) | Trạng thái quy tắc, cấp, chặn, ngừng hỗ trợ |
|
|
575
|
+
| [docs/VERSIONING.md](docs/VERSIONING.md) | Chính sách semver, giao diện đóng băng, chu kỳ ngừng hỗ trợ |
|
|
576
|
+
| [docs/machine-contract.md](docs/machine-contract.md) | Kết quả chuẩn mà máy đọc được |
|
|
577
|
+
| [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md) | Đầu ra SARIF và thiết lập trình soạn thảo hoặc CI |
|
|
578
|
+
| [docs/GITLAB-CI.md](docs/GITLAB-CI.md) | GitLab: báo cáo Code Quality, công thức cho MR, cổng |
|
|
579
|
+
| [docs/rules/](docs/rules/) | Danh mục được tạo cho từng quy tắc |
|
|
580
|
+
| [CONTRIBUTING.md](CONTRIBUTING.md) | Thiết lập môi trường phát triển và quy trình đóng góp |
|
|
581
|
+
| [SUPPORT.md](SUPPORT.md) | Nơi hỏi, báo cáo và nhận trợ giúp |
|
|
582
|
+
| [SECURITY.md](SECURITY.md) | Báo cáo lỗ hổng |
|
|
583
|
+
| [CHANGELOG.md](CHANGELOG.md) | Lịch sử phát hành |
|
|
646
584
|
|
|
647
|
-
|
|
585
|
+
### Trạng thái
|
|
648
586
|
|
|
649
|
-
|
|
587
|
+
**Phiên bản 1.** Schema JSON và mã thoát là những hợp đồng đã đóng băng. TypeScript và Python có độ phủ đã đo rộng nhất. Java và C# mới hơn; hãy đọc chúng qua [bảng độ trưởng thành](https://sergey-bar.github.io/Mjolnir/reference/rule-lifecycle). Những gì sắp tới, không có ngày tháng bịa đặt: [lộ trình công khai](https://sergey-bar.github.io/Mjolnir/reference/roadmap).
|
|
650
588
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
fixture cho đến khi
|
|
589
|
+
### Đóng góp
|
|
590
|
+
|
|
591
|
+
Quy tắc mới là đóng góp đầu tiên dễ nhất. Một lệnh duy nhất tạo khung cho quy tắc cùng các fixture must-fire **và** must-not-fire của nó. Quy tắc được tạo cố ý thất bại trên chính các fixture của nó cho đến khi logic phát hiện thật được viết, vì một bản nháp được phát hành là một quy tắc chưa ai đo:
|
|
654
592
|
|
|
655
593
|
```bash
|
|
656
594
|
mjolnir create-rule QA-PW-140 --title "Screenshot without diff bound"
|
|
657
595
|
```
|
|
658
596
|
|
|
659
|
-
|
|
660
|
-
lửa fixture nằm trong [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
597
|
+
Thiết lập môi trường phát triển, các lệnh cổng thường trực, cùng các luật anti-creep và tường lửa fixture có trong [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
661
598
|
|
|
662
|
-
|
|
599
|
+
<br />
|
|
663
600
|
|
|
664
601
|
<div align="center">
|
|
665
602
|
|
|
666
|
-
|
|
603
|
+
<img src="assets/readme/closing.svg" alt="Chạy nó trên kho của bạn." width="100%" />
|
|
667
604
|
|
|
668
605
|
```bash
|
|
669
606
|
npx mjolnir-qa@latest
|
|
670
607
|
```
|
|
671
608
|
|
|
672
|
-
|
|
609
|
+
[Đọc hướng dẫn](https://sergey-bar.github.io/Mjolnir/guide/getting-started) · [Trang tài liệu](https://sergey-bar.github.io/Mjolnir/) · [npm](https://www.npmjs.com/package/mjolnir-qa)
|
|
610
|
+
|
|
611
|
+
<br />
|
|
612
|
+
|
|
613
|
+
Đừng hỏi test có qua hay không.<br />
|
|
614
|
+
Hãy hỏi bằng chứng có chứng minh rằng chúng xứng đáng được tin hay không.
|
|
673
615
|
|
|
674
|
-
|
|
616
|
+
<sub>Được xây dựng bởi [Sergey Bar](https://www.linkedin.com/in/sergeybar/) · Giấy phép MIT</sub>
|
|
675
617
|
|
|
676
618
|
</div>
|