mjolnir-qa 0.5.16 → 0.5.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +1 -0
- package/dist/cli.d.mts +1 -1
- package/dist/cli.mjs +61 -3
- package/dist/mcp/stdio.mjs +61 -3
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,18 @@ Rule behavior changes (new rules, FP-rate changes against the corpus,
|
|
|
9
9
|
severity changes) are first-class entries here — rule IDs are immutable
|
|
10
10
|
once shipped, so this file is the record of what changed between versions.
|
|
11
11
|
|
|
12
|
+
## [0.5.18] — 2026-09-07
|
|
13
|
+
|
|
14
|
+
### Changes since 0.5.17
|
|
15
|
+
|
|
16
|
+
- README: add npm downloads badge
|
|
17
|
+
|
|
18
|
+
## [0.5.17] — 2026-09-07
|
|
19
|
+
|
|
20
|
+
### Changes since 0.5.16
|
|
21
|
+
|
|
22
|
+
- Fixture integrity gate: Layer A structural typecheck, doctor fixture-integrity, Python tier (F1, D9, G3) (#53)
|
|
23
|
+
|
|
12
24
|
## [0.5.16] — 2026-09-07
|
|
13
25
|
|
|
14
26
|
### Changes since 0.5.15
|
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
pipelines, reports a worthiness score, and shows exactly where trust breaks.
|
|
9
9
|
|
|
10
10
|
[](https://www.npmjs.com/package/mjolnir-qa)
|
|
11
|
+
[](https://www.npmjs.com/package/mjolnir-qa)
|
|
11
12
|
[](https://github.com/Sergey-Bar/Mjolnir/actions/workflows/ci.yml)
|
|
12
13
|
[](LICENSE)
|
|
13
14
|
[](https://nodejs.org)
|
package/dist/cli.d.mts
CHANGED
|
@@ -699,7 +699,7 @@ declare const runScan: typeof runScan$1, buildUniversalRules: typeof buildUniver
|
|
|
699
699
|
* `scripts/sync-sarif-version.cjs` on release and guarded by
|
|
700
700
|
* `tests/version-consistency.spec.ts` locally.
|
|
701
701
|
*/
|
|
702
|
-
declare const CLI_VERSION = "0.5.
|
|
702
|
+
declare const CLI_VERSION = "0.5.18";
|
|
703
703
|
/** A usage-error detail: the offending token, when one exists. */
|
|
704
704
|
interface UsageErrorDetail {
|
|
705
705
|
/** The unknown flag or rejected value (e.g. `--nope`, `loud`). */
|
package/dist/cli.mjs
CHANGED
|
@@ -13264,7 +13264,7 @@ function renderSarif(result, repoRootUri) {
|
|
|
13264
13264
|
tool: { driver: {
|
|
13265
13265
|
name: "Mjölnir",
|
|
13266
13266
|
informationUri: "https://github.com/Sergey-Bar/Mjolnir",
|
|
13267
|
-
version: "0.5.
|
|
13267
|
+
version: "0.5.18",
|
|
13268
13268
|
rules: [...rules.values()].map((r) => {
|
|
13269
13269
|
const meta = RULES.find((x) => x.id === r.id);
|
|
13270
13270
|
return {
|
|
@@ -17826,6 +17826,63 @@ function checkQuarantineEnforcement(rules = RULES) {
|
|
|
17826
17826
|
details
|
|
17827
17827
|
};
|
|
17828
17828
|
}
|
|
17829
|
+
/**
|
|
17830
|
+
* Check 8 (certification-audit Phase 2.5, plan G3 Layer A support):
|
|
17831
|
+
* fixture-integrity — deterministic structural census of the fixture
|
|
17832
|
+
* trees. Complements the fixture firewall (which proves must-fire /
|
|
17833
|
+
* must-not-fire PRESENCE per rule) with: orphaned fixture dirs (no
|
|
17834
|
+
* registered rule), empty dirs, and the Layer A typecheck allowlist
|
|
17835
|
+
* census. Blocking: an orphaned dir or an empty fixture dir means the
|
|
17836
|
+
* structural layer has silently drifted.
|
|
17837
|
+
*/
|
|
17838
|
+
function checkFixtureIntegrity(fixturesRoot, rules = RULES) {
|
|
17839
|
+
const details = [];
|
|
17840
|
+
let ok = true;
|
|
17841
|
+
const registeredIds = new Set(rules.map((r) => r.id));
|
|
17842
|
+
const fixtureRoots = [fixturesRoot, join(fixturesRoot, "..", "corpus", "positive-fixtures")];
|
|
17843
|
+
let fixtureFiles = 0;
|
|
17844
|
+
let fixtureDirs = 0;
|
|
17845
|
+
for (const root of fixtureRoots) {
|
|
17846
|
+
if (!existsSync(root)) continue;
|
|
17847
|
+
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
17848
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
17849
|
+
fixtureDirs++;
|
|
17850
|
+
const ruleId = entry.name;
|
|
17851
|
+
if (!registeredIds.has(ruleId)) {
|
|
17852
|
+
ok = false;
|
|
17853
|
+
details.push(`orphaned fixture dir (no registered rule): ${ruleId}`);
|
|
17854
|
+
continue;
|
|
17855
|
+
}
|
|
17856
|
+
const sub = join(root, ruleId);
|
|
17857
|
+
for (const child of readdirSync(sub, { withFileTypes: true })) if (child.isDirectory()) {
|
|
17858
|
+
const files = nonHiddenFiles(join(sub, child.name));
|
|
17859
|
+
fixtureFiles += files.length;
|
|
17860
|
+
if (files.length === 0) {
|
|
17861
|
+
ok = false;
|
|
17862
|
+
details.push(`empty fixture dir: ${ruleId}/${child.name}`);
|
|
17863
|
+
}
|
|
17864
|
+
} else if (!child.name.startsWith(".")) fixtureFiles++;
|
|
17865
|
+
}
|
|
17866
|
+
}
|
|
17867
|
+
const allowlistPath = join(fixturesRoot, "typecheck-allowlist.json");
|
|
17868
|
+
if (existsSync(allowlistPath)) try {
|
|
17869
|
+
const parsed = JSON.parse(readFileSync(allowlistPath, "utf8"));
|
|
17870
|
+
if (Array.isArray(parsed.entries)) details.push(`Layer A typecheck allowlist: ${parsed.entries.length} justified entr(ies)`);
|
|
17871
|
+
else {
|
|
17872
|
+
ok = false;
|
|
17873
|
+
details.push("typecheck-allowlist.json entries is not an array");
|
|
17874
|
+
}
|
|
17875
|
+
} catch {
|
|
17876
|
+
ok = false;
|
|
17877
|
+
details.push("typecheck-allowlist.json is unreadable/malformed");
|
|
17878
|
+
}
|
|
17879
|
+
details.unshift(`fixture trees: ${fixtureDirs} rule dirs, ${fixtureFiles} fixture files — orphaned dirs and empty dirs are blocking`);
|
|
17880
|
+
return {
|
|
17881
|
+
name: "fixture-integrity",
|
|
17882
|
+
ok,
|
|
17883
|
+
details
|
|
17884
|
+
};
|
|
17885
|
+
}
|
|
17829
17886
|
function runDoctorSelfAudit(fixturesRoot) {
|
|
17830
17887
|
const verdictsDir = join(fixturesRoot, "..", "corpus", "verdicts");
|
|
17831
17888
|
const checks = [
|
|
@@ -17835,7 +17892,8 @@ function runDoctorSelfAudit(fixturesRoot) {
|
|
|
17835
17892
|
checkEvidenceHonesty(),
|
|
17836
17893
|
checkTierEnforcement(verdictsDir),
|
|
17837
17894
|
checkAntiCreep(),
|
|
17838
|
-
checkQuarantineEnforcement()
|
|
17895
|
+
checkQuarantineEnforcement(),
|
|
17896
|
+
checkFixtureIntegrity(fixturesRoot)
|
|
17839
17897
|
];
|
|
17840
17898
|
return {
|
|
17841
17899
|
checks,
|
|
@@ -18074,7 +18132,7 @@ const { runScan, buildUniversalRules, fallbackWorkspace, pathMatchesGlob, isVali
|
|
|
18074
18132
|
* `scripts/sync-sarif-version.cjs` on release and guarded by
|
|
18075
18133
|
* `tests/version-consistency.spec.ts` locally.
|
|
18076
18134
|
*/
|
|
18077
|
-
const CLI_VERSION = "0.5.
|
|
18135
|
+
const CLI_VERSION = "0.5.18";
|
|
18078
18136
|
function parseArgs(argv, onError) {
|
|
18079
18137
|
const args = {
|
|
18080
18138
|
target: ".",
|
package/dist/mcp/stdio.mjs
CHANGED
|
@@ -13263,7 +13263,7 @@ function renderSarif(result, repoRootUri) {
|
|
|
13263
13263
|
tool: { driver: {
|
|
13264
13264
|
name: "Mjölnir",
|
|
13265
13265
|
informationUri: "https://github.com/Sergey-Bar/Mjolnir",
|
|
13266
|
-
version: "0.5.
|
|
13266
|
+
version: "0.5.18",
|
|
13267
13267
|
rules: [...rules.values()].map((r) => {
|
|
13268
13268
|
const meta = RULES.find((x) => x.id === r.id);
|
|
13269
13269
|
return {
|
|
@@ -17308,6 +17308,63 @@ function checkQuarantineEnforcement(rules = RULES) {
|
|
|
17308
17308
|
details
|
|
17309
17309
|
};
|
|
17310
17310
|
}
|
|
17311
|
+
/**
|
|
17312
|
+
* Check 8 (certification-audit Phase 2.5, plan G3 Layer A support):
|
|
17313
|
+
* fixture-integrity — deterministic structural census of the fixture
|
|
17314
|
+
* trees. Complements the fixture firewall (which proves must-fire /
|
|
17315
|
+
* must-not-fire PRESENCE per rule) with: orphaned fixture dirs (no
|
|
17316
|
+
* registered rule), empty dirs, and the Layer A typecheck allowlist
|
|
17317
|
+
* census. Blocking: an orphaned dir or an empty fixture dir means the
|
|
17318
|
+
* structural layer has silently drifted.
|
|
17319
|
+
*/
|
|
17320
|
+
function checkFixtureIntegrity(fixturesRoot, rules = RULES) {
|
|
17321
|
+
const details = [];
|
|
17322
|
+
let ok = true;
|
|
17323
|
+
const registeredIds = new Set(rules.map((r) => r.id));
|
|
17324
|
+
const fixtureRoots = [fixturesRoot, join(fixturesRoot, "..", "corpus", "positive-fixtures")];
|
|
17325
|
+
let fixtureFiles = 0;
|
|
17326
|
+
let fixtureDirs = 0;
|
|
17327
|
+
for (const root of fixtureRoots) {
|
|
17328
|
+
if (!existsSync(root)) continue;
|
|
17329
|
+
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
17330
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
17331
|
+
fixtureDirs++;
|
|
17332
|
+
const ruleId = entry.name;
|
|
17333
|
+
if (!registeredIds.has(ruleId)) {
|
|
17334
|
+
ok = false;
|
|
17335
|
+
details.push(`orphaned fixture dir (no registered rule): ${ruleId}`);
|
|
17336
|
+
continue;
|
|
17337
|
+
}
|
|
17338
|
+
const sub = join(root, ruleId);
|
|
17339
|
+
for (const child of readdirSync(sub, { withFileTypes: true })) if (child.isDirectory()) {
|
|
17340
|
+
const files = nonHiddenFiles(join(sub, child.name));
|
|
17341
|
+
fixtureFiles += files.length;
|
|
17342
|
+
if (files.length === 0) {
|
|
17343
|
+
ok = false;
|
|
17344
|
+
details.push(`empty fixture dir: ${ruleId}/${child.name}`);
|
|
17345
|
+
}
|
|
17346
|
+
} else if (!child.name.startsWith(".")) fixtureFiles++;
|
|
17347
|
+
}
|
|
17348
|
+
}
|
|
17349
|
+
const allowlistPath = join(fixturesRoot, "typecheck-allowlist.json");
|
|
17350
|
+
if (existsSync(allowlistPath)) try {
|
|
17351
|
+
const parsed = JSON.parse(readFileSync(allowlistPath, "utf8"));
|
|
17352
|
+
if (Array.isArray(parsed.entries)) details.push(`Layer A typecheck allowlist: ${parsed.entries.length} justified entr(ies)`);
|
|
17353
|
+
else {
|
|
17354
|
+
ok = false;
|
|
17355
|
+
details.push("typecheck-allowlist.json entries is not an array");
|
|
17356
|
+
}
|
|
17357
|
+
} catch {
|
|
17358
|
+
ok = false;
|
|
17359
|
+
details.push("typecheck-allowlist.json is unreadable/malformed");
|
|
17360
|
+
}
|
|
17361
|
+
details.unshift(`fixture trees: ${fixtureDirs} rule dirs, ${fixtureFiles} fixture files — orphaned dirs and empty dirs are blocking`);
|
|
17362
|
+
return {
|
|
17363
|
+
name: "fixture-integrity",
|
|
17364
|
+
ok,
|
|
17365
|
+
details
|
|
17366
|
+
};
|
|
17367
|
+
}
|
|
17311
17368
|
function runDoctorSelfAudit(fixturesRoot) {
|
|
17312
17369
|
const verdictsDir = join(fixturesRoot, "..", "corpus", "verdicts");
|
|
17313
17370
|
const checks = [
|
|
@@ -17317,7 +17374,8 @@ function runDoctorSelfAudit(fixturesRoot) {
|
|
|
17317
17374
|
checkEvidenceHonesty(),
|
|
17318
17375
|
checkTierEnforcement(verdictsDir),
|
|
17319
17376
|
checkAntiCreep(),
|
|
17320
|
-
checkQuarantineEnforcement()
|
|
17377
|
+
checkQuarantineEnforcement(),
|
|
17378
|
+
checkFixtureIntegrity(fixturesRoot)
|
|
17321
17379
|
];
|
|
17322
17380
|
return {
|
|
17323
17381
|
checks,
|
|
@@ -17710,7 +17768,7 @@ const { runScan, buildUniversalRules, fallbackWorkspace, pathMatchesGlob, isVali
|
|
|
17710
17768
|
* `scripts/sync-sarif-version.cjs` on release and guarded by
|
|
17711
17769
|
* `tests/version-consistency.spec.ts` locally.
|
|
17712
17770
|
*/
|
|
17713
|
-
const CLI_VERSION = "0.5.
|
|
17771
|
+
const CLI_VERSION = "0.5.18";
|
|
17714
17772
|
function parseArgs(argv, onError) {
|
|
17715
17773
|
const args = {
|
|
17716
17774
|
target: ".",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mjolnir-qa",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.18",
|
|
4
4
|
"description": "Mjölnir — the Verification Trust Engine for QA. Audits test suites and CI pipelines, reports a worthiness score and prioritized findings.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"test:watch": "vitest",
|
|
29
29
|
"lint": "eslint . --max-warnings=0 && prettier --check .",
|
|
30
30
|
"format": "prettier --write .",
|
|
31
|
-
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json",
|
|
31
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json && tsx scripts/typecheck-fixtures.ts",
|
|
32
32
|
"golden:update": "tsx tests/golden/gen.ts",
|
|
33
33
|
"corpus:regression": "tsx tests/corpus/audit.ts",
|
|
34
34
|
"corpus:regression:update": "tsx tests/corpus/audit.ts --update",
|