mjolnir-qa 0.4.0 → 0.5.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.th.md ADDED
@@ -0,0 +1,672 @@
1
+ <div align="center">
2
+
3
+ <img src="assets/readme/logo.png" alt="Mjölnir — Verification Trust Engine" width="800" />
4
+
5
+ ### การทดสอบของคุณโกหกคุณ เราพิสูจน์ให้เห็น
6
+
7
+ **Verification Trust Engine สำหรับ QA** Mjölnir ตรวจสอบชุดทดสอบและ
8
+ CI pipelines รายงานคะแนนความน่าเชื่อถือ และแสดงให้เห็นอย่างแม่นยำว่า
9
+ ความไว้วางใจพังตรงไหน
10
+
11
+ [![npm](https://img.shields.io/npm/v/mjolnir-qa.svg?style=flat-square&color=C9A227&labelColor=0B0F17)](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=0B0F17)](https://github.com/Sergey-Bar/Mjolnir/actions/workflows/ci.yml)
13
+ [![license](https://img.shields.io/badge/license-MIT-C9A227.svg?style=flat-square&labelColor=0B0F17)](LICENSE)
14
+ [![node](https://img.shields.io/badge/node-%E2%89%A5%2022.18-2E8C7F.svg?style=flat-square&labelColor=0B0F17)](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) | ไทย | [Türkçe](README.tr.md) | [Українська](README.uk.md) | [বাংলা](README.bn.md) | [Ελληνικά](README.gr.md) | [Tiếng Việt](README.vi.md) | [עברית](README.he.md) | [العربية](README.ar.md) | [Bosanski](README.bs.md)
17
+
18
+ > 🤖 Machine-assisted translation. The [English README](README.md) is canonical. Last synced: 2026-09-04.
19
+
20
+ ```bash
21
+ npx mjolnir-qa@latest
22
+ ```
23
+
24
+ **การทดสอบของคุณคู่ควรแก่ความไว้วางใจหรือไม่?**
25
+
26
+ [ดูมันทำงาน](#-ดูมันทำงาน) ·
27
+ [เริ่มเร็ว](#-เริ่มเร็ว) ·
28
+ [มันตรวจอะไร](#-mjölnir-ตรวจอะไร) ·
29
+ [การให้คะแนน](#การให้คะแนนทำงานอย่างไร) ·
30
+ [CI](#-การเชื่อมต่อ-ci) · [การตั้งค่า](#การตั้งค่า) ·
31
+ [เอกสาร](#-เอกสาร)
32
+
33
+ </div>
34
+
35
+ ---
36
+
37
+ ## 🎬 ดูมันทำงาน
38
+
39
+ <p align="center">
40
+ <img src="assets/readme/demo.svg" alt="รายงาน --verbose ฉบับเต็มของ Mjölnir บน demo repo: WORTHINESS 75/100 NEEDS WORK, การแจกแจงการวินิจฉัยตามหมวด, รายการ FIX THIS FIRST และทุก finding พร้อม rule ID และเลขบรรทัด ครอบคลุมกฎ CI, Playwright, สุขอนามัยการทดสอบ และกฎ Python" width="900" />
41
+ </p>
42
+
43
+ <sub>ผลลัพธ์ฉบับเต็มของ `npx mjolnir-qa ./examples/demo-repo --verbose`
44
+ เรนเดอร์จาก reporter จริง — ไม่ตัดทอนอะไรเลย สร้างใหม่ด้วย
45
+ `npm run docs:demo`;
46
+ [`tests/demo-asset-reproducibility.spec.ts`](tests/demo-asset-reproducibility.spec.ts)
47
+ จะทำให้ CI ล้ม หากสินค้าเองเหลื่อมจากสิ่งที่เครื่องมือพิมพ์</sub>
48
+
49
+ **เกิดอะไรขึ้นเมื่อครู่:**
50
+
51
+ 1. Mjölnir ค้นพบ Playwright specs, config, CI workflow และไฟล์ทดสอบ
52
+ Python — สี่ภาษา/ฟอร์แมต ในหนึ่งรอบ
53
+ 2. มันพบหลักฐานที่บั่นทอนความไว้วางใจต่อชุดทดสอบ — `continue-on-error`
54
+ ที่ปกปิด job, `|| true` ที่กลืน exit code, hard sleep, selector เปราะ,
55
+ URL staging ฝังตาย, การรอ `networkidle`
56
+ 3. มันเปลี่ยนแต่ละเรื่องเป็น finding ที่จับต้องได้ พร้อม rule ID,
57
+ ตำแหน่ง และวิธีแก้ — และเป็นคะแนนเดียวที่คุณ gate PR ได้
58
+
59
+ ### Finding หนึ่งชิ้น ระยะชิด
60
+
61
+ รัน `mjolnir explain QA-CI-001` กับ finding แรกด้านบน แล้วคุณจะได้:
62
+
63
+ ```text
64
+ ▚▞ QA-CI-001 — continue-on-error masks a failing verification gate
65
+
66
+ Severity: error
67
+ Confidence: high
68
+ Evidence: E2
69
+ Measured FP: not yet measured — this rule ships on assumption (see docs/FP-AUDIT.md)
70
+
71
+ WHAT WAS FOUND (real detector output, not a mockup)
72
+ Job `security-scan` runs a verification gate under `continue-on-error: true`.
73
+
74
+ WHY IT MATTERS
75
+ This job can fail every day and CI will still show green. The checkmark
76
+ on this workflow cannot be trusted.
77
+
78
+ HOW TO FIX
79
+ Remove continue-on-error, or scope it to individual non-blocking steps only.
80
+ ```
81
+
82
+ นี่คือหน่วยของคุณค่า: ไม่ใช่เรื่องสไตล์เล็ก ๆ แต่เป็นจุดที่ CI ของคุณ
83
+ บอกว่าบางอย่างผ่าน ทั้งที่ไม่ได้ผ่าน
84
+
85
+ ---
86
+
87
+ ## ⚡ เริ่มเร็ว
88
+
89
+ รันกับ repo เพื่อรายงานฉบับเต็มและคะแนนความน่าเชื่อถือ:
90
+
91
+ ```bash
92
+ npx mjolnir-qa@latest
93
+ ```
94
+
95
+ **ใน CI ผลิตภัณฑ์คือคำสั่งเดียว** มันสแกนเฉพาะสิ่งที่ branch แตะ
96
+ และออกด้วยเลขไม่ใช่ศูนย์เมื่อมีปัญหาใหม่:
97
+
98
+ ```bash
99
+ npx mjolnir-qa@latest --scope changed
100
+ ```
101
+
102
+ หย่อนลงใน PR check — `mjolnir ci install` เขียน workflow ให้ — แล้วจบ
103
+ ส่วนที่เหลือเป็นทางเลือกทั้งหมด
104
+
105
+ | คำสั่ง | มันทำอะไร |
106
+ | ----------------------------------- | ---------------------------------------------------- |
107
+ | `mjolnir` | สแกนทั้ง repo + คะแนนความน่าเชื่อถือ |
108
+ | `mjolnir --scope changed` | เฉพาะสิ่งที่ branch คุณแนะนำ — รูปแบบ CI |
109
+ | `mjolnir ci install` | สร้าง CI workflow แบบที่ปรึกษาสำหรับ PR |
110
+ | `mjolnir explain QA-CI-001` | อะไร / ทำไม / วิธีแก้ + อัตรา FP ที่วัดได้ของกฎเดียว |
111
+ | `mjolnir rules --unmeasured` | กฎที่ทำงานด้วยข้อสมมติ ไม่ใช่การวัด |
112
+ | `mjolnir --json` / `--format sarif` | เครื่องอ่านได้ / GitHub Code Scanning |
113
+ | `mjolnir --strict` | รันกฎ tier quarantine ด้วย (ความเสี่ยง FP สูงกว่า) |
114
+
115
+ <details>
116
+ <summary><strong>เมื่ออะไรบางอย่าง flaky</strong></summary>
117
+
118
+ | คำสั่ง | มันทำอะไร |
119
+ | ----------------------------------- | -------------------------------------------------- |
120
+ | `mjolnir forensics ./test-results/` | ข้อมูลรันจริง → คำพิพากษา `TRUE-FLAKE`, `FLAKY.md` |
121
+ | `mjolnir triage ./test-results/` | ข้อเสนอการกักกันจากประวัติการรัน |
122
+ | `mjolnir pw-report ./test-results/` | สรุปการรัน Playwright — retry / flake / ช้าสุด |
123
+ | `mjolnir doctor:playwright` | สแกนลึกเฉพาะ Playwright + Selector Health Score |
124
+
125
+ </details>
126
+
127
+ <details>
128
+ <summary><strong>ใช้เป็นครั้งคราว / รายงาน</strong></summary>
129
+
130
+ | คำสั่ง | มันทำอะไร |
131
+ | ------------------------------- | ----------------------------------------------------- |
132
+ | `mjolnir fix --dry-run` / `fix` | แก้อัตโนมัติอย่างปลอดภัย พร้อมหลักฐาน |
133
+ | `mjolnir baseline` / `diff` | บันทึก snapshot ของ finding แล้วรายงานเฉพาะใหม่/แย่ลง |
134
+ | `mjolnir impact --since <ref>` | อะไรเปลี่ยนไปตั้งแต่ commit ก่อนหน้า |
135
+ | `mjolnir debt` | ทะเบียนหนี้การทดสอบ พร้อมโมเดลต้นทุน |
136
+ | `mjolnir handover` | แผนที่ onboarding ชุดทดสอบสำหรับ QA หน้าใหม่ |
137
+ | `mjolnir stats` | ตัวนับตลอดกาลในเครื่อง ของ fix ที่เคยเห็น |
138
+ | `mjolnir badge` | JSON endpoint ของ shields.io + snippet |
139
+ | `mjolnir rules --md` | แคตตาล็อกกฎเต็มรูปแบบ (JSON หรือ Markdown) |
140
+ | `mjolnir doctor` | ตรวจตรวามของฐานกฎของ Mjölnir เอง |
141
+ | `mjolnir create-rule <ID>` | สร้างโครงกฎใหม่ + fixtures |
142
+ | `mjolnir --format mermaid` | แผนภาพสถาปัตยกรรมการทดสอบสำหรับคอมเมนต์ PR |
143
+
144
+ </details>
145
+
146
+ ติดตั้งแบบ global แทน `npx` หากคุณชอบ: `npm i -g mjolnir-qa`
147
+ ต้องใช้ Node.js ≥ 22.18 ทำงานบน Windows, macOS และ Linux
148
+
149
+ ---
150
+
151
+ ## 👥 สำหรับใคร?
152
+
153
+ - **QA / SDET** เจ้าของชุด e2e หรือ integration ที่ต้องการหลักฐานว่า
154
+ ชุดทดสอบสมควรได้เครื่องหมายเขียวที่มันผลิตจริง
155
+ - **ทีม Platform / DevEx** ผู้รับผิดชอบความสมบูรณ์ของ CI และ release
156
+ gates — คนที่ใส่ใจว่า `continue-on-error` จะไม่หลอกเปลี่ยน pipeline
157
+ แดงให้เขียวอย่างเงียบ ๆ
158
+ - **ผู้ดูแล OSS** ที่อยากได้เกตตรวจสอบที่ถูก เปิดตลอดเวลา รันได้ทั้ง
159
+ ในเครื่องและใน CI โดยไม่มีการเรียกเครือข่าย
160
+
161
+ ---
162
+
163
+ ## 🔨 Mjölnir ตรวจอะไร
164
+
165
+ | | |
166
+ | --- | --------------------------------------------------------------------------------------------------------------- |
167
+ | ⚖️ | **คะแนนความน่าเชื่อถือ** — ตัวเลขเดียว ตารางหักโปร่งใส ไม่มีกล่องดำ |
168
+ | 🎭 | **Selector Health Score** — ให้เกรด locator ของ Playwright คุณ ไม่ใช่แค่อัตราผ่าน |
169
+ | 🔬 | **นิติวิทยาศาสตร์ runtime** — อ่านข้อมูลรันจริงของ Playwright/JUnit เพื่อจับ `TRUE-FLAKE` ไม่ใช่แค่เดาแบบสถิต |
170
+ | 🚨 | **กฎความสมบูรณ์ของ CI** — จับ `continue-on-error`, `\|\| true` และเทคนิคเขียวลวงอื่น ๆ |
171
+ | 🐍 | **ทั้งสี่ Playwright bindings** — TypeScript, Python, Java, C#/.NET — บวก pytest, JUnit/TestNG และ CI workflows |
172
+ | 🔒 | **Local-first** — ศูนย์การเรียกเครือข่ายระหว่างสแกน ศูนย์เทเลเมทรี รันเสร็จในไม่กี่วินาที |
173
+
174
+ ### กฎทั้งหมด
175
+
176
+ ทุกกฎมาพร้อม fixture must-fire **และ** must-not-fire กฎที่ยิงบน
177
+ fixture ลบของตัวเองจะปล่อยไม่ได้ — นั่นคือกำแพงกัน false positive
178
+
179
+ <details>
180
+ <summary><strong>สุขอนามัยการทดสอบ</strong></summary>
181
+
182
+ | ID | กฎ | Severity |
183
+ | ----------- | --------------------------------------------------- | -------- |
184
+ | QA-TEST-001 | ทดสอบแบบโฟกัสถูก commit (`.only`, `fit`) | error |
185
+ | QA-TEST-002 | ข้ามทดสอบโดยไม่มีเหตุผล | error |
186
+ | QA-TEST-002 | ข้ามทดสอบโดยมีเหตุผลที่ถูกบันทึก | warning |
187
+ | QA-TEST-003 | ทดสอบไม่มี assertion | error |
188
+ | QA-TEST-004 | hard sleep (`waitForTimeout`, `sleep()`, `delay()`) | warning |
189
+ | QA-TEST-006 | ใช้ retry เกินควร ซ่อนความไม่นิ่ง | warning |
190
+ | QA-TEST-010 | เนื้อความทดสอบว่าง | error |
191
+
192
+ </details>
193
+
194
+ <details>
195
+ <summary><strong>คุณภาพการทดสอบ</strong></summary>
196
+
197
+ | ID | กฎ | Severity |
198
+ | ------------ | -------------------------------------- | -------- |
199
+ | QA-TQUAL-001 | ตรวจยืนยันด้วย mock เท่านั้น | info |
200
+ | QA-TQUAL-002 | assertion พรรคพวกตัวเอง (tautological) | error |
201
+ | QA-TQUAL-009 | assertion ของ promise ที่ไม่ await | error |
202
+ | QA-TQUAL-011 | ทดสอบที่ถูกคอมเมนต์ทิ้งไว้ | warning |
203
+
204
+ </details>
205
+
206
+ <details>
207
+ <summary><strong>Playwright 🎭</strong></summary>
208
+
209
+ | ID | กฎ | Severity |
210
+ | --------- | ----------------------------------------- | -------- |
211
+ | QA-PW-002 | assertion ของ locator ที่ไม่ await | error |
212
+ | QA-PW-003 | `page.pause()` / `test.only()` ถูก commit | error |
213
+ | QA-PW-004 | selector CSS/XPath เปราะ | warning |
214
+ | QA-PW-005 | ตรรกะธุรกิจใน `page.evaluate()` | info |
215
+ | QA-PW-114 | element handles รุ่นเก่า (`page.$`) | info |
216
+ | QA-PW-118 | การรอ `networkidle` (flaky by design) | info |
217
+ | QA-PW-123 | URL สภาพแวดล้อมฝังตาย | warning |
218
+
219
+ </details>
220
+
221
+ <details>
222
+ <summary><strong>ความสมบูรณ์ของ CI</strong></summary>
223
+
224
+ | ID | กฎ | Severity |
225
+ | --------- | ------------------------------------------------------------- | -------- |
226
+ | QA-CI-001 | `continue-on-error` ปกปิดความล้มเหลว | error |
227
+ | QA-CI-002 | `\|\| true` กลืน exit code | error |
228
+ | QA-CI-005 | รายงานถูกใช้แต่ไม่เคยถูกสร้าง | error |
229
+ | QA-CI-007 | ครอบ retry รอบการทดสอบ | warning |
230
+ | QA-CI-008 | step สำเร็จเสมอ ปกปิดความล้มเหลว | error |
231
+ | QA-CI-009 | exit code ของทดสอบไม่ถูกส่งต่อ (`\|` ไม่มี pipefail, โซ่ `;`) | error |
232
+ | QA-CI-010 | ข้ามทดสอบในที่ที่ต้องบล็อก (skip-on-PR guards) | error |
233
+
234
+ </details>
235
+
236
+ <details>
237
+ <summary><strong>Python / pytest 🐍</strong></summary>
238
+
239
+ | ID | กฎ | Severity |
240
+ | --------- | ----------------------------------------- | -------- |
241
+ | QA-PY-002 | ทดสอบถูกข้าม (`skip`, `xfail` ไม่เข้มงวด) | warning |
242
+ | QA-PY-003 | ฟังก์ชันทดสอบไม่มี assertion | error |
243
+ | QA-PY-005 | `time.sleep()` ในการทดสอบ | warning |
244
+ | QA-PY-006 | เนื้อความทดสอบว่าง (`pass`) | info |
245
+ | QA-PY-010 | พึ่งพาความสุ่ม/เวลาโดยไม่ freeze | info |
246
+ | QA-PY-012 | assertion พรรคพวกตัวเอง | error |
247
+
248
+ กฎ Python รวม 20 ข้อ (QA-PY-001…012 สุขอนามัย pytest + QA-PY-101…108 Playwright-Python)
249
+
250
+ </details>
251
+
252
+ <details>
253
+ <summary><strong>Java / JUnit · TestNG ☕</strong></summary>
254
+
255
+ | ID | กฎ | Severity |
256
+ | --------- | ---------------------------------------- | -------- |
257
+ | QA-JV-101 | ทดสอบถูกปิด (`@Disabled`) | warning |
258
+ | QA-JV-102 | hard sleep (`Thread.sleep()`) | warning |
259
+ | QA-JV-103 | วิธีทดสอบไม่มี assertion | error |
260
+ | QA-JV-105 | hard sleep Playwright `waitForTimeout()` | warning |
261
+ | QA-JV-106 | selector เปราะแทน role locator | warning |
262
+ | QA-JV-108 | URL สภาพแวดล้อมฝังตายในทดสอบ | info |
263
+ | QA-JV-111 | mock ครอบคลุมหมด `page.route("**")` | info |
264
+
265
+ </details>
266
+
267
+ <details>
268
+ <summary><strong>C# / .NET — NUnit · xUnit · MSTest 🟣</strong></summary>
269
+
270
+ | ID | กฎ | Severity |
271
+ | --------- | ------------------------------------------ | -------- |
272
+ | QA-CS-101 | ทดสอบถูกข้าม (`[Ignore]`, `[Fact(Skip=)]`) | warning |
273
+ | QA-CS-102 | hard sleep (`Thread.Sleep` / `Task.Delay`) | warning |
274
+ | QA-CS-103 | วิธีทดสอบไม่มี assertion | error |
275
+ | QA-CS-105 | hard sleep `WaitForTimeoutAsync()` | warning |
276
+ | QA-CS-106 | selector เปราะแทน role locator | warning |
277
+ | QA-CS-108 | URL สภาพแวดล้อมฝังตายในทดสอบ | info |
278
+ | QA-CS-111 | mock ครอบคลุมหมด `page.RouteAsync("**")` | info |
279
+
280
+ </details>
281
+
282
+ > แคตตาล็อกสดฉบับเต็ม — ทุกกฎพร้อม tier, confidence, ความเสี่ยง false
283
+ > positive และความพร้อมของ autofix — สร้างจาก registry:
284
+ >
285
+ > ```bash
286
+ > mjolnir rules --md
287
+ > ```
288
+ >
289
+ > หน้าต่อกฎอยู่ใต้ [`docs/rules/`](docs/rules/)
290
+
291
+ ### วัดไปแล้วเท่าไร
292
+
293
+ **74 จาก 99 กฎ มีอัตรา false positive ที่วัดกับโค้ด OSS จริง** (อย่างน้อย
294
+ 10 findings ที่จัดหมวดด้วยมือต่อกฎ; ดู
295
+ [docs/FP-AUDIT.md](docs/FP-AUDIT.md)) อีก 19 กฎออกมาบนการประเมินของผู้เขียน
296
+ ส่วนท้ายของทุกการสแกนบอกว่ากฎที่ _ยิง_ มีกี่กฎที่วัดแล้ว;
297
+ `mjolnir rules --unmeasured` แสดงกฎที่ยังไม่วัด; หน้า `mjolnir explain`
298
+ ของทุกกฎระบุสถานะ เราเผยแพร่อัตรานี้แม้มันจะน่าเกลียด — QA-CS-103 ตรวจได้
299
+ ที่ 95 % และถูกส่งไปกักกันเพราะเหตุนี้ การทำให้ตัวเลข 78 นั้นโตขึ้นคืองาน
300
+ ต่อเนื่องของโปรเจกต์
301
+
302
+ ### Tier ของกฎและความสุกงอมของภาษา
303
+
304
+ ทุกกฎเป็น `core`, `extended` หรือ `quarantine` กำหนดจากอัตรา false
305
+ positive **ที่วัดได้**:
306
+
307
+ | Tier | ความหมาย | สแกนปกติ | `--strict` |
308
+ | ------------ | ----------------------------------- | :------: | :--------: |
309
+ | `core` | ≤ 10 % FP ที่วัดได้ | ✅ | ✅ |
310
+ | `extended` | ≤ 30 % FP ที่วัดได้ | ✅ | ✅ |
311
+ | `quarantine` | สูงกว่า 30 % หรือยังไม่วัด (n < 10) | ❌ | ✅ |
312
+
313
+ | ภาษา | Adapter | ความครอบคลุมวันนี้ |
314
+ | --------------- | -------------- | ------------------------------------------------- |
315
+ | TypeScript / JS | AST คอมไพเลอร์ | กว้างสุด วัดมากสุด — ส่วนใหญ่ `core`/`extended` |
316
+ | Python / pytest | ชั้น regex | กว้าง ตรวจกับ corpus — ส่วนใหญ่ `core`/`extended` |
317
+ | Java | ชั้น regex | ใหม่กว่า — ส่วนใหญ่ `extended`/`quarantine` |
318
+ | C# / .NET | ชั้น regex | ใหม่กว่า — ส่วนใหญ่ `extended`/`quarantine` |
319
+
320
+ TypeScript และ Python มีความครอบคลุมที่วัดได้กว้างที่สุด Java และ C#
321
+ ปล่อยแล้ว มีเอกสาร และยังอยู่นอกตัวเลขพาดหัว จนกว่าชุดทดสอบผู้ใช้จริง
322
+ (ไม่ใช่ทดสอบของไลบรารี binding เอง) จะถูกตรวจ
323
+
324
+ ---
325
+
326
+ ## การให้คะแนนทำงานอย่างไร
327
+
328
+ <p align="center">
329
+ <img src="assets/readme/terminal-hero.svg" alt="ผลลัพธ์ terminal ของ Mjölnir — WORTHINESS 75/100 NEEDS WORK, การแจกแจงการวินิจฉัยตามหมวด และรายการ FIX THIS FIRST" width="820" />
330
+ </p>
331
+
332
+ <sub>สร้างใหม่ด้วย `npm run docs:hero`;
333
+ [`tests/hero-asset-reproducibility.spec.ts`](tests/hero-asset-reproducibility.spec.ts)
334
+ จะทำให้ CI ลม หากสินค้าเองเหลื่อมจากสิ่งที่ reporter พิมพ์จริง</sub>
335
+
336
+ คะแนนโปร่งใส: **error −8, warning −3, info −1** แล้ว normalize ด้วย
337
+ exposure ของชุดทดสอบ (การหักต่อการประกาศทดสอบ) การหักที่ถ่วงน้ำหนักด้วย
338
+ หลักฐาน หมายความว่าสัญญาณอ่อนแพงกว่า แสดงผลใน terminal ใช้ตัวเลขที่
339
+ ลดแล้วชุดเดียวกับที่คะแนนใช้ — ไม่มีกล่องดำ วิธีเต็ม:
340
+ [docs/SCORING.md](docs/SCORING.md)
341
+
342
+ **คำพิพากษา**
343
+
344
+ | Score | คำพิพากษา |
345
+ | ------- | ---------------- |
346
+ | ≥ 80 | ✓ **WORTHY** |
347
+ | 50 – 79 | ⚠ **NEEDS WORK** |
348
+ | < 50 | ✖ **UNWORTHY** |
349
+
350
+ **ระดับหลักฐาน** — ทุก finding พกระดับหนึ่ง; มันกำหนดน้ำหนักของ finding
351
+ ในคะแนน:
352
+
353
+ | ระดับ | ความหมาย | ผลต่อคะแนน | ตัวอย่าง |
354
+ | ----- | --------------------- | --------------------- | ----------------------------------------------------- |
355
+ | E2 | ข้อบกพร่องเชิงกำหนด | หักเต็มจำนวน | commit `.only` — พิสูจน์เชิงโครงสร้างได้ |
356
+ | E1 | รูปแบบเชิงเฮิร์ริสติก | หักครึ่งหนึ่ง | `sleep()` ที่ regex เจอ — สัญญาณแข็งแรง ไม่ใช่หลักฐาน |
357
+ | E0 | การสังเกต | ศูนย์ (info เท่านั้น) | รายงานแต่ไม่ gate CI และไม่หักเลย |
358
+
359
+ กฎส่วนใหญ่เป็น **E1** คำสโลแกน «we prove it» อ้างถึงระบบนี้: finding
360
+ E2 คือหลักฐานเชิงโครงสร้าง; finding E1 คือคำเตือนที่วางตำแหน่งถูกต้อง
361
+ ไม่ใช่หลักฐานทางการ
362
+
363
+ repo ว่างจะได้คะแนน `null` ไม่ใช่เลข 100 ปลอม — ดู
364
+ [โมเดลความไว้วางใจ](#โมเดลความไว้วางใจ)
365
+
366
+ ---
367
+
368
+ ## 🎭 Selector Health Score
369
+
370
+ ตัวชี้วัดพาดหัวสำหรับชุด Playwright — locator ของคุณทนทานแค่ไหน:
371
+
372
+ ```text
373
+ ▚▞ SELECTOR HEALTH — e2e/checkout.spec.ts
374
+
375
+ [█████████████████░░░] 83 / 100
376
+ role/text: 2 · testid: 1 · css-chains: 1 ⚠ · xpath: 0
377
+ ```
378
+
379
+ locator ที่อิง role ได้คะแนนเต็ม ห่วงโซ่ CSS class และ XPath จมคะแนน —
380
+ มันแตกทุกครั้งที่ refactor DOM โดยไม่บอกว่าพฤติกรรมใดถดถอย
381
+
382
+ ---
383
+
384
+ ## 🔬 หลักฐานระดับ runtime
385
+
386
+ การตรวจจับความไม่นิ่งแบบสถิตคือการเดา Mjölnir อ่าน **ข้อมูลการรันจริง** —
387
+ รายงาน JSON ของ Playwright และ XML ของ JUnit จาก runner ใดก็ได้:
388
+
389
+ ```bash
390
+ mjolnir forensics ./test-results/
391
+ ```
392
+
393
+ ```text
394
+ ▚▞ FLAKINESS LEADERBOARD
395
+
396
+ 3 tests · 1 failed · 1 flaky · 1 retried
397
+
398
+ TRUE-FLAKE completes checkout with saved card (e2e/checkout.spec.ts)
399
+ ████████████████████ 6.0s · 2 attempts
400
+ FAILING declines an expired card (e2e/checkout.spec.ts)
401
+ ████░░░░░░░░░░░░░░░░ 1.1s · 1 attempt
402
+ ```
403
+
404
+ ทดสอบที่ผ่านเฉพาะตั้งแต่ attempt ≥ 2 ไม่ใช่ทดสอบที่ผ่าน — มันเป็น
405
+ ทดสอบที่โชคดี มันถูกติดธง `TRUE-FLAKE` ไม่ว่าเครื่องหมายเขียวสุดท้าย
406
+ จะเป็นอย่างไร
407
+
408
+ ---
409
+
410
+ ## ⚡ Mjölnir ไม่ใช่ linter อีกตัว
411
+
412
+ Linter บอกว่าโค้ดทำตามกฎหรือไม่ Mjölnir บอกว่าการตรวจสอบของคุณ
413
+ น่าเชื่อถือหรือไม่
414
+
415
+ | | ESLint / SonarQube | เครื่องมือ coverage | รีวิวด้วยมือ | **Mjölnir** |
416
+ | ------------------------------------------------------------- | :----------------: | :-----------------: | :----------: | :---------: |
417
+ | ความสมบูรณ์ของ CI workflow (`continue-on-error`, `\|\| true`) | ❌ | ❌ | ไม่ค่อย | ✅ |
418
+ | ข้ามภาษา (TS, Python, Java, C#) จากเครื่องมือเดียว | ❌ | ❌ | ❌ | ✅ |
419
+ | ให้เกรดความทนทานของ locator Playwright (Selector Health) | ❌ | ❌ | ไม่ค่อย | ✅ |
420
+ | ติดธงทดสอบไม่มี assertion จริง | ✅ (ปลั๊กอิน)\* | ❌ | บางครั้ง | ✅ |
421
+ | จับ hard sleep (`waitForTimeout`, `time.sleep`) | ✅ (ปลั๊กอิน)\* | ❌ | บางครั้ง | ✅ |
422
+ | รันในไม่กี่วินาที ศูนย์การเรียกเครือข่ายระหว่างสแกน | ✅ | ✅ | — | ✅ |
423
+
424
+ \*`eslint-plugin-jest` (`expect-expect`) และ `eslint-plugin-playwright`
425
+ (`expect-expect`, `no-wait-for-timeout`) ครอบคลุมสิ่งเหล่านี้ให้ framework
426
+ ตามลัพธ์ของมัน
427
+
428
+ **การวิเคราะห์ runtime** เป็นหมวดหมู่แยกจากการ lint แบบสถิต:
429
+
430
+ | | Playwright retry reporter | Allure / ReportPortal | **Mjölnir forensics** |
431
+ | -------------------------------------------- | :-----------------------: | :-------------------: | :-------------------: |
432
+ | อ่านข้อมูลรันจริงเพื่อคำพิพากษา `TRUE-FLAKE` | บางส่วน\* | บางส่วน (tag) | ✅ |
433
+ | รายงาน triage ความไม่นิ่งจากประวัติการรัน | ❌ | ✅ | ✅ |
434
+ | เชื่อมกับคะแนนความน่าเชื่อถือแบบสถิต | ❌ | ❌ | ✅ |
435
+
436
+ \*Playwright ติดตาม retry ภายใน แต่ไม่ผลิตรายงานความไม่นิ่งแบบ
437
+ ยืนเดี่ยวพร้อมป้ายคำพิพากษา
438
+
439
+ ---
440
+
441
+ ## 🤖 ทำไมไม่ใช้แค่ AI code review?
442
+
443
+ ปัญหาต่างกัน ชั้นต่างกัน AI review เห็นการเปลี่ยนทดสอบที่น่าสงสัยใน
444
+ diff ได้ แต่มันไม่พิสูจน์ว่าระบบตรวจสอบทั้งหมดน่าไว้วางใจ — และมันเห็น
445
+ แค่ diff ที่คุณให้ดู
446
+
447
+ | | AI code review (Copilot ฯลฯ) | **Mjölnir** |
448
+ | ------------------------------------------- | :--------------------------: | :------------------------------------: |
449
+ | ต้นทุนต่อการสแกน | Tokens (ขยายตามขนาด diff) | **ศูนย์** (ทำงานในเครื่อง ติดตั้งแล้ว) |
450
+ | เห็นทั้งชุดทดสอบ + ทุก config CI | เฉพาะ PR diff ที่คุณให้ดู | **ทุกอย่าง ทุกครั้ง** |
451
+ | กำหนดตาย (input เดียวกัน → output เดียวกัน) | ❌ (ไม่กำหนดตาย) | **✅** |
452
+ | จับรูปแบบที่หลับมาหลายเดือน | เฉพาะถ้าอยู่ในบริบท | **✅** (สแกนทุกไฟล์) |
453
+ | จำ finding ข้ามการรัน | ❌ (ไม่มีความจำข้ามเซสชัน) | **✅** (baseline + diff) |
454
+ | รันโดยไม่ต้องมีคนสั่ง | ต้องมี PR หรือ prompt | **✅** (hook CI, 3 วินาที) |
455
+
456
+ **ใช้ทั้งสอง** AI เก็บรายละเอียดปลีกย่อย เจตนา และข้อบกพร่องเชิงออกแบบ
457
+ ที่ regex หาไม่เจอ Mjölnir เก็บรูปแบบเชิงโครงสร้างที่ AI มองข้ามเพราะ
458
+ มันดู «ตั้งใจ» — `.only` ที่ถูก commit, exit code ที่ถูกกลืน,
459
+ `continue-on-error` บน job ทดสอบ นี่ไม่ใช่บั๊กที่ต้องใช้การใคร่ครวญ;
460
+ นี่คือข้อเท็จจริงที่ต้องใช้การสแกน
461
+
462
+ ---
463
+
464
+ ## 🤖 การเชื่อมต่อ CI
465
+
466
+ คำสั่งเดียวสร้าง PR workflow — เป็นแบบที่ปรึกษาโดยดีฟอลต์ ไม่เคยบล็อก:
467
+
468
+ ```bash
469
+ mjolnir ci install
470
+ ```
471
+
472
+ หรือต่อเข้า GitHub Code Scanning แบบ native ผ่าน SARIF:
473
+
474
+ ```yaml
475
+ - run: npx mjolnir-qa@latest --format sarif > mjolnir.sarif
476
+ - uses: github/codeql-action/upload-sarif@v3
477
+ with:
478
+ sarif_file: mjolnir.sarif
479
+ ```
480
+
481
+ การตั้งค่า editor และ pipeline สำหรับ SARIF:
482
+ [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md)
483
+
484
+ ### ความครอบคลุมแบบ changed scope
485
+
486
+ `--scope changed` ผูก finding กับบรรทัดที่ branch คุณเพิ่ม เทียบกับ
487
+ merge-base กับ `main` ครอบคลุมไฟล์ทดสอบ (`*.spec.*`, `*.test.*`) บวก
488
+ ไฟล์ GitHub workflow และ config Playwright ใน diff เมื่อ merge-base
489
+ หาค่าไม่ได้ — shallow clone, detached HEAD, เป้าหมายไม่ใช่ git,
490
+ default branch ต่างกัน — มันลดรูปอย่างซื่อสัตย์: finding กลับไปผูก
491
+ ทั้งไฟล์ และรายงานก็บอก แทนที่ base ref ได้ด้วย `--base <ref>`
492
+
493
+ ---
494
+
495
+ ## การตั้งค่า
496
+
497
+ Mjölnir เป็น zero-config `mjolnir.config.json` (หรือ `.mjolnir.json`)
498
+ ทางเลือกที่ราก repo ปรับ severity, gating และ scope — ไม่เคยเปลี่ยน
499
+ ความหมายการตรวจจับ
500
+
501
+ | Key | ชนิด | ผล |
502
+ | ------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
503
+ | `exclude` | `string[]` | glob ignore เพิ่มเติม (ส่วนย่อยของ gitignore) บนค่าดีฟอลต์ที่มีให้ |
504
+ | `gate` | `"advisory" \| "error" \| "warning"` | ความรุนแรงระดับใดที่ออกด้วยเลขไม่ใช่ศูนย์ (ดีฟอลต์ `error`; `advisory` ไม่เคยบล็อก) |
505
+ | `severityOverrides` | `{ "<RULE-ID>": severity }` | จัดลำดับ finding ของกฎใหม่สำหรับ repo ของคุณ |
506
+ | `ignore` | `IgnoreEntry[]` | กด finding — **`reason` จำเป็น**; รายการหมดอายุใน 90 วัน (วันที่ `expires` ชัดเจน หรือเวลาแก้ไขล่าสุดของไฟล์ config สำหรับรายการที่ไม่ระบุ) |
507
+ | `plugins` | `string[]` | แพ็กเกจกฎบุคคลที่สาม (ดู [โมเดลความไว้วางใจ](#โมเดลความไว้วางใจ)) |
508
+
509
+ ```json
510
+ {
511
+ "gate": "error",
512
+ "exclude": ["legacy/**"],
513
+ "severityOverrides": { "QA-PW-118": "warning" },
514
+ "ignore": [
515
+ {
516
+ "ruleId": "QA-TEST-004",
517
+ "files": ["e2e/legacy-login.spec.ts"],
518
+ "reason": "Third-party widget needs a settle delay; tracked in JIRA-4821",
519
+ "expires": "2026-12-31"
520
+ }
521
+ ]
522
+ }
523
+ ```
524
+
525
+ - **`.mjolnirignore`** — ไฟล์สไตล์ gitignore เรียบ ๆ สำหรับยกเว้นพาธ
526
+ ภาษาเดียวกับ `exclude` ใช้มันสำหรับสัญญาณรบกวนเฉพาะเครื่อง; ใช้
527
+ `exclude` เมื่อรายการควรอยู่ใน version control ร่วมกับ config ที่เหลือ
528
+ - **CLI overrides** — `--strict` (รวมกฎกักกัน), `--width <cols>` และ
529
+ `--ascii` / `--no-ascii` (เรนเดอร์เทอร์มินัล), `--tone blunt`
530
+ (ข้อความตรงขึ้น), `--max-duration <sec>` (สแกนบางส่วนที่จำกัดเวลา)
531
+ - การกดกฎและวงจรชีวิตการเลิกใช้: [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md)
532
+
533
+ รายการ `ignore` ยังหล่อเลี้ยงคำสั่ง `mjolnir suppressions` แบบเดี่ยว
534
+ ซึ่งแสดงสิ่งที่ถูกกดอยู่ และเมื่อใดรายการแต่ละรายการหมดอายุ
535
+
536
+ ---
537
+
538
+ ## 📐 รหัสออก & สัญญา
539
+
540
+ แช่แข็ง — ปลอดภัยที่จะสร้างตรรกะ CI บน:
541
+
542
+ | รหัสออก | ความหมาย |
543
+ | ------- | ----------------------------------------------------- |
544
+ | `0` | สะอาด — ไม่มี finding ที่ระดับเกตหรือสูงกว่า |
545
+ | `1` | มี finding ที่ระดับเกตหรือสูงกว่า |
546
+ | `2` | สแกนบางส่วน (หมดงบเวลา, ไฟล์อ่านไม่ได้) — ไม่เคยบล็อก |
547
+ | `10` | ใช้งานผิด (flag ผิด, ไม่ระบุเป้าหมาย) |
548
+ | `20` | ข้อผิดพลาดภายใน |
549
+
550
+ รายงาน JSON/SARIF คือ `schemaVersion: 1` rule ID (`QA-<FAMILY>-NNN`)
551
+ หลังปล่อยแล้วเปลี่ยนไม่ได้ และไม่เคยถูกนำกลับมาใช้
552
+
553
+ ---
554
+
555
+ ## โมเดลความไว้วางใจ
556
+
557
+ - **Local-first** — ศูนย์การเรียกเครือข่ายระหว่างสแกน เด็ดขาด ศูนย์
558
+ เทเลเมทรี
559
+ - **ไม่มีหลักฐานปลอม** — เราพูด «ไม่รู้» มากกว่า «ตรวจแล้ว» repo ว่าง
560
+ ได้ `score: null` ไม่ใช่ 100 ปลอม
561
+ - **ความซื่อสัตย์บางส่วน** — ถ้าการวิเคราะห์ถูกตัดสั้น ผลลัพธ์บอก
562
+ ไม่เคย «เสร็จ» เมื่อไม่ได้เสร็จ
563
+ - **กำแพง FP** — การตรวจจับทำงานบนมุมมองโค้ดที่ปราศจากคอมเมนต์/สตริง
564
+ (กฎ TypeScript ใช้ AST คอมไพเลอร์): รูปแบบในคอมเมนต์ร้อยเรียงหรือ
565
+ สตริงตัวอย่างเอกสาร คือเอกสาร ไม่ใช่ finding
566
+ - **วัด ไม่ใช่อ้าง** — เฉพาะกฎที่มีอัตรา false positive จากโค้ด OSS จริง
567
+ จึงอยู่ใน tier พาดหัว (ดู [วัดไปแล้วเท่าไร](#วัดไปแล้วเท่าไร));
568
+ ส่วนท้ายการสแกนและ `mjolnir rules --unmeasured` บอกว่ากฎไหนสถานะไหน
569
+ - **ความไว้วางใจต่อปลั๊กอิน** — ปลั๊กอินคือแพ็กเกจ npm ประกาศใต้
570
+ `"plugins"` **ไม่มี sandbox**: โค้ดปลั๊กอินรันด้วยสิทธิ์ Node เต็ม
571
+ โมเดลความไว้วางใจเดียวกับปลั๊กอิน ESLint หรือ Vitest คำนำหน้า rule ID
572
+ ของ core สงวนไว้และถูกปฏิเสธจากปลั๊กอิน เพื่อกันการอ้างปลอม
573
+ - **กฎภายนอกประจำ workspace** (อิงโฟลเดอร์ ศูนย์เครือข่าย) — ไดเรกทอรี
574
+ `mjolnir-rules/` ติดกับเป้าสแกนโหลดกฎกำหนดเอง: ไฟล์ JSON ประกาศรูปแบบ
575
+ regex (ไม่รันโค้ด) โมดูล `.mjs`/`.js` export `rules` (ความไว้วางใจ
576
+ Node เต็ม เหมือนปลั๊กอิน) กฎภายนอกพก trust metadata เดียวกับ core;
577
+ ไม่เคยขึ้นไปใน tier core (core ต้องมีอัตรา FP ที่วัดจาก sidecar
578
+ corpus — `tier: "core"` ที่ประกาศมาถูกบีบลงเหลือ `extended`),
579
+ ทำตามเพดาน tier และถูกตรวจเรื่องเหลื่อมล้ำ: `mjolnir rules --md
580
+ --external` เรนเดอร์แคตตาล็อกจากไฟล์ที่โหลด (แหล่งที่มา `external`)
581
+ และตัวสร้างเมทริกซ์รับ `--external <root>`
582
+
583
+ ---
584
+
585
+ ## 🏗️ สถาปัตยกรรม
586
+
587
+ <details>
588
+ <summary>ขยายแผนผัง</summary>
589
+
590
+ ```
591
+ mjolnir/
592
+ ├── src/
593
+ │ ├── engine/ # LanguageAdapter interface + rule runner
594
+ │ ├── adapters/ # typescript · python · java · csharp · github-actions
595
+ │ ├── rules/ # rules across 8 families + the measured-FP table
596
+ │ ├── playwright/ # Selector Health Score engine
597
+ │ ├── discovery/ # workspace, frameworks, ignore resolution
598
+ │ ├── scope/ # git merge-base changed-scope engine
599
+ │ ├── scorer/ # transparent deduction table + prioritization
600
+ │ ├── reporter/ # terminal · JSON · SARIF 2.1 · Mermaid
601
+ │ ├── forensics/ # run-data ingestion · flake verdicts · triage
602
+ │ ├── config/ # mjolnir.config.json + suppressions
603
+ │ ├── plugins/ # third-party rule loading (no sandbox)
604
+ │ └── commands/ # every subcommand
605
+ └── tests/
606
+ ├── fixtures/ # must-fire / must-not-fire per rule
607
+ └── golden/ # frozen score regression locks
608
+ ```
609
+
610
+ </details>
611
+
612
+ - **กฎเป็นฟังก์ชันบริสุทธิ์** — `(SourceFileContext) → Finding[]`,
613
+ ไม่มี I/O ไม่มี global เพิ่มระบบนิเวศใหม่ = หนึ่ง adapter + กฎของมัน
614
+ - **TypeScript/Playwright ใช้ AST คอมไพเลอร์** (ts-morph) Python, Java
615
+ และ C# รันบนชั้น regex ร่วมกันที่ปิดบังคอมเมนต์/สตริง
616
+ - ชั้น AST tree-sitter WASM สำหรับ Java และ C# มีอยู่และเป็นก้าวความ
617
+ แม่นยำถัดไป — ยังไม่ได้เสียบเข้ากับ pipeline สแกนแบบซิงโครนัส
618
+
619
+ ---
620
+
621
+ ## 📚 เอกสาร
622
+
623
+ | เอกสาร | มีอะไรในนั้น |
624
+ | ------------------------------------------------------ | ----------------------------------------------- |
625
+ | [docs/SCORING.md](docs/SCORING.md) | การ normalize คะแนน + การถ่วงน้ำหนักด้วยหลักฐาน |
626
+ | [docs/FP-AUDIT.md](docs/FP-AUDIT.md) | อัตรา false positive ที่วัดได้ + วิธีวัด |
627
+ | [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md) | สถานะกฎ การกด การเลิกใช้ |
628
+ | [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md) | ผลลัพธ์ SARIF + การตั้งค่า editor/CI |
629
+ | [docs/rules/](docs/rules/) | แคตตาล็อกต่อกฎที่สร้างอัตโนมัติ |
630
+ | [CONTRIBUTING.md](CONTRIBUTING.md) | การตั้งค่า dev + ขั้นตอนการร่วมพัฒนา |
631
+ | [CHANGELOG.md](CHANGELOG.md) | ประวัติการเผยแพร่ |
632
+ | [SECURITY.md](SECURITY.md) | รายงานช่องโหว่ |
633
+
634
+ ---
635
+
636
+ ## 📈 สถานะ
637
+
638
+ **v0.5.x · โอเพนเบตา** JSON schema และรหัสออกเป็นสัญญาแช่แข็ง
639
+ TypeScript และ Python มีความครอบคลุมที่วัดได้กว้างสุด; Java และ C#
640
+ ใหม่กว่า — อ่านผ่าน
641
+ [ตาราง tier](#tier-ของกฎและความสุกงอมของภาษา)
642
+
643
+ ---
644
+
645
+ ## 🤝 ร่วมพัฒนา
646
+
647
+ กฎใหม่คือรายการแรกที่ง่ายที่สุด — หนึ่งคำสั่งสร้างโครงกฎพร้อม fixture
648
+ must-fire **และ** must-not-fire (กฎที่สร้างให้จงใจล้มเหลวบน fixture
649
+ จนกว่าคุณจะเขียนการตรวจจับจริง — stub ปล่อยไม่ได้):
650
+
651
+ ```bash
652
+ mjolnir create-rule QA-PW-140 --title "Screenshot without diff bound"
653
+ ```
654
+
655
+ การตั้งค่า dev เต็มรูปแบบ คำสั่ง standing gate และกฎหมาย anti-creep /
656
+ กำแพง fixture อยู่ใน [CONTRIBUTING.md](CONTRIBUTING.md)
657
+
658
+ ---
659
+
660
+ <div align="center">
661
+
662
+ **หยุดปล่อยการทดสอบที่คุณไว้วางใจไม่ได้**
663
+
664
+ ```bash
665
+ npx mjolnir-qa@latest
666
+ ```
667
+
668
+ **Star ⭐ · Watch 👀 · Contribute 🤝**
669
+
670
+ สร้างโดย [Sergey Bar](https://www.linkedin.com/in/sergeybar/)
671
+
672
+ </div>