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.bs.md ADDED
@@ -0,0 +1,690 @@
1
+ <div align="center">
2
+
3
+ <img src="assets/readme/logo.png" alt="Mjölnir — Verification Trust Engine" width="800" />
4
+
5
+ ### Tvoji testovi lažu. Mi to dokazujemo.
6
+
7
+ **Verification Trust Engine za QA.** Mjölnir audita test suite-ove i CI
8
+ pipeline-ove, izvještava ocjenjivački rezultat i pokazuje tačno gdje se
9
+ povjerenje lomi.
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) | [ไทย](README.th.md) | [Türkçe](README.tr.md) | [Українська](README.uk.md) | [বাংলা](README.bn.md) | [Ελληνικά](README.gr.md) | [Tiếng Việt](README.vi.md) | [עברית](README.he.md) | [العربية](README.ar.md) | Bosanski
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
+ **Jesu li tvoji testovi dostojni povjerenja?**
25
+
26
+ [Vidi ga na djelu](#-vidi-ga-na-djelu) ·
27
+ [Brzi početak](#-brzi-početak) ·
28
+ [Šta provjerava](#-šta-mjölnir-provjerava) ·
29
+ [Bodovanje](#kako-radi-bodovanje) ·
30
+ [CI](#-ci-integracija) · [Konfiguracija](#konfiguracija) ·
31
+ [Dokumentacija](#-dokumentacija)
32
+
33
+ </div>
34
+
35
+ ---
36
+
37
+ ## 🎬 Vidi ga na djelu
38
+
39
+ <p align="center">
40
+ <img src="assets/readme/demo.svg" alt="Kompletan --verbose izvještaj Mjölnira nad demo repoom: WORTHINESS 75/100 NEEDS WORK, dijagnostika po kategorijama, lista FIX THIS FIRST i svaki nalaz sa ID-om pravila i brojem linije kroz CI, Playwright, test higijenu i Python pravila" width="900" />
41
+ </p>
42
+
43
+ <sub>Cjelokupan izlaz `npx mjolnir-qa ./examples/demo-repo --verbose`,
44
+ renderiran od pravog reportera — ništa skraćeno. Regenerira se preko
45
+ `npm run docs:demo`;
46
+ [`tests/demo-asset-reproducibility.spec.ts`](tests/demo-asset-reproducibility.spec.ts)
47
+ obara CI ako artefakt odskoči od onoga što alat ispisuje.</sub>
48
+
49
+ **Šta se upravo desilo:**
50
+
51
+ 1. Mjölnir je pronašao Playwright specifikacije, svoju konfiguraciju,
52
+ CI workflow i Python test fajl — četiri jezika/formata, jedan prolaz.
53
+ 2. Pronašao je dokaze koji slabe povjerenje u suite — `continue-on-error`
54
+ koji maskira job, `|| true` koji guta exit kod, tvrdi sleepovi,
55
+ krhki selektor, ugrađene staging URL-ove, `networkidle` čekanje.
56
+ 3. Svaki je pretvorio u konkretan nalaz s ID-om pravila, lokacijom i
57
+ fixom — i u jedan rezultat na koji možeš gate-ovati PR.
58
+
59
+ ### Jedan nalaz izbliza
60
+
61
+ Pokreni `mjolnir explain QA-CI-001` na prvom nalaženom iznad i dobiješ:
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
+ To je jedinica vrijednosti: ne sitnica stila, nego mjesto gdje ti CI
83
+ poručuje da je nešto prošlo, a nije prošlo.
84
+
85
+ ---
86
+
87
+ ## ⚡ Brzi početak
88
+
89
+ Pokreni ga nad repoom za kompletan izvještaj i ocjenjivački rezultat:
90
+
91
+ ```bash
92
+ npx mjolnir-qa@latest
93
+ ```
94
+
95
+ **U CI je proizvod jedna komanda.** Skenira samo ono što je grana
96
+ dotakla i izlazi s ne-nula kodom kod novih problema:
97
+
98
+ ```bash
99
+ npx mjolnir-qa@latest --scope changed
100
+ ```
101
+
102
+ Ubaci to kao check u PR — `mjolnir ci install` piše workflow — i
103
+ gotovo. Sve ostalo je opciono.
104
+
105
+ | Komanda | Šta radi |
106
+ | ----------------------------------- | ------------------------------------------------------- |
107
+ | `mjolnir` | Sken cijelog repoa + ocjenjivački rezultat |
108
+ | `mjolnir --scope changed` | Samo ono što je tvoja grana unijela — CI oblik |
109
+ | `mjolnir ci install` | Generiše savjetodavni PR workflow |
110
+ | `mjolnir explain QA-CI-001` | Šta / zašto / fix + izmjerena FP stopa za jedno pravilo |
111
+ | `mjolnir rules --unmeasured` | Pravila koja rade pretpostavkom, a ne mjerenjem |
112
+ | `mjolnir --json` / `--format sarif` | Mašinski čitljivo / GitHub Code Scanning |
113
+ | `mjolnir --strict` | Pokreće i pravila quarantine tier-a (veći rizik FP) |
114
+
115
+ <details>
116
+ <summary><strong>Kad je nešto flaky</strong></summary>
117
+
118
+ | Komanda | Šta radi |
119
+ | ----------------------------------- | -------------------------------------------------------- |
120
+ | `mjolnir forensics ./test-results/` | Stvarni podaci runova → presude `TRUE-FLAKE`, `FLAKY.md` |
121
+ | `mjolnir triage ./test-results/` | Prijedlog karantene iz historije izvršavanja |
122
+ | `mjolnir pw-report ./test-results/` | Sažetak Playwright runa — retry / flake / najsporiji |
123
+ | `mjolnir doctor:playwright` | Dubinski skan samo Playwright + Selector Health Score |
124
+
125
+ </details>
126
+
127
+ <details>
128
+ <summary><strong>Povremeno / izvještaji</strong></summary>
129
+
130
+ | Komanda | Šta radi |
131
+ | ------------------------------- | ------------------------------------------------- |
132
+ | `mjolnir fix --dry-run` / `fix` | Sigurne automatske popravke s dokazom |
133
+ | `mjolnir baseline` / `diff` | Snimak nalaza, pa izvještaj samo novih/pogoršanih |
134
+ | `mjolnir impact --since <ref>` | Šta se promijenilo od ranijeg commita |
135
+ | `mjolnir debt` | Registarr testnog duga s modelom troška |
136
+ | `mjolnir handover` | Karta onboardingu suite-a za novog QA |
137
+ | `mjolnir stats` | Lokalni ukupni brojači viđenih fixova |
138
+ | `mjolnir badge` | shields.io endpoint JSON + snippet |
139
+ | `mjolnir rules --md` | Potpuni katalog pravila (JSON ili Markdown) |
140
+ | `mjolnir doctor` | Samoaudit Mjölnirove vlastite baze pravila |
141
+ | `mjolnir create-rule <ID>` | Scafholduje novo pravilo + fixture |
142
+ | `mjolnir --format mermaid` | Dijagram test arhitekture za PR komentar |
143
+
144
+ </details>
145
+
146
+ Instaliraj globalno umjesto `npx` ako preferiraš: `npm i -g mjolnir-qa`.
147
+ Zahtijeva Node.js ≥ 22.18. Radi na Windows, macOS i Linux.
148
+
149
+ ---
150
+
151
+ ## 👥 Za koga je ovo?
152
+
153
+ - **QA / SDET** koji drže e2e ili integracioni suite i trebaju dokaz da
154
+ suite stvarno zaslužuje zeleni check koji proizvodi.
155
+ - **Platform / DevEx timovi** odgovorni za CI integritet i release
156
+ gateove — ljudi kojima je stalo da `continue-on-error` nikad ne
157
+ preboji crveni pipeline u zeleni u tišini.
158
+ - **OSS maintaineri** koji žele jeftin, uvijek uključen verifikacioni
159
+ gate koji radi lokalno i u CI bez mrežnih poziva.
160
+
161
+ ---
162
+
163
+ ## 🔨 Šta Mjölnir provjerava
164
+
165
+ | | |
166
+ | --- | --------------------------------------------------------------------------------------------------------------------- |
167
+ | ⚖️ | **Ocjenjivački rezultat** — jedan broj, transparentna tabela odbitaka, bez crne kutije |
168
+ | 🎭 | **Selector Health Score** — ocjenjuje tvoje Playwright locatore, ne samo prolaznost |
169
+ | 🔬 | **Runtime forenzika** — čita stvarne Playwright/JUnit podatke runova i hvata `TRUE-FLAKE`, ne samo statičke nagađanja |
170
+ | 🚨 | **Pravila CI integriteta** — hvata `continue-on-error`, `\|\| true` i druge trikove lažno zelenog |
171
+ | 🐍 | **Sva četiri Playwright bindinga** — TypeScript, Python, Java, C#/.NET — plus pytest, JUnit/TestNG i CI workflowi |
172
+ | 🔒 | **Local-first** — nula mrežnih poziva pri skeniranju, nula telemetrije, radi u sekundama |
173
+
174
+ ### Pravila
175
+
176
+ Svako pravilo dolazi s must-fire **i** must-not-fire fixtureima.
177
+ Pravilo koje okida na svojoj negativnoj fixture ne može se isporučiti —
178
+ to je vatrozid lažnih pozitiva.
179
+
180
+ <details>
181
+ <summary><strong>Test higijena</strong></summary>
182
+
183
+ | ID | Pravilo | Severity |
184
+ | ----------- | ---------------------------------------------------- | -------- |
185
+ | QA-TEST-001 | Commitiran fokusirani test (`.only`, `fit`) | error |
186
+ | QA-TEST-002 | Preskočen test bez opravdanja | error |
187
+ | QA-TEST-002 | Preskočen test s evidentiranim opravdanjem | warning |
188
+ | QA-TEST-003 | Test bez asercija | error |
189
+ | QA-TEST-004 | Tvrdi sleep (`waitForTimeout`, `sleep()`, `delay()`) | warning |
190
+ | QA-TEST-006 | Zloupotreba retrya koja krije flakiness | warning |
191
+ | QA-TEST-010 | Prazno tijelo testa | error |
192
+
193
+ </details>
194
+
195
+ <details>
196
+ <summary><strong>Kvalitet testova</strong></summary>
197
+
198
+ | ID | Pravilo | Severity |
199
+ | ------------ | ----------------------------- | -------- |
200
+ | QA-TQUAL-001 | Verifikacija samo mockovima | info |
201
+ | QA-TQUAL-002 | Tautološka asercija | error |
202
+ | QA-TQUAL-009 | Asercija neawaitanog promisea | error |
203
+ | QA-TQUAL-011 | Komentarisani testovi | warning |
204
+
205
+ </details>
206
+
207
+ <details>
208
+ <summary><strong>Playwright 🎭</strong></summary>
209
+
210
+ | ID | Pravilo | Severity |
211
+ | --------- | ------------------------------------------ | -------- |
212
+ | QA-PW-002 | Asercija lokatora bez awaita | error |
213
+ | QA-PW-003 | `page.pause()` / `test.only()` commitirani | error |
214
+ | QA-PW-004 | Krhki CSS/XPath selektori | warning |
215
+ | QA-PW-005 | Poslovna logika unutar `page.evaluate()` | info |
216
+ | QA-PW-114 | Legacy element handleovi (`page.$`) | info |
217
+ | QA-PW-118 | `networkidle` čekanja (flaky po dizajnu) | info |
218
+ | QA-PW-123 | Ugrađeni URL-ovi okruženja | warning |
219
+
220
+ </details>
221
+
222
+ <details>
223
+ <summary><strong>CI integritet</strong></summary>
224
+
225
+ | ID | Pravilo | Severity |
226
+ | --------- | --------------------------------------------------------------- | -------- |
227
+ | QA-CI-001 | `continue-on-error` maskira padove | error |
228
+ | QA-CI-002 | `\|\| true` guta exit kodove | error |
229
+ | QA-CI-005 | Izvještaj se troši ali nikad ne generira | error |
230
+ | QA-CI-007 | Retry omotači oko testova | warning |
231
+ | QA-CI-008 | Uvijek uspješan step maskira padove | error |
232
+ | QA-CI-009 | Exit kod testa se ne propagira (`\|` bez pipefail, `;` lanci) | error |
233
+ | QA-CI-010 | Testovi preskaču se gdje moraju blokirati (skip-on-PR guardovi) | error |
234
+
235
+ </details>
236
+
237
+ <details>
238
+ <summary><strong>Python / pytest 🐍</strong></summary>
239
+
240
+ | ID | Pravilo | Severity |
241
+ | --------- | ------------------------------------------ | -------- |
242
+ | QA-PY-002 | Preskočen test (`skip`, nestrogi `xfail`) | warning |
243
+ | QA-PY-003 | Test funkcija bez asercija | error |
244
+ | QA-PY-005 | `time.sleep()` u testovima | warning |
245
+ | QA-PY-006 | Prazno tijelo testa (`pass`) | info |
246
+ | QA-PY-010 | Ovisnost o slučajnosti/vremenu bez freezea | info |
247
+ | QA-PY-012 | Tautološka asercija | error |
248
+
249
+ Ukupno 20 Python pravila (QA-PY-001…012 pytest higijena + QA-PY-101…108 Playwright-Python).
250
+
251
+ </details>
252
+
253
+ <details>
254
+ <summary><strong>Java / JUnit · TestNG ☕</strong></summary>
255
+
256
+ | ID | Pravilo | Severity |
257
+ | --------- | ----------------------------------------- | -------- |
258
+ | QA-JV-101 | Onemogućen test (`@Disabled`) | warning |
259
+ | QA-JV-102 | Tvrdi sleep (`Thread.sleep()`) | warning |
260
+ | QA-JV-103 | Test metoda bez asercija | error |
261
+ | QA-JV-105 | Tvrdi sleep Playwright `waitForTimeout()` | warning |
262
+ | QA-JV-106 | Krhki selektor umjesto role lokatora | warning |
263
+ | QA-JV-108 | Ugrađeni URL okruženja u testu | info |
264
+ | QA-JV-111 | Pokrivni mock `page.route("**")` | info |
265
+
266
+ </details>
267
+
268
+ <details>
269
+ <summary><strong>C# / .NET — NUnit · xUnit · MSTest 🟣</strong></summary>
270
+
271
+ | ID | Pravilo | Severity |
272
+ | --------- | -------------------------------------------- | -------- |
273
+ | QA-CS-101 | Preskočen test (`[Ignore]`, `[Fact(Skip=)]`) | warning |
274
+ | QA-CS-102 | Tvrdi sleep (`Thread.Sleep` / `Task.Delay`) | warning |
275
+ | QA-CS-103 | Test metoda bez asercija | error |
276
+ | QA-CS-105 | Tvrdi sleep `WaitForTimeoutAsync()` | warning |
277
+ | QA-CS-106 | Krhki selektor umjesto role lokatora | warning |
278
+ | QA-CS-108 | Ugrađeni URL okruženja u testu | info |
279
+ | QA-CS-111 | Pokrivni mock `page.RouteAsync("**")` | info |
280
+
281
+ </details>
282
+
283
+ > Potpuni živi katalog — svako pravilo s tierom, confidence, rizikom
284
+ > lažnih pozitiva i dostupnošću autofixa — generira se iz registra:
285
+ >
286
+ > ```bash
287
+ > mjolnir rules --md
288
+ > ```
289
+ >
290
+ > Stranice po pravilu žive u [`docs/rules/`](docs/rules/).
291
+
292
+ ### Koliko je od ovoga izmjereno
293
+
294
+ **74 od 99 pravila nose stopu lažnih pozitiva izmjerenu nad stvarnim
295
+ OSS kodom** (≥ 10 ručno klasificiranih nalaza svako; vidi
296
+ [docs/FP-AUDIT.md](docs/FP-AUDIT.md)). Ostalih 19 izlazi na autorovoj
297
+ procjeni. Podnožje svakog skana kaže koliko od _okinutih_ pravila je
298
+ izmjereno; `mjolnir rules --unmeasured` izlista neizmjerena; stranica
299
+ `mjolnir explain` svakog pravila navodi njen status. Objavljujemo stopu
300
+ čak i kad je ružna — QA-CS-103 se audita na 95 % i u karanteni je radi
301
+ toga. Rast tog 78 je neprekidni rad projekta.
302
+
303
+ ### Tierovi pravila i jezična zrelost
304
+
305
+ Svako pravilo je `core`, `extended` ili `quarantine`, dodijeljeno prema
306
+ njegovoj **izmjerenij** stopi lažnih pozitiva:
307
+
308
+ | Tier | Značenje | Zadani skan | `--strict` |
309
+ | ------------ | ---------------------------------------- | :---------: | :--------: |
310
+ | `core` | ≤ 10 % izmjerena FP | ✅ | ✅ |
311
+ | `extended` | ≤ 30 % izmjerena FP | ✅ | ✅ |
312
+ | `quarantine` | iznad 30 %, ili još neizmjereno (n < 10) | ❌ | ✅ |
313
+
314
+ | Jezik | Adapter | Pokrivenost danas |
315
+ | --------------- | -------------- | --------------------------------------------------------- |
316
+ | TypeScript / JS | AST kompajlera | najšira, najviše mjerena — pretežno `core`/`extended` |
317
+ | Python / pytest | Regex sloj | široka, auditrana na korpusu — pretežno `core`/`extended` |
318
+ | Java | Regex sloj | novija — pretežno `extended`/`quarantine` |
319
+ | C# / .NET | Regex sloj | novija — pretežno `extended`/`quarantine` |
320
+
321
+ TypeScript i Python imaju najširu izmjerenu pokrivenost. Java i C# su
322
+ isporučeni, dokumentirani i ostaju izvan glavnog broja dok se prava
323
+ korisnička suite (ne vlastiti testovi binding biblioteke) ne audita.
324
+
325
+ ---
326
+
327
+ ## Kako radi bodovanje
328
+
329
+ <p align="center">
330
+ <img src="assets/readme/terminal-hero.svg" alt="Terminalni izlaz Mjölnira — WORTHINESS 75/100 NEEDS WORK, dijagnostika po kategorijama i lista FIX THIS FIRST" width="820" />
331
+ </p>
332
+
333
+ <sub>Regenerira se preko `npm run docs:hero`;
334
+ [`tests/hero-asset-reproducibility.spec.ts`](tests/hero-asset-reproducibility.spec.ts)
335
+ obara CI ako artefakt odskoči od onoga što reporter stvarno ispisuje.</sub>
336
+
337
+ Bodovanje je transparentno: **error −8, warning −3, info −1**, pa
338
+ normalizirano izloženošću suite-a (odbitci po deklaraciji testa).
339
+ Odbitci ponderirani dokazima znače da slabi signali koštaju manje.
340
+ Terminal pokazuje iste popustljive brojeve koje koristi bodovanje —
341
+ bez crne kutije. Puna metoda: [docs/SCORING.md](docs/SCORING.md).
342
+
343
+ **Presude**
344
+
345
+ | Score | Presuda |
346
+ | ------- | ---------------- |
347
+ | ≥ 80 | ✓ **WORTHY** |
348
+ | 50 – 79 | ⚠ **NEEDS WORK** |
349
+ | < 50 | ✖ **UNWORTHY** |
350
+
351
+ **Nivoi dokaza** — svaki nalaz nosi jedan; on postavlja težinu nalaza u
352
+ bodovanju:
353
+
354
+ | Nivo | Značenje | Utjecaj na bodovanje | Primjer |
355
+ | ---- | ---------------------- | -------------------- | -------------------------------------------------- |
356
+ | E2 | Deterministički defekt | Potpuni odbitak | Commitirani `.only` — strukturno dokazivo |
357
+ | E1 | Heuristički obrazac | Polovina odbitka | Regexom pogođen `sleep()` — jak signal, nije dokaz |
358
+ | E0 | Zapažanje | Nula (samo info) | Prijavljeno ali nikad ne gate-uje CI niti odbija |
359
+
360
+ Većina pravila je **E1**. Slogan „we prove it" odnosi se na ovaj
361
+ sistem: E2 nalazi su strukturni dokaz; E1 nalazi su ispravno
362
+ pozicionirana upozorenja, ne formalni dokazi.
363
+
364
+ Prazan repo boduje `null`, nikad lažnih 100 — vidi
365
+ [Model povjerenja](#model-povjerenja).
366
+
367
+ ---
368
+
369
+ ## 🎭 Selector Health Score
370
+
371
+ Vodeća metrika za Playwright suiteove — koliko su otporni tvoji
372
+ lokatori:
373
+
374
+ ```text
375
+ ▚▞ SELECTOR HEALTH — e2e/checkout.spec.ts
376
+
377
+ [█████████████████░░░] 83 / 100
378
+ role/text: 2 · testid: 1 · css-chains: 1 ⚠ · xpath: 0
379
+ ```
380
+
381
+ Role-bazirani lokatori osvajaju pun bodovni rezultat. CSS lanac klasa i
382
+ XPath tone rezultat — lome se na svakom DOM refactoru ne govoreći ti
383
+ koje ponašanje je regresiralo.
384
+
385
+ ---
386
+
387
+ ## 🔬 Runtime dokazi
388
+
389
+ Statička detekcija flakinessa je nagađanje. Mjölnir čita **stvarne
390
+ podatke izvršavanja** — Playwright JSON izvještaje i JUnit XML od
391
+ bilo kojeg runnera:
392
+
393
+ ```bash
394
+ mjolnir forensics ./test-results/
395
+ ```
396
+
397
+ ```text
398
+ ▚▞ FLAKINESS LEADERBOARD
399
+
400
+ 3 tests · 1 failed · 1 flaky · 1 retried
401
+
402
+ TRUE-FLAKE completes checkout with saved card (e2e/checkout.spec.ts)
403
+ ████████████████████ 6.0s · 2 attempts
404
+ FAILING declines an expired card (e2e/checkout.spec.ts)
405
+ ████░░░░░░░░░░░░░░░░ 1.1s · 1 attempt
406
+ ```
407
+
408
+ Test koji prolazi tek od pokušaja ≥ 2 nije prolazni test — to je
409
+ sretan test. Označava se kao `TRUE-FLAKE` bez obzira na konačni zeleni
410
+ check.
411
+
412
+ ---
413
+
414
+ ## ⚡ Mjölnir nije još jedan linter
415
+
416
+ Linteri ti kažu prati li kod pravila. Mjölnir ti kaže može li se tvojoj
417
+ verifikaciji vjerovati.
418
+
419
+ | | ESLint / SonarQube | Coverage alati | Ručni review | **Mjölnir** |
420
+ | --------------------------------------------------------- | :----------------: | :------------: | :----------: | :---------: |
421
+ | CI workflow integritet (`continue-on-error`, `\|\| true`) | ❌ | ❌ | rijetko | ✅ |
422
+ | Unakrsno-jezično (TS, Python, Java, C#) iz jednog alata | ❌ | ❌ | ❌ | ✅ |
423
+ | Ocjenjuje otpornost Playwright lokatora (Selector Health) | ❌ | ❌ | rijetko | ✅ |
424
+ | Označava testove bez pravih asercija | ✅ (plugin)\* | ❌ | ponekad | ✅ |
425
+ | Hvata tvrde sleepove (`waitForTimeout`, `time.sleep`) | ✅ (plugin)\* | ❌ | ponekad | ✅ |
426
+ | Radi u sekundama, nula mrežnih poziva pri skeniranju | ✅ | ✅ | — | ✅ |
427
+
428
+ \*`eslint-plugin-jest` (`expect-expect`) i `eslint-plugin-playwright`
429
+ (`expect-expect`, `no-wait-for-timeout`) pokrivaju ovo za svoje
430
+ frameworkove.
431
+
432
+ **Runtime analiza** je odvojena kategorija od statičkog lintanja:
433
+
434
+ | | Playwright retry reporter | Allure / ReportPortal | **Mjölnir forensics** |
435
+ | --------------------------------------------------- | :-----------------------: | :-------------------: | :-------------------: |
436
+ | Čita stvarne podatke runova za presude `TRUE-FLAKE` | djelimično\* | djelimično (tag) | ✅ |
437
+ | Izvještaj flaky triaže iz historije izvršavanja | ❌ | ✅ | ✅ |
438
+ | Integrira se sa statičkim ocjenjivačkim rezultatom | ❌ | ❌ | ✅ |
439
+
440
+ \*Playwright interno prati retryje ali ne proizvodi samostalan izvještaj
441
+ flakinessa s oznakama presuda.
442
+
443
+ ---
444
+
445
+ ## 🤖 Zašto ne koristiti samo AI code review?
446
+
447
+ Drugi problem, drugi sloj. AI review može primijetiti sumnjivu promjenu
448
+ testa u diffu; ne dokazuje da je verifikacioni sistem kao cjelina
449
+ dostojan povjerenja — i vidi samo diff koji mu pokažeš.
450
+
451
+ | | AI code review (Copilot i sl.) | **Mjölnir** |
452
+ | ---------------------------------------- | :------------------------------: | :-----------------------------: |
453
+ | Trošak po skanu | Tokeni (raste s veličinom diffa) | **Nula** (lokalno, instalirano) |
454
+ | Vidi cijeli suite + sve CI konfiguracije | Samo PR diff koji pokažeš | **Sve, svaki put** |
455
+ | Deterministički (isti ulaz → isti izlaz) | ❌ (nedeterministički) | **✅** |
456
+ | Hvata obrasce dormantne mjesecima | Samo ako je u kontekstu | **✅** (skenira sve fajlove) |
457
+ | Pamti nalaze između runova | ❌ (nema memorije između sesija) | **✅** (baseline + diff) |
458
+ | Radi bez ljudskog okidača | Treba PR ili prompt | **✅** (CI hook, 3 sekunde) |
459
+
460
+ **Koristi oba.** AI hvata nijansu, namjeru i dizajnerske mane koje
461
+ nijedan regex ne nađe. Mjölnir hvata strukturne obrasce koje AI
462
+ zaobilazi jer izgledaju „namjerno" — commitirani `.only`, progutani
463
+ exit kod, `continue-on-error` na test jobu. To nisu bugovi koji trebaju
464
+ razmišljanje; to su činjenice koje trebaju skeniranje.
465
+
466
+ ---
467
+
468
+ ## 🤖 CI integracija
469
+
470
+ Jedna komanda generira PR workflow — po defaultu savjetodavan, nikad
471
+ blokirajući:
472
+
473
+ ```bash
474
+ mjolnir ci install
475
+ ```
476
+
477
+ Ili ga poveži nativno u GitHub Code Scanning preko SARIF-a:
478
+
479
+ ```yaml
480
+ - run: npx mjolnir-qa@latest --format sarif > mjolnir.sarif
481
+ - uses: github/codeql-action/upload-sarif@v3
482
+ with:
483
+ sarif_file: mjolnir.sarif
484
+ ```
485
+
486
+ Editor i pipeline postavka za SARIF:
487
+ [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md).
488
+
489
+ ### Pokrivenost promijenjenog opsega
490
+
491
+ `--scope changed` pripisuje nalazima linije dodane u tvojoj grani naspram
492
+ merge-base s `main`. Pokriva test fajlove (`*.spec.*`, `*.test.*`) plus
493
+ GitHub workflow fajlove i Playwright konfiguracije u diffu. Kad se
494
+ merge-base ne može razriješiti — shallow clone, detached HEAD, non-git
495
+ cilj, drugi zadani branch — pošteno degradira: nalazi se vraćaju na
496
+ atribuciju po cijelom fajlu i izvještaj to kaže. Prepiši baznu ref s
497
+ `--base <ref>`.
498
+
499
+ ---
500
+
501
+ ## Konfiguracija
502
+
503
+ Mjölnir je zero-config. Opcioni `mjolnir.config.json` (ili
504
+ `.mjolnir.json`) u korijenu repoa podešava severity, gating i opseg —
505
+ nikad ne mijenja semantiku detekcije.
506
+
507
+ | Key | Tip | Efekat |
508
+ | ------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
509
+ | `exclude` | `string[]` | Dodatni ignore globovi (gitignore podskup), preko ugrađenih defaulta |
510
+ | `gate` | `"advisory" \| "error" \| "warning"` | Koji severity izlaze s ne-nula kodom (default `error`; `advisory` nikad ne blokira) |
511
+ | `severityOverrides` | `{ "<RULE-ID>": severity }` | Prerangiraje nalaze pravila za tvoj repo |
512
+ | `ignore` | `IgnoreEntry[]` | Potiskuje nalaze — **`reason` je obavezan**; unosi istječu nakon 90 dana (eksplicitni `expires` datum, ili vrijeme zadnje izmjene config fajla za unose bez njega) |
513
+ | `plugins` | `string[]` | Paketi pravila trećih strana (vidi [Model povjerenja](#model-povjerenja)) |
514
+
515
+ ```json
516
+ {
517
+ "gate": "error",
518
+ "exclude": ["legacy/**"],
519
+ "severityOverrides": { "QA-PW-118": "warning" },
520
+ "ignore": [
521
+ {
522
+ "ruleId": "QA-TEST-004",
523
+ "files": ["e2e/legacy-login.spec.ts"],
524
+ "reason": "Third-party widget needs a settle delay; tracked in JIRA-4821",
525
+ "expires": "2026-12-31"
526
+ }
527
+ ]
528
+ }
529
+ ```
530
+
531
+ - **`.mjolnirignore`** — jednostavna gitignore-slična datoteka za
532
+ isključenja putanja, isti dijalekt kao `exclude`. Koristi je za
533
+ mašinski šum; koristi `exclude` kad lista pripadne version kontroli,
534
+ uz ostatak konfiguracije.
535
+ - **CLI nadjačavanja** — `--strict` (uključi quarantine pravila),
536
+ `--width <cols>` i `--ascii` / `--no-ascii` (terminalski render),
537
+ `--tone blunt` (oštrije poruke), `--max-duration <sec>` (ograničen
538
+ djelomični skan).
539
+ - Potiskivanje pravila i životni ciklus deprecacije:
540
+ [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md).
541
+
542
+ `ignore` unosi hrane i samostalnu komandu `mjolnir suppressions`, koja
543
+ izlista šta je trenutno potisnuto i kada ističe svaki unos.
544
+
545
+ ---
546
+
547
+ ## 📐 Exit kodovi i ugovori
548
+
549
+ Zamrznuti — sigurni za gradnju CI logike:
550
+
551
+ | Exit kod | Značenje |
552
+ | -------- | ---------------------------------------------------------------------------------- |
553
+ | `0` | Čisto — nema nalaza na ili iznad gatea |
554
+ | `1` | Nalazi na ili iznad gatea |
555
+ | `2` | Djelimičan skan (potrošen vremenski budžet, nečitljivi fajlovi) — nikad ne blokira |
556
+ | `10` | Greška upotrebe (loš flag, nedostaje cilj) |
557
+ | `20` | Interna greška |
558
+
559
+ JSON/SARIF izvještaj je `schemaVersion: 1`. ID-jevi pravila
560
+ (`QA-<FAMILY>-NNN`) su nepromjenjivi jednom isporučeni i nikad se ne
561
+ ponovo koriste.
562
+
563
+ ---
564
+
565
+ ## Model povjerenja
566
+
567
+ - **Local-first** — nula mrežnih poziva tokom skeniranja. Nikad. Nula
568
+ telemetrije.
569
+ - **Bez lažnih dokaza** — radije kažemo „nepoznato" nego „verifikovano".
570
+ Prazan repo dobija `score: null`, nikad lažnih 100.
571
+ - **Djelimična iskrenost** — ako je analiza prekinuta, izlaz to kaže.
572
+ Nikad „complete" kad nije.
573
+ - **FP vatrozid** — detekcija radi na pregledu koda bez komentara i
574
+ stringova (TypeScript pravila koriste AST kompajlera): obrazac unutar
575
+ prosačnog komentara ili doc-primjera-stringa je dokumentacija, ne
576
+ nalaz.
577
+ - **Izmjereno, ne tvrđeno** — u glavne tierove ulaze samo pravila sa
578
+ stopom lažnih pozitiva iz stvarnog OSS koda (vidi
579
+ [Koliko je od ovoga izmjereno](#koliko-je-od-ovoga-izmjereno));
580
+ podnožje skana i `mjolnir rules --unmeasured` kažu koje su koje.
581
+ - **Povjerenje u pluginove** — pluginovi su npm paketi deklarirani pod
582
+ `"plugins"`. **Nema sandboxa**: plugin kod radi s punim Node
583
+ privilegijama, isti model povjerenja kao ESLint ili Vitest pluginovi.
584
+ Core prefiksi ID-jeva pravila su rezervirani i odbijaju se od
585
+ pluginova radi sprečavanja spoofinga.
586
+ - **Eksterna pravila lokalna workspaceu** (folder-bazirana, nula
587
+ mreže) — `mjolnir-rules/` direktorij pored skan cilja učitava
588
+ vlastita pravila: JSON fajlovi deklariraju regex obrasce (nikakav kod
589
+ se ne izvršava), `.mjs`/`.js` moduli eksportuju `rules` (potpuno Node
590
+ povjerenje, kao pluginovi). Eksterna pravila nose iste metadata
591
+ povjerenja kao core; nikad ne mogu ući u core tier (core traži
592
+ izmjerenu FP stopu iz corpus sidecara — deklarirani `tier: "core"`
593
+ se stega na `extended`), poštuju tier limite i provjeravaju se na
594
+ drift: `mjolnir rules --md --external` renderira katalog iz
595
+ učitanih fajlova (provenijencija `external`), a generator matrice
596
+ prima `--external <root>`.
597
+
598
+ ---
599
+
600
+ ## 🏗️ Arhitektura
601
+
602
+ <details>
603
+ <summary>Raširi stablo</summary>
604
+
605
+ ```
606
+ mjolnir/
607
+ ├── src/
608
+ │ ├── engine/ # LanguageAdapter interface + rule runner
609
+ │ ├── adapters/ # typescript · python · java · csharp · github-actions
610
+ │ ├── rules/ # rules across 8 families + the measured-FP table
611
+ │ ├── playwright/ # Selector Health Score engine
612
+ │ ├── discovery/ # workspace, frameworks, ignore resolution
613
+ │ ├── scope/ # git merge-base changed-scope engine
614
+ │ ├── scorer/ # transparent deduction table + prioritization
615
+ │ ├── reporter/ # terminal · JSON · SARIF 2.1 · Mermaid
616
+ │ ├── forensics/ # run-data ingestion · flake verdicts · triage
617
+ │ ├── config/ # mjolnir.config.json + suppressions
618
+ │ ├── plugins/ # third-party rule loading (no sandbox)
619
+ │ └── commands/ # every subcommand
620
+ └── tests/
621
+ ├── fixtures/ # must-fire / must-not-fire per rule
622
+ └── golden/ # frozen score regression locks
623
+ ```
624
+
625
+ </details>
626
+
627
+ - **Pravila su čiste funkcije** — `(SourceFileContext) → Finding[]`,
628
+ bez I/O-a, bez globala. Novi ekosistem = jedan adapter + njegova
629
+ pravila.
630
+ - **TypeScript/Playwright koristi AST kompajlera** (ts-morph). Python,
631
+ Java i C# rade na zajedničkom regex sloju s maskiranim komentarima i
632
+ stringovima.
633
+ - Tree-sitter WASM AST sloj za Javu i C# postoji i sljedeći je korak
634
+ preciznosti — još nije povezan u sinhroni skan pipeline.
635
+
636
+ ---
637
+
638
+ ## 📚 Dokumentacija
639
+
640
+ | Dokument | Šta je unutra |
641
+ | ------------------------------------------------------ | ----------------------------------------------- |
642
+ | [docs/SCORING.md](docs/SCORING.md) | Normalizacija rezultata + ponderiranje dokazima |
643
+ | [docs/FP-AUDIT.md](docs/FP-AUDIT.md) | Izmjerene stope lažnih pozitiva + metodologija |
644
+ | [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md) | Stanja pravila, potiskivanje, deprecacija |
645
+ | [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md) | SARIF izlaz + editor/CI postavka |
646
+ | [docs/rules/](docs/rules/) | Generirani katalog po pravilu |
647
+ | [CONTRIBUTING.md](CONTRIBUTING.md) | Dev postavka + workflow doprinosa |
648
+ | [CHANGELOG.md](CHANGELOG.md) | Historija izdanja |
649
+ | [SECURITY.md](SECURITY.md) | Prijavljivanje ranjivosti |
650
+
651
+ ---
652
+
653
+ ## 📈 Status
654
+
655
+ **v0.5.x · otvorena beta.** JSON shema i exit kodovi su zamrznuti
656
+ ugovori. TypeScript i Python imaju najširu izmjerenu pokrivenost; Java
657
+ i C# su noviji — čitaj ih kroz
658
+ [tabelu tierova](#tierovi-pravila-i-jezična-zrelost).
659
+
660
+ ---
661
+
662
+ ## 🤝 Doprinos
663
+
664
+ Nova pravila su najlakši prvi doprinos — jedna komanda scaffolduje
665
+ pravilo plus njegove must-fire **i** must-not-fire fixture (generirano
666
+ pravilo namjerno pada na fixtureima dok ne implementiraš stvarnu
667
+ detekciju — stub se ne može isporučiti):
668
+
669
+ ```bash
670
+ mjolnir create-rule QA-PW-140 --title "Screenshot without diff bound"
671
+ ```
672
+
673
+ Potpuni dev setup, komande stalnog gatea i zakoni anti-creep / fixture
674
+ vatrozida su u [CONTRIBUTING.md](CONTRIBUTING.md).
675
+
676
+ ---
677
+
678
+ <div align="center">
679
+
680
+ **Prestani isporučivati testovima kojima ne možeš vjerovati.**
681
+
682
+ ```bash
683
+ npx mjolnir-qa@latest
684
+ ```
685
+
686
+ **Star ⭐ · Watch 👀 · Contribute 🤝**
687
+
688
+ Izgradio [Sergey Bar](https://www.linkedin.com/in/sergeybar/)
689
+
690
+ </div>