mjolnir-qa 4.0.0 → 5.0.0
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 +269 -1
- package/README.br.md +10 -10
- package/README.md +12 -12
- package/dist/cli.d.mts +181 -4
- package/dist/cli.mjs +1481 -120
- package/dist/mcp/stdio.mjs +390 -193
- package/dist/scan-pipeline-DNYbnWjE.mjs +2 -0
- package/dist/{scan-pipeline-CCJdEKNa.mjs → scan-pipeline-DT8DWAMl.mjs} +344 -179
- package/package.json +25 -8
- package/dist/scan-pipeline-Bg-46cLS.mjs +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,8 @@ once shipped, so this file is the record of what changed between versions.
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
+
## [5.0.0] — 2026-09-27
|
|
15
|
+
|
|
14
16
|
### Breaking changes
|
|
15
17
|
|
|
16
18
|
- **`business-case` no longer prints a dollar figure you did not supply.** The
|
|
@@ -47,6 +49,18 @@ once shipped, so this file is the record of what changed between versions.
|
|
|
47
49
|
The generation timestamp moved out of the visible body into
|
|
48
50
|
`<meta name="mjolnir-generated-at">`, and `--deterministic` omits it entirely
|
|
49
51
|
so the artifact is byte-identical and can be diffed in review.
|
|
52
|
+
- **`mjolnir report` no longer publishes a test-run status.** The top-level
|
|
53
|
+
`status` was computed from the findings
|
|
54
|
+
(`partial ? "interrupted" : hasError ? "failed" : "passed"`), so a clean scan
|
|
55
|
+
wrote `{status: "passed", totalTests: 0}` — the first field a Playwright
|
|
56
|
+
consumer reads, asserting a green run over zero executed tests, with the
|
|
57
|
+
contradicting `execution: "STATIC_ANALYSIS"` marker buried in the extension
|
|
58
|
+
block. `failed` was equally dishonest: it implies tests ran and lost. The
|
|
59
|
+
field is now the literal `interrupted` on every path, and what the scan
|
|
60
|
+
actually found is reported separately as
|
|
61
|
+
`mjolnir.scanOutcome` (`clean` / `blocked` / `partial`), which the previous
|
|
62
|
+
`mjolnir.status` field carried. Consumers reading the root `status` will see
|
|
63
|
+
`passed` → `interrupted`; that is the correction, not a regression.
|
|
50
64
|
- **Score bands are decided in one place.** The dashboard split at 60 while the
|
|
51
65
|
terminal split at 80, so a score the terminal called UNWORTHY rendered amber
|
|
52
66
|
in HTML; the contributor handover used 90; `mermaid.ts` and
|
|
@@ -55,6 +69,14 @@ once shipped, so this file is the record of what changed between versions.
|
|
|
55
69
|
|
|
56
70
|
### Added
|
|
57
71
|
|
|
72
|
+
- **`npm run ci:standard` now runs in CI.** `scripts/check-ci-standard.mjs`
|
|
73
|
+
was unit-tested by `tests/integrations/ci-standard.spec.ts` but referenced
|
|
74
|
+
by no workflow, so the invariants it checks — the PR scan's partial-analysis
|
|
75
|
+
handling, the coverage and ratchet gates, and the read-only scan job's
|
|
76
|
+
permissions — were enforced nowhere. It runs as a step in `workflow-lint`,
|
|
77
|
+
next to actionlint, since both are static checks of the repo's own CI
|
|
78
|
+
config.
|
|
79
|
+
|
|
58
80
|
- **Opt-in Sentry crash reporting.** Set `SENTRY_DSN` and the CLI and the MCP
|
|
59
81
|
stdio server report their own fatal errors — release-tagged as
|
|
60
82
|
`mjolnir-qa@<version>`, tagged by surface, and nothing else: no user data
|
|
@@ -67,7 +89,11 @@ once shipped, so this file is the record of what changed between versions.
|
|
|
67
89
|
the two existing top-level catch blocks rather than `uncaughtException` /
|
|
68
90
|
`unhandledRejection` handlers, which would have changed the frozen exit-code
|
|
69
91
|
contract. `npm run sentry:release` creates the release and uploads source maps
|
|
70
|
-
(the build now emits them; the published tarball still excludes them).
|
|
92
|
+
(the build now emits them; the published tarball still excludes them). Both
|
|
93
|
+
release pipelines now run it after `npm run build` and before `npm pack`,
|
|
94
|
+
because the debug ids it stamps are written into `dist/` in place: pack first
|
|
95
|
+
and the shipped tarball carries no ids, so the maps uploaded for the release
|
|
96
|
+
match nothing and every stack trace degrades to a bundle frame.
|
|
71
97
|
- `npm run report:honesty` — no surface may print a zero for a measurement that
|
|
72
98
|
may be absent. Four reviewed exceptions are recorded for computation inputs,
|
|
73
99
|
each with a reason and a follow-up.
|
|
@@ -93,8 +119,229 @@ once shipped, so this file is the record of what changed between versions.
|
|
|
93
119
|
or descriptor to print. It opens no file, touches no clock and formats no
|
|
94
120
|
document, and a test enforces that.
|
|
95
121
|
|
|
122
|
+
### Changed
|
|
123
|
+
|
|
124
|
+
- **Ruleset `01` now enforces 11 required checks instead of 4.** It required
|
|
125
|
+
only the four `build-test` matrix contexts, which meant the repo's entire
|
|
126
|
+
review surface — self-scan, certification, generated-docs-drift, site-build,
|
|
127
|
+
workflow-lint, property, fuzz — could be red while a PR still merged. That
|
|
128
|
+
made every one of those checks advice rather than a gate, and it meant the
|
|
129
|
+
deleted `merge-verify.yml` header was describing a ruleset that never
|
|
130
|
+
existed. The enforced set is now the four matrix entries plus
|
|
131
|
+
`property-tests`, `fuzz`, `site-build`, `workflow-lint`, `self-scan`,
|
|
132
|
+
`certification` and `generated-docs-drift` — every check that can actually
|
|
133
|
+
block. Deliberately still _not_ required: `detector-revision-diff` (advisory
|
|
134
|
+
by design — a hard fail would only manufacture routine revision bumps),
|
|
135
|
+
`scan` / `publish` (the Mjölnir PR report, which reviewers read but which
|
|
136
|
+
duplicates `self-scan` on the `src` tree), and the CodeQL/OSV workflows,
|
|
137
|
+
which report but do not gate. `strict_required_status_checks_policy` and
|
|
138
|
+
`do_not_enforce_on_create` are preserved, so a PR must still be current with
|
|
139
|
+
`main` to merge.
|
|
140
|
+
- **`docs/BRANCH-PROTECTION.md` now states the enforced set instead of an
|
|
141
|
+
aspiration.** It listed eight required checks; three of them were enforced.
|
|
142
|
+
Its verification command also read `branches/main/protection`, which does not
|
|
143
|
+
carry required checks at all on this repo — they live in ruleset `01` — so it
|
|
144
|
+
could not have worked. Both are corrected.
|
|
145
|
+
|
|
146
|
+
### Removed
|
|
147
|
+
|
|
148
|
+
- **The Snyk webhook is gone.** Snyk posted a `security/snyk (sergey-bar)`
|
|
149
|
+
commit status on every push, duplicating the first-party `osv-scanner.yml`
|
|
150
|
+
(`scan-pr` / `scan-scheduled`) and the Dependabot security updates that are
|
|
151
|
+
already configured. Repo webhooks are now empty. Restore with Snyk's own
|
|
152
|
+
integration settings if it is ever wanted back — the deleted hook pointed at
|
|
153
|
+
`https://api.snyk.io/webhook/github/4ac3f376-3f24-42f7-a2c4-8830700f671d`.
|
|
154
|
+
- **The `merge-verify` workflow is gone.** It re-ran the entire PR gate
|
|
155
|
+
(build, typecheck, lint, coverage, property, fuzz, site, self-scan, docs
|
|
156
|
+
regen) on every push to `main` — roughly doubling CI cost. Its stated
|
|
157
|
+
justification was that "a green PR that merges into a red main is not a
|
|
158
|
+
green PR", but that failure class is already closed by ruleset `01` running
|
|
159
|
+
`strict_required_status_checks_policy`, which refuses to merge a PR that is
|
|
160
|
+
not current with `main`. The file also asserted that ruleset `01` "requires
|
|
161
|
+
the complete PR matrix, property/fuzz, site, workflow-lint, self-scan,
|
|
162
|
+
certification, detector-revision, and generated-docs checks before merge" —
|
|
163
|
+
it did not, and never has. The ruleset requires only the four `build-test`
|
|
164
|
+
matrix contexts. Both the workflow and the claim are removed rather than left
|
|
165
|
+
to mislead a reviewer about what blocks a merge.
|
|
166
|
+
- **The `bench-advisory` CI job is gone.** It spent a full `npm ci` plus a
|
|
167
|
+
3 000-file benchmark run on every PR to produce a comparison that
|
|
168
|
+
`continue-on-error: true` then discarded, and whose only output was a
|
|
169
|
+
warning nobody could gate on. `npm run bench:scan`, the pinned
|
|
170
|
+
`.mjolnir/bench-baseline.json`, and their schema/comparison tests in
|
|
171
|
+
`tests/adversarial/bench-gate.spec.ts` are untouched — the benchmark is
|
|
172
|
+
still runnable and still tested, it just no longer costs every PR a runner.
|
|
173
|
+
- **Four copies of the soft job time-budget report.** Each of `build-test`,
|
|
174
|
+
`self-scan`, `generated-docs-drift` and `bench-advisory` re-derived its own
|
|
175
|
+
wall-clock minutes into `$GITHUB_STEP_SUMMARY` and printed a warning that,
|
|
176
|
+
by its own comment, "never gates". The jobs already declare
|
|
177
|
+
`timeout-minutes`, which is the hard bound, and GitHub reports job duration
|
|
178
|
+
natively.
|
|
179
|
+
|
|
96
180
|
### Fixed
|
|
97
181
|
|
|
182
|
+
- **Order in the report no longer depends on the machine that produced it.** 23
|
|
183
|
+
sites in `src/` sorted with a bare `localeCompare`, which resolves against the
|
|
184
|
+
process's default locale. `scan-cache.ts` was the worst: it sorted INSIDE
|
|
185
|
+
`hashDir`, so the order was fed to `hash.update(entry.name)` and became a
|
|
186
|
+
semantic input to the detector fingerprint that `isIncrementalSafe` keys off
|
|
187
|
+
— one repository, two fingerprints, one per locale. All 23 now use
|
|
188
|
+
`compareCodePoints` from `src/lib/compare.ts`, except two whose key is a
|
|
189
|
+
human-readable label, which use a comparator pinned to `en` rather than
|
|
190
|
+
inheriting the environment. `tests/contract/deterministic-ordering.spec.ts`
|
|
191
|
+
fails the build on any unpinned `localeCompare` in `src/`.
|
|
192
|
+
- **`mjolnir bind` can no longer be hijacked by a repository's own `git`.**
|
|
193
|
+
`src/engine/candidate-binding.ts` — called on every scan — ran
|
|
194
|
+
`execFileSync("git", …)` with a bare name. On Windows `CreateProcess` searches
|
|
195
|
+
the current directory before `PATH`, so a committed `git.exe` in an untrusted
|
|
196
|
+
repo chose the `commit` and `tree` that land in `runIdentity` and the evidence
|
|
197
|
+
graph. It is the last bare-name process launch left in `src/`, and
|
|
198
|
+
`tests/contract/no-bare-process-exec.spec.ts` now audits the whole tree.
|
|
199
|
+
- **The coverage exemption ledger can no longer drift silently.** Each
|
|
200
|
+
non-structural entry in `docs/COVERAGE-EXEMPTIONS.json` now carries
|
|
201
|
+
`defectSignatures` — regexes that must still match its file — or an explicit
|
|
202
|
+
`closureState` when the defect it described is gone. A signature that stops
|
|
203
|
+
matching fails the gate, so a code fix forces a ledger edit in the same
|
|
204
|
+
commit. Six entries were already describing defects the current code had
|
|
205
|
+
fixed (`maturity`, `business-case`, `dashboard`, `enterprise`, `exec-report`
|
|
206
|
+
and the `report-playwright` row), and are reclassified accordingly.
|
|
207
|
+
- **Skipped and expected-fail tests now have a price.** Nothing in the
|
|
208
|
+
repository counted them, so the count could grow from zero to hundreds with
|
|
209
|
+
every other signal still green. `docs/SKIP-BUDGET.json` records the current
|
|
210
|
+
counts — 8 skipped tests, 1 skipped file, 3 expected failures — measured by
|
|
211
|
+
`scripts/vitest-skip-budget-reporter.mjs` during the run. They may fall;
|
|
212
|
+
raising them fails `npm run skip:budget` and names the tests that spent the
|
|
213
|
+
budget. A missing or incomplete measurement fails too, so a reporter that
|
|
214
|
+
stops being wired in cannot read as a clean run.
|
|
215
|
+
- **The M26 release gate and the version-drift checks are covered where it
|
|
216
|
+
matters.** Both run inside `certify` and both were excluded from the coverage
|
|
217
|
+
ratchet, so their reject arms — the paths that only execute when something is
|
|
218
|
+
wrong — had no reviewer pressure on them. `tests/ledger/m26-validator-reject-arms.spec.ts`
|
|
219
|
+
is table-driven over every required field, so a field that is added without a
|
|
220
|
+
test fails; `tests/release/version-surface-drift-arms.spec.ts` covers the
|
|
221
|
+
fail-fast arms of the version synchronizer.
|
|
222
|
+
- **One error derivation, one `isRecord`, one crash-isolation path.** The tree
|
|
223
|
+
carried `isRecord` eight times (seven byte-identical), error rendering four
|
|
224
|
+
times under two names, and crash isolation seven times — and the copies
|
|
225
|
+
disagreed, which is the only reason any of this mattered. `String(obj)`
|
|
226
|
+
renders `[object Object]`, so the two _thinner_ error helpers turned a real
|
|
227
|
+
Node failure shape like `throw { code: "EISDIR" }` into a message naming
|
|
228
|
+
nothing — in a doctor detail, an MCP tool result and a `summary` line
|
|
229
|
+
respectively. The richer body survived the collapse, and
|
|
230
|
+
`isRecord` — the predicate every JSON reader uses to decide "this is an
|
|
231
|
+
object I may index into", which is a question a hostile saved report gets
|
|
232
|
+
to ask — is now the exported one in `src/lib/safe-json.ts`. The four
|
|
233
|
+
adapters' duplicated `try { rule.run } catch { onCrash }` is now
|
|
234
|
+
`runRuleIsolated`, because that block is what the `rulesCrashed` count is
|
|
235
|
+
built on and seven copies mean seven chances to lose it.
|
|
236
|
+
The per-adapter _filter chain_ and budget check were deliberately **not**
|
|
237
|
+
folded into the existing `runRulesShared`: the TypeScript adapter injects
|
|
238
|
+
an AST, framework tags, a lazy `codeText` getter, config-only gating and
|
|
239
|
+
`configGateMatches`, and it _aborts the whole file_ on budget exhaustion
|
|
240
|
+
where `runRulesShared` only skips one rule. Routing every adapter through
|
|
241
|
+
it as written would have silently changed which rules run — the exact
|
|
242
|
+
unmeasured claim this program exists to delete.
|
|
243
|
+
`tests/contract/single-error-helper.spec.ts` holds the counts at one, and
|
|
244
|
+
pins the object arm so a future "simplification" cannot drop it.
|
|
245
|
+
|
|
246
|
+
- **A crashed rule is no longer certified as a rule that stayed silent.**
|
|
247
|
+
The doc generator caught a rule THROW while running it against its own
|
|
248
|
+
must-not-fire fixture and returned `null`; the caller read that as
|
|
249
|
+
`fired: false` and committed, into a generated doc page and the
|
|
250
|
+
certification surface, the sentence _"Verified against … — a legitimate,
|
|
251
|
+
similar-looking pattern this rule correctly leaves alone."_ A detector that
|
|
252
|
+
crashes produces exactly the same absence of findings as one that correctly
|
|
253
|
+
abstains, so that claim had no evidence behind it — and it contradicted the
|
|
254
|
+
same function's own docstring, which says the field degrades honestly.
|
|
255
|
+
The outcome is now the tri-state the doctor's own model already defines:
|
|
256
|
+
`FIRED` (a real firewall violation), `DID_NOT_FIRE` (certified), and
|
|
257
|
+
`INCONCLUSIVE` (the rule did not run). An `INCONCLUSIVE` page says so and
|
|
258
|
+
says why, and it never renders the certification sentence. A missing
|
|
259
|
+
fixture is a fourth, separate thing — nothing was attempted — and says
|
|
260
|
+
that instead. The field is typed as a `FixtureOutcome` union rather than a
|
|
261
|
+
boolean, so reintroducing a derived `fired` flag does not typecheck.
|
|
262
|
+
|
|
263
|
+
- **A human-facing table stopped sorting by code unit.** Pinning every
|
|
264
|
+
`localeCompare` to code-unit order (the fix for the ambient-locale drift)
|
|
265
|
+
made the per-rule corpus-occurrence table read
|
|
266
|
+
`SeleniumHQ-selenium` before `microsoft-playwright-dotnet`, because `S`
|
|
267
|
+
sorts before `m`. Stable, and worse to read than the alphabetical order the
|
|
268
|
+
table implies it has. That surface is a table a person chooses repos from,
|
|
269
|
+
so it now uses an **explicitly pinned** locale — readable order, identical
|
|
270
|
+
on every machine, which is the actual defect being avoided.
|
|
271
|
+
|
|
272
|
+
- **Fifteen silent capability losses are now counted, and the count reaches the
|
|
273
|
+
report.** A `catch` in a detection path that returned a clean default made
|
|
274
|
+
the scan exit 0, report `analysisComplete`, and quietly lose the thing that
|
|
275
|
+
made its verdict trustworthy — the reader had no way to tell a whole scan
|
|
276
|
+
from a narrowed one. The worst case was `getCodeOnlyText` returning raw text
|
|
277
|
+
after a throw, which switches OFF the comment/string false-positive firewall
|
|
278
|
+
for that file rather than merely slowing it down. Every such site now records
|
|
279
|
+
a reason in one append-only ledger (`src/engine/degradation-ledger.ts`),
|
|
280
|
+
following the precedent already set by `parserRetryDegradationCount`, and the
|
|
281
|
+
scan result carries `analysisStatus.degradations` — reason-coded counts,
|
|
282
|
+
present only when something was actually lost, alongside a
|
|
283
|
+
`degraded:<reason>:<count>` entry in `reasons`. A non-empty set makes the
|
|
284
|
+
scan `partial`. `truncationReasons` is untouched: a degradation is not
|
|
285
|
+
truncation, and a reader must be able to tell "the scan stopped" from "the
|
|
286
|
+
scan lost a layer".
|
|
287
|
+
- Two distinctions the ledger refuses to blur. **Absent is not degraded:** a
|
|
288
|
+
Python, Java, Go or Rust repository has no `package.json`, and recording
|
|
289
|
+
that would mark every non-Node project partial while training readers to
|
|
290
|
+
ignore the field — so only a manifest that exists and will not parse
|
|
291
|
+
counts. **A no-AST adapter is not a downgrade either:** the same line
|
|
292
|
+
W1.4 draws for the deadline fallback.
|
|
293
|
+
- Two sites outside the original list were found by the new contract spec
|
|
294
|
+
and fixed in the same commit: the tree-sitter Java/C#/Python parses (left
|
|
295
|
+
alone, because the pipeline already counts every `parseAst → undefined` as
|
|
296
|
+
one parse fallback, and counting again would tally one file twice in two
|
|
297
|
+
different fields) and `hashDir`'s `readdirSync`, whose early `return`
|
|
298
|
+
omitted a whole subtree from the detector fingerprint — two rule trees
|
|
299
|
+
differing only inside an unreadable directory then hash identically, which
|
|
300
|
+
is a stale-cache hit wearing a fresh one's clothes.
|
|
301
|
+
- `mjolnir trend show` no longer prints "No trend data yet." and exits clean
|
|
302
|
+
for a history file whose every line failed to parse. The count of
|
|
303
|
+
discarded lines is now reported on stderr, and a history that exists but
|
|
304
|
+
cannot be read is `EXIT_INTERNAL` — the difference between no history and
|
|
305
|
+
unreadable history, which both used to arrive as the same empty array.
|
|
306
|
+
- `mjolnir explain` distinguishes _the rule found nothing_ from _the rule
|
|
307
|
+
never ran_. A rule that THREW on its own fixture produced the same output
|
|
308
|
+
as a correctly silent one, in the one surface whose purpose is to show
|
|
309
|
+
that a rule works. It now says so, with the reason.
|
|
310
|
+
- `tests/contract/no-uncounted-degradation.spec.ts` is the durable half: a
|
|
311
|
+
`catch` in the detection path that RETURNS A VALUE must record a reason,
|
|
312
|
+
rethrow, route to a counted sink, or be exempted with a stated failure
|
|
313
|
+
direction. The rule is about the _return_, not the `catch` — 37 of the 70
|
|
314
|
+
detection-path catches return nothing and cannot lie to a caller, and a
|
|
315
|
+
spec that listed all 70 as individual exemptions would be the same as no
|
|
316
|
+
exemption list at all.
|
|
317
|
+
|
|
318
|
+
- **Scanning a subdirectory no longer reports a framework it read from its
|
|
319
|
+
parent.** `runScan` walks up past the target to find the project root, and
|
|
320
|
+
re-anchors the scan root to the target when the target is a strict
|
|
321
|
+
descendant — but it kept the discovered root's parsed `package.json`. So
|
|
322
|
+
`mjolnir scan monorepo/packages/foo` reported `jest` because
|
|
323
|
+
`monorepo/package.json` said so: a read outside the explicit scan root, and
|
|
324
|
+
a framework verdict about a tree nobody pointed at. Framework detection now
|
|
325
|
+
works from in-root evidence only (config files and the anchor's own
|
|
326
|
+
manifest), and reports `frameworkDetectionUnknown` when that evidence is
|
|
327
|
+
absent — an absent verdict you can see, rather than a parent's you cannot.
|
|
328
|
+
Scanning the project root itself is unchanged. This is the CLI counterpart
|
|
329
|
+
to the containment property the MCP workspace-boundary contract already
|
|
330
|
+
enforced.
|
|
331
|
+
- **A scan that ran out of time no longer loses its AST stage silently.** When a
|
|
332
|
+
scan passes `--max-duration`, the remaining files were analyzed by regex
|
|
333
|
+
instead of AST — a different detection capability, not a slower version of the
|
|
334
|
+
same one, so the same tree produced different findings and a different score.
|
|
335
|
+
The mode was faithfully recorded in the scan cache and reported nowhere; the
|
|
336
|
+
only nearby disclosure, `file-budget`, fired incidentally because the
|
|
337
|
+
per-file rule-loop deadline was usually already spent too. Such a scan now
|
|
338
|
+
carries the truncation reason `ast-budget-fallback` and an
|
|
339
|
+
`analysisStatus.reasons` entry `ast-budget-fallback-files:<n>` saying how many
|
|
340
|
+
files were analyzed by regex, and is honestly `partial`. An adapter that
|
|
341
|
+
declares no AST stage at all (a YAML workflow, for instance) is a declared
|
|
342
|
+
capability envelope and is NOT reported as truncation. `truncationReasons` is
|
|
343
|
+
an open `string[]` by design, so consumers should test for members rather than
|
|
344
|
+
assert the exact array.
|
|
98
345
|
- **`frontier:contracts` was reporting 19 suites it never ran.** It named 24
|
|
99
346
|
test files; nineteen had been deleted by the `cc5fcb88` cleanup and its
|
|
100
347
|
follow-up. Vitest treats a missing path as "no tests here" rather than an
|
|
@@ -285,6 +532,27 @@ were live in 3.0.0 and neither was in the plan.
|
|
|
285
532
|
|
|
286
533
|
### Known open, deliberately
|
|
287
534
|
|
|
535
|
+
- [ ] **Trust certification is not claimed, and this release was authorized
|
|
536
|
+
without it.** `candidate:readiness` reports `BLOCKED` /
|
|
537
|
+
`INCONCLUSIVE` on all ten of its checks — candidate identity is not a
|
|
538
|
+
release candidate, the candidate SHA is not authorized, engineering
|
|
539
|
+
certification is not complete, and external validation, protected holdout
|
|
540
|
+
proof, real-world repository proof, the platform matrix, consumer install
|
|
541
|
+
proof and remote workflow proof are all absent.
|
|
542
|
+
`m26:audit` is likewise unsatisfied. Both ran, both printed, and neither
|
|
543
|
+
blocked the publish, because `vars.STABLE_RELEASE_AUTHORIZED` is `true` —
|
|
544
|
+
the same explicit override 4.0.0 shipped under.
|
|
545
|
+
The distinction is the point: **publication of a package is not the same
|
|
546
|
+
claim as a certification of it.** 5.0.0 asserts that the reporting
|
|
547
|
+
surface is honest, that a silent capability loss is disclosed, and that no
|
|
548
|
+
verdict is published without a measurement behind it. It does not assert
|
|
549
|
+
that a holdout corpus, a platform matrix and an external validation run
|
|
550
|
+
have been completed, because none of them have.
|
|
551
|
+
Consumers are not asked to take that on trust: the npm package publishes
|
|
552
|
+
the candidate manifest, which reads `NOT_CERTIFIED` and
|
|
553
|
+
`NOT_AUTHORIZED`, and every one of those ten blockers is enumerated above
|
|
554
|
+
rather than summarised. Closing them is the work that would make a
|
|
555
|
+
certification claim possible; it is not work this release did.
|
|
288
556
|
- [ ] `CERTIFICATION_STATES` is not in ladder order, so `rankOf` — the only
|
|
289
557
|
ordering function — ranks `KNOWN` above `TRUST-COMPLETE`, and
|
|
290
558
|
`requiresEvidence` asks `BLOCKED` and `DEGRADED` for a corpus their own
|
package/README.br.md
CHANGED
|
@@ -18,7 +18,7 @@ e depois pontua até onde o resultado merece confiança, com a evidência de cad
|
|
|
18
18
|
[](https://nodejs.org)
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
npx mjolnir-qa@
|
|
21
|
+
npx mjolnir-qa@5.0.0
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
[Veja funcionando](#veja-funcionando) · [Início rápido](#início-rápido) · [O que encontra](#o-que-o-mjölnir-encontra) · [Pontuação](#a-pontuação-de-confiabilidade) · [Evidência](#o-modelo-de-evidência) · [Forense](#forense-de-execução) · [CI](#integridade-de-ci) · [Agentes](#agentes-de-ia) · [Segurança](#confiança-e-segurança) · [Limites](#o-que-o-mjölnir-não-pode-dizer) · [Docs](#documentação)
|
|
@@ -72,7 +72,7 @@ Nenhum deles deixa o pipeline vermelho, e todos parecem intencionais na revisão
|
|
|
72
72
|
|
|
73
73
|
<sub>Cada achado que o scan de demonstração reportou para este workflow, na linha reportada. Gerado por `npm run docs:readme-brand` a partir de [`demo-report.json`](assets/readme/demo-report.json) e travado contra desvios na CI.</sub>
|
|
74
74
|
|
|
75
|
-
**Modo estrito.** As detecções mais agressivas — `.only`, `continue-on-error`, testes vazios, abuso de retry — ficam na quarentena. Só rodam com `--strict` e são limitadas a severidade `info`: elas sinalizam, nunca bloqueiam. O scan padrão (`npx mjolnir-qa@
|
|
75
|
+
**Modo estrito.** As detecções mais agressivas — `.only`, `continue-on-error`, testes vazios, abuso de retry — ficam na quarentena. Só rodam com `--strict` e são limitadas a severidade `info`: elas sinalizam, nunca bloqueiam. O scan padrão (`npx mjolnir-qa@5.0.0` sem `--strict`) cobre apenas regras core e extended. Adicione `--strict` quando quiser a camada de consultoria também.
|
|
76
76
|
|
|
77
77
|
O Mjölnir lê a suíte, os workflows de CI e, se você tiver, o relatório de uma execução real. Ele não roda seus testes, não instala suas dependências e não executa o código que analisa. E quando não tem evidência, ele diz isso em vez de inventar confiança:
|
|
78
78
|
|
|
@@ -107,7 +107,7 @@ Um scan real de [`examples/demo-repo`](examples/demo-repo), uma pequena suíte P
|
|
|
107
107
|
|
|
108
108
|
<p align="center">
|
|
109
109
|
<a href="assets/video/mjolnir-demo.mp4">
|
|
110
|
-
<img src="assets/video/mjolnir-demo-poster.png" alt="Um quadro da gravação de demonstração: npx mjolnir-qa@
|
|
110
|
+
<img src="assets/video/mjolnir-demo-poster.png" alt="Um quadro da gravação de demonstração: npx mjolnir-qa@5.0.0 analisando o repositório de demonstração em uma janela de terminal" width="900" />
|
|
111
111
|
</a>
|
|
112
112
|
</p>
|
|
113
113
|
|
|
@@ -176,7 +176,7 @@ Essa é a unidade de valor: um lugar onde a CI reporta uma aprovação que não
|
|
|
176
176
|
## Início rápido
|
|
177
177
|
|
|
178
178
|
```bash
|
|
179
|
-
npx mjolnir-qa@
|
|
179
|
+
npx mjolnir-qa@5.0.0
|
|
180
180
|
```
|
|
181
181
|
|
|
182
182
|
Ele analisa o diretório atual e imprime o Trust Report: o que encontrou, até onde você pode confiar, por quê e o que fazer em seguida. Sai com `0` quando nada foi encontrado no nível do gate ou acima.
|
|
@@ -184,7 +184,7 @@ Ele analisa o diretório atual e imprime o Trust Report: o que encontrou, até o
|
|
|
184
184
|
Na CI, analise só o que a branch introduziu, para que uma suíte legada não afogue seu primeiro pull request:
|
|
185
185
|
|
|
186
186
|
```bash
|
|
187
|
-
npx mjolnir-qa@
|
|
187
|
+
npx mjolnir-qa@5.0.0 --scope changed
|
|
188
188
|
```
|
|
189
189
|
|
|
190
190
|
`mjolnir ci install` grava isso como um workflow do GitHub Actions, usando a [action](https://github.com/Sergey-Bar/Mjolnir#readme) fixada na tag principal `v3` (ou `npx` puro com `--no-action`). Ele continua consultivo até você decidir que deve bloquear.
|
|
@@ -492,7 +492,7 @@ Fixe `@v3` para acompanhar a linha principal, ou uma tag exata (`@v0.5.32`) para
|
|
|
492
492
|
Para levar os achados ao GitHub Code Scanning, envie o SARIF (requer `security-events: write` no escopo do workflow ou job):
|
|
493
493
|
|
|
494
494
|
```yaml
|
|
495
|
-
- run: npx mjolnir-qa@
|
|
495
|
+
- run: npx mjolnir-qa@5.0.0 --format sarif > mjolnir.sarif
|
|
496
496
|
continue-on-error: true
|
|
497
497
|
- uses: github/codeql-action/upload-sarif@v3
|
|
498
498
|
if: ${{ !cancelled() }}
|
|
@@ -505,7 +505,7 @@ No GitLab, `--format codequality` grava o relatório do Code Quality que o widge
|
|
|
505
505
|
### Atribuição no escopo alterado
|
|
506
506
|
|
|
507
507
|
```bash
|
|
508
|
-
npx mjolnir-qa@
|
|
508
|
+
npx mjolnir-qa@5.0.0 --scope changed
|
|
509
509
|
```
|
|
510
510
|
|
|
511
511
|
Os achados são atribuídos às linhas que sua branch adicionou, medidas contra a **merge-base**. O escopo é o mesmo conjunto de arquivos que um scan completo descobre (specs TS/JS e configurações de adaptadores, `test_*.py`, `*Test.java`, `*Tests.cs`, `.github/workflows/*.yml`), mais as alterações não commitadas e não rastreadas, então funciona antes do commit. A base é resolvida como `main → master → origin/main → origin/master → origin/HEAD`; substitua com `--base <ref>`.
|
|
@@ -533,7 +533,7 @@ SCAN → EVIDENCE → HANDOFF → AGENT → RE-SCAN → PROOF
|
|
|
533
533
|
Adicione-o a um cliente que tenha sua própria CLI:
|
|
534
534
|
|
|
535
535
|
```bash
|
|
536
|
-
claude mcp add mjolnir -- npx -y mjolnir-qa@
|
|
536
|
+
claude mcp add mjolnir -- npx -y mjolnir-qa@5.0.0 mcp
|
|
537
537
|
```
|
|
538
538
|
|
|
539
539
|
Ou a qualquer cliente que aceite um bloco `mcpServers`:
|
|
@@ -541,7 +541,7 @@ Ou a qualquer cliente que aceite um bloco `mcpServers`:
|
|
|
541
541
|
```json
|
|
542
542
|
{
|
|
543
543
|
"mcpServers": {
|
|
544
|
-
"mjolnir": { "command": "npx", "args": ["-y", "mjolnir-qa@
|
|
544
|
+
"mjolnir": { "command": "npx", "args": ["-y", "mjolnir-qa@5.0.0", "mcp"] }
|
|
545
545
|
}
|
|
546
546
|
}
|
|
547
547
|
```
|
|
@@ -632,7 +632,7 @@ O ambiente de desenvolvimento, os comandos dos gates permanentes e as leis anti-
|
|
|
632
632
|
<img src="assets/readme/closing.svg" alt="Rode no seu repo." width="100%" />
|
|
633
633
|
|
|
634
634
|
```bash
|
|
635
|
-
npx mjolnir-qa@
|
|
635
|
+
npx mjolnir-qa@5.0.0
|
|
636
636
|
```
|
|
637
637
|
|
|
638
638
|
[Leia o guia](https://sergey-bar.github.io/Mjolnir/guide/getting-started) · [Site de documentação](https://sergey-bar.github.io/Mjolnir/) · [npm](https://www.npmjs.com/package/mjolnir-qa)
|
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ when verification isn't earning its keep.
|
|
|
18
18
|
[](https://nodejs.org)
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
npx mjolnir-qa@
|
|
21
|
+
npx mjolnir-qa@5.0.0
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
[See it work](#see-it-work) · [Quickstart](#quickstart) · [What it finds](#what-mjölnir-finds) · [Score](#the-worthiness-score) · [Evidence](#the-evidence-model) · [Forensics](#runtime-forensics) · [CI](#ci-integrity) · [Agents](#ai-agents) · [Security](#trust-and-security) · [Limits](#what-mjölnir-cannot-tell-you) · [Docs](#documentation)
|
|
@@ -67,7 +67,7 @@ reported. Generated by `npm run docs:readme-brand` from
|
|
|
67
67
|
**Strict mode.** The most aggressive detections — `.only`, `continue-on-error`,
|
|
68
68
|
empty tests, retry abuse — live in the quarantine tier. They run only
|
|
69
69
|
under `--strict` and are capped to `info` severity: they flag, they never
|
|
70
|
-
gate. The default scan (`npx mjolnir-qa@
|
|
70
|
+
gate. The default scan (`npx mjolnir-qa@5.0.0` without `--strict`)
|
|
71
71
|
covers core and extended rules only. Add `--strict` when you want the
|
|
72
72
|
advisory layer too.
|
|
73
73
|
|
|
@@ -127,7 +127,7 @@ CI. The full `--verbose` report of the same scan is
|
|
|
127
127
|
|
|
128
128
|
<p align="center">
|
|
129
129
|
<a href="assets/video/mjolnir-demo.mp4">
|
|
130
|
-
<img src="assets/video/mjolnir-demo-poster.png" alt="A frame of the demo recording: npx mjolnir-qa@
|
|
130
|
+
<img src="assets/video/mjolnir-demo-poster.png" alt="A frame of the demo recording: npx mjolnir-qa@5.0.0 scanning the demo repository in a terminal window" width="900" />
|
|
131
131
|
</a>
|
|
132
132
|
</p>
|
|
133
133
|
|
|
@@ -211,13 +211,13 @@ That is the unit of value: one place where CI reports a pass it did not earn.
|
|
|
211
211
|
## Quickstart
|
|
212
212
|
|
|
213
213
|
```bash
|
|
214
|
-
npx mjolnir-qa@
|
|
214
|
+
npx mjolnir-qa@5.0.0
|
|
215
215
|
```
|
|
216
216
|
|
|
217
217
|
Three steps to trust your release pipeline:
|
|
218
218
|
|
|
219
|
-
1. **Scan** — `npx mjolnir-qa@
|
|
220
|
-
2. **Block in CI** — `npx mjolnir-qa@
|
|
219
|
+
1. **Scan** — `npx mjolnir-qa@5.0.0` — scans the current directory, prints the Trust Report, exits `1` if findings at the gate
|
|
220
|
+
2. **Block in CI** — `npx mjolnir-qa@5.0.0 --scope changed` — scans only changed lines, blocks PRs with error findings
|
|
221
221
|
3. **Fix and re-verify** — `mjolnir fix` applies safe auto-fixes, then re-scans to prove each one landed
|
|
222
222
|
|
|
223
223
|
Install the advisory PR workflow first, so existing trust debt cannot break
|
|
@@ -627,14 +627,14 @@ In your existing workflow, pin the action and explicitly opt into an error gate:
|
|
|
627
627
|
The `--blocking error` flag does the same in any CI pipeline:
|
|
628
628
|
|
|
629
629
|
```bash
|
|
630
|
-
npx mjolnir-qa@
|
|
630
|
+
npx mjolnir-qa@5.0.0 --scope changed --blocking error
|
|
631
631
|
```
|
|
632
632
|
|
|
633
633
|
To put findings in GitHub Code Scanning, upload SARIF (requires
|
|
634
634
|
`security-events: write` at workflow or job scope):
|
|
635
635
|
|
|
636
636
|
```yaml
|
|
637
|
-
- run: npx mjolnir-qa@
|
|
637
|
+
- run: npx mjolnir-qa@5.0.0 --format sarif > mjolnir.sarif
|
|
638
638
|
continue-on-error: true
|
|
639
639
|
- uses: github/codeql-action/upload-sarif@v3
|
|
640
640
|
if: ${{ !cancelled() }}
|
|
@@ -649,7 +649,7 @@ Editor and pipeline setup: [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md
|
|
|
649
649
|
### Changed-scope attribution
|
|
650
650
|
|
|
651
651
|
```bash
|
|
652
|
-
npx mjolnir-qa@
|
|
652
|
+
npx mjolnir-qa@5.0.0 --scope changed
|
|
653
653
|
```
|
|
654
654
|
|
|
655
655
|
Findings are attributed to the lines your branch added, measured against
|
|
@@ -687,7 +687,7 @@ re-scan, never from the agent's own report of success.
|
|
|
687
687
|
Add it to a client that ships its own CLI:
|
|
688
688
|
|
|
689
689
|
```bash
|
|
690
|
-
claude mcp add mjolnir -- npx -y mjolnir-qa@
|
|
690
|
+
claude mcp add mjolnir -- npx -y mjolnir-qa@5.0.0 mcp
|
|
691
691
|
```
|
|
692
692
|
|
|
693
693
|
Or to any client that takes an `mcpServers` block:
|
|
@@ -697,7 +697,7 @@ Or to any client that takes an `mcpServers` block:
|
|
|
697
697
|
"mcpServers": {
|
|
698
698
|
"mjolnir": {
|
|
699
699
|
"command": "npx",
|
|
700
|
-
"args": ["-y", "mjolnir-qa@
|
|
700
|
+
"args": ["-y", "mjolnir-qa@5.0.0", "mcp"]
|
|
701
701
|
}
|
|
702
702
|
}
|
|
703
703
|
}
|
|
@@ -839,7 +839,7 @@ fixture-firewall laws are in [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
|
839
839
|
<img src="assets/readme/closing.svg" alt="Run it on your repo." width="100%" />
|
|
840
840
|
|
|
841
841
|
```bash
|
|
842
|
-
npx mjolnir-qa@
|
|
842
|
+
npx mjolnir-qa@5.0.0
|
|
843
843
|
```
|
|
844
844
|
|
|
845
845
|
[Read the guide](https://sergey-bar.github.io/Mjolnir/guide/getting-started) · [Docs site](https://sergey-bar.github.io/Mjolnir/) · [npm](https://www.npmjs.com/package/mjolnir-qa)
|