kane-cli cover measures coverage on two independent axes over the same store:
- Depth is what a real execution proved. It is read from the coverage records inside an evidence pack. These are facts sealed into the pack along with their verdicts, and
cover never recomputes them.
- Completeness is what the design still owes. It is computed live from the
.context/ graph and never reads packs. Even a perfect pack can leave a criterion unverified or a use-case tested only on its happy path.
A run can look green and still leave coverage missing. The two axes make that situation visible.
cover gaps reads only the graph and does not take --from. To audit a specific pack, use kane-cli cover --from <pack>. The default output of cover gaps is the coverage ribbon described below, and its nested --json document is the format for scripts and tools.
cover: the pack panel
The panel always shows both axes: first the depth the pack proved, then the completeness worklist from the live graph, with commands ready to paste. --json emits both as one document.
- The default pack is the newest one in
<cwd>/.testmuai/evidence. --from takes a pack directory, a sealed .evidence file, or an execution id.
- Depth is risk-weighted and lenient. A high-risk criterion counts for more, and a criterion that passed but is now stale still counts as proven. Staleness is shown (
· N stale), never silently subtracted.
- Each use-case gets one status:
covered (every AC proved), blocked (something could not run, but nothing failed), partial, or uncovered.
- Coverage reflects this run only: a sealed pack covers only what its run touched. Project-wide coverage is the graph axis, which no single pack affects.
--json emits the full panel as structured data.
cover gaps: the coverage ribbon
The default output is one continuous table of bands across two axes. It is built entirely from the graph, so no pack is opened or needed:
- designed is how much of the live requirement set has a live test verifying it. It is risk-weighted, so high-risk criteria count for more. Freshness never changes the number; stale criteria appear as
STALE debt instead.
- proven comes from the store’s own execution facts: every
testmd run and testrun run records its verdicts when it finishes. These are scored with the same formula a sealed pack uses, so the live number and a pack’s number are calculated the same way. --rollup lenient|strict picks the formula (default lenient). If nothing has run anywhere, the proven line says so in words rather than showing a misleading 0%.
The ribbon is a high-level view by design. It has:
- A three-line header: the title, with the rollup and how recently anything ran; then the two axis bars, where the proven line also carries a count per class.
- One row per live use-case, ordered by severity (failing → blocked → design debt → stale → pending-run → clean). Each row shows the id, the title, designed and proven percentage bars colored by value, and debt counts per class (
FAILING · BLOCKED · STALE · TO DESIGN · TO RUN). A count column appears only when some use-case has that kind of debt. On a narrow terminal the counts are dropped, and the id, title, and bars stay.
- A last line that tells you how to drill in.
The ribbon shows no AC rows, commands, or lists of gaps; that detail lives in the dossier. When nothing has run yet, the all-clear reads designed · nothing run yet. The ribbon only says “proven” when every criterion’s verifying test actually passed. Piped output follows the same structure; only the color and the width-responsive layout differ.
If you deferred a change during a reconcile review, it still shows up here: in the ribbon’s debt counts, and as a pending row in the dossier and the --json document, with the reconcile command as its fix. Deferring postpones a decision; it never hides one.
cover gaps <uc-id>: the dossier
The dossier shows the full detail for one use-case:
- the axis bars, scoped to that use-case;
- every live acceptance criterion in a full-text table (
id · STATE · RISK · criterion), wrapped and never truncated, including rows that are proven or not yet run.
A failing row names the test file when its id matches exactly one file, and shows only the id otherwise. A next block of actions starts with the evidence, never with a blind re-run:
The run or design fix comes second, with its own warning: a failed test re-run in authoring mode may heal itself around the failure, so fix the app first.
Flags
--stage design|cover|all (default all) filters the pending rows in the --json document. It never changes a number, a row, or the order of the table.
--top <n> limits the pending rows per use-case in the --json document. The table always shows one row per use-case and is never truncated.
--rollup lenient|strict picks the formula for the proven axis (default lenient).
--json emits the nested document for scripts: the designed axis, the proven axis (left out entirely when the store has no execution facts), and the pending rows per use-case, each with its ready_command. With a <uc-id>, the document contains only that use-case: one entry, with project-wide leftovers emptied. Debt that belongs to no live use-case appears only in the --json document, never in the human views.
Agents and CI
Both cover and cover gaps take --mode agent|ci. The run then uses the same NDJSON envelope as the other assurance commands:
- The whole
--json payload arrives as a single coverage event (or gaps event). With a <uc-id>, it is the document scoped to that use-case.
done is always last, and done’s next[] carries the worklist’s commands, ready to paste.
- A refusal is an
error event plus done, with exit 2.
See Automation.
The join: how a pack knows your graph
Every per-test result in an evidence pack carries a definition_id: a hash of the resolved test definition, identical to the one design stamps on each test it writes. A pack is matched to your graph by this hash and nothing else, so there are no ids to sync and no registry to maintain.
If you edit a test by hand, it hashes differently and stops matching until the graph catches up. That happens on the test’s next run, which records the edit as a superseding version and restores the link. A redesign (design tests --force or maintain evolve) re-stamps the hash in the same way.
Coverage records are added to packs automatically whenever the project has a .context/ store. The inline run/testmd path and testrun both write them before sealing the pack. A project without a store gets packs with no coverage records, exactly as if assurance were not in use. If writing the coverage records fails, the pack is still sealed.
The authoring bridge
A freshly designed test has never been executed, and the tools say so:
kane-cli design tests writes t-…_test.md files. They are runnable, but they have no recording yet.
kane-cli testrun accepts them anyway. At preflight, each test in the run is classified as replay (authored before) or author (fresh). Author tests run on their own, with the agent authoring each one in a real browser. After both passes, a best-effort consolidation merges the evidence into one published execution, recomputes coverage, and syncs the facts into the graph. If consolidation cannot complete (for example, the run was cancelled or the merge degrades), the evidence is kept split rather than lost.
kane-cli testmd run <file> authors one test on its own.
- Either way, an authored test replays like any other, and its verdicts join the pack through
definition_id. testrun --from-context selects tests by their assurance test ids and follows edit supersessions, so a superseded test stays connected. Only tombstoned (deleted) tests are excluded.
Until a test’s first authored run, cover reports its criteria as covered on paper, not yet proven in execution (covered_by present, execution not-run). This is intentional: a designed test is a claim until a run proves it.
Inside the pack: coverage/usecases.yaml
A pack’s coverage record is one YAML file you can read, diff, and archive. It has one row per live use-case: its identity and risk, sources and provenance, scenarios, and each acceptance criterion with its verdict (covered_by, execution: passed|failed|blocked|not-run, fresh, the expected answer, and what satisfied it). Diff the usecases.yaml of two packs to see exactly how a release changed proven coverage.
Next steps