crapkit 0.4.4__tar.gz → 0.4.5__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.
- {crapkit-0.4.4 → crapkit-0.4.5}/PKG-INFO +194 -56
- crapkit-0.4.4/src/crapkit.egg-info/PKG-INFO → crapkit-0.4.5/README.md +963 -862
- {crapkit-0.4.4 → crapkit-0.4.5}/pyproject.toml +23 -6
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/__init__.py +1 -1
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/analyze.py +26 -9
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/churn_cache.py +42 -5
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/churn_log.py +45 -1
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/cli/__init__.py +19 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/cli/admin.py +355 -55
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/cli/analyses.py +49 -5
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/cli/claude_hook.py +8 -1
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/cli/parser.py +9 -3
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/cli/queue.py +62 -29
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/cli/ratchet_cmds.py +29 -13
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/cli/reports.py +2 -1
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/cli/scoring.py +41 -15
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/cli/verifying.py +112 -23
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/config.py +209 -32
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/coupling.py +45 -10
- crapkit-0.4.5/src/crapkit/coupling_cache.py +147 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/covstream.py +38 -3
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/dup.py +53 -5
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/gitio.py +179 -23
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/lanes.py +42 -22
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/lizardcognitive.py +90 -6
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/lizardshell.py +3 -3
- crapkit-0.4.5/src/crapkit/mutate_pool.py +308 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/packet.py +10 -3
- crapkit-0.4.5/src/crapkit/procs.py +66 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/scaffold.py +68 -18
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/store.py +173 -40
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/uncovered.py +96 -8
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/universe.py +61 -23
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/verify.py +28 -4
- crapkit-0.4.4/README.md → crapkit-0.4.5/src/crapkit.egg-info/PKG-INFO +1000 -827
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit.egg-info/SOURCES.txt +2 -0
- crapkit-0.4.4/src/crapkit/mutate_pool.py +0 -152
- {crapkit-0.4.4 → crapkit-0.4.5}/LICENSE +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/setup.cfg +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/__main__.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/_pygdefer.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/cache.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/churn.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/cli/_shared.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/coverage_istanbul.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/coverage_py.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/diffparse.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/digest.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/discover.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/doctor.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/errors.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/hook.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/junitparse.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/keys.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/lizardpowershell.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/lizardrust.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/mcp_server.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/merge.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/mutate.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/override.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/ratchet.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/ratchet_report.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/report.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/sarif.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/sarifio.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/score.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/snapshot.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/watch.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit/worklist.py +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit.egg-info/dependency_links.txt +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit.egg-info/entry_points.txt +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/src/crapkit.egg-info/requires.txt +0 -0
- {crapkit-0.4.4 → crapkit-0.4.5}/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.
|
|
3
|
+
Version: 0.4.5
|
|
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
|
|
@@ -9,18 +9,20 @@ Project-URL: Documentation, https://jeanfrancoisgagne.github.io/crapkit/handbook
|
|
|
9
9
|
Project-URL: Changelog, https://github.com/JeanFrancoisGagne/crapkit/blob/main/CHANGELOG.md
|
|
10
10
|
Project-URL: Issues, https://github.com/JeanFrancoisGagne/crapkit/issues
|
|
11
11
|
Project-URL: Source, https://github.com/JeanFrancoisGagne/crapkit
|
|
12
|
-
Keywords: complexity,coverage,crap,code-quality,technical-debt,cyclomatic,ratchet,pre-commit,claude-code
|
|
12
|
+
Keywords: complexity,coverage,crap,code-quality,technical-debt,cyclomatic,ratchet,pre-commit,claude-code,mcp,mutation-testing,static-analysis
|
|
13
13
|
Classifier: Development Status :: 4 - Beta
|
|
14
14
|
Classifier: Environment :: Console
|
|
15
15
|
Classifier: Intended Audience :: Developers
|
|
16
16
|
Classifier: License :: OSI Approved :: MIT License
|
|
17
17
|
Classifier: Operating System :: OS Independent
|
|
18
18
|
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
20
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
21
22
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
23
|
Classifier: Topic :: Software Development :: Quality Assurance
|
|
23
24
|
Classifier: Topic :: Software Development :: Testing
|
|
25
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
24
26
|
Requires-Python: >=3.11
|
|
25
27
|
Description-Content-Type: text/markdown
|
|
26
28
|
License-File: LICENSE
|
|
@@ -96,9 +98,29 @@ out to your own test runner, and the runner needs its coverage package installed
|
|
|
96
98
|
`pytest-cov` for pytest, `@vitest/coverage-v8` (pinned to your vitest major) for vitest.
|
|
97
99
|
Without it the lane produces no artifact and `coverage` exits 5 quoting the runner's own
|
|
98
100
|
error. For pytest, `init` probes the python its lane will run and prints the install
|
|
99
|
-
command when `pytest_cov` is missing; `pip install
|
|
100
|
-
alongside crapkit when the two share a venv.
|
|
101
|
-
|
|
101
|
+
command when `pytest_cov` is missing; `pip install "crapkit[py]"` pulls the plugin
|
|
102
|
+
alongside crapkit when the two share a venv. On a Windows PATH holding only the `py`
|
|
103
|
+
launcher it writes `py`, not a `python3` the lane could never run, and when cmd.exe cannot
|
|
104
|
+
start the interpreter at all (exit 9009, the Store alias) it names that instead of guessing
|
|
105
|
+
at pytest-cov. The two quickstarts below walk a real repo end to end.
|
|
106
|
+
|
|
107
|
+
**On Windows a lane command is read by cmd.exe**, the shell that will run it, not by sh.
|
|
108
|
+
Double quotes are the portable quoting. A single-quoted value is refused at config load
|
|
109
|
+
with exit 3, because cmd.exe would hand pytest five words and the lane would write no
|
|
110
|
+
artifact:
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
# the lane in crapkit.toml
|
|
114
|
+
command = "python -m pytest -m 'not live and not perf' --cov=calc --cov-branch --cov-report=json:.crapkit/cov/py.json"
|
|
115
|
+
|
|
116
|
+
$ crapkit doctor
|
|
117
|
+
crapkit: lane 'py': positional argument 'live' narrows a full-suite coverage run; drop it, attach it to the flag it belongs to (-n8, --numprocesses=8), or set full_suite = false deliberately (cmd.exe does not treat ' as a quote: write the value in double quotes)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Write it `-m "not live and not perf"`. Carets, `&&` and `|` segments, redirections and
|
|
121
|
+
empty quoted arguments all read the way the shell reads them, so a chained lane
|
|
122
|
+
(`cd tests && python -m pytest --cov ...`) is checked one segment at a time. `doctor` reads
|
|
123
|
+
a lane the same way, and FAILs one whose runner will not start.
|
|
102
124
|
|
|
103
125
|
## Install
|
|
104
126
|
|
|
@@ -121,30 +143,73 @@ changing crapkit.
|
|
|
121
143
|
|
|
122
144
|
```
|
|
123
145
|
$ crapkit --version
|
|
124
|
-
crapkit 0.4.
|
|
146
|
+
crapkit 0.4.5
|
|
125
147
|
```
|
|
126
148
|
|
|
127
149
|
`python -m crapkit` works identically to the console script and is what to use from a
|
|
128
150
|
source checkout. Every subcommand accepts `--repo PATH` (default: the current directory),
|
|
129
|
-
so you never have to `cd` into the repo you are scoring
|
|
130
|
-
|
|
151
|
+
so you never have to `cd` into the repo you are scoring; [Subcommands](#subcommands) shows
|
|
152
|
+
where the flag goes.
|
|
153
|
+
|
|
154
|
+
## Upgrading from 0.4.4
|
|
131
155
|
|
|
132
|
-
|
|
156
|
+
**Run `crapkit ratchet seed` first.** Shell cognitive complexity now nests, which is
|
|
157
|
+
analysis version 8, and marks measured under version 7 are not comparable. Until you
|
|
158
|
+
re-seed, `verify` refuses at exit 3:
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
$ crapkit verify
|
|
162
|
+
crapkit: ratchet marks were recorded under [crapkit-analysis=7 lizard=1.24.0] but this run measures [crapkit-analysis=8 lizard=1.24.0] — CRAP scores are not comparable across metric versions; re-baseline with `crapkit ratchet seed`
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Only shell and PowerShell cognitive numbers move. `ccn` does not, so a re-seed re-stamps
|
|
166
|
+
the file and leaves the marks where they were.
|
|
167
|
+
|
|
168
|
+
Five more things change under you. Three of them need nothing from you:
|
|
169
|
+
|
|
170
|
+
- **New cache files.** `.crapkit/coupling-cache-v1.json` joins `churn-cache-v2.json` and
|
|
171
|
+
`churn-log-v2.z`. A warm 0.4.4 churn cache is adopted once and its file removed, and
|
|
172
|
+
`.crapkit/` is already gitignored, so nothing new reaches your index.
|
|
173
|
+
- **`trend` and `report` write.** Both read a per-run rollup table, filled once per run and
|
|
174
|
+
pruned with its run, instead of rescanning every scored row. A read-only `.crapkit/`
|
|
175
|
+
costs the speedup, never the command.
|
|
176
|
+
- **Nested scopes may move files.** One predicate decides scope ownership now, and the
|
|
177
|
+
deepest declared path wins, so a repo whose `[[scope]]` paths nest inside each other can
|
|
178
|
+
see files change scope, rollup and ceiling on the next scan. Scopes that do not nest see
|
|
179
|
+
no change.
|
|
180
|
+
|
|
181
|
+
The other two put something in front of you:
|
|
182
|
+
|
|
183
|
+
- **`mutate` keeps a worktree pool.** With `mutation_workers > 1` the worker worktrees now
|
|
184
|
+
live under `.crapkit/mutate-pool/` between runs and are re-prepared each run, which is
|
|
185
|
+
the setup cost gone (30.6 s to build four on a 31,459-file repo, 0.46 s to re-prepare
|
|
186
|
+
them). The pool is not size-bounded and nothing sweeps it: `crapkit mutate --drop-pool`
|
|
187
|
+
removes it and exits. Single-worker runs are untouched.
|
|
188
|
+
- **`doctor` WARNs on a lane with no `results_artifact`.** Every `coveragepy` or `istanbul`
|
|
189
|
+
lane written before 0.4.5 gets one, with the two lines that fix it. Coverage is
|
|
190
|
+
unaffected. What the lane cannot feed without a results file is the crashed-worker check
|
|
191
|
+
and the no-new-failures check (exit 8).
|
|
192
|
+
|
|
193
|
+
### The exe lock on Windows
|
|
133
194
|
|
|
134
195
|
`uv tool upgrade crapkit`, and `pip install -U` into a tool venv, fail with `os error 32`
|
|
135
196
|
("The process cannot access the file because it is being used by another process") while a
|
|
136
197
|
crapkit MCP server is live: an agent session spawns `crapkit.exe mcp`, which holds the
|
|
137
198
|
launcher, and Windows will not overwrite a running executable. The venv upgrades before
|
|
138
|
-
that copy fails, so `crapkit --version` already reports the new version and the
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
199
|
+
that copy fails, so `crapkit --version` already reports the new version and only the
|
|
200
|
+
launcher is stale. Quit the agent session and rerun the upgrade, or rename the locked exe
|
|
201
|
+
aside (Windows allows renaming a running one) and copy the new one in. Two lines in
|
|
202
|
+
cmd.exe, where both `%` variables expand:
|
|
142
203
|
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
|
|
204
|
+
```bat
|
|
205
|
+
move %USERPROFILE%\.local\bin\crapkit.exe %USERPROFILE%\.local\bin\crapkit.exe.old
|
|
206
|
+
copy %APPDATA%\uv\tools\crapkit\Scripts\crapkit.exe %USERPROFILE%\.local\bin\crapkit.exe
|
|
146
207
|
```
|
|
147
208
|
|
|
209
|
+
Git Bash has no `move` and passes `%APPDATA%` through as literal text, so that block
|
|
210
|
+
fails there on its first line. Its form is `mv` and `cp` over `"$USERPROFILE"` and
|
|
211
|
+
`"$APPDATA"`, which Git Bash sets to the same two directories.
|
|
212
|
+
|
|
148
213
|
## The Claude Code plugin
|
|
149
214
|
|
|
150
215
|
```
|
|
@@ -211,10 +276,21 @@ different powers:
|
|
|
211
276
|
| `crapkit verify` | before you push, and in CI | **the verdict.** Gate, ratchet, new test failures, diff coverage, against the trusted baseline |
|
|
212
277
|
|
|
213
278
|
Both hooks exempt a function the committed ratchet already carries a mark for, so touching
|
|
214
|
-
signed debt never refuses a commit. `verify` is what fails a mark that rises.
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
279
|
+
signed debt never refuses a commit. `verify` is what fails a mark that rises. Since 0.4.5
|
|
280
|
+
its gate exempts a touched function whose fresh CRAP sits **at or under** its mark, the
|
|
281
|
+
rule `rescore --gate` already applied; push it past the mark and the gate fires again. The
|
|
282
|
+
pre-commit hook still exempts on the mark's existence alone, on purpose: a staged blob has
|
|
283
|
+
no coverage, so there is no fresh CRAP to compare against. It reports each exemption count
|
|
284
|
+
on stderr (`staged function(s) carry a ratchet mark and were not gated`), and says the same
|
|
285
|
+
about a staged file no `[[scope]]` claims, so a new top-level directory cannot go ungated
|
|
286
|
+
in silence.
|
|
287
|
+
|
|
288
|
+
**The crapkit root does not have to be the git top.** Since 0.4.5 every git spawn runs with
|
|
289
|
+
`diff.relative=true` and `core.quotePath=false`, so a `crapkit.toml` in `packages/api`
|
|
290
|
+
gates that package's own staged files and names them `app/m.py`, not
|
|
291
|
+
`packages/api/app/m.py`, and a dirty non-ASCII path is a real row rather than an invisible
|
|
292
|
+
one. Before that a nested root matched staged paths against no scope, and a function at
|
|
293
|
+
twice the ceiling committed with a warning.
|
|
218
294
|
|
|
219
295
|
Git runs hooks outside your shell's activated venv. Bare `python` must resolve to an
|
|
220
296
|
interpreter that has crapkit installed, or spell it out
|
|
@@ -268,11 +344,22 @@ crapkit ships a `.pre-commit-hooks.yaml` declaring `id: crapkit-gate`. In your
|
|
|
268
344
|
repos:
|
|
269
345
|
- repo: https://github.com/JeanFrancoisGagne/crapkit
|
|
270
346
|
# crapkit's release step rewrites this line to the tag it just cut
|
|
271
|
-
rev: v0.4.
|
|
347
|
+
rev: v0.4.5
|
|
272
348
|
hooks:
|
|
273
349
|
- id: crapkit-gate
|
|
274
350
|
```
|
|
275
351
|
|
|
352
|
+
That file arms nothing on its own. The framework writes `.git/hooks/pre-commit` when you
|
|
353
|
+
tell it to, and until then `git commit` runs no gate and says nothing:
|
|
354
|
+
|
|
355
|
+
```sh
|
|
356
|
+
pip install pre-commit
|
|
357
|
+
pre-commit install
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
`pre-commit install` is the line every clone needs, the way Route 2 needs its
|
|
361
|
+
`git config core.hooksPath` line.
|
|
362
|
+
|
|
276
363
|
`rev` is a git ref pre-commit resolves against that remote. Pin a release tag, not a
|
|
277
364
|
branch: `pre-commit autoupdate` only moves between tags, and a moving `main` would change
|
|
278
365
|
your gate under you.
|
|
@@ -295,6 +382,45 @@ crapkit verify --baseline-tsv crapkit-baseline.tsv --github
|
|
|
295
382
|
writes SARIF 2.1.0 for code-scanning upload. Refresh the committed baseline whenever the
|
|
296
383
|
default branch's verify passes.
|
|
297
384
|
|
|
385
|
+
Two things the job has to do before those lines run. **Install crapkit**, `pip install
|
|
386
|
+
crapkit`, and pin the version the way Route 3 pins `rev`: an unpinned install moves your
|
|
387
|
+
gate on whatever day a release lands. **Fetch the whole history.** `actions/checkout`
|
|
388
|
+
clones one commit by default, `verify` reads the diff against the baseline's commit out of
|
|
389
|
+
git, and a shallow clone does not have that commit:
|
|
390
|
+
|
|
391
|
+
```
|
|
392
|
+
$ crapkit verify --baseline-tsv crapkit-baseline.tsv
|
|
393
|
+
crapkit: baseline commit a74260f321f is not an ancestor of HEAD (rebase or amend rewrote history) — run `crapkit coverage` for a fresh baseline
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
That is exit 4 on a `git clone --depth 1` of a repo whose baseline verifies at full depth.
|
|
397
|
+
Set `fetch-depth: 0` on the checkout step, which is what crapkit's own
|
|
398
|
+
[.github/workflows/ci.yml](.github/workflows/ci.yml) does.
|
|
399
|
+
|
|
400
|
+
The whole PR job, on GitHub Actions:
|
|
401
|
+
|
|
402
|
+
```yaml
|
|
403
|
+
on: pull_request
|
|
404
|
+
jobs:
|
|
405
|
+
crapkit:
|
|
406
|
+
runs-on: ubuntu-latest
|
|
407
|
+
steps:
|
|
408
|
+
- uses: actions/checkout@v4
|
|
409
|
+
with:
|
|
410
|
+
fetch-depth: 0 # verify needs the baseline's commit
|
|
411
|
+
- uses: actions/setup-python@v5
|
|
412
|
+
with:
|
|
413
|
+
python-version: "3.12"
|
|
414
|
+
- run: pip install crapkit
|
|
415
|
+
- run: pip install -e ".[dev]" # your own test dependencies
|
|
416
|
+
- run: crapkit verify --baseline-tsv crapkit-baseline.tsv --github
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
The second install is the one people leave out. `verify` reruns your lanes, so the job
|
|
420
|
+
needs whatever your test command needs: the coverage plugin, `npm ci`, a database, all of
|
|
421
|
+
it. Without them the lane writes no artifact and `verify` exits 5 quoting the runner's own
|
|
422
|
+
error, which is a broken job and not a verdict.
|
|
423
|
+
|
|
298
424
|
### What a refusal looks like
|
|
299
425
|
|
|
300
426
|
```
|
|
@@ -339,30 +465,30 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
|
|
|
339
465
|
| Command | What it does |
|
|
340
466
|
|---|---|
|
|
341
467
|
| `init` | Sniffs tracked source into per-directory scopes, writes a self-validated starter `crapkit.toml` whose lanes report into `.crapkit/cov/`, and appends `.crapkit/` plus each runner's own droppings to `.gitignore`. Writes a live `[[lane]]` when it can detect the test runner, otherwise a commented template. Refuses to clobber an existing config. |
|
|
342
|
-
| `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). |
|
|
468
|
+
| `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 reads each lane command with the shell that will run it, so a quoted interpreter path is one word and a runner after `&&` is checked too, and it FAILs a lane whose runner does not resolve on PATH or that the shell cannot start, naming the word to change; each distinct runner is probed once, not once per lane. It WARNs on a lane writing its artifact at the repo root, a `coveragepy` or `istanbul` lane with no `results_artifact` (the crashed-worker and no-new-failures checks are off for it, whichever runner the lane spells), 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; PATH is the plugin root or any directory above it, `~/.claude` included (only manifests named `crapkit` count, and the newest install wins), and with no PATH it looks in Claude Code's plugin cache. A root it found rather than one you typed is named first, as `crapkit doctor: checking PATH`. See [docs/agent-json.md](docs/agent-json.md#doctor---json). |
|
|
343
469
|
| `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. |
|
|
344
470
|
| `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). |
|
|
345
|
-
| `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. |
|
|
346
|
-
| `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. |
|
|
471
|
+
| `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. It reads each istanbul artifact once for coverage, dead lines and its digest, and skips the artifact walk on an empty diff; skipping the whole run on an unchanged tree was measured and rejected, because a key made of HEAD plus the dirty names cannot see a second edit to a file that was already dirty. |
|
|
472
|
+
| `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, off the same cached pairs `coupling` reads; the normal keys stay. |
|
|
347
473
|
| `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. |
|
|
348
474
|
| `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. |
|
|
349
|
-
| `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. |
|
|
350
|
-
| `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. |
|
|
351
|
-
| `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
|
|
475
|
+
| `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 and one duplication pass over the snapshot for the whole batch (batch of 5: 11.8 s to 5.2 s, output byte-identical to five separate calls). |
|
|
476
|
+
| `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. It also takes the function's start line, the form `brief` takes, which is how you open one printed `(anonymous)`. `--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. |
|
|
477
|
+
| `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 whose CRAP sits at or under their ratchet mark, and exits 6. A marked function past its mark is gated; the pre-commit hook exempts on the mark's existence instead, because a staged blob has no coverage to score. |
|
|
352
478
|
| `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). |
|
|
353
479
|
| `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. |
|
|
354
480
|
| `overrides [--json]` | The override audit trail: who granted what, when, and why. |
|
|
355
|
-
| `trend [--json]` | Totals per trusted run: functions, over-target count, CRAP load, average, per-scope rollup. |
|
|
481
|
+
| `trend [--json]` | Totals per trusted run: functions, over-target count, CRAP load, average, per-scope rollup. It reads a per-run rollup table rather than rescanning every scored row, and fills that table for any run missing one, so it writes to the store (best effort: a read-only `.crapkit/` costs the speed, not the command). |
|
|
356
482
|
| `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. |
|
|
357
|
-
| `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. |
|
|
483
|
+
| `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. It reads the same per-run rollups `trend` does, and writes them on the same terms. |
|
|
358
484
|
| `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. |
|
|
359
|
-
| `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. |
|
|
360
|
-
| `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. |
|
|
485
|
+
| `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. The ranked pairs are cached in `.crapkit/coupling-cache-v1.json`, keyed on HEAD, the churn window, today's UTC date, the path format and a digest of the tracked set, and shared with `brief` and `worklist --batches` (warm: 1.05 s to 0.11 s on a 72k-commit repo). The date is part of that key, so the first run after midnight UTC rebuilds the pairs on an unchanged HEAD. `--top` reads the cache, because it truncates that same order; `--min-support` or `--min-confidence` off their defaults ask a wider question than the file answers, so they bypass it and recompute. |
|
|
486
|
+
| `mutate [--files F ...] [--max-mutants N] [--drop-pool] [--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. With `mutation_workers > 1` the worker worktrees are kept at `.crapkit/mutate-pool/` and re-prepared per run (30.6 s to build four on a 31,459-file repo, 0.46 s to re-prepare them); `--drop-pool` removes them and exits. |
|
|
361
487
|
| `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. |
|
|
362
488
|
| `hook-precommit` | The cc-only gate on staged blobs. No coverage, no snapshot, no repo-wide cache. Exit 6 on a violation. |
|
|
363
489
|
| `claude-hook [--protocol N]` | Reads one Claude Code PostToolUse payload from stdin and judges the file it edited: ccn against the scope ceiling, on functions the edit changed, minus functions a ratchet mark already covers. Advisory only. The edit has landed, nothing is blocked, and `hook-precommit` stays the enforcement point. Exit 2 with three lines on stderr is the only thing it ever says: no `crapkit.toml` above the edited file, an unscoped file, mid-rebase or mid-merge, a `--protocol` other than 1, source that parses to no functions, or any internal failure all exit 0 in silence. Takes no `--repo`, because the root is the first `crapkit.toml` above the edited file and the upward walk stops at a `.git` entry, so a worktree never borrows its parent's config. It opens no snapshot and writes nothing. |
|
|
364
490
|
| `watch [--interval SECONDS] [--cycles N]` | Rescores tracked files as they change (mtime polling, default 2s, subprocess-isolated so a half-saved syntax error never kills the watcher). `--cycles N` polls exactly N times and exits 0; without it the loop runs until ctrl-c. |
|
|
365
|
-
| `mcp` | A dependency-free stdio MCP server (newline JSON-RPC 2.0) exposing nine read-only tools. See [docs/agent-json.md](docs/agent-json.md#mcp-server). |
|
|
491
|
+
| `mcp` | A dependency-free stdio MCP server (newline JSON-RPC 2.0) exposing nine read-only tools. Every tool shells to the CLI's own `--json` surface, so the MCP view cannot drift from what the CLI reports. Answering from a kept in-process store was benchmarked and rejected: a packet's `source` would go stale behind the edit it describes. See [docs/agent-json.md](docs/agent-json.md#mcp-server). |
|
|
366
492
|
|
|
367
493
|
## Reading the output
|
|
368
494
|
|
|
@@ -433,9 +559,10 @@ baseline**. `crapkit runs list` marks which one that is today.
|
|
|
433
559
|
never qualifies, and neither does a `partial` run (a lane failed, so some scope fell back
|
|
434
560
|
to `no-lane`) nor a `hook` override record, which carries no scored rows at all. In `runs
|
|
435
561
|
list`, `verdict=-` marks a run that produces no verdict rather than one that failed: only
|
|
436
|
-
`verify` renders a verdict.
|
|
437
|
-
|
|
438
|
-
|
|
562
|
+
`verify` renders a verdict. Four readers ask this one question and get this one answer: the
|
|
563
|
+
baseline pick here, `ratchet seed`, `prune`, and the tighten damping that compares a mark
|
|
564
|
+
against the same commit's previous run. A mark can no longer be signed off a run `verify`
|
|
565
|
+
refused.
|
|
439
566
|
|
|
440
567
|
**What advances it.** Any qualifying run. `coverage` writes one wherever HEAD is, so a
|
|
441
568
|
dashboard cron advances the baseline exactly as CI does. A passing `verify` advances it
|
|
@@ -457,7 +584,7 @@ $ crapkit verify
|
|
|
457
584
|
warning: run 3 is not the baseline: verify run 2 FAILED with 1 finding(s) and no passing verify has cleared it since — measuring against run 1 @ 88012a148f6 instead, so those findings stay visible. Fix them, or pass `--baseline 3` to accept the newer run deliberately.
|
|
458
585
|
verify FAILED @ d89068de7f3 vs baseline 88012a148f6 (2 changed files)
|
|
459
586
|
GATE crap 72.0 ccn 8 cov 0% calc/legacy.py:7 legacy_router( a , b , c , d , e ) -> decompose
|
|
460
|
-
findings: 1 committed / 0 dirty (uncommitted
|
|
587
|
+
findings: 1 committed / 0 dirty (uncommitted edits and untracked files)
|
|
461
588
|
```
|
|
462
589
|
|
|
463
590
|
Run 3 is a `coverage` run somebody took on the tree run 2 refused, and it scores the same
|
|
@@ -470,6 +597,14 @@ bypasses the rule, and the run history records which run the verdict used. Nothi
|
|
|
470
597
|
touches a repo that has never run `verify`: with no failure to protect, `coverage` alone
|
|
471
598
|
always advances the baseline.
|
|
472
599
|
|
|
600
|
+
**When the id you pass cannot serve.** A `--baseline ID` naming a real run that is not a
|
|
601
|
+
candidate says which run it is, why, and which ones can:
|
|
602
|
+
|
|
603
|
+
```
|
|
604
|
+
$ crapkit verify --baseline 3
|
|
605
|
+
crapkit: run 3 is an inventory run (no coverage was measured) and cannot serve as a baseline; trusted runs: 1, 2; pass `--baseline 2` for the newest
|
|
606
|
+
```
|
|
607
|
+
|
|
473
608
|
## Exit codes
|
|
474
609
|
|
|
475
610
|
| Code | Meaning |
|
|
@@ -477,11 +612,11 @@ always advances the baseline.
|
|
|
477
612
|
| 0 | OK. For `verify` and `hook-precommit`: the gate passed. |
|
|
478
613
|
| 1 | **Overloaded.** Three unrelated things, listed below the table. |
|
|
479
614
|
| 2 | Usage error from argparse: unknown flag, missing positional. Raised before crapkit's own error handling. |
|
|
480
|
-
| 3 | Config error: `crapkit.toml` missing or unparseable, an unknown language or parser, a ratchet metric-stamp mismatch, a `test-scoped` file under no scope or under a scope with no template. |
|
|
615
|
+
| 3 | Config error: `crapkit.toml` missing or unparseable, an unknown language or parser, a lane command the shell that runs it reads as a narrowed suite, a ratchet metric-stamp mismatch ([Upgrading from 0.4.4](#upgrading-from-044)), a `test-scoped` file under no scope or under a scope with no template. |
|
|
481
616
|
| 4 | Git error: not a repository, a baseline commit rewritten out of the history. |
|
|
482
|
-
| 5 | Tool error: lizard not importable, a lane produced no artifact, a lane timed out past its retries, an override alert command failed. |
|
|
483
|
-
| 6 | Gate violation. A function the diff touched is over its ceiling
|
|
484
|
-
| 7 | Ratchet regression. A marked function scores worse than its recorded high-water mark
|
|
617
|
+
| 5 | Tool error: lizard not importable, a lane produced no artifact, a lane timed out past its retries, an override alert command failed. A `timeout_seconds` kills the whole process tree, so no orphan suite keeps running behind the failure. |
|
|
618
|
+
| 6 | Gate violation. A function the diff touched is over its ceiling and past any ratchet mark it carries: an edit that leaves a marked function at or under its mark is the debt the repo signed for and is exempt. Also `rescore --gate`, which applies the same rule, and `hook-precommit`, which exempts on the mark's existence instead. |
|
|
619
|
+
| 7 | Ratchet regression the diff never touched. A marked function scores worse than its recorded high-water mark; a touched one past its mark reports 6. |
|
|
485
620
|
| 8 | New test failures against the baseline run. Failures the baseline already had do not count. |
|
|
486
621
|
| 9 | Diff-coverage ceiling breached: `diff_uncovered_max` is set and more changed lines than that never ran. |
|
|
487
622
|
|
|
@@ -510,9 +645,7 @@ writes runs `pytest --cov` and those flags come from `pytest-cov`:
|
|
|
510
645
|
pip install pytest-cov
|
|
511
646
|
```
|
|
512
647
|
|
|
513
|
-
(
|
|
514
|
-
as the suite — skip it and `init` prints this same fix when its probe finds the plugin
|
|
515
|
-
missing).
|
|
648
|
+
(`pip install "crapkit[py]"` pulls both at once when crapkit shares the suite's venv.)
|
|
516
649
|
|
|
517
650
|
### 1. Scaffold the config
|
|
518
651
|
|
|
@@ -545,16 +678,18 @@ globs = ["**/node_modules/**", "**/dist/**", "**/build/**", "**/vendor/**", "**/
|
|
|
545
678
|
|
|
546
679
|
[[lane]]
|
|
547
680
|
name = "py"
|
|
548
|
-
command = "python -m pytest --cov --cov-branch --cov-report=json:.crapkit/cov/py.json"
|
|
681
|
+
command = "python -m pytest --cov --cov-branch --cov-report=json:.crapkit/cov/py.json --junitxml=.crapkit/cov/junit-py.xml"
|
|
549
682
|
artifact = ".crapkit/cov/py.json"
|
|
683
|
+
results_artifact = ".crapkit/cov/junit-py.xml"
|
|
550
684
|
parser = "coveragepy"
|
|
551
685
|
scopes = ["calc"]
|
|
552
686
|
|
|
553
687
|
# Declare one [[lane]] per coverage command, then run `crapkit coverage`.
|
|
554
688
|
# [[lane]]
|
|
555
689
|
# name = "js"
|
|
556
|
-
# command = "npx vitest run --coverage --coverage.reportsDirectory=.crapkit/cov/js"
|
|
690
|
+
# command = "npx vitest run --coverage --coverage.reportsDirectory=.crapkit/cov/js --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml"
|
|
557
691
|
# artifact = ".crapkit/cov/js/coverage-final.json"
|
|
692
|
+
# results_artifact = ".crapkit/cov/js/junit.xml"
|
|
558
693
|
# parser = "istanbul"
|
|
559
694
|
# scopes = ["<your-scope>"]
|
|
560
695
|
|
|
@@ -599,11 +734,9 @@ Columns: `risk`, `ccn` with the standard-only ccn in parentheses,
|
|
|
599
734
|
`<commits>c/<authors>a` in the churn window with `w<weight>`, `path:line`, the function's
|
|
600
735
|
long name, then a marker on rows the burn-down queue will not hand out (`ok`, `no-lane`).
|
|
601
736
|
|
|
602
|
-
**`worklist` is the risk map, not a to-do list.** It ranks
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
by `crap` descending instead of by risk. Its `empty: true` is the stop condition; the
|
|
606
|
-
worklist has none.
|
|
737
|
+
**`worklist` is the risk map, not a to-do list.** It ranks finished rows too, so it does
|
|
738
|
+
not empty when the burn-down does. `next-item` is the other view of that run: it drops the
|
|
739
|
+
`no-lane` rows, ranks by `crap`, and its `empty: true` is the stop condition.
|
|
607
740
|
|
|
608
741
|
### 4. Take the top item
|
|
609
742
|
|
|
@@ -669,11 +802,16 @@ added to .gitignore: .crapkit/
|
|
|
669
802
|
```
|
|
670
803
|
|
|
671
804
|
The lane `init` wrote is
|
|
672
|
-
`npm run test -- --coverage --coverage.reportsDirectory=.crapkit/cov/js
|
|
673
|
-
vitest's `json` reporter from `.crapkit/cov/js/coverage-final.json`; the
|
|
674
|
-
`reportsDirectory` flag is what keeps that report out of your root.
|
|
675
|
-
|
|
676
|
-
|
|
805
|
+
`npm run test -- --coverage --coverage.reportsDirectory=.crapkit/cov/js --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml`.
|
|
806
|
+
It reads vitest's `json` reporter from `.crapkit/cov/js/coverage-final.json`; the
|
|
807
|
+
`reportsDirectory` flag is what keeps that report out of your root. The junit half is the
|
|
808
|
+
lane's `results_artifact`, which the crashed-worker and no-new-failures checks read; both
|
|
809
|
+
reporters are named because `--reporter=junit` alone would replace the console output you
|
|
810
|
+
watch the suite through. Anything that produces
|
|
811
|
+
an istanbul `coverage-final.json` works; see [docs/lanes.md](docs/lanes.md) for the
|
|
812
|
+
[jest](docs/lanes.md#jest) and [pytest](docs/lanes.md#pytest) recipes, a package
|
|
813
|
+
[one directory down](docs/lanes.md#running-from-a-subdirectory), and a
|
|
814
|
+
[crapkit root below the repo top](docs/lanes.md#a-crapkit-root-below-the-repo-top).
|
|
677
815
|
|
|
678
816
|
### 2. Install a coverage provider
|
|
679
817
|
|
|
@@ -683,7 +821,7 @@ exit 5:
|
|
|
683
821
|
|
|
684
822
|
```
|
|
685
823
|
$ crapkit coverage
|
|
686
|
-
crapkit: lane 'js' FAILED: lane 'js' produced no artifact at .crapkit/cov/js/coverage-final.json (command exit 1); last output: $ npm run test -- --coverage --coverage.reportsDirectory=.crapkit/cov/js
|
|
824
|
+
crapkit: lane 'js' FAILED: lane 'js' produced no artifact at .crapkit/cov/js/coverage-final.json (command exit 1); last output: $ npm run test -- --coverage --coverage.reportsDirectory=.crapkit/cov/js --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml
|
|
687
825
|
|
|
688
826
|
MISSING DEPENDENCY Cannot find dependency '@vitest/coverage-v8'
|
|
689
827
|
|