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/README.vi.md CHANGED
@@ -1,391 +1,431 @@
1
1
  <div align="center">
2
2
 
3
- <img src="assets/readme/logo.png" alt="Mjölnir — Verification Trust Engine" width="800" />
3
+ <img src="assets/readme/hero.svg" alt="Mjölnir. 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
- ### Các kiểm thử của bạn đang nói dối bạn. Chúng tôi chứng minh điều đó.
5
+ <br />
6
6
 
7
- **Verification Trust Engine cho QA.** Mjölnir kiểm toán các suite kiểm
8
- thử và pipeline CI, báo cáo điểm độ đáng tin và chỉ ra chính xác nơi
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
- [![npm](https://img.shields.io/npm/v/mjolnir-qa.svg?style=flat-square&color=C19A34&labelColor=0A1119)](https://www.npmjs.com/package/mjolnir-qa)
12
- [![ci](https://img.shields.io/github/actions/workflow/status/Sergey-Bar/Mjolnir/ci.yml?branch=main&style=flat-square&label=ci&labelColor=0A1119)](https://github.com/Sergey-Bar/Mjolnir/actions/workflows/ci.yml)
13
- [![license](https://img.shields.io/badge/license-MIT-C19A34.svg?style=flat-square&labelColor=0A1119)](LICENSE)
14
- [![node](https://img.shields.io/badge/node-%E2%89%A5%2022.18-37ABBD.svg?style=flat-square&labelColor=0A1119)](https://nodejs.org)
15
-
16
- [English](README.md) | [简体中文](README.zh.md) | [繁體中文](README.zht.md) | [한국어](README.ko.md) | [Deutsch](README.de.md) | [Español](README.es.md) | [Français](README.fr.md) | [Italiano](README.it.md) | [Dansk](README.da.md) | [日本語](README.ja.md) | [Polski](README.pl.md) | [Русский](README.ru.md) | [Norsk](README.no.md) | [Português (Brasil)](README.br.md) | [ไทย](README.th.md) | [Türkçe](README.tr.md) | [Українська](README.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
- > 🤖 Machine-assisted translation. The [English README](README.md) is canonical. Last synced: 2026-09-08.
12
+ [![npm](https://img.shields.io/npm/v/mjolnir-qa.svg?style=flat-square&color=1F6F7C&labelColor=0A1119)](https://www.npmjs.com/package/mjolnir-qa)
13
+ [![downloads](https://img.shields.io/npm/dm/mjolnir-qa.svg?style=flat-square&color=1F6F7C&labelColor=0A1119)](https://www.npmjs.com/package/mjolnir-qa)
14
+ [![ci](https://img.shields.io/github/actions/workflow/status/Sergey-Bar/Mjolnir/ci.yml?branch=main&style=flat-square&label=ci&labelColor=0A1119)](https://github.com/Sergey-Bar/Mjolnir/actions/workflows/ci.yml)
15
+ [![coverage](https://img.shields.io/codecov/c/github/Sergey-Bar/Mjolnir?style=flat-square&color=1F6F7C&labelColor=0A1119&label=coverage)](https://codecov.io/gh/Sergey-Bar/Mjolnir)
16
+ [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/Sergey-Bar/Mjolnir/badge)](https://scorecard.dev/viewer/?uri=github.com/Sergey-Bar/Mjolnir)
17
+ [![license](https://img.shields.io/badge/license-MIT-1F6F7C.svg?style=flat-square&labelColor=0A1119)](LICENSE)
18
+ [![node](https://img.shields.io/badge/node-%E2%89%A5%2022.18-1F6F7C.svg?style=flat-square&labelColor=0A1119)](https://nodejs.org)
19
19
 
20
20
  ```bash
21
21
  npx mjolnir-qa@latest
22
22
  ```
23
23
 
24
- **Các kiểm thử của bạn có đáng tin không?**
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
- [Xem nó hoạt động](#-xem-nó-hoạt-động) ·
27
- [Khởi động nhanh](#-khởi-động-nhanh) ·
28
- [Nó kiểm tra gì](#-mjölnir-kiểm-tra-gì) ·
29
- [Chấm điểm](#cách-thức-chấm-điểm) ·
30
- [CI](#-tích-hợp-ci) · [Cấu hình](#cấu-hình) ·
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 80/100 WORTHY, đ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
- ## 🎬 Xem nó hoạt động
92
+ <br />
38
93
 
39
94
  <p align="center">
40
- <img src="assets/readme/demo.svg" alt="Báo cáo --verbose đầy đủ của Mjölnir trên một repo demo: WORTHINESS 75/100 NEEDS WORK, phân loại chẩn đoán theo nhóm, danh sách FIX THIS FIRST và mỗi finding với rule ID cùng số dòng, trải rộng qua các quy tắc CI, Playwright, vệ sinh kiểm thử và Python" width="900" />
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>Toàn bộ đầu ra `npx mjolnir-qa ./examples/demo-repo --verbose`,
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
- **Chuyện gì vừa xảy ra:**
102
+ </details>
50
103
 
51
- 1. Mjölnir phát hiện các spec Playwright, config của nó, CI workflow và
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
- ### Một finding, nhìn gần
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
- Chạy `mjolnir explain QA-CI-001` trên finding đầu tiên ở trên và bạn nhận
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
- ▚ QA-CI-001 — continue-on-error masks a failing verification gate
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
- Measured FP: not yet measured — this rule ships on assumption (see docs/FP-AUDIT.md)
121
+ QA impact: False-green risk (FALSE-GREEN)
122
+ Measured FP: 11% (19 hand-classified corpus verdicts)
123
+ FP risk: low (author estimate)
124
+ Languages: yaml
125
+ Frameworks: github-actions, azure-pipelines
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
- on this workflow cannot be trusted.
131
+ This job can fail every day and CI will still show green. The checkmark on
132
+ this workflow cannot be trusted.
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
- Đó là đơn vị giá trị: không phải lỗi phong cách, mà là một nơi CI của
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
- ## ⚡ Khởi động nhanh
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
- Chạy trên một repo để có báo cáo đầy đủ và điểm độ đáng tin:
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
- ```bash
93
- npx mjolnir-qa@latest
155
+ Docs: mjolnir rules --md (full catalog, this rule included)
94
156
  ```
95
157
 
96
- **Trong CI, sản phẩm là một lệnh.** Nó chỉ quét những gì branch chạm tới
97
- và thoát khác 0 khi có vấn đề mới:
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 --scope changed
165
+ npx mjolnir-qa@latest
101
166
  ```
102
167
 
103
- Thả cái đó vào một check của PR — `mjolnir ci install` ghi workflow —
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
- <details>
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
- | Lệnh | Nó làm gì |
120
- | ----------------------------------- | ------------------------------------------------------- |
121
- | `mjolnir forensics ./test-results/` | Dữ liệu chạy thật → phán quyết `TRUE-FLAKE`, `FLAKY.md` |
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
- </details>
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>Thỉnh thoảng / báo cáo</strong></summary>
130
-
131
- | Lệnh | Nó làm gì |
132
- | ------------------------------- | ----------------------------------------------------- |
133
- | `mjolnir fix --dry-run` / `fix` | Sửa tự động an toàn kèm bằng chứng |
134
- | `mjolnir baseline` / `diff` | Chụp lại các finding, rồi chỉ báo cáo cái mới/xấu hơn |
135
- | `mjolnir impact --since <ref>` | Những gì thay đổi kể từ commit trước đó |
136
- | `mjolnir debt` | Sổ nợ kiểm thử với mô hình chi phí |
137
- | `mjolnir handover` | Bản đồ onboarding suite cho QA mới |
138
- | `mjolnir stats` | Bộ đếm mọi thời đại cục bộ của các fix đã thấy |
139
- | `mjolnir badge` | JSON endpoint shields.io + snippet |
140
- | `mjolnir rules --md` | Danh mục quy tắc đầy đủ (JSON hoặc Markdown) |
141
- | `mjolnir doctor` | Tự kiểm toán chính cơ sở quy tắc của Mjölnir |
142
- | `mjolnir create-rule <ID>` | Scaffold quy tắc mới + fixtures |
143
- | `mjolnir --format mermaid` | Sơ đồ kiến trúc kiểm thử cho comment PR |
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
- Cài toàn cục thay vì `npx` nếu bạn thích: `npm i -g mjolnir-qa`.
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
- ## 👥 Dành cho ai?
228
+ <br />
153
229
 
154
- - **QA / SDET** sở hữu suite e2e hoặc tích hợp, cần bằng chứng rằng
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
- ## 🔨 Mjölnir kiểm tra gì
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
- ### Các quy tắc
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
- Mọi quy tắc đều có fixture must-fire **và** must-not-fire. Quy tắc mà
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>Vệ sinh kiểm thử</strong></summary>
183
-
184
- | ID | Quy tắc | Severity |
185
- | ----------- | --------------------------------------------------- | -------- |
186
- | QA-TEST-001 | Kiểm thử tập trung bị commit (`.only`, `fit`) | error |
187
- | QA-TEST-002 | Kiểm thử bị bỏ qua mà không có lý do | error |
188
- | QA-TEST-002 | Kiểm thử bị bỏ qua có lý do được theo dõi | warning |
189
- | QA-TEST-003 | Kiểm thử không có assertion | error |
190
- | QA-TEST-004 | Sleep cứng (`waitForTimeout`, `sleep()`, `delay()`) | warning |
191
- | QA-TEST-006 | Lạm dụng retry che giấu flakiness | warning |
192
- | QA-TEST-010 | Thân kiểm thử rỗng | error |
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
- <details>
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
- | ID | Quy tắc | Severity |
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
- </details>
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
- <details>
208
- <summary><strong>Playwright 🎭</strong></summary>
307
+ ```text
308
+ ▍ SELECTOR HEALTH
209
309
 
210
- | ID | Quy tắc | Severity |
211
- | --------- | ---------------------------------------- | -------- |
212
- | QA-PW-002 | Assertion locator không await | error |
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
- </details>
314
+ e2e/checkout.spec.ts
315
+ [██████████████████░░] 88 / 100
316
+ role/text: 4 · testid: 1 · plain-css: 0 · css-chains: 1 ⚠ · xpath: 0
317
+ ```
218
318
 
219
- <details>
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
- </details>
321
+ <br />
233
322
 
234
- <details>
235
- <summary><strong>Python / pytest 🐍</strong></summary>
323
+ ## Điểm đáng tin
236
324
 
237
- | ID | Quy tắc | Severity |
238
- | --------- | ------------------------------------------------- | -------- |
239
- | QA-PY-002 | Kiểm thử bị bỏ qua (`skip`, `xfail` không nghiêm) | warning |
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
- Tổng cộng 20 quy tắc Python (QA-PY-001…012 vệ sinh pytest + QA-PY-101…108 Playwright-Python).
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
- </details>
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
- <details>
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
- | ID | Quy tắc | Severity |
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
- </details>
343
+ <br />
260
344
 
261
- <details>
262
- <summary><strong>C# / .NET — NUnit · xUnit · MSTest 🟣</strong></summary>
345
+ ## Mô hình bằng chứng
263
346
 
264
- | ID | Quy tắc | Severity |
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
- </details>
349
+ **Chắc chắn đến mức nào — mức bằng chứng.**
273
350
 
274
- > Danh mục sống đầy đủ — mọi quy tắc với tier, confidence, rủi ro false
275
- > positive và khả năng autofix — sinh từ registry:
276
- >
277
- > ```bash
278
- > mjolnir rules --md
279
- > ```
280
- >
281
- > Trang theo từng quy tắc nằm dưới [`docs/rules/`](docs/rules/).
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/terminal-hero.svg" alt="Đầu ra terminal của Mjölnir — WORTHINESS 75/100 NEEDS WORK, phân loại chẩn đoán theo nhóm và danh sách FIX THIS FIRST" width="820" />
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
- <sub>Tạo lại bằng `npm run docs:hero`;
326
- [`tests/hero-asset-reproducibility.spec.ts`](tests/hero-asset-reproducibility.spec.ts)
327
- khiến CI fail nếu sản phẩm lệch khỏi những gì reporter thực sự in ra.</sub>
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
- Điểm số minh bạch: **error −8, warning −3, info −1**, sau đó chuẩn hoá
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
- **Phán quyết**
376
+ ### Bao nhiêu phần trong số này đã được đo
336
377
 
337
- | Score | Phán quyết |
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
- **Mức bằng chứng** — mỗi finding mang một; nó đặt trọng số của finding
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
- | Mức | Ý nghĩa | Tác động điểm | Ví dụ |
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
- Đa số quy tắc là **E1**. Khẩu hiệu «we prove it» ám chỉ hệ thống này:
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
- Repo rỗng chấm `null`, không bao giờ 100 giả — xem
357
- [Mô hình niềm tin](#mô-hình-niềm-tin).
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
- ## 🎭 Selector Health Score
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
- Chỉ số tiêu đề cho suite Playwright — locator của bạn bền bao nhiêu:
397
+ ### Vì sao đây không phải một linter
364
398
 
365
- ```text
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
- [█████████████████░░░] 83 / 100
369
- role/text: 2 · testid: 1 · css-chains: 1 ⚠ · xpath: 0
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
- Locator dựa trên role nhận điểm tối đa. Chuỗi class CSS và XPath hạ điểm
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
- ## 🔬 Bằng chứng runtime
419
+ ## Phân tích pháp chứng lúc chạy
379
420
 
380
- Phát hiện flakiness tĩnh là đoán mò. Mjölnir đọc **dữ liệu thực thi
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
- ▚ FLAKINESS LEADERBOARD
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
- Một kiểm thử chỉ pass từ lần thử ≥ 2 không phải kiểm thử pass — đó là
399
- kiểm thử may mắn. Nó bị gắn cờ `TRUE-FLAKE` bất kể dấu xanh cuối cùng.
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
- ## ⚡ Mjölnir không phải một linter nữa
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
- Linter cho bạn biết mã có tuân thủ quy tắc không. Mjölnir cho bạn biết
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
- | | ESLint / SonarQube | Công cụ coverage | Review thủ công | **Mjölnir** |
409
- | ------------------------------------------------------- | :----------------: | :--------------: | :-------------: | :---------: |
410
- | Toàn vẹn CI workflow (`continue-on-error`, `\|\| true`) | ❌ | ❌ | hiếm khi | ✅ |
411
- | Đa ngôn ngữ (TS, Python, Java, C#) từ một công cụ | ❌ | ❌ | ❌ | ✅ |
412
- | Chấm độ bền locator Playwright (Selector Health) | ❌ | ❌ | hiếm khi | ✅ |
413
- | Gắn cờ kiểm thử không có assertion thật | ✅ (plugin)\* | ❌ | thi thoảng | ✅ |
414
- | Bắt sleep cứng (`waitForTimeout`, `time.sleep`) | ✅ (plugin)\* | ❌ | thi thoảng | ✅ |
415
- | Chạy trong vài giây, 0 lệnh gọi mạng khi quét | ✅ | ✅ | — | ✅ |
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
- \*`eslint-plugin-jest` (`expect-expect`) và `eslint-plugin-playwright`
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
- **Phân tích runtime** là một hạng mục riêng ngoài linting tĩnh:
476
+ ### Quy trách nhiệm theo phạm vi thay đổi
422
477
 
423
- | | Playwright retry reporter | Allure / ReportPortal | **Mjölnir forensics** |
424
- | ------------------------------------------------- | :-----------------------: | :-------------------: | :-------------------: |
425
- | Đọc dữ liệu chạy thật cho phán quyết `TRUE-FLAKE` | một phần\* | một phần (tag) | ✅ |
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
- \*Playwright theo dõi retry bên trong nhưng không tạo báo cáo flakiness
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
- ## 🤖 Tại sao không chỉ dùng AI code review?
486
+ <br />
435
487
 
436
- Vấn đề khác, tầng khác. AI review có thể phát hiện thay đổi kiểm thử
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
- | | AI code review (Copilot v.v.) | **Mjölnir** |
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
- **Dùng cả hai.** AI bắt được sắc thái, ý đồ và lỗi thiết kế không regex
450
- nào tìm ra. Mjölnir bắt các mẫu cấu trúc AI bỏ sót vì chúng trông
451
- «có chủ ý» — một `.only` bị commit, exit code bị nuốt, một
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
- ## 🤖 Tích hợp CI
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
- Một lệnh sinh PR workflow — tư vấn mặc định, không bao giờ chặn:
504
+ Thêm vào một client có CLI riêng:
460
505
 
461
506
  ```bash
462
- mjolnir ci install
507
+ claude mcp add mjolnir -- npx -y mjolnir-qa@latest mcp
463
508
  ```
464
509
 
465
- Hoặc nối native vào GitHub Code Scanning qua SARIF:
510
+ Hoặc vào bất kỳ client nào nhận khối `mcpServers`:
466
511
 
467
- ```yaml
468
- - run: npx mjolnir-qa@latest --format sarif > mjolnir.sarif
469
- - uses: github/codeql-action/upload-sarif@v3
470
- with:
471
- sarif_file: mjolnir.sarif
512
+ ```json
513
+ {
514
+ "mcpServers": {
515
+ "mjolnir": { "command": "npx", "args": ["-y", "mjolnir-qa@latest", "mcp"] }
516
+ }
517
+ }
472
518
  ```
473
519
 
474
- Cấu hình trình soạn thảo và pipeline cho SARIF:
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
- ### Độ phủ phạm vi thay đổi
522
+ <br />
478
523
 
479
- `--scope changed` gán finding cho các dòng được thêm vào branch của bạn
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
- ## Cấu hình
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
- Mjölnir là zero-config. Một `mjolnir.config.json` tuỳ chọn (hoặc
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
- | Key | Kiểu | Tác dụng |
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
- ```json
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
- - **`.mjolnirignore`** — một file kiểu gitignore thuần cho loại trừ đường
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
- <details>
591
- <summary>Mở cây</summary>
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
- </details>
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
- - **Quy tắc là hàm thuần** — `(SourceFileContext) → Finding[]`, không
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
- ## 📚 Tài liệu
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
- | Tài liệu | Có gì trong đó |
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
- ## 📈 Tình trạng
567
+ Trang tài liệu đầy đủ ở <https://sergey-bar.github.io/Mjolnir/>.
641
568
 
642
- **v0.5.x · beta mở.** JSON schema và mã thoát là hợp đồng đóng băng.
643
- TypeScript và Python có độ phủ đo được rộng nhất; Java và C# mới hơn —
644
- đọc qua
645
- [bảng tier](#tier-quy-tắc-và-độ-trưởng-thành-theo-ngôn-ngữ).
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
- ## 🤝 Đóng góp
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
- Quy tắc mới là đóng góp đầu tiên dễ nhất — một lệnh scaffold quy tắc
652
- cùng fixtures must-fire **và** must-not-fire (quy tắc sinh ra cố ý fail
653
- fixture cho đến khi bạn cài phát hiện thật — stub không thể ship):
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
- Cài đặt dev đầy đủ, các lệnh standing gate và luật anti-creep / tường
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
- **Ngừng ship kiểm thử mà bạn không thể tin.**
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
- **Star ⭐ · Watch 👀 · Contribute 🤝**
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
- Xây bởi [Sergey Bar](https://www.linkedin.com/in/sergeybar/)
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>