Skip to content

Make coverage mean coverage - #417

Merged
remyluslosius merged 4 commits into
mainfrom
feat/coverage-command-finalization
Sep 11, 2026
Merged

Make coverage mean coverage#417
remyluslosius merged 4 commits into
mainfrom
feat/coverage-command-finalization

Conversation

@remyluslosius

@remyluslosius remyluslosius commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Summary

kensa coverage meant two unrelated things depending on whether --framework
was present: without it, a deprecated alias for the mechanism listing; with it,
the framework coverage report. It also warned that the name would change meaning
"in v0.2" — a version tagged 2026-05-28, still being announced as future at
v0.10.0, describing a flip that had already partly happened behind a flag.

The name now selects one operation and flags only refine it.

Invocation Before After
kensa coverage mechanism listing, exit 0, warning usage error naming --framework, exit 2
kensa coverage --help alias help + warning the one coverage help, stdout only
kensa coverage --framework X --rules-dir D coverage report unchanged
kensa coverage --framework nist_800_171 objective report, no --rules-dir unchanged
kensa mechanisms mechanism listing unchanged
kensa mechanisms --framework X usage error unchanged

Bare coverage never falls back. A script relying on it for the mechanism list
must call kensa mechanisms; it previously exited 0 and now exits 2.

--rules-dir stays conditional, and both directions are pinned by tests: a
framework whose controls kensa reads from the corpus requires it, and one that
ships an embedded objective catalog does not, because kensa can already
enumerate its controls. Today that is nist_800_171. The asymmetry is
surprising enough that a later tidying could plausibly break the working path.

The warning and KENSA_NO_REPURPOSE_WARNINGS are gone from the runtime, help,
completion, guide and manpage. Setting the variable now does nothing.
KENSA_NO_DEPRECATION_WARNINGS is a separate knob and is untouched.

Two corrections that fall out of removing the alias

  • Coverage help now shows --quiet, --full and --from-scan. The report
    always accepted them; the alias help listed only the mechanism flags, which is
    why a spec and two tests concluded coverage had no --quiet.
  • The manpage now has a coverage section. It was excluded because, as an
    alias, it would have duplicated one flag table under two headings.

Verification

Check Result
go test ./... 84 packages ok
go test -race ./cmd/kensa ./internal/coverage ok
make lint 0 issues
specter check --strict 151 specs, no orphan constraints
make spec-sync / spec-coverage-strict 151 passing, 0 failing
make manpage-check in sync
make cli-smoke 227 passed, 0 failed
make docs-check / docs-style-sync OK, 13 files clean
make comment-lint, gofmt, both diff checks clean

Pre-fix, the flip-specific criteria failed on the base for their stated reasons:
bare coverage exited 0 printing mechanisms (AC-01), its help was the alias help
(AC-02), the warning function and env read were live (AC-06), completion omitted
four real flags (AC-07), the manpage had no coverage section (AC-08), and the
missing-framework cases entered alias mode (AC-09).

AC-03, AC-04 and AC-05 are regression contracts, not flip criteria: they pin
behavior that already worked on the base and must survive the change. They
passed before and after, which is the point of having them.

Fifteen mutations were applied across two rounds and each killed its target,
including a renamed JSON key caught by a raw key-set comparison rather than a
typed decode, and a renamed manpage flag that a substring check had accepted.

Scope

CLI dispatch, help, completion, docs and specs. No change to api/, the engine,
handlers, coverage or NIST calculations, report schemas, mappings, rule loading,
corpus, VERSION, or packaging. The coverage document and its key sets are
unchanged, locked by a test.

The old rename spec is marked deprecated with a dated note pointing at the new
approved spec. cli-framework-coverage, cli-quiet and cli-manpage keep
status: draft — their result semantics are not promoted; only clauses that
contradicted the shipped runtime carry dated supersession notes.

Out of scope, recorded

The manpage generator omits the registered recover and completion commands
while its draft spec says it covers every registered subcommand. Pre-existing and
left for a separate documentation slice.

kensa coverage meant two unrelated things depending on whether --framework
was present: without it, a deprecated alias for the mechanism listing; with
it, the framework coverage report. It also warned that the name would
change meaning "in v0.2", which shipped on 2026-05-28 and was still being
announced at v0.10.0 as though it were ahead of us.

The name now selects one operation. Running coverage without --framework is
a usage error naming the flag, not a silent fallback that returns an
unrelated document with exit 0 to a script that asked for coverage.
mechanisms is the only name for the mechanism listing.

--rules-dir stays conditional and both directions are pinned by tests. A
framework whose controls kensa reads from the corpus needs it; one that
ships an embedded objective catalog does not, because kensa can already
enumerate its controls. Today that is nist_800_171. The asymmetry is
surprising enough that a later tidying could plausibly remove the working
path, so it is contract rather than incident.

The warning and its KENSA_NO_REPURPOSE_WARNINGS suppression knob are gone
from the runtime, help, completion, guide, and manpage.
KENSA_NO_DEPRECATION_WARNINGS is a separate knob and is untouched.

Two corrections fall out of removing the alias. Coverage help now shows
--quiet, --full and --from-scan: the report always accepted them and the
alias help was what hid them. The generated manpage now has a coverage
section, which it lacked because the alias would have duplicated one flag
table under two headings.
Review found four assertions weaker than the criteria they were mapped to,
false spec traceability, comments that still described the alias, and an
uncounted command failure in the smoke script.

Traceability. Every test in coverage_report_test.go carried a sequentially
numbered annotation against the deprecated rename spec and a shifted one
against the framework draft. The numbering was mechanical, not evidence:
the bad-format test claimed the missing-rules-dir criterion, the JSON test
claimed the mechanisms rejection, and so on. Each annotation now names the
criterion its test actually proves, or is gone. Two tests survived that
asserted a warning nobody emits any more and passed only because the new
error text happens to lack the searched words; both are deleted.

The deprecated spec keeps the two criteria kensa still satisfies and
declares its measured 25% rather than borrowing credit from unrelated
tests. Its threshold has to be that number: zero reads as unset.

Assertions. Help now checks exit codes, not just stdout equality. The
control list is compared in order, because the approved document is
ordered and a map accepted either arrangement. The objective digest must
be 64 lowercase hex, not any hex. The environment baseline genuinely
unsets the variable instead of setting it empty, which code using
LookupEnv can tell apart. Top-level help compares whole command rows, so a
swapped description fails. A new test reads the manpage, guide, changelog
and spec lifecycle, comparing whole flag names: a substring check accepted
--fullx for --full.

Comments. The orphaned migrate line under the coverage synopsis, the
function comment describing dispatch fall-through, the unconditional
--rules-dir description, and the package comment denying any objective
catalog all still contradicted the shipped behavior.

The smoke script contained echoecho. With set -uo pipefail and no -e the
shell reported command not found, continued, and still printed 227 passed.
Two review findings, both of them mine.

The traceability correction reported in the previous commit was not in it.
While reverting a mutation I ran git checkout on mechanisms_test.go, which
restores from HEAD and discarded the real edit along with the mutation. I
then reported a measurement taken before that wipe, so the deprecated spec
was described at 25% covering two criteria while it actually stood at 50%
covering four, and the stale warning-suppression test was still present.
The lesson is narrow and mechanical: restore a mutated file from the copy
made before mutating it, never from the index, when the file also carries
work in progress.

That test is now deleted. It asserted that an environment variable
silences a warning; neither the variable nor the warning exists, and it
passed only because the current error text happens to lack the words it
searched for. TestRunMechanisms_NoWarning moves to AC-04, the criterion it
proves, and TestRunMechanisms_HelpExitsZero loses its annotation because
AC-07 requires both subcommands and it exercises one. The remaining
references to deleted tests in the deprecated spec now read as supersession
statements rather than dangling attributions.

The flag assertions compared help, completion and the manpage against each
other, so they agreed on a set that nobody approved: adding --rogue to all
three passed every criterion and manpage-check. Each now compares against
one approved literal set, in both directions, so an extra flag fails as
loudly as a missing one.
AC-02's expected output names the exact seven-flag set, but its test only
compared the three help invocations against each other. An extra flag
appeared in all three identically, so it agreed with itself and passed;
only the completion criterion caught it. Both criteria now call one shared
assertion against the approved literal set, so each fails on its own.

The rest is comments that still described the removed alias as current.

The direct-handler missing-framework test claimed the dispatch criterion
while calling the handler directly, and its comment said dispatch falls
through to the mechanism alias. It keeps its value as a unit regression and
loses the mapping: the criterion is about dispatch, and a runCLI test
proves that.

The heading for a deleted test was left behind. The hasFrameworkFlag
comment still described routing coverage between report and alias modes,
when the helper now exists so the mechanisms rejection agrees with what the
coverage flagset would accept. The mechanisms-rejection test names both
criteria it satisfies and why they overlap.

mechanisms_test.go called itself tests for the rename and the alias. The
one-sided help test no longer claims the historical criterion that requires
both commands. quiet_test.go pointed at an alias test deleted with the
alias, and now points at the coverage case, which has the flag.
@remyluslosius
remyluslosius merged commit 4b63e62 into main Sep 11, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant