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.bn.md ADDED
@@ -0,0 +1,678 @@
1
+ <div align="center">
2
+
3
+ <img src="assets/readme/logo.png" alt="Mjölnir — Verification Trust Engine" width="800" />
4
+
5
+ ### আপনার টেস্টগুলো আপনাকে মিথ্যা বলছে। আমরা তা প্রমাণ করি।
6
+
7
+ **QA-এর জন্য Verification Trust Engine।** Mjölnir টেস্ট সুইট ও CI
8
+ পাইপলাইন অডিট করে, একটি নির্ভরযোগ্যতা স্কোর রিপোর্ট করে এবং ঠিক কোথায়
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) | [ไทย](README.th.md) | [Türkçe](README.tr.md) | [Українська](README.uk.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="ডেমো রিপোর উপর Mjölnir-এর সম্পূর্ণ --verbose রিপোর্ট: 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 থেকে রেন্ডার করা — কিছুই কাটা নয়। `npm run docs:demo`
45
+ দিয়ে আবার তৈরি হয়;
46
+ [`tests/demo-asset-reproducibility.spec.ts`](tests/demo-asset-reproducibility.spec.ts)
47
+ টেস্টটি টুল যা প্রিন্ট করে সে থেকে সরে গেলে CI ব্যর্থ করে।</sub>
48
+
49
+ **এইমাত্র কী ঘটল:**
50
+
51
+ 1. Mjölnir Playwright spec-গুলো, তার কনফিগ, CI workflow ও একটি Python
52
+ টেস্ট ফাইল আবিষ্কার করল — চার ভাষা/ফরম্যাট, এক পাসে।
53
+ 2. সুইটের ওপর আস্থা দুর্বল করে এমন প্রমাণ পেল — একটি job ঢেকে রাখা
54
+ `continue-on-error`, একটি exit code গিলে ফেলা `|| true`, কড়া sleep,
55
+ ভঙ্গুর selector, hardcode করা staging URL, `networkidle` wait।
56
+ 3. প্রতিটিকে বানাল একটি সুনির্দিষ্ট finding — rule ID, অবস্থান ও ফিক্স
57
+ সহ — এবং একটি একক স্কোর, যার উপর আপনি PR gate করতে পারেন।
58
+
59
+ ### একটি finding, কাছ থেকে
60
+
61
+ উপরের প্রথম finding-এর জন্য `mjolnir explain QA-CI-001` চালান, এবং
62
+ পাবেন:
63
+
64
+ ```text
65
+ ▚▞ QA-CI-001 — continue-on-error masks a failing verification gate
66
+
67
+ Severity: error
68
+ Confidence: high
69
+ Evidence: E2
70
+ Measured FP: not yet measured — this rule ships on assumption (see docs/FP-AUDIT.md)
71
+
72
+ WHAT WAS FOUND (real detector output, not a mockup)
73
+ Job `security-scan` runs a verification gate under `continue-on-error: true`.
74
+
75
+ 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.
78
+
79
+ HOW TO FIX
80
+ Remove continue-on-error, or scope it to individual non-blocking steps only.
81
+ ```
82
+
83
+ এটাই মূল্যের একক: স্টাইলের খোঁচা নয়, বরং সেই জায়গা যেখানে আপনার CI
84
+ বলছে কিছু পাস হয়েছে — অথচ হয়নি।
85
+
86
+ ---
87
+
88
+ ## ⚡ দ্রুত শুরু
89
+
90
+ একটি রিপোর বিরুদ্ধে চালান — সম্পূর্ণ রিপোর্ট ও নির্ভরযোগ্যতা স্কোরের
91
+ জন্য:
92
+
93
+ ```bash
94
+ npx mjolnir-qa@latest
95
+ ```
96
+
97
+ **CI-তে পণ্যটি একটি মাত্র কমান্ড।** শুধু branch যা স্পর্শ করেছে তা
98
+ স্ক্যান করে এবং নতুন সমস্যায় অশূন্য কোডে বের হয়:
99
+
100
+ ```bash
101
+ npx mjolnir-qa@latest --scope changed
102
+ ```
103
+
104
+ এটি একটি PR check-এ ফেলে দিন — `mjolnir ci install` workflow লিখে দেয় —
105
+ এবং শেষ। বাকি সব ঐচ্ছিক।
106
+
107
+ | কমান্ড | এটি কী করে |
108
+ | ----------------------------------- | --------------------------------------------------- |
109
+ | `mjolnir` | পূর্ণ-রিপো স্ক্যান + নির্ভরযোগ্যতা স্কোর |
110
+ | `mjolnir --scope changed` | শুধু আপনার branch যা আনল — CI রূপ |
111
+ | `mjolnir ci install` | উপদেশমূলক PR workflow তৈরি করে |
112
+ | `mjolnir explain QA-CI-001` | কী / কেন / ফিক্স + একটি রুলের জন্য পরিমাপকৃত FP হার |
113
+ | `mjolnir rules --unmeasured` | পরিমাপ নয়, অনুমানের ভিত্তিতে চলা রুলগুলো |
114
+ | `mjolnir --json` / `--format sarif` | মেশিন-পাঠযোগ্য / GitHub Code Scanning |
115
+ | `mjolnir --strict` | quarantine টিয়ারের রুলও চালায় (উচ্চতর FP ঝুঁকি) |
116
+
117
+ <details>
118
+ <summary><strong>কিছু flaky হলে</strong></summary>
119
+
120
+ | কমান্ড | এটি কী করে |
121
+ | ----------------------------------- | ------------------------------------------------------- |
122
+ | `mjolnir forensics ./test-results/` | প্রকৃত রান-ডেটা → `TRUE-FLAKE` রায়, `FLAKY.md` |
123
+ | `mjolnir triage ./test-results/` | এক্সিকিউশন ইতিহাস থেকে কোয়ারেন্টাইন প্রস্তাব |
124
+ | `mjolnir pw-report ./test-results/` | Playwright রান-সারসংক্ষেপ — retry / flake / সবচেয়ে ধীর |
125
+ | `mjolnir doctor:playwright` | শুধু-Playwright গভীর স্ক্যান + Selector Health Score |
126
+
127
+ </details>
128
+
129
+ <details>
130
+ <summary><strong>মাঝে মাঝে / রিপোর্ট</strong></summary>
131
+
132
+ | কমান্ড | এটি কী করে |
133
+ | ------------------------------- | ---------------------------------------------------- |
134
+ | `mjolnir fix --dry-run` / `fix` | প্রমাণসহ নিরাপদ অটো-ফিক্স |
135
+ | `mjolnir baseline` / `diff` | finding স্ন্যাপশট, পরে শুধু নতুন/নাজুক হওয়া রিপোর্ট |
136
+ | `mjolnir impact --since <ref>` | আগের কমিটের পর কী বদলেছে |
137
+ | `mjolnir debt` | খরচের মডেলসহ টেস্ট-ঋণ রেজিস্টার |
138
+ | `mjolnir handover` | নতুন QA-র জন্য সুইটের অনবোর্ডিং ম্যাপ |
139
+ | `mjolnir stats` | দেখা ফিক্সগুলোর লোকাল সর্বকালের গণনা |
140
+ | `mjolnir badge` | shields.io endpoint JSON + snippet |
141
+ | `mjolnir rules --md` | সম্পূর্ণ রুল ক্যাটালগ (JSON বা Markdown) |
142
+ | `mjolnir doctor` | Mjölnir-এর নিজের রুল বেসের আত্ম-অডিট |
143
+ | `mjolnir create-rule <ID>` | নতুন রুল + ফিক্সচার স্কাফোল্ড |
144
+ | `mjolnir --format mermaid` | PR কমেন্টের জন্য টেস্ট-আর্কিটেকচার ডায়াগ্রাম |
145
+
146
+ </details>
147
+
148
+ চাইলে `npx`-এর বদলে গ্লোবালি ইনস্টল করুন: `npm i -g mjolnir-qa`।
149
+ Node.js ≥ 22.18 প্রয়োজন। Windows, macOS ও Linux-এ চলে।
150
+
151
+ ---
152
+
153
+ ## 👥 এটা কাদের জন্য?
154
+
155
+ - **QA / SDET** — e2e বা ইন্টিগ্রেশন সুইটের মালিক, যাদের প্রমাণ দরকার
156
+ যে সুইট সত্যিই সেই সবুজ টিক দাগের যোগ্য, যা সে তৈরি করে।
157
+ - **Platform / DevEx টিম** — CI অখণ্ডতা ও release gate-এর দায়িত্বে
158
+ থাকা মানুষ, যারা চান না `continue-on-error` কখনো চুপচাপ লাল
159
+ পাইপলাইনকে সবুজ করে ফেলুক।
160
+ - **OSS maintainers** — যারা সস্তা, সবসময় চালু একটি যাচাই-গেট চান,
161
+ যা লোকালি ও CI-তে শূন্য নেটওয়ার্ক কলে চলে।
162
+
163
+ ---
164
+
165
+ ## 🔨 Mjölnir কী পরীক্ষা করে
166
+
167
+ | | |
168
+ | --- | ---------------------------------------------------------------------------------------------------------------- |
169
+ | ⚖️ | **নির্ভরযোগ্যতা স্কোর** — একটি সংখ্যা, স্বচ্ছ বাদ টেবিল, কোনো কালো বাক্স নয় |
170
+ | 🎭 | **Selector Health Score** — শুধু আপনার পাস-রেট নয়, আপনার Playwright locator-এর গ্রেড দেয় |
171
+ | 🔬 | **রানটাইম ফরেনসিক** — আসল Playwright/JUnit রান-ডেটা পড়ে `TRUE-FLAKE` ধরে, শুধু স্ট্যাটিক জল্পনা নয় |
172
+ | 🚨 | **CI-অখণ্ডতার রুল** — `continue-on-error`, `\|\| true` ও অন্যান্য মিথ্যা-সবুজ কৌশল ধরে |
173
+ | 🐍 | **চারটি Playwright বাইন্ডিং-ই** — TypeScript, Python, Java, C#/.NET — এর সাথে pytest, JUnit/TestNG ও CI workflow |
174
+ | 🔒 | **Local-first** — স্ক্যানের সময় শূন্য নেটওয়ার্ক কল, শূন্য টেলিমেট্রি, সেকেন্ডে চলে |
175
+
176
+ ### রুলগুলো
177
+
178
+ প্রতিটি রুল must-fire **এবং** must-not-fire ফিক্সচারসহ আসে। যে রুল
179
+ তার নিজের নেগেটিভ ফিক্সচারে ফায়ার করে সে শিপ করতে পারে না — এটাই
180
+ false-positive ফায়ারওয়াল।
181
+
182
+ <details>
183
+ <summary><strong>টেস্ট হাইজিন</strong></summary>
184
+
185
+ | ID | রুল | Severity |
186
+ | ----------- | --------------------------------------------------- | -------- |
187
+ | QA-TEST-001 | ফোকাসড টেস্ট কমিট করা (`.only`, `fit`) | error |
188
+ | QA-TEST-002 | কোনো কারণ ছাড়া বাদ দেওয়া টেস্ট | error |
189
+ | QA-TEST-002 | সংরক্ষিত কারণসহ বাদ দেওয়া টেস্ট | warning |
190
+ | QA-TEST-003 | assertion-বিহীন টেস্ট | error |
191
+ | QA-TEST-004 | কড়া sleep (`waitForTimeout`, `sleep()`, `delay()`) | warning |
192
+ | QA-TEST-006 | flakiness লুকানো retry-অপব্যবহার | warning |
193
+ | QA-TEST-010 | খালি টেস্ট বডি | error |
194
+
195
+ </details>
196
+
197
+ <details>
198
+ <summary><strong>টেস্ট কোয়ালিটি</strong></summary>
199
+
200
+ | ID | রুল | Severity |
201
+ | ------------ | ----------------------------- | -------- |
202
+ | QA-TQUAL-001 | শুধু mock-ভিত্তিক যাচাই | info |
203
+ | QA-TQUAL-002 | টাটলজিক্যাল assertion | error |
204
+ | QA-TQUAL-009 | await-বিহীন promise assertion | error |
205
+ | QA-TQUAL-011 | কমেন্ট-আউট করা টেস্ট | warning |
206
+
207
+ </details>
208
+
209
+ <details>
210
+ <summary><strong>Playwright 🎭</strong></summary>
211
+
212
+ | ID | রুল | Severity |
213
+ | --------- | --------------------------------------- | -------- |
214
+ | QA-PW-002 | await-বিহীন locator assertion | error |
215
+ | QA-PW-003 | কমিট করা `page.pause()` / `test.only()` | error |
216
+ | QA-PW-004 | ভঙ্গুর CSS/XPath selector | warning |
217
+ | QA-PW-005 | `page.evaluate()`-এর ভিতরে বিজনেস লজিক | info |
218
+ | QA-PW-114 | লিগেসি element handle (`page.$`) | info |
219
+ | QA-PW-118 | `networkidle` wait (flaky by design) | info |
220
+ | QA-PW-123 | hardcode করা পরিবেশ URL | warning |
221
+
222
+ </details>
223
+
224
+ <details>
225
+ <summary><strong>CI অখণ্ডতা</strong></summary>
226
+
227
+ | ID | রুল | Severity |
228
+ | --------- | ------------------------------------------------------------------- | -------- |
229
+ | QA-CI-001 | `continue-on-error` ব্যর্থতা ঢেকে রাখে | error |
230
+ | QA-CI-002 | `\|\| true` exit code গিলে ফেলে | error |
231
+ | QA-CI-005 | রিপোর্ট ব্যবহৃত হয় কিন্তু কখনো তৈরিই হয় না | error |
232
+ | QA-CI-007 | টেস্টের চারপাশে retry-র‍্যাপার | warning |
233
+ | QA-CI-008 | সর্বদা-সফল step ব্যর্থতা ঢেকে রাখে | error |
234
+ | QA-CI-009 | টেস্টের exit code প্রচারিত হয় না (`\|` pipefail ছাড়া, `;` চেইন) | error |
235
+ | QA-CI-010 | যেখানে ব্লক করা চাই সেখানেই টেস্ট বাদ দেওয়া হয় (skip-on-PR guard) | error |
236
+
237
+ </details>
238
+
239
+ <details>
240
+ <summary><strong>Python / pytest 🐍</strong></summary>
241
+
242
+ | ID | রুল | Severity |
243
+ | --------- | ----------------------------------------------- | -------- |
244
+ | QA-PY-002 | বাদ দেওয়া টেস্ট (`skip`, কঠোর-নয় এমন `xfail`) | warning |
245
+ | QA-PY-003 | assertion-বিহীন টেস্ট ফাংশন | error |
246
+ | QA-PY-005 | টেস্টে `time.sleep()` | warning |
247
+ | QA-PY-006 | খালি টেস্ট বডি (`pass`) | info |
248
+ | QA-PY-010 | freeze ছাড়া এলোমেলো/সময় নির্ভরতা | info |
249
+ | QA-PY-012 | টাটলজিক্যাল assertion | error |
250
+
251
+ মোট ২০টি Python রুল (QA-PY-001…012 pytest হাইজিন + QA-PY-101…108 Playwright-Python)।
252
+
253
+ </details>
254
+
255
+ <details>
256
+ <summary><strong>Java / JUnit · TestNG ☕</strong></summary>
257
+
258
+ | ID | রুল | Severity |
259
+ | --------- | ---------------------------------------- | -------- |
260
+ | QA-JV-101 | নিষ্ক্রিয় টেস্ট (`@Disabled`) | warning |
261
+ | QA-JV-102 | কড়া sleep (`Thread.sleep()`) | warning |
262
+ | QA-JV-103 | assertion-বিহীন টেস্ট মেথড | error |
263
+ | QA-JV-105 | Playwright কড়া sleep `waitForTimeout()` | warning |
264
+ | QA-JV-106 | role locator-এর বদলে ভঙ্গুর selector | warning |
265
+ | QA-JV-108 | টেস্টে hardcode করা পরিবেশ URL | info |
266
+ | QA-JV-111 | ছাতা-মক `page.route("**")` | info |
267
+
268
+ </details>
269
+
270
+ <details>
271
+ <summary><strong>C# / .NET — NUnit · xUnit · MSTest 🟣</strong></summary>
272
+
273
+ | ID | রুল | Severity |
274
+ | --------- | ---------------------------------------------- | -------- |
275
+ | QA-CS-101 | বাদ দেওয়া টেস্ট (`[Ignore]`, `[Fact(Skip=)]`) | warning |
276
+ | QA-CS-102 | কড়া sleep (`Thread.Sleep` / `Task.Delay`) | warning |
277
+ | QA-CS-103 | assertion-বিহীন টেস্ট মেথড | error |
278
+ | QA-CS-105 | কড়া sleep `WaitForTimeoutAsync()` | warning |
279
+ | QA-CS-106 | role locator-এর বদলে ভঙ্গুর selector | warning |
280
+ | QA-CS-108 | টেস্টে hardcode করা পরিবেশ URL | info |
281
+ | QA-CS-111 | ছাতা-মক `page.RouteAsync("**")` | info |
282
+
283
+ </details>
284
+
285
+ > সম্পূর্ণ লাইভ ক্যাটালগ — প্রতিটি রুল tier, confidence, false-positive
286
+ > ঝুঁকি ও autofix উপলব্ধতাসহ — registry থেকে তৈরি হয়:
287
+ >
288
+ > ```bash
289
+ > mjolnir rules --md
290
+ > ```
291
+ >
292
+ > প্রতি-রুল পেজগুলো [`docs/rules/`](docs/rules/)-এর অধীনে।
293
+
294
+ ### এর মধ্যে কতটা পরিমাপ করা হয়েছে
295
+
296
+ **৯৯টি রুলের ৭৪টি বাস্তব OSS কোডের বিরুদ্ধে পরিমাপকৃত false-positive
297
+ হার বহন করে** (প্রতিটিতে ≥ ১০টি হাতে-শ্রেণিবদ্ধ finding; দেখুন
298
+ [docs/FP-AUDIT.md](docs/FP-AUDIT.md))। বাকি ১৯টি লেখকের অনুমানে শিপ হয়।
299
+ প্রতিটি স্ক্যানের ফুটার বলে দেয় _ফায়ার_ করা রুলগুলোর কতগুলো পরিমাপকৃত;
300
+ `mjolnir rules --unmeasured` যেগুলো নয় তা তালিকাভুক্ত করে; প্রতিটি রুলের
301
+ `mjolnir explain` পেজ তার অবস্থা জানায়। আমরা হারটি প্রকাশ করি — এমনকি
302
+ কুৎসিত হলেও — QA-CS-103 ৯৫%-এ অডিট হয় এবং এজন্যই কোয়ারেন্টাইনে। সেই
303
+ ৭৮ বাড়ানোই প্রজেক্টের চলমান কাজ।
304
+
305
+ ### রুল টিয়ার ও ভাষা-পরিপক্বতা
306
+
307
+ প্রতিটি রুল `core`, `extended` বা `quarantine`, তার **পরিমাপকৃত**
308
+ false-positive হার অনুযায়ী নির্ধারিত:
309
+
310
+ | Tier | অর্থ | ডিফল্ট স্ক্যান | `--strict` |
311
+ | ------------ | ----------------------------------- | :------------: | :--------: |
312
+ | `core` | ≤ ১০ % পরিমাপকৃত FP | ✅ | ✅ |
313
+ | `extended` | ≤ ৩০ % পরিমাপকৃত FP | ✅ | ✅ |
314
+ | `quarantine` | এর বেশি, বা এখনো অপরিমাপিত (n < ১০) | ❌ | ✅ |
315
+
316
+ | ভাষা | Adapter | আজকের কভারেজ |
317
+ | --------------- | ------------- | ----------------------------------------------------- |
318
+ | TypeScript / JS | কম্পাইলার AST | ব্যাপকতম, সর্বাধিক পরিমাপকৃত — মূলত `core`/`extended` |
319
+ | Python / pytest | regex স্তর | ব্যাপক, corpus-অডিটেড — মূলত `core`/`extended` |
320
+ | Java | regex স্তর | নতুন — মূলত `extended`/`quarantine` |
321
+ | C# / .NET | regex স্তর | নতুন — মূলত `extended`/`quarantine` |
322
+
323
+ TypeScript ও Python-এর পরিমাপকৃত কভারেজ সবচেয়ে ব্যাপক। Java ও C# শিপ
324
+ হয়েছে, ডকুমেন্টেড, এবং আসল কনজিউমার সুইট (বাইন্ডিং লাইব্রেরির নিজের
325
+ টেস্ট নয়) অডিট হওয়া পর্যন্ত হেডলাইন সংখ্যার বাইরে থাকে।
326
+
327
+ ---
328
+
329
+ ## স্কোর কীভাবে কাজ করে
330
+
331
+ <p align="center">
332
+ <img src="assets/readme/terminal-hero.svg" alt="Mjölnir-এর টার্মিনাল আউটপুট — WORTHINESS 75/100 NEEDS WORK, বিভাগভিত্তিক ডায়াগনস্টিক ভাঙানো ও FIX THIS FIRST তালিকা" width="820" />
333
+ </p>
334
+
335
+ <sub>`npm run docs:hero` দিয়ে আবার তৈরি হয়;
336
+ [`tests/hero-asset-reproducibility.spec.ts`](tests/hero-asset-reproducibility.spec.ts)
337
+ টেস্টটি reporter যা সত্যিই প্রিন্ট করে সে থেকে সরে গেলে CI ব্যর্থ করে।</sub>
338
+
339
+ স্কোর স্বচ্ছ: **error −8, warning −3, info −1**, পরে সুইট-এক্সপোজার দিয়ে
340
+ নরমালাইজ (প্রতি টেস্ট ডিক্লারেশনে বাদ)। প্রমাণ-ভিত্তিক বাদ মানে দুর্বল
341
+ সিগন্যালের দাম কম। টার্মিনাল সেই একই ডিসকাউন্টেড সংখ্যা দেখায় যা স্কোর
342
+ ব্যবহার করে — কোনো কালো বাক্স নয়। পূর্ণ পদ্ধতি:
343
+ [docs/SCORING.md](docs/SCORING.md)।
344
+
345
+ **রায়**
346
+
347
+ | Score | রায় |
348
+ | ------- | ---------------- |
349
+ | ≥ 80 | ✓ **WORTHY** |
350
+ | 50 – 79 | ⚠ **NEEDS WORK** |
351
+ | < 50 | ✖ **UNWORTHY** |
352
+
353
+ **প্রমাণ স্তর** — প্রতিটি finding একটি বহন করে; এটি স্কোরে finding-এর
354
+ ওজন নির্ধারণ করে:
355
+
356
+ | স্তর | অর্থ | স্কোরে প্রভাব | উদাহরণ |
357
+ | ---- | -------------------- | ----------------- | ----------------------------------------------------- |
358
+ | E2 | নির্ধারণী ত্রুটি | পূর্ণ বাদ | কমিট করা `.only` — কাঠামোগতভাবে প্রমাণযোগ্য |
359
+ | E1 | হিউরিস্টিক প্যাটার্ন | অর্ধেক বাদ | regex-এ ধরা `sleep()` — জোরালো সংকেত, প্রমাণ নয় |
360
+ | E0 | পর্যবেক্ষণ | শূন্য (শুধু info) | রিপোর্ট হয় কিন্তু কখনো CI gate করে না বা বাদ দেয় না |
361
+
362
+ বেশিরভাগ রুল **E1**। «we prove it» স্লোগানটি এই ব্যবস্থাকে বোঝায়: E2
363
+ finding কাঠামোগত প্রমাণ; E1 finding সঠিকভাবে স্থাপিত সতর্কতা, আনুষ্ঠানিক
364
+ প্রমাণ নয়।
365
+
366
+ খালি রিপো `null` স্কোর পায়, কখনোই নকল ১০০ নয় — দেখুন
367
+ [আস্থার মডেল](#আস্থার-মডেল)।
368
+
369
+ ---
370
+
371
+ ## 🎭 Selector Health Score
372
+
373
+ Playwright সুইটের হেডলাইন মেট্রিক — আপনার locator কতটা টেকসই:
374
+
375
+ ```text
376
+ ▚▞ SELECTOR HEALTH — e2e/checkout.spec.ts
377
+
378
+ [█████████████████░░░] 83 / 100
379
+ role/text: 2 · testid: 1 · css-chains: 1 ⚠ · xpath: 0
380
+ ```
381
+
382
+ role-ভিত্তিক locator পূর্ণ স্কোর পায়। CSS class চেইন ও XPath স্কোর
383
+ ডুবিয়ে দেয় — যেকোনো DOM refactor-এ ভেঙে পড়ে, বলে না কোন আচরণ
384
+ নষ্ট হলো।
385
+
386
+ ---
387
+
388
+ ## 🔬 রানটাইম প্রমাণ
389
+
390
+ স্ট্যাটিক flakiness সনাক্তকরণ হলো আন্দাজ। Mjölnir **প্রকৃত এক্সিকিউশন
391
+ ডেটা** পড়ে — যেকোনো রানারের Playwright JSON রিপোর্ট ও JUnit XML:
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
+ প্রথম থেকে attempt ≥ 2-তে পাস করা টেস্ট পাস করা টেস্ট নয় — সেটি ভাগ্যবান
409
+ টেস্ট। চূড়ান্ত সবুজ টিক নির্বিশেষে এটি `TRUE-FLAKE` চিহ্নিত হয়।
410
+
411
+ ---
412
+
413
+ ## ⚡ Mjölnir আরেকটি linter নয়
414
+
415
+ Linter বলে কোড রুল মানে কি না। Mjölnir বলে আপনার যাচাই-ব্যবস্থার ওপর
416
+ আস্থা রাখা যায় কি না।
417
+
418
+ | | ESLint / SonarQube | Coverage টুল | ম্যানুয়াল রিভিউ | **Mjölnir** |
419
+ | ---------------------------------------------------------------- | :----------------: | :----------: | :--------------: | :---------: |
420
+ | CI workflow অখণ্ডতা (`continue-on-error`, `\|\| true`) | ❌ | ❌ | বিরল | ✅ |
421
+ | ক্রস-ভাষা (TS, Python, Java, C#) এক টুল থেকে | ❌ | ❌ | ❌ | ✅ |
422
+ | Playwright locator-এর স্থিতিস্থাপকতা গ্রেড করে (Selector Health) | ❌ | ❌ | বিরল | ✅ |
423
+ | আসল assertion-বিহীন টেস্ট চিহ্নিত করে | ✅ (প্লাগইন)\* | ❌ | কখনো কখনো | ✅ |
424
+ | কড়া sleep ধরে (`waitForTimeout`, `time.sleep`) | ✅ (প্লাগইন)\* | ❌ | কখনো কখনো | ✅ |
425
+ | সেকেন্ডে চলে, স্ক্যানের সময় শূন্য নেটওয়ার্ক কল | ✅ | ✅ | — | ✅ |
426
+
427
+ \*`eslint-plugin-jest` (`expect-expect`) ও `eslint-plugin-playwright`
428
+ (`expect-expect`, `no-wait-for-timeout`) নিজ নিজ ফ্রেমওয়ার্কের জন্য এগুলো
429
+ কভার করে।
430
+
431
+ **রানটাইম বিশ্লেষণ** স্ট্যাটিক লিন্টিং থেকে আলাদা বিভাগ:
432
+
433
+ | | Playwright retry reporter | Allure / ReportPortal | **Mjölnir forensics** |
434
+ | --------------------------------------------- | :-----------------------: | :-------------------: | :-------------------: |
435
+ | `TRUE-FLAKE` রায়ের জন্য আসল রান-ডেটা পড়ে | আংশিক\* | আংশিক (tag) | ✅ |
436
+ | এক্সিকিউশন ইতিহাস থেকে flaky-ট্রায়াজ রিপোর্ট | ❌ | ✅ | ✅ |
437
+ | স্ট্যাটিক নির্ভরযোগ্যতা স্কোরের সাথে সংযুক্ত | ❌ | ❌ | ✅ |
438
+
439
+ \*Playwright ভেতরে ভেতরে retry ট্র্যাক করে কিন্তু রায়-লেবেলসহ স্বতন্ত্র
440
+ flakiness রিপোর্ট তৈরি করে না।
441
+
442
+ ---
443
+
444
+ ## 🤖 শুধু AI কোড রিভিউ ব্যবহার করলেই তো হয়?
445
+
446
+ ভিন্ন সমস্যা, ভিন্ন স্তর। AI রিভিউ diff-এ সন্দেহজনক টেস্ট-পরিবর্তন ধরতে
447
+ পারে; এটি প্রমাণ করে না যে যাচাই-ব্যবস্থা সমগ্রভাবে আস্থার যোগ্য — এবং
448
+ এটি কেবল আপনি দেখানো diff-ই দেখে।
449
+
450
+ | | AI কোড রিভিউ (Copilot ইত্যাদি) | **Mjölnir** |
451
+ | ---------------------------------- | :------------------------------: | :---------------------------: |
452
+ | প্রতি স্ক্যানে খরচ | Token (diff-এর আকারে বাড়ে) | **শূন্য** (লোকাল, ইনস্টল করা) |
453
+ | পুরো সুইট + সব CI কনফিগ দেখে | শুধু আপনার দেখানো PR diff | **সবকিছু, প্রতিবার** |
454
+ | নির্ধারণী (একই ইনপুট → একই আউটপুট) | ❌ (অনির্ধারণী) | **✅** |
455
+ | মাসের পর মাস সুপ্ত প্যাটার্ন ধরে | শুধু কনটেক্সটে থাকলে | **✅** (সব ফাইল স্ক্যান করে) |
456
+ | রানের মাঝে finding মনে রাখে | ❌ (সেশনের মাঝে কোনো স্মৃতি নেই) | **✅** (baseline + diff) |
457
+ | মানুষের ট্রিগার ছাড়াই চলে | PR বা prompt দরকার | **✅** (CI হুক, ৩ সেকেন্ড) |
458
+
459
+ **দুটোই ব্যবহার করুন।** AI সূক্ষ্মতা, অভিপ্রায় ও এমন ডিজাইন-ত্রুটি ধরে
460
+ যা কোনো regex খুঁজে পায় না। Mjölnir সেই কাঠামোগত প্যাটার্ন ধরে যা AI
461
+ «ইচ্ছাকৃত» মনে হওয়ায় এড়িয়ে যায় — কমিট করা `.only`, গিলে ফেলা exit
462
+ code, টেস্ট job-এ `continue-on-error`। এগুলো ভাবনার প্রয়োজন এমন বাগ
463
+ নয়; স্ক্যানের প্রয়োজন এমন সত্য।
464
+
465
+ ---
466
+
467
+ ## 🤖 CI ইন্টিগ্রেশন
468
+
469
+ এক কমান্ডে PR workflow তৈরি হয় — ডিফল্টে উপদেশমূলক, কখনো ব্লক করে না:
470
+
471
+ ```bash
472
+ mjolnir ci install
473
+ ```
474
+
475
+ অথবা SARIF দিয়ে GitHub Code Scanning-এ নেটিভভাবে যুক্ত করুন:
476
+
477
+ ```yaml
478
+ - run: npx mjolnir-qa@latest --format sarif > mjolnir.sarif
479
+ - uses: github/codeql-action/upload-sarif@v3
480
+ with:
481
+ sarif_file: mjolnir.sarif
482
+ ```
483
+
484
+ SARIF-এর জন্য এডিটর ও পাইপলাইন সেটআপ:
485
+ [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md)।
486
+
487
+ ### Changed-scope কভারেজ
488
+
489
+ `--scope changed` finding-গুলোকে সেই লাইনগুলোতে দায়ী করে যা আপনার branch
490
+ `main`-এর সাথে merge-base-এর বিপরীতে যোগ করেছে। এটি টেস্ট ফাইল
491
+ (`*.spec.*`, `*.test.*`) এবং diff-এর GitHub workflow ফাইল ও Playwright
492
+ কনফিগ কভার করে। merge-base সমাধান না হলে — shallow clone, detached HEAD,
493
+ git-বিহীন টার্গেট, ভিন্ন ডিফল্ট branch — এটি সৎভাবে অবনমিত হয়: finding
494
+ সম্পূর্ণ-ফাইল দায়ীত্বে ফিরে যায় এবং রিপোর্ট তা বলে। বেস ref
495
+ `--base <ref>` দিয়ে ওভাররাইড করুন।
496
+
497
+ ---
498
+
499
+ ## কনফিগারেশন
500
+
501
+ Mjölnir zero-config। রিপো রুটে ঐচ্ছিক `mjolnir.config.json` (বা
502
+ `.mjolnir.json`) severity, gating ও scope মেরামত করে — ডিটেকশন
503
+ সেমান্টিক্স কখনো বদলায় না।
504
+
505
+ | Key | টাইপ | প্রভাব |
506
+ | ------------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
507
+ | `exclude` | `string[]` | অতিরিক্ত ignore glob (gitignore উপসেট), বিল্ট-ইন ডিফল্টের উপরে |
508
+ | `gate` | `"advisory" \| "error" \| "warning"` | কোন severity অশূন্য কোডে বের হবে (ডিফল্ট `error`; `advisory` কখনো ব্লক করে না) |
509
+ | `severityOverrides` | `{ "<RULE-ID>": severity }` | আপনার রিপোর জন্য একটি রুলের finding পুনঃস্থাপন করে |
510
+ | `ignore` | `IgnoreEntry[]` | finding দমন করে — **`reason` আবশ্যক**; এন্ট্রি ৯০ দিন পরে মেয়াদোত্তীর্ণ হয় (স্পষ্ট `expires` তারিখ, বা না-থাকলে কনফিগ ফাইলের last-modified সময়) |
511
+ | `plugins` | `string[]` | তৃতীয় পক্ষের রুল প্যাকেজ (দেখুন [আস্থার মডেল](#আস্থার-মডেল)) |
512
+
513
+ ```json
514
+ {
515
+ "gate": "error",
516
+ "exclude": ["legacy/**"],
517
+ "severityOverrides": { "QA-PW-118": "warning" },
518
+ "ignore": [
519
+ {
520
+ "ruleId": "QA-TEST-004",
521
+ "files": ["e2e/legacy-login.spec.ts"],
522
+ "reason": "Third-party widget needs a settle delay; tracked in JIRA-4821",
523
+ "expires": "2026-12-31"
524
+ }
525
+ ]
526
+ }
527
+ ```
528
+
529
+ - **`.mjolnirignore`** — পাথ বর্জনের জন্য সাধারণ gitignore-স্টাইল ফাইল,
530
+ `exclude`-এর একই ভাষা। মেশিন-ব্যাপী নয়েজের জন্য এটি; `exclude` ব্যবহার
531
+ করুন যখন তালিকাটি version control-এ, বাকি কনফিগের পাশে থাকবে।
532
+ - **CLI ওভাররাইড** — `--strict` (quarantine রুল অন্তর্ভুক্ত),
533
+ `--width <cols>` ও `--ascii` / `--no-ascii` (টার্মিনাল রেন্ডারিং),
534
+ `--tone blunt` (আরও সোজাসাপ্টা বার্তা), `--max-duration <sec>`
535
+ (সীমিত আংশিক স্ক্যান)।
536
+ - রুল দমন ও deprecation জীবনচক্র: [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md)।
537
+
538
+ `ignore` এন্ট্রি স্বতন্ত্র `mjolnir suppressions` কমান্ডকেও চালিত করে,
539
+ যা বর্তমানে দমন করা বিষয়গুলো ও প্রতিটি এন্ট্রির মেয়াদ তালিকাভুক্ত করে।
540
+
541
+ ---
542
+
543
+ ## 📐 এক্সিট কোড ও চুক্তি
544
+
545
+ ফ্রিজ করা — ওপরে CI লজিক বানানো নিরাপদ:
546
+
547
+ | এক্সিট কোড | অর্থ |
548
+ | ---------- | ----------------------------------------------------------------- |
549
+ | `0` | পরিষ্কার — গেটে বা তার উপরে কোনো finding নেই |
550
+ | `1` | গেটে বা তার উপরে finding |
551
+ | `2` | আংশিক স্ক্যান (সময়-বাজেট শেষ, অপঠনযোগ্য ফাইল) — কখনো ব্লক করে না |
552
+ | `10` | ব্যবহার-ত্রুটি (ভুল ফ্ল্যাগ, টার্গেট অনুপস্থিত) |
553
+ | `20` | অভ্যন্তরীণ ত্রুটি |
554
+
555
+ JSON/SARIF রিপোর্ট `schemaVersion: 1`। রুল ID (`QA-<FAMILY>-NNN`)
556
+ শিপ হওয়ার পর অপরিবর্তনীয় এবং কখনো পুনরায় ব্যবহার করা হয় না।
557
+
558
+ ---
559
+
560
+ ## আস্থার মডেল
561
+
562
+ - **Local-first** — স্ক্যানের সময় শূন্য নেটওয়ার্ক কল। কখনোই নয়।
563
+ শূন্য টেলিমেট্রি।
564
+ - **মিথ্যা প্রমাণ নেই** — «যাচাই হয়েছে» বলার চেয়ে «অজানা» বলা পছন্দ।
565
+ খালি রিপো `score: null` পায়, নকল ১০০ নয়।
566
+ - **আংশিক সততা** — বিশ্লেষণ কাটা পড়লে আউটপুট তা বলে। সত্যি না হলে
567
+ কখনো «complete» নয়।
568
+ - **FP ফায়ারওয়াল** — ডিটেকশন চলে কমেন্ট/স্ট্রিং-মুক্ত কোড-ভিউতে
569
+ (TypeScript রুল কম্পাইলার AST ব্যবহার করে): গদ্য কমেন্টের ভিতরের বা
570
+ ডক-উদাহরণ স্ট্রিংয়ের প্যাটার্ন ডকুমেন্টেশন, finding নয়।
571
+ - **পরিমাপকৃত, দাবিকৃত নয়** — শুধু যে রুলের বাস্তব OSS কোড থেকে
572
+ false-positive হার আছে তা হেডলাইন টিয়ারে শিপ হয় (দেখুন
573
+ [এর মধ্যে কতটা পরিমাপ করা হয়েছে](#এর-মধ্যে-কতটা-পরিমাপ-করা-হয়েছে));
574
+ স্ক্যান ফুটার ও `mjolnir rules --unmeasured` কোনটি কী তা বলে।
575
+ - **প্লাগইন-আস্থা** — প্লাগইন হলো `"plugins"`-এর অধীনে ঘোষিত npm
576
+ প্যাকেজ। **কোনো sandbox নেই**: প্লাগইন কোড পূর্ণ Node বিশেষাধিকারে
577
+ চলে, ESLint বা Vitest প্লাগইনের একই আস্থার মডেল। core রুল-ID প্রিফিক্স
578
+ সংরক্ষিত এবং জালিয়াতি রোধে প্লাগইন থেকে প্রত্যাখ্যাত।
579
+ - **Workspace-লোকাল বাহ্যিক রুল** (ফোল্ডার-ভিত্তিক, শূন্য নেটওয়ার্ক) —
580
+ স্ক্যান টার্গেটের পাশে একটি `mjolnir-rules/` ডিরেক্টরি কাস্টম রুল লোড
581
+ করে: JSON ফাইল regex প্যাটার্ন ঘোষণা করে (কোনো কোড চালানো হয় না),
582
+ `.mjs`/`.js` মডিউল `rules` export করে (পূর্ণ Node আস্থা, প্লাগইনের
583
+ মতো)। বাহ্যিক রুল core-এর একই trust মেটাডেটা বহন করে; কখনোই core
584
+ টিয়ারে শিপ হতে পারে না (core-এর জন্য corpus sidecar থেকে পরিমাপকৃত FP
585
+ হার দরকার — ঘোষিত `tier: "core"` `extended`-এ নিচু হয়ে যায়), tier
586
+ সীমা মানে এবং ড্রিফট-চেক হয়: `mjolnir rules --md --external` লোড
587
+ করা ফাইল থেকে ক্যাটালগ রেন্ডার করে (প্রোভেন্যান্স `external`), এবং
588
+ ম্যাট্রিক্স জেনারেটর `--external <root>` গ্রহণ করে।
589
+
590
+ ---
591
+
592
+ ## 🏗️ আর্কিটেকচার
593
+
594
+ <details>
595
+ <summary>ট্রি প্রসারিত করুন</summary>
596
+
597
+ ```
598
+ mjolnir/
599
+ ├── src/
600
+ │ ├── engine/ # LanguageAdapter interface + rule runner
601
+ │ ├── adapters/ # typescript · python · java · csharp · github-actions
602
+ │ ├── rules/ # rules across 8 families + the measured-FP table
603
+ │ ├── playwright/ # Selector Health Score engine
604
+ │ ├── discovery/ # workspace, frameworks, ignore resolution
605
+ │ ├── scope/ # git merge-base changed-scope engine
606
+ │ ├── scorer/ # transparent deduction table + prioritization
607
+ │ ├── reporter/ # terminal · JSON · SARIF 2.1 · Mermaid
608
+ │ ├── forensics/ # run-data ingestion · flake verdicts · triage
609
+ │ ├── config/ # mjolnir.config.json + suppressions
610
+ │ ├── plugins/ # third-party rule loading (no sandbox)
611
+ │ └── commands/ # every subcommand
612
+ └── tests/
613
+ ├── fixtures/ # must-fire / must-not-fire per rule
614
+ └── golden/ # frozen score regression locks
615
+ ```
616
+
617
+ </details>
618
+
619
+ - **রুলগুলো বিশুদ্ধ ফাংশন** — `(SourceFileContext) → Finding[]`, I/O নেই,
620
+ গ্লোবাল নেই। নতুন ইকোসিস্টেম = এক অ্যাডাপ্টার + তার রুল।
621
+ - **TypeScript/Playwright কম্পাইলার AST ব্যবহার করে** (ts-morph)।
622
+ Python, Java ও C# মাস্কড কমেন্ট/স্ট্রিং-সহ যৌথ regex স্তরে চলে।
623
+ - Java ও C#-এর জন্য tree-sitter WASM AST স্তর বিদ্যমান এবং পরবর্তী
624
+ নির্ভুলতার পদক্ষেপ — এখনো সিনক্রোনাস স্ক্যান পাইপলাইনে যুক্ত নয়।
625
+
626
+ ---
627
+
628
+ ## 📚 ডকুমেন্টেশন
629
+
630
+ | ডকুমেন্ট | এর মধ্যে কী আছে |
631
+ | ------------------------------------------------------ | ------------------------------------- |
632
+ | [docs/SCORING.md](docs/SCORING.md) | স্কোর নরমালাইজেশন + প্রমাণ-ওয়েটিং |
633
+ | [docs/FP-AUDIT.md](docs/FP-AUDIT.md) | পরিমাপকৃত false-positive হার + পদ্ধতি |
634
+ | [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md) | রুল অবস্থা, দমন, deprecation |
635
+ | [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md) | SARIF আউটপুট + এডিটর/CI সেটআপ |
636
+ | [docs/rules/](docs/rules/) | তৈরি করা প্রতি-রুল ক্যাটালগ |
637
+ | [CONTRIBUTING.md](CONTRIBUTING.md) | ডেভ সেটআপ + অবদানের ধারা |
638
+ | [CHANGELOG.md](CHANGELOG.md) | রিলিজ ইতিহাস |
639
+ | [SECURITY.md](SECURITY.md) | দুর্বলতা রিপোর্টিং |
640
+
641
+ ---
642
+
643
+ ## 📈 অবস্থা
644
+
645
+ **v0.5.x · ওপেন বিটা।** JSON schema ও এক্সিট কোড হিমশীতল চুক্তি।
646
+ TypeScript ও Python-এর পরিমাপকৃত কভারেজ ব্যাপকতম; Java ও C# নতুন —
647
+ [টিয়ার টেবিল](#রুল-টিয়ার-ও-ভাষা-পরিপক্বতা) দিয়ে পড়ুন।
648
+
649
+ ---
650
+
651
+ ## 🤝 অবদান
652
+
653
+ নতুন রুলই সবচেয়ে সহজ প্রথম অবদান — এক কমান্ডে রুল ও তার must-fire **এবং**
654
+ must-not-fire ফিক্সচার স্কাফোল্ড হয় (তৈরি রুলটি ইচ্ছাকৃতভাবে ফিক্সচারে
655
+ ব্যর্থ হয় যতক্ষণ না আপনি আসল ডিটেকশন লেখেন — stub শিপ হতে পারে না):
656
+
657
+ ```bash
658
+ mjolnir create-rule QA-PW-140 --title "Screenshot without diff bound"
659
+ ```
660
+
661
+ সম্পূর্ণ ডেভ সেটআপ, standing-gate কমান্ড ও anti-creep / ফিক্সচার-ফায়ারওয়াল
662
+ আইন [CONTRIBUTING.md](CONTRIBUTING.md)-এ।
663
+
664
+ ---
665
+
666
+ <div align="center">
667
+
668
+ **যে টেস্টে আস্থা রাখতে পারেন না, সেগুলো শিপ করা বন্ধ করুন।**
669
+
670
+ ```bash
671
+ npx mjolnir-qa@latest
672
+ ```
673
+
674
+ **Star ⭐ · Watch 👀 · Contribute 🤝**
675
+
676
+ নির্মাণ করেছেন [Sergey Bar](https://www.linkedin.com/in/sergeybar/)
677
+
678
+ </div>