mjolnir-qa 6.0.0 → 6.0.1

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 CHANGED
@@ -9,6 +9,379 @@ 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
+ ## [Unreleased]
13
+
14
+ ## [6.0.1] — 2026-10-28
15
+
16
+ A patch release. 6.0.0 shipped a GitHub Action whose report step calls a
17
+ command the same release removed, so every job that used the Action failed,
18
+ whatever the scan found. This release repairs that, along with the findings of
19
+ the maintenance review that followed. Each change fixes behaviour that did not
20
+ match its documentation or its contract. No verb, flag or Action input was
21
+ added or removed.
22
+
23
+ **Upgrading from 6.0.0:**
24
+
25
+ - **The Action.** Pin `Sergey-Bar/Mjolnir@v6.0.1`, or `@v6`, which the stable
26
+ release workflow now moves to the newest 6.x release. The `version` input
27
+ defaults to `6.0.1`.
28
+ - **`mjolnir ci install`.** Run `npx mjolnir-qa@6.0.1 ci install` again with
29
+ the same flags. It recognises the workflow 6.0.0 wrote and replaces its
30
+ broken steps; a workflow you edited still needs `--force`.
31
+ - **Exit codes now match the contract.** `fix` and `explain --plan` return `2`
32
+ on a partial scan, `explain --evidence` returns `2` rather than `1` on an
33
+ incomplete report, an invalid config returns `10` rather than `20` on every
34
+ verb, and `ci verify` now honours `--blocking` and the E0 exemption. A
35
+ pipeline that keyed on the old codes can change colour.
36
+
37
+ ### Fixed: the stable release moves the Action's major tag
38
+
39
+ - **`stable-release.yml` moved `v3` and nothing else.** The step that follows a
40
+ stable tag with the `vN` Action tag exited early for every major but 3, and a
41
+ spec required it to. Its tag push uses the job token, which starts no other
42
+ workflow, so `action-tags.yml` never ran either. That is why
43
+ `Sergey-Bar/Mjolnir@v6`, the pin the documentation recommends, does not
44
+ resolve. The step now derives the major from the tag, and leaves the major
45
+ where it is when a newer release on that line already exists.
46
+
47
+ ### Fixed: the release record
48
+
49
+ - **5.1.0 was never published.** Its changelog section was cut and its stable
50
+ run reached the tag approval, which nobody gave, and 6.0.0 shipped first.
51
+ There is no `v5.1.0` tag and no 5.1.0 on npm. `docs/PUBLISHING.md` listed it
52
+ as published with links to a registry page and a GitHub release that do not
53
+ exist, and the roadmap called it shipped. Both now say what happened, and the
54
+ 5.1.0 section of this file says so too.
55
+ - **The run still waiting at that approval was cancelled.** If it had been
56
+ approved, it would have tagged 5.1.0 and published it as npm `latest`, over
57
+ 6.0.0.
58
+ - **Install commands that were left behind.** The site's guided proof and its
59
+ agent guide pinned 5.0.0, where the guided proof's `ci verify` does not
60
+ exist. `docs/guide/PERSONAS.md` pinned 6.0.0-rc.1, which was never
61
+ published, and sent readers to `stats` for per-rule FP rates; `stats` is the
62
+ fix-tracking ledger and the rates are `explain --list`. None of these files
63
+ is covered by `version:surface:sync`, so they now name 6.0.1 by hand.
64
+
65
+ ### Fixed: the `ci install` Action template pins an Action that works
66
+
67
+ - **It pinned `4a588bc`, a pull-request branch commit that `main` never
68
+ contained.** That commit's `action.yml` installed `mjolnir-qa@latest`, called
69
+ the retired `trust-report` verb and wrote `mjolnir-trust-report.md`. The
70
+ template's publish job reads `mjolnir-comment.md`, so no workflow it
71
+ generated ever published a comment. It now pins the merge commit of the fix
72
+ above, on `main`.
73
+ - **The template passes `version:`**, the published version the generating
74
+ package resolves, which is the npx template's pin. Omitting it ran the pinned
75
+ Action's own default: the stable published when that commit was made, which
76
+ can predate the formats its steps call.
77
+ - **`ci install` printed the pin as a separately typed SHA with 41 hex
78
+ digits**, which names no commit. It now prints the template's own value.
79
+ - Recognition ignores the `version:` value and still recognises 6.0.0's
80
+ template, which had no such line.
81
+
82
+ ### Fixed: the Action, `ci install` and the PR workflow render their reports again
83
+
84
+ The v6 carve retired `summary` and `pr-comment` onto `scan --format
85
+ github-summary` and `scan --format pr-comment`, and shipped only the half of
86
+ each that scans. Every CI consumer renders the `--json` report its scan step
87
+ saved. All of them kept calling the retired verbs, which exit 10:
88
+
89
+ - **The Action.** Its report step runs by default, so v6.0.0 failed every
90
+ consumer's job, on a clean scan as well as a dirty one. Its summary step
91
+ failed too.
92
+ - **`ci install --no-action`.** The generated summary and comment steps are
93
+ `continue-on-error`, so they failed without a sound: no annotations and no
94
+ PR comment, on any run.
95
+ - **This repository's PR workflow**, which has posted "Mjölnir report
96
+ unavailable" on every pull request since 6.0.0.
97
+
98
+ What changed:
99
+
100
+ - **`scan --format github-summary <report.json>` and `scan --format pr-comment
101
+ <report.json>` render a saved report**, as `site/reference/cli.md` already
102
+ said they did. They delegate to the retired verbs' own code, so the
103
+ contracts are the verbs': annotations on stdout inside GitHub Actions, the
104
+ summary written to `$GITHUB_STEP_SUMMARY`, and a render that never gates.
105
+ No verb or flag was added. A directory target still scans.
106
+ - **The comment's commit is checked, not passed in.** It states the commit the
107
+ scan recorded only while the checkout is at that commit, and `unknown`
108
+ otherwise. The Action used to hand `--commit "$GITHUB_SHA"` to a verb that
109
+ printed whatever it was given. The live `--format pr-comment` render, which
110
+ always said `unknown`, now states the commit it scanned.
111
+ - **The Action's summary step no longer pipes stdout into the summary**, which
112
+ copied every annotation command into it as text. Its `strict` input passes
113
+ `--include-warn` instead of the deprecated `--strict`, which printed a
114
+ deprecation notice into every consumer's log.
115
+ - **`ci install` recognises the workflows 6.0.0 wrote**, so re-running it
116
+ replaces their broken steps without `--force`. Recognition required byte
117
+ equality with the current template, so the pinned version alone made every
118
+ earlier release's file read as hand-customised. Line endings, the pinned
119
+ version and the Action SHA are now ignored, and any other edit still refuses.
120
+ - **The PR workflow's render step fails the job when the render fails**,
121
+ instead of `exit 0` under `continue-on-error`. Its fallback report for a
122
+ failed analysis now validates: it carried `"reason":"analysis-failed"`, which
123
+ the report validator rejects, so the one report written for that case could
124
+ never be rendered.
125
+ - **A `--include-warn` scan's report loads again.** With the quarantine tier
126
+ running, nothing is withheld, and `rulesWithheld` said 0 while
127
+ `withheldRuleIds` still named the whole tier. The loader rejects a report
128
+ that contradicts itself, so the Action's `strict: true` would have failed
129
+ every render even with the verbs fixed, and no saved-report command could
130
+ read such a report. Lifecycle resolution also read those rules as withheld,
131
+ so a quarantine-tier finding that a strict scan had fixed resolved as
132
+ `INCONCLUSIVE(rule-withheld)`.
133
+
134
+ The same defect in three more places, found by the widened gate below:
135
+
136
+ - `release.yml` ran `release-trust`, retired onto `ci release-trust`, so that
137
+ step exits 10 when it runs. `stable-release.yml` already used `ci`.
138
+ - `site/guide/forensics.md` told readers to run `npx mjolnir-qa@6.0.0
139
+ forensics`, and the version-surface check required that line. Both now say
140
+ `explain --evidence`.
141
+ - `docs/AI-DISCOVERY.md` pinned 5.0.0 and named the retired `baseline` and
142
+ `verify`. It now pins 6.0.0 with `--save-baseline` and `ci verify`, and
143
+ `site/public/llms.txt` says 6.0.0 too.
144
+
145
+ Why nothing caught it:
146
+
147
+ - **`check-cli-contract` matched only `mjolnir <verb>`.** CI steps launch the
148
+ CLI as `npx "$MJ_PACKAGE"`, `npx …/mjolnir-qa-<version>.tgz`, `npx
149
+ mjolnir-qa@<version>` or `node dist/cli.mjs`, and it now reads all of them.
150
+ `docs/MIGRATION-3.0.0.md` joins the frozen-report list: its commands pin
151
+ 3.0.0, where those verbs exist.
152
+ - **Four specs asserted the command text**, and text is satisfied by a command
153
+ that cannot run. `tests/integrations/ci-render-commands.spec.ts` takes each
154
+ consumer's command from the file that ships it and runs it against the CLI,
155
+ and checks every scan argument the Action can pass.
156
+
157
+ Still open:
158
+
159
+ - The Action runs the published package, so Action users get this with the
160
+ next release, which moves the `version` default to it.
161
+ - The floating `v6` Action tag was never created, because the tag job for
162
+ v6.0.0 was cancelled. The docs recommend `Sergey-Bar/Mjolnir@v6`, which does
163
+ not resolve yet.
164
+
165
+ ### Changed: every devDependency is an exact pin, and current
166
+
167
+ `npm outdated` now lists only the two blocked upgrades below. Runtime
168
+ `dependencies` and `peerDependencies` keep their ranges on purpose: they
169
+ resolve inside every user's install, and an exact pin there would force
170
+ duplicate copies and stop transitive security patches from reaching users.
171
+
172
+ - **Pinned and updated.** Every devDependency in the root, the
173
+ `packages/playwright-reporter` workspace and `site/` is now an exact
174
+ version, taken to its latest release inside the existing major. This
175
+ covers Playwright 1.64.1 (and `playwright-core`, which two scripts import
176
+ directly, at the same version), Sentry 11.9.0, Vitest 5.0.5, ESLint
177
+ 10.13.0, typescript-eslint 8.72.1, Prettier 3.9.10, tsdown 0.23.3 and
178
+ Lighthouse 13.6.0.
179
+ - **commitlint 19 → 21.** The two majors break nothing here. 20 makes the
180
+ body line-length rule skip URL lines, and 21 drops Node 18 and 20. 21's
181
+ parser does read a body line that opens with `word: ` as a footer, which
182
+ `footer-leading-blank` then rejects. Across the last 150 commits on `main`
183
+ that rejected 2 that 19 accepted, and `.commitlintrc.cjs` now says how to
184
+ avoid it.
185
+ - **vitepress 2.0.0-alpha.19 → alpha.21.** There is no stable 2.x, and 1.6.4
186
+ would be a downgrade. The site still renders 99 pages, its 44 tests and
187
+ doctor pass, and `home.data.ts` no longer trips the warning Vite prints for
188
+ its planned native config loader.
189
+ - **The reporter's TypeScript 7 is gone.** Its build resolves the root
190
+ compiler, and building with and without the nested 7.0.2 gives
191
+ byte-identical output. Removing it ends what read as a split toolchain and
192
+ drops the compiler's 20 native binaries.
193
+ - **Lockfile 589 → 461 entries.** Everything that left had no dependent in
194
+ the new tree, and the file still has the linux, darwin and win32 variants
195
+ every native package needs.
196
+ - **`scripts/complete-lockfile.mjs` no longer resurrects removed packages.** It
197
+ restored every key the known-good lockfile had, so `siginfo` and
198
+ `stackback` sat in the committed lockfile with nothing depending on them.
199
+ It now restores only platform entries the tree still wants, and has tests.
200
+ - **Dependabot.** Minor and patch updates come back as weekly grouped PRs:
201
+ runtime and tooling separately, plus the site, which had no entry and
202
+ received only security updates. 5.0.0 ignored them to cut noise, but with
203
+ every version locked an ignored update is never taken, which is how the
204
+ tree fell behind. Each blocked upgrade states when it was re-verified and a
205
+ review-by date.
206
+
207
+ Still blocked, re-verified 2026-10-28, review by 2027-01-26:
208
+
209
+ - `web-tree-sitter` 0.27.1 cannot load the Java or C# grammars of
210
+ `tree-sitter-wasms` 0.1.13, still its latest release, and 0.25.6 can.
211
+ - TypeScript 7 has no JS API for ts-morph, and typescript-eslint 8.72.1 still
212
+ requires TypeScript below 6.1.
213
+
214
+ Decided against, on inspection:
215
+
216
+ - **Moving Lighthouse into `site/`.** The root deliberately holds Lighthouse,
217
+ axe-core and Playwright for `site:lighthouse` and `site:audit`, so the site
218
+ build that CI and Pages run installs only vitepress. Lighthouse's dependency
219
+ closure is 137 lockfile entries, which would be added to every site build.
220
+ - **Dropping `axe-core`.** It looked unused only because `site:audit` loads
221
+ `axe.min.js` by path.
222
+
223
+ ### Removed: scripts and npm aliases that nothing ran
224
+
225
+ Files under `scripts/`, 169 → 138. Tracked npm scripts, 147 → 140.
226
+ `git show 05af655:<path>` recovers any deleted file.
227
+
228
+ - **`scripts/archive/` (24 files).** One-shot fixture authors and
229
+ adjudicators from 2026-06 to 6.0. Their output is committed and none of
230
+ them ran; they were kept only as `HISTORY` entries in the orphan-script
231
+ list, and that list and the archive lint block are gone with them.
232
+ - **Orphans:**
233
+ - `scripts/lib/omitted-measured.ts` and `scripts/lib/test001-probe.ts`:
234
+ scratch probes with zero references.
235
+ - `scripts/lib/coverage-exemption-ledger.ts`: a typed wrapper nothing
236
+ imported.
237
+ - `scripts/lib/candidate-decision.ts`: reachable only through an
238
+ extensionless import that resolved to the `.mjs` anyway. Its spec now
239
+ imports the `.mjs`, typed by the existing `.d.mts`.
240
+ - `scripts/compute-release.mjs` and its spec: release-bump logic whose
241
+ orphan entry said "reachable by nobody".
242
+ - `scripts/verify-remediation-isolation.mjs` (`verify:isolation`): a drill
243
+ hard-wired to a branch that no longer exists.
244
+ - `scripts/retired-external-evidence.ts` (`docs:external-evidence`): a
245
+ tombstone that exited 0 and checked nothing. The three documents that
246
+ named it now call it "the `docs:external-evidence` script (since
247
+ removed)".
248
+ - **Aliases for suites `npm test` already runs:** `frontier:contracts`,
249
+ `test:integration`, `test:mcp:parity`, `test:release` and
250
+ `artifact:deterministic`. Three of their `docs/MANUAL-SCRIPTS.md` rows were
251
+ false: `test:mcp:parity` did not "need a live MCP client", `test:release`
252
+ needed no credentials, and `test:integration` was never "kept out of the
253
+ certify chain". All three ran in every `npm test`. The agent skills now name
254
+ the commands the aliases stood for.
255
+ - **`docs/CI-STANDARD.md`** credited the compatibility lanes with
256
+ `npm run test:integration`. They run `npm test`, and the row now says so.
257
+
258
+ Not done, on inspection: `check-promotion-throughput.mjs` keeps running under
259
+ `tsx`, because it imports `src/commands/doctor.ts` and plain `node` cannot load
260
+ it.
261
+
262
+ ### Removed: tracked files with no reader
263
+
264
+ Six paths were tracked and read by nothing. Git history keeps all of them,
265
+ and `repo-hygiene.spec.ts` now fails if any returns:
266
+
267
+ - `enterprise-output/`: a 4.0.0 copy of `capability-manifest.json` with no reference.
268
+ - `QA/`: one 2026-09-06 report that only linked to itself.
269
+ - `archive/`: five exported stash patches. Their own README records each as
270
+ superseded, and they apply only at their old base commits.
271
+ - `assets/readme/homepage-linkedin-aurora.gif`: 4.85 MB, referenced nowhere.
272
+ - `.github/workflows/jekyll-gh-pages.yml`: a workflow whose only step was an
273
+ `echo`, plus its tier exemption.
274
+ - `.claude/launch.json`: hard-coded another machine's worktree path; now gitignored.
275
+
276
+ Also fixed:
277
+
278
+ - **CHANGELOG.** A second `## [Unreleased]` block (about 1,000 lines of
279
+ 6.0.0 working notes) sat below `[6.0.0]`, where the release script would
280
+ have swept it into the next version. It is now titled as the 6.0.0
281
+ development log, and a test asserts exactly one `[Unreleased]`, first.
282
+ - **Stale references.** Removed a `package.json` `allowScripts` entry for
283
+ `@vitest/coverage-istanbul@5.0.1`, which the lockfile does not install.
284
+ Fixed the commit-msg hook's `.commitlintrc.json` reference (the file is
285
+ `.cjs`), the `.prettierignore` entries for retired M26 files, two Sonar
286
+ paths that do not exist, and the CodeRabbit workflow count.
287
+
288
+ Kept on purpose: `PRODUCT-ENHANCEMENT-ANALYSIS.md` is a pinned redirect to
289
+ its archive copy (`GAP-V6-007`, two specs), and `kilo.json` is the
290
+ documented shared skills wiring (`docs/EXTERNAL-CONFIG.md`).
291
+
292
+ ### Fixed: the P0/P1 findings of the maintenance review (#732)
293
+
294
+ Each has a regression test that failed before its commit.
295
+
296
+ - **SARIF results pointed one directory too high.** `SRCROOT` had no trailing
297
+ `/` (SARIF 2.1.0 §3.14.14), so RFC 3986 resolution of `src/a.ts` against
298
+ `file:///repo` landed on `file:///src/a.ts` in every Code Scanning upload.
299
+ - **Failing .NET tests disappeared from TRX runs.** A `UnitTestResult` body
300
+ over 4,000 characters (normal with StdOut and a stack trace) did not match,
301
+ so the failure was dropped and the run read greener than it was.
302
+ - **Quadratic parsing of untrusted input.** JUnit `<failure>` tags without a
303
+ closer took 63 s for 2.2 MB, and `countTestDeclarations` took 152 s for
304
+ 300,000 blank lines (it runs on every scanned file). Both are linear now.
305
+ - **Exit codes.** `--save-baseline` no longer corrupts `--json` output, never
306
+ saves a partial scan, and reports a failed save as `2` (not `1`) after the
307
+ report. `fix` and `explain --plan` exit `2` on a partial scan. An invalid
308
+ config exits `10` on every verb, not `20`. `ci verify` gates new findings
309
+ through the same matrix as `scan`, so `--blocking` and the E0 exemption
310
+ apply. `explain --evidence` exits `2`, not `1`, on an incomplete report.
311
+ - **MCP workspace root.** With `MJOLNIR_WORKSPACE_ROOT` set, a symlink or
312
+ junction inside the root that pointed outside it was accepted, and a child
313
+ named `..cache` was rejected. Both sides are now real paths. Without the
314
+ variable the read-only tools stay open, as before; the server header no
315
+ longer claims otherwise.
316
+ - **Evidence store lock.** A live lock was bypassed after ~0.4 s: the write
317
+ ran unlocked and deleted the owner's lock. It now waits until the lock
318
+ could be stale and otherwise times out without running.
319
+ - **Half-applied runtime corroboration.** A throw part-way through left some
320
+ findings at a runtime trust level (up to L5). Findings are restored and the
321
+ new `runtime-corroboration-failed` degradation is recorded.
322
+ - **Action output.** `sarif_file` was nested under `findings:` in `action.yml`
323
+ and was always empty for consumers.
324
+
325
+ Deferred, recorded on #732: the same `^\s*` pattern in three detectors also
326
+ reports the wrong line (the blank line above the declaration); fixing it
327
+ changes line attribution, so it ships with a `detectorRevision` bump and a
328
+ corpus re-measure.
329
+
330
+ ### Fixed: `main` was red on every CI run since 2026-10-06
331
+
332
+ - **Lockfile.** `package-lock.json` had been regenerated on one platform and
333
+ lost the linux/darwin `lightningcss` binaries, so `npm ci` failed on the
334
+ Node 24 job and on any fresh install. Restored with
335
+ `scripts/complete-lockfile.mjs`; `npm run lockfile:platforms` passes.
336
+ - **README contracts.** The 6.0.0 README rewrite dropped the release-status
337
+ section, the `Status` heading, the documentation table, the roadmap link,
338
+ the census sentinels, the rule table, the WORTHINESS alt text and the
339
+ "a CI step fails" wording on exit `2` — every one pinned by a contract spec,
340
+ 13 failures in all. They are back in the short layout. Two claims in the
341
+ rewrite were wrong and are corrected: `QA-CI-008` is "always-success step
342
+ masks failures", not "mutable Action refs", and the YAML example named
343
+ detections no rule makes (`--maxfail=0`, `--workers=1`, `@main`).
344
+ - **README specs restored, not weakened.** The same rewrite had cut three
345
+ contract specs to match it: `readme-doctest` (which runs every README
346
+ command and checks the `explain QA-CI-001` transcript) became
347
+ `expect(true).toBe(true)`, `readme-samples` asserted the samples were
348
+ _absent_, and `readme-commands` grew a ten-verb exemption list. Those specs
349
+ were the only tests reaching several command paths, so function coverage
350
+ fell to 98.58 % against a 98.67 % ratchet floor. The specs are restored
351
+ verbatim from 6.0.0, and the command table, the explain transcript and the
352
+ two terminal samples come back as collapsed `<details>` blocks in a
353
+ Reference section, which keeps the page short. Functions are at 98.76 %;
354
+ the suite runs 87 more tests.
355
+ - **Version surface.** `checkVersionSurfaceEnvelope` read an inline-code
356
+ install command (`` `npx mjolnir-qa@X` ``) as `` X` ``, so an unpublished
357
+ version written in backticks was never reported. The terminator set now
358
+ includes the backtick, matching the sync routine.
359
+ - **Weekly SARIF scan.** `sarif-code-scanning.yml` scans `.` and had failed
360
+ every run since 2026-09-28 with exit `2`: `mjolnir.config.json` excluded
361
+ `tests/golden/**`, which covers the real spec `tests/golden/golden.spec.ts`,
362
+ so the scan honestly reported PARTIAL. The exclude is narrowed to the
363
+ fixture repo, `tests/golden/repo/**`, and `repo-hygiene.spec.ts` now asserts
364
+ the whole-repo scan is complete.
365
+ - **Promotion ratchet.** `docs/RULE-PROMOTION-LEDGER.json` still bounded
366
+ `core` at `0` after 6.0.0 promoted `QA-PW-117` and `QA-JV-101`. The ratchet
367
+ passed only because a stale `[Unreleased]` block below the 6.0.0 heading
368
+ still carried that release's anti-creep exception marker; a new entry at the
369
+ top turned it red. The growth is ABSORBED (bound `0 → 2`, the same convention
370
+ as `docs/ANTI-CREEP-BASELINE.json`), and the licensed-growth arm, which the
371
+ committed tree no longer reaches, is now driven by a fixture pair. (This
372
+ entry spells the marker in lower case on purpose: the gate matches the
373
+ upper-case token anywhere in the top entry, so quoting it would license
374
+ growth that nobody declared.)
375
+ - **Dependency advisories.** With `npm ci` failing, `npm run audit:ci` had
376
+ never been reached; it now ran and failed on `zod` (high, GHSA-mrhx-c7xc-m3w5,
377
+ via lighthouse → puppeteer-core → chromium-bidi) and `fast-uri` (moderate).
378
+ The site lockfile carried a `vite` advisory (high, GHSA-vfpm-58rq-9qcg). All
379
+ three are lockfile-only `npm audit fix` updates inside the declared ranges,
380
+ in dev tooling only — nothing in the published package changes.
381
+ `lockfile:platforms` passes for both lockfiles.
382
+ - Regenerated: `docs/BLAST-RADIUS-AUDIT.md`, `docs/CARVE-MANIFEST.json`,
383
+ `candidate-trust-manifest.json`.
384
+
12
385
  ## [6.0.0] — 2026-10-04
13
386
 
14
387
  <!-- ANTI-CREEP-EXCEPTION: The launch set grew from 0 to 2 — QA-PW-117 and QA-JV-101 both earned core at n=35 with a 9.89% Wilson upper bound against the 10% ceiling, every row adjudicated by hand against a pinned corpus commit. Law 1 requires an equal-size removal or a recorded exception. Nothing was removed, deliberately: the quarantine population is what keeps unmeasured rules out of default scans, and promoting two rules by demoting two measured ones would have traded measured accuracy for a smaller number. The growth is arithmetic, not preference. -->
@@ -82,7 +455,12 @@ The check RAN and was RECORDED as unsatisfied. It was not treated as blocking
82
455
  because `vars.STABLE_RELEASE_AUTHORIZED` is `true`: a decision someone made and
83
456
  wrote down, not a check that passed.
84
457
 
85
- ## [Unreleased]
458
+ ### Development log: 6.0.0-rc.1 → 6.0.0
459
+
460
+ The working notes written between the release candidate and the cut. They
461
+ sat under a second `## [Unreleased]` heading below this release, which
462
+ `scripts/release-changelog.mjs` would have swept into the next version as if
463
+ it were new work. They are history of 6.0.0 and are kept verbatim below.
86
464
 
87
465
  ### Two core rules, measured: QA-PW-117 and QA-JV-101 - `ANTI-CREEP-EXCEPTION`
88
466
 
@@ -1362,6 +1740,11 @@ and the replacement itself never warns.
1362
1740
 
1363
1741
  ## [5.1.0] — 2026-10-02
1364
1742
 
1743
+ > **Never published.** This section was cut on its release branch, but the
1744
+ > stable release run waited at the tag approval and was not approved, and 6.0.0
1745
+ > shipped first, carrying everything below. There is no `v5.1.0` tag and no
1746
+ > 5.1.0 on npm.
1747
+
1365
1748
  ### Two subtractions declined, because the surface they would remove says something the replacement does not
1366
1749
 
1367
1750
  **`mjolnir capability`: delete the M0–M5 ladder, render the tier instead.** The