chore(release): v6.0.0 — "Prose is not a contract" (MAJOR: finding IDs name the check)

MAJOR, because M-BUG-28 changed what a finding ID means: the {NNN} in
CA-{SCANNER}-{NNN} names the CHECK, not its emission position. IDs are
therefore not unique per finding -- one check failing in three files emits
three findings sharing an ID -- and any consumer keying on `id` alone must
move to (id, file, line).

The release theme is a class of defect rather than a feature area. Three
sweeps (Q1, Q2, Q_AUDIT) kept surfacing the same shape: a command template
stated an invariant in prose, code on the other side depended on it, and
nothing checked that the two still agreed. Q1 put the write-scope gate in
code (measured: 9 writers under scanners/, 1 imported the gate). Q2 checks
each template's argv against the CLI that receives it (--stale-after 30
reached its CLI as one argument under zsh and was ignored while the command
reported success). Q_AUDIT measured and ranked the third instance --
data contracts hand-built by the model and parsed by engines that know one
frozen example -- with the recovery path on top, deliberately not yet fixed.

Measured this session, not carried forward:
  37 commits since v5.13.0 (git log v5.13.0..HEAD --oneline | wc -l)
  1752 tests, 0 failing, post-bump
  self-audit --check-readme: PASS, readmeCheck.passed, 0 mismatches
  check-versions.mjs: 0 ERROR (1 WARN = the unreleased bump this cut closes)
  scanners 16, agents 7, commands 21, hooks 4, knowledge 8

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XKeJyEVn9GuBcBQbE6EYwm
This commit is contained in:
Kjell Tore Guttormsen 2026-08-18 20:57:58 +02:00
commit b35ff449e8
3 changed files with 30 additions and 2 deletions

View file

@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [6.0.0] - 2026-08-18
### Summary
"Prose is not a contract" — a MAJOR release whose theme is a *class* of defect rather than a feature
area. Across three sweeps (Q1, Q2, Q_AUDIT) the same shape kept surfacing: a command template stated
an invariant in prose, code on the other side depended on it, and nothing checked that the two still
agreed. The write-scope **gate** was policy paraphrased in five templates while exactly one writer
imported it. The **argv** a template built was never checked against the CLI receiving it —
`--stale-after 30` arrived as a single argument, matched no flag, and the command reported success
about a threshold the user had just overridden. And the **data contracts** — backup manifests,
`state.yaml`, `scope.yaml` — are hand-built by the model and parsed by engines that know one frozen
example. The first two are now enforced in code and tests; the third is measured and ranked in
`docs/q-audit-prose-invariants.md`, with the recovery path (`rollback`) at the top as the surface
that runs precisely when the user is already in trouble.
**Breaking — a finding ID's `{NNN}` names the check, not its emission position (`M-BUG-28`).**
IDs are therefore not unique per finding: one check failing in three files emits three findings
sharing an ID, and `(id, file, line)` is the instance key. Any consumer keying on `id` alone must
move to the triple. `scanners/lib/finding-codes.mjs` is now the single authority — an undeclared or
missing code **throws**, with no counter fallback, because a fallback lets a half-converted scanner
ship IDs that look valid. Retired numbers are never reissued. Frozen `v5.0.0` baselines mask IDs
rather than re-deriving them.
**37** commits since 5.13.0. **1752** tests, 0 failing. GAP dimensions **25 → 24** (one `/doctor`
duplicate retired). No component-count change: scanners **16**, agents **7**, commands **21**,
hooks **4**, knowledge entries **8**.
### Fixed
- **`M-BUG-45``/config-audit knowledge-refresh --stale-after N` was silently dead under zsh.** The
command built `STALE_AFTER="--stale-after 30"` and expanded it unquoted, relying on the shell to