crapkit 0.4.1__tar.gz → 0.4.3__tar.gz

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.
Files changed (70) hide show
  1. {crapkit-0.4.1 → crapkit-0.4.3}/PKG-INFO +55 -17
  2. crapkit-0.4.1/src/crapkit.egg-info/PKG-INFO → crapkit-0.4.3/README.md +820 -815
  3. {crapkit-0.4.1 → crapkit-0.4.3}/pyproject.toml +6 -2
  4. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/__init__.py +1 -1
  5. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/analyze.py +26 -17
  6. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/cli/__init__.py +7 -0
  7. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/cli/admin.py +27 -7
  8. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/cli/claude_hook.py +14 -4
  9. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/cli/parser.py +2 -0
  10. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/cli/queue.py +59 -16
  11. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/cli/ratchet_cmds.py +37 -8
  12. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/cli/reports.py +16 -3
  13. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/cli/scoring.py +61 -8
  14. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/cli/verifying.py +88 -11
  15. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/config.py +102 -10
  16. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/doctor.py +1 -1
  17. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/dup.py +16 -3
  18. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/hook.py +19 -4
  19. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/junitparse.py +53 -3
  20. crapkit-0.4.3/src/crapkit/keys.py +86 -0
  21. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/lanes.py +30 -0
  22. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/lizardcognitive.py +37 -7
  23. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/mcp_server.py +11 -5
  24. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/override.py +5 -3
  25. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/packet.py +36 -1
  26. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/ratchet.py +76 -18
  27. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/scaffold.py +29 -4
  28. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/store.py +61 -13
  29. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/verify.py +19 -6
  30. crapkit-0.4.1/README.md → crapkit-0.4.3/src/crapkit.egg-info/PKG-INFO +853 -783
  31. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit.egg-info/SOURCES.txt +1 -0
  32. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit.egg-info/requires.txt +1 -0
  33. {crapkit-0.4.1 → crapkit-0.4.3}/LICENSE +0 -0
  34. {crapkit-0.4.1 → crapkit-0.4.3}/setup.cfg +0 -0
  35. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/__main__.py +0 -0
  36. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/_pygdefer.py +0 -0
  37. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/cache.py +0 -0
  38. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/churn.py +0 -0
  39. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/churn_cache.py +0 -0
  40. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/churn_log.py +0 -0
  41. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/cli/_shared.py +0 -0
  42. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/cli/analyses.py +0 -0
  43. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/coupling.py +0 -0
  44. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/coverage_istanbul.py +0 -0
  45. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/coverage_py.py +0 -0
  46. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/covstream.py +0 -0
  47. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/diffparse.py +0 -0
  48. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/digest.py +0 -0
  49. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/discover.py +0 -0
  50. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/errors.py +0 -0
  51. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/gitio.py +0 -0
  52. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/lizardpowershell.py +0 -0
  53. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/lizardrust.py +0 -0
  54. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/lizardshell.py +0 -0
  55. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/merge.py +0 -0
  56. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/mutate.py +0 -0
  57. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/mutate_pool.py +0 -0
  58. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/ratchet_report.py +0 -0
  59. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/report.py +0 -0
  60. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/sarif.py +0 -0
  61. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/sarifio.py +0 -0
  62. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/score.py +0 -0
  63. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/snapshot.py +0 -0
  64. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/uncovered.py +0 -0
  65. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/universe.py +0 -0
  66. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/watch.py +0 -0
  67. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit/worklist.py +0 -0
  68. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit.egg-info/dependency_links.txt +0 -0
  69. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit.egg-info/entry_points.txt +0 -0
  70. {crapkit-0.4.1 → crapkit-0.4.3}/src/crapkit.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crapkit
3
- Version: 0.4.1
3
+ Version: 0.4.3
4
4
  Summary: Scores every function on complexity times uncovered risk, ranks the worst, and blocks commits that add more.
5
5
  Author: Jean-Francois Gagne
6
6
  License: MIT
@@ -28,6 +28,7 @@ Requires-Dist: lizard>=1.24.0
28
28
  Provides-Extra: dev
29
29
  Requires-Dist: pytest>=8; extra == "dev"
30
30
  Requires-Dist: pytest-cov>=5; extra == "dev"
31
+ Requires-Dist: pytest-xdist>=3; extra == "dev"
31
32
  Dynamic: license-file
32
33
 
33
34
  # crapkit
@@ -115,7 +116,7 @@ changing crapkit.
115
116
 
116
117
  ```
117
118
  $ crapkit --version
118
- crapkit 0.4.1
119
+ crapkit 0.4.3
119
120
  ```
120
121
 
121
122
  `python -m crapkit` works identically to the console script and is what to use from a
@@ -123,6 +124,22 @@ source checkout. Every subcommand accepts `--repo PATH` (default: the current di
123
124
  so you never have to `cd` into the repo you are scoring. The flag goes after the
124
125
  subcommand; [Subcommands](#subcommands) shows both orders.
125
126
 
127
+ ### Upgrading on Windows
128
+
129
+ `uv tool upgrade crapkit`, and `pip install -U` into a tool venv, fail with `os error 32`
130
+ ("The process cannot access the file because it is being used by another process") while a
131
+ crapkit MCP server is live: an agent session spawns `crapkit.exe mcp`, which holds the
132
+ launcher, and Windows will not overwrite a running executable. The venv upgrades before
133
+ that copy fails, so `crapkit --version` already reports the new version and the launcher is
134
+ the only stale piece. Quit the agent session and rerun the upgrade, or rename the locked
135
+ exe aside (Windows allows renaming a running one) and copy the new one in; the `.old` file
136
+ goes at the next reboot.
137
+
138
+ ```
139
+ mv ~/.local/bin/crapkit.exe ~/.local/bin/crapkit.exe.old
140
+ cp %APPDATA%/uv/tools/crapkit/Scripts/crapkit.exe ~/.local/bin/crapkit.exe
141
+ ```
142
+
126
143
  ## The Claude Code plugin
127
144
 
128
145
  ```
@@ -165,10 +182,16 @@ lane. Nothing about it is provisional: the ceiling still binds and the gate stil
165
182
  a function over it. Add a coverage lane the day a parser exists and the same scope starts
166
183
  joining coverage.
167
184
 
185
+ `crapkit init` writes that key itself, on every scope whose languages all lack a parser,
186
+ and leaves it off any scope a lane could still measure. So the 60-second start above runs
187
+ unchanged on a Go, Rust or shell repo: `crapkit coverage` scores it with no lane at all,
188
+ and that run is the baseline `worklist`, `next-item`, `ratchet seed` and `verify` read.
189
+
168
190
  Three readers are crapkit's own. lizard ships none for shell or PowerShell, so crapkit
169
191
  counts their functions itself. Its Rust reader scores a 7-arm `match` as ccn 2 (filed as
170
192
  lizard #494), so crapkit counts each non-wildcard arm like a C `case` and retires the
171
- override the day upstream fixes it.
193
+ override the day upstream fixes it. The cognitive column charges that same block once,
194
+ the way Sonar charges a `switch`.
172
195
 
173
196
  ## The gate
174
197
 
@@ -178,8 +201,8 @@ different powers:
178
201
  | Surface | Fires | Power |
179
202
  |---|---|---|
180
203
  | `crapkit claude-hook` | after an agent's edit lands | **advisory.** Names the breach on stderr. Blocks nothing, because PostToolUse runs after the write |
181
- | `crapkit rescore FILE --gate` | when you ask | **preview.** The commit gate's verdict on demand, sub-second, before you stage |
182
- | `crapkit hook-precommit` | `git commit` | **blocks.** Exit 6. Staged blobs only, so it costs the size of the commit and needs no coverage |
204
+ | `crapkit rescore FILE --gate` | when you ask, after the first coverage run | **preview.** The commit gate's verdict on demand, sub-second, before you stage. With no run behind it, exit 1 and `no snapshot` |
205
+ | `crapkit hook-precommit` | `git commit` | **blocks.** The hook exits 6; git reports 1. Staged blobs only, so it costs the size of the commit and needs no coverage |
183
206
  | `crapkit verify` | before you push, and in CI | **the verdict.** Gate, ratchet, new test failures, diff coverage, against the trusted baseline |
184
207
 
185
208
  Both hooks exempt a function the committed ratchet already carries a mark for, so touching
@@ -239,7 +262,8 @@ crapkit ships a `.pre-commit-hooks.yaml` declaring `id: crapkit-gate`. In your
239
262
  ```yaml
240
263
  repos:
241
264
  - repo: https://github.com/JeanFrancoisGagne/crapkit
242
- rev: v0.4.0
265
+ # crapkit's release step rewrites this line to the tag it just cut
266
+ rev: v0.4.3
243
267
  hooks:
244
268
  - id: crapkit-gate
245
269
  ```
@@ -275,7 +299,9 @@ crapkit gate: 1 staged function(s) exceed the complexity ceiling of 6:
275
299
  decompose before committing (coverage cannot save a function above the target).
276
300
  ```
277
301
 
278
- Run directly, `crapkit hook-precommit` exits 6 on a violation and 0 otherwise.
302
+ That commit exited **1**, not 6. Git collapses any failed hook to 1, so 6 is a code you
303
+ only ever see by running the hook yourself: `crapkit hook-precommit` exits 6 on a
304
+ violation and 0 otherwise. The stderr block above is the same either way.
279
305
 
280
306
  `CRAPKIT_OVERRIDE_REASON` is not a bypass. Setting it routes the commit through the full
281
307
  three-record audit: an alert line through `alert_command`, a ratchet entry staged into the
@@ -311,12 +337,12 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
311
337
  | `doctor [--show-files] [--json] [--tune] [--plugin-root PATH]` | Checks the config still describes the repo: unknown keys (with the accepted spellings), zero-file scopes, tracked source no scope claims, scopes no lane covers, lane cwds and commands that no longer resolve, lizard importable, oversized files. It WARNs on a lane writing its artifact at the repo root, a committed hook under `core.hooksPath` that is not executable in the index, a directory whose functions are all `untested` while its tests exist, and a scope a lane measures with no `[crapkit.scoped_tests]` template behind it, which is the loop's step 4 with nothing to run. `--tune` prints suggested parallelism knobs and writes nothing. `--plugin-root PATH` reads no repo at all: it checks an installed [plugin](plugin/) against this CLI on both version and hook `--protocol`, one line per disagreement and silence when they agree. See [docs/agent-json.md](docs/agent-json.md#doctor---json). |
312
338
  | `inventory [--db PATH] [--export PATH] [--json]` | One lizard pass over every in-scope file into a SQLite snapshot run, cached by content hash. `--db` is the only way to point crapkit at a store outside `.crapkit/`, and only this command accepts it. |
313
339
  | `coverage [--lane NAME] [--reuse-artifacts] [--reuse-unchanged] [--export PATH] [--sarif PATH] [--github] [--json]` | Runs the lanes, joins branch coverage onto a fresh inventory, writes a scored run. A failed lane is recorded, not fatal: its scopes fall back to `no-lane` and the run is typed `partial`, so it can never serve as a baseline. See [docs/lanes.md](docs/lanes.md). |
314
- | `verify [--baseline ID \| --base REF \| --baseline-tsv PATH] [--emit-baseline PATH] [--override REASON] [--reuse-artifacts] [--reuse-unchanged] [--sarif PATH] [--github] [--json]` | The full verdict against the trusted baseline: gate on touched functions, ratchet, no new test failures, optional diff-coverage ceiling. The three baseline selectors are mutually exclusive; `--baseline ID` also bypasses the taint rule ([The trusted baseline](#the-trusted-baseline)), and `--baseline-tsv` reads a commit-stamped file so a fresh clone verifies with no store. Findings a dirty tree produced are tagged `dirty` and counted apart. |
340
+ | `verify [--baseline ID \| --base REF \| --baseline-tsv PATH] [--emit-baseline PATH] [--override REASON] [--reuse-artifacts] [--reuse-unchanged] [--no-tighten] [--sarif PATH] [--github] [--json]` | The full verdict against the trusted baseline: gate on touched functions, ratchet, no new test failures, optional diff-coverage ceiling. The three baseline selectors are mutually exclusive; `--baseline ID` also bypasses the taint rule ([The trusted baseline](#the-trusted-baseline)), and `--baseline-tsv` reads a commit-stamped file so a fresh clone verifies with no store. `--no-tighten` passes the verdict without rewriting the ratchet. Findings a dirty tree produced are tagged `dirty` and counted apart. |
315
341
  | `worklist [--top N] [--scope NAME] [--batches N] [--json]` | The risk map: every admitted function ranked by `ccn * churn weight`, floored by `worklist_floor`, with hot simple code and anything over its ceiling admitted past that floor. It ranks finished rows and `no-lane` rows too, marked `ok` and `no-lane`, so it never empties; `next-item` carries the stop condition. `--scope NAME` (repeatable) is exact, not a substring. `--batches N` **adds** a `batches[]` view cutting the active list into at most N file-disjoint batches with co-changing files kept together; the normal keys stay. |
316
342
  | `next-item [--top N] [--exclude FRAG] [--scope NAME] [--claim]` | The actionable queue as JSON, with churn, budget estimates and uncovered lines. Same run and same admission floor as `worklist`, a different view of it: `no-lane` rows are skipped and counted in `skipped_no_lane`, and what is left is ranked by `crap` descending rather than by risk, so the item it hands out is often not the worklist's first row. `--exclude FRAG` (repeatable) skips items whose path or function name contains FRAG; `--scope NAME` (repeatable) is exact, not a substring. `--claim` holds what it hands out so a second session skips it. `stale` is true when the ranked run's commit is not HEAD, the same field `worklist` carries. Every item carries a `handle`: the bare identifier, or `(anonymous)#N` for a function with no name, which is the name form that survives the edit the item asks for. |
317
343
  | `claims [list \| release PATH NAME \| release --all] [--json]` | The open claims, and the way to hand one back without waiting for a verify. `release` takes the bare identifier, the whole long name, or the `handle` the claim was taken under, which is the only one that picks out a single `(anonymous)` claim. |
318
- | `brief FILE NAME [--batch N] [--json]` | The start-editing packet for one function: its own `source` text, every function in the file, the scored row and the scope ceiling, the ratchet mark and what the gate will bind on, uncovered lines, duplication twins, file churn, coupling partners, the config's notes, and the literal commands for the rest of the loop. Plus `handle`, `remedy` and the same `est_splits` / `est_uncovered_paths` the queue prints, and a `commands.refresh` that writes a run (`refresh_writes_run`) rather than re-reading the stale one. `NAME` takes the bare identifier, the long name `next-item` printed, the function's start line, or `(anonymous)#N` for a function printed `(anonymous)`, counting the file's anonymous functions from the top. `--batch N` drops the positionals and emits `packets[]` instead: the top N of the queue, built from one read of the store. |
319
- | `explain FILE NAME [--history] [--tests] [--json]` | A function's score across runs plus its mark. `--history` adds the commits that touched it (`git log -L`), each carrying its message `body`, `--tests` the tests that covered it, which needs coverage.py contexts turned on ([recipe](docs/lanes.md#test-attribution-for-explain---tests)). `--json` emits the same content as one `schema` 1 object. |
344
+ | `brief FILE NAME [--batch N] [--json]` | The start-editing packet for one function: its own `source` text, every function in the file, the scored row and the scope ceiling, the ratchet mark and what the gate will bind on, uncovered lines, duplication twins, file churn, coupling partners, the config's notes, and the literal commands for the rest of the loop. Plus `handle`, `remedy` and the same `est_splits` / `est_uncovered_paths` the queue prints, and a `commands.refresh` that writes a run (`refresh_writes_run`) rather than re-reading the stale one. `NAME` takes the bare identifier, the long name `next-item` printed, the function's start line, `(anonymous)#N` for a function printed `(anonymous)` counting the file's anonymous functions from the top, or `NAME#2` for the second of several functions a file gives one name to. `--batch N` drops the positionals and emits `packets[]` instead: the top N of the queue, built from one read of the store. |
345
+ | `explain FILE NAME [--history] [--tests] [--json]` | A function's score across runs plus its mark. `NAME` resolves exact first: a function whose bare identifier or long name is exactly `NAME` wins, and only when nothing matches exactly does it fall back to a prefix match, so `route` explains `route` rather than every `route_*` beside it. `--history` adds the commits that touched it (`git log -L`), each carrying its message `body`, `--tests` the tests that covered it, which needs coverage.py contexts turned on ([recipe](docs/lanes.md#test-attribution-for-explain---tests)). `--json` emits the same content as one `schema` 1 object. |
320
346
  | `rescore FILE ... [--gate] [--json]` | Fresh complexity for named files over the latest run's stale coverage, joined by name. Advisory: it writes no run. `--gate` applies the pre-commit hook's policy to the same selection the hook uses (functions the tree changed since HEAD), minus functions a ratchet mark already covers, and exits 6. |
321
347
  | `ratchet seed \| prune \| merge \| move \| report [--enforce] [--json]` | The mark lifecycle: seed new debt, prune gone code (a mark whose file git renamed follows it), merge as a git driver, move re-paths marks, report reads burn-down from the file's own git history. See [docs/ratchet.md](docs/ratchet.md). |
322
348
  | `runs [list \| prune [--keep N]] [--json]` | Run history, and retention. `list` marks the run `verify` compares against today `baseline`, and prints `verdict=-` for a run that produces no verdict rather than one that failed. See [The trusted baseline](#the-trusted-baseline). `--keep` (default 5) is a floor on the newest trusted runs, not a cap: the digest pair, every passing verify baseline, every run an override names, and the newest non-hook run are kept too. `prune` VACUUMs afterwards. |
@@ -324,7 +350,7 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
324
350
  | `trend [--json]` | Totals per trusted run: functions, over-target count, CRAP load, average, per-scope rollup. |
325
351
  | `digest [--alert]` | The delta between the two newest runs with identical lane sets. Silent when nothing changed. `--alert` pipes the body to `alert_command` on stdin. Plain lines, never JSON. |
326
352
  | `report [--out PATH]` | One self-contained HTML page written to `.crapkit/report.html` (or `--out PATH`, repo-relative), with the path printed on stdout. It renders what `worklist --json` and `trend --json` already answer at their defaults: the ranked worklist capped at `worklist_top`, the per-scope grades off the newest run, the trend series, and a banner naming every stale lane. It measures nothing and opens no network connection. Per-function CRAP and coverage are absent because no repo-wide payload carries them; each row prints the `crapkit explain` call that does. |
327
- | `duplication [--min-lines N] [--similarity F] [--top N] [--json]` | Near-duplicate functions by normalized line shingles with containment scoring. Defaults: `--min-lines 8`, `--similarity 0.8`, `--top 50`. `--top` truncates the list. |
353
+ | `duplication [--min-lines N] [--similarity F] [--top N] [--json]` | Near-duplicate functions by normalized line shingles with containment scoring. Defaults: `--min-lines 8`, `--similarity 0.8`, `--top 50`. `--top` truncates the list. A function and a function nested inside it never pair: their spans nest, they score 1.0 by construction, and nobody can deduplicate a factory from its own closure. |
328
354
  | `coupling [--min-support N] [--min-confidence F] [--top N] [--json]` | File pairs that keep landing in the same commits. Defaults: `--min-support 5` shared commits, `--min-confidence 0.5` max-direction ratio, `--top 50`. Bulk commits never couple pairs, and a young repo returns nothing at the default support. |
329
355
  | `mutate [--files F ...] [--max-mutants N] [--json]` | Diff-scoped mutation testing: flips comparisons, boundary shifts, boolean connectives and boolean literals on changed lines, runs `mutation_command` per mutant, lists survivors. `--files` replaces diff scope with the whole file. `--max-mutants` (default 100) caps the run and the cap warning goes to stderr only, so `mutants` in `--json` is the capped count. Shell and PowerShell files are refused by name on stderr rather than mutated: `<` and `>` are redirections there, not comparisons. |
330
356
  | `test-scoped FILE ...` | Runs each owning scope's `[crapkit.scoped_tests]` template on the files (quoted, longest-prefix scope wins). A template with no `{files}` runs as written, which is how a scope whose tests live outside its own paths runs its whole suite. Exit code only; a nonzero runner exits 1. |
@@ -402,7 +428,9 @@ baseline**. `crapkit runs list` marks which one that is today.
402
428
  never qualifies, and neither does a `partial` run (a lane failed, so some scope fell back
403
429
  to `no-lane`) nor a `hook` override record, which carries no scored rows at all. In `runs
404
430
  list`, `verdict=-` marks a run that produces no verdict rather than one that failed: only
405
- `verify` renders a verdict.
431
+ `verify` renders a verdict. Three readers ask this one question and get this one answer:
432
+ the baseline pick here, `ratchet seed` and `prune`, and the tighten damping that compares a
433
+ mark against the same commit's previous run.
406
434
 
407
435
  **What advances it.** Any qualifying run. `coverage` writes one wherever HEAD is, so a
408
436
  dashboard cron advances the baseline exactly as CI does. A passing `verify` advances it
@@ -536,6 +564,11 @@ makes it step 4 of the burn-down loop. Every key is in
536
564
 
537
565
  ```
538
566
  $ crapkit doctor
567
+ ok config keys all recognized
568
+ ok scope 'calc': 1 files
569
+ ok every tracked source file belongs to a scope
570
+ ok 1 lane(s) declared
571
+ ok lizard 1.24.0
539
572
  doctor: no problems found
540
573
  ```
541
574
 
@@ -567,11 +600,12 @@ worklist has none.
567
600
 
568
601
  ```
569
602
  $ crapkit next-item
570
- {"commit": "fae4db93108b4841a00959f9117430679e7250ca", "empty": false, "item": {"authors": 1, "ccn": 14, "ccn_std": 14, "cognitive": 13, "commits": 1, "cov": 0.5, "crap": 38.5, "end": 28, "est_splits": 3, "est_uncovered_paths": 7, "flag": "measured", "function": "classify( score , attempts , late , bonus )", "nesting": 8, "nloc": 22, "path": "calc/grade.py", "remedy": "decompose", "scope": "calc", "start": 7, "target": 6, "uncovered_lines": [9, 11, 15, 17, 19, 24, 25, 26, 27, 28]}, "run_id": 1, "schema": 1, "skipped_no_lane": 0}
603
+ {"commit": "fae4db93108b4841a00959f9117430679e7250ca", "empty": false, "item": {"authors": 1, "ccn": 14, "ccn_std": 14, "cognitive": 13, "commits": 1, "cov": 0.5, "crap": 38.5, "end": 28, "est_splits": 3, "est_uncovered_paths": 7, "flag": "measured", "function": "classify( score , attempts , late , bonus )", "handle": "classify", "nesting": 8, "nloc": 22, "path": "calc/grade.py", "remedy": "decompose", "scope": "calc", "start": 7, "target": 6, "uncovered_lines": [9, 11, 15, 17, 19, 24, 25, 26, 27, 28]}, "run_id": 1, "schema": 1, "skipped_no_lane": 0, "stale": false}
571
604
  ```
572
605
 
573
606
  `remedy: "decompose"`, `est_splits: 3` (this needs roughly three pieces to fit under 6),
574
- and `uncovered_lines` naming the ten lines no test walks. Every field is in
607
+ and `uncovered_lines` naming the ten lines no test walks. `handle` is the name form to
608
+ pass back, and `stale: false` says the run still describes HEAD. Every field is in
575
609
  [docs/agent-json.md](docs/agent-json.md).
576
610
 
577
611
  ### 5. Seed the ratchet
@@ -648,17 +682,21 @@ crapkit: lane 'js' FAILED: lane 'js' produced no artifact at .crapkit/cov/js/cov
648
682
  crapkit: every lane failed: ...
649
683
  ```
650
684
 
651
- Install the provider, pinned to your vitest major or npm refuses the peer dependency:
685
+ That failure **writes no run**. Every lane failed, so `coverage` exits before it opens a
686
+ store: there is no `.crapkit/crap.sqlite` yet and the run ids below still start at 1.
687
+
688
+ Install the provider, and pin the major yourself. Unpinned, npm resolves the newest
689
+ provider against your older vitest and refuses the tree:
652
690
 
653
691
  ```
654
- npm i -D @vitest/coverage-v8
692
+ npm i -D "@vitest/coverage-v8@<your vitest major>"
655
693
  ```
656
694
 
657
695
  | Question | Answer |
658
696
  |---|---|
659
697
  | Which provider? | Either works. `@vitest/coverage-v8` is vitest's default and needs no config. `@vitest/coverage-istanbul` also works and needs `coverage.provider = "istanbul"` in your vitest config. |
660
698
  | Which crapkit parser? | Both feed `parser = "istanbul"`. The provider name and the parser name are unrelated: v8 output is remapped to the istanbul JSON schema before it is written. |
661
- | Which version? | It must match your vitest major. npm refuses the install otherwise (`peer vitest@"4.x" from @vitest/coverage-v8@4.x`). On vitest 2, `npm i -D "@vitest/coverage-v8@2"`. |
699
+ | Which version? | The provider's major has to match vitest's. On vitest 2 that is `npm i -D "@vitest/coverage-v8@2"`, on vitest 3 `npm i -D "@vitest/coverage-v8@3"`. Drop the pin and npm answers `ERESOLVE unable to resolve dependency tree`, naming the peer it could not satisfy. |
662
700
 
663
701
  The artifact crapkit wants is `coverage-final.json`, written by vitest's `json` coverage
664
702
  reporter, which is on by default. If your vitest config sets `coverage.reporter`