crapkit 0.4.10__tar.gz → 0.4.12__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.10 → crapkit-0.4.12}/PKG-INFO +86 -59
- crapkit-0.4.10/src/crapkit.egg-info/PKG-INFO → crapkit-0.4.12/README.md +1174 -1186
- {crapkit-0.4.10 → crapkit-0.4.12}/pyproject.toml +1 -1
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/__init__.py +1 -1
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/cli/__init__.py +26 -0
- crapkit-0.4.12/src/crapkit/cli/_shared.py +228 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/cli/admin.py +273 -34
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/cli/analyses.py +7 -3
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/cli/parser.py +78 -4
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/cli/queue.py +11 -9
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/cli/ratchet_cmds.py +8 -6
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/cli/reports.py +12 -10
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/cli/scoring.py +14 -6
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/cli/verifying.py +55 -16
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/config.py +56 -3
- crapkit-0.4.12/src/crapkit/coverage_py.py +164 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/covstream.py +34 -30
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/doctor.py +16 -8
- crapkit-0.4.12/src/crapkit/invocation.py +45 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/lanes.py +265 -26
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/mcp_server.py +3 -1
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/mutate_pool.py +57 -3
- crapkit-0.4.12/src/crapkit/procs.py +137 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/ratchet.py +32 -5
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/report.py +7 -2
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/scaffold.py +288 -24
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/uncovered.py +3 -1
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/universe.py +10 -1
- crapkit-0.4.10/README.md → crapkit-0.4.12/src/crapkit.egg-info/PKG-INFO +1213 -1147
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit.egg-info/SOURCES.txt +1 -0
- crapkit-0.4.10/src/crapkit/cli/_shared.py +0 -130
- crapkit-0.4.10/src/crapkit/coverage_py.py +0 -87
- crapkit-0.4.10/src/crapkit/procs.py +0 -66
- {crapkit-0.4.10 → crapkit-0.4.12}/LICENSE +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/setup.cfg +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/__main__.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/_pygdefer.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/analyze.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/cache.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/churn.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/churn_cache.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/churn_log.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/cli/claude_hook.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/coupling.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/coupling_cache.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/coverage_istanbul.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/diffparse.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/digest.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/discover.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/dup.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/errors.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/gitio.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/hook.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/junitparse.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/keys.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/lizardcognitive.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/lizardpowershell.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/lizardrust.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/lizardshell.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/merge.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/mutate.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/override.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/packet.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/ratchet_report.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/sarif.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/sarifio.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/score.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/snapshot.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/store.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/verify.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/watch.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit/worklist.py +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit.egg-info/dependency_links.txt +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit.egg-info/entry_points.txt +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/src/crapkit.egg-info/requires.txt +0 -0
- {crapkit-0.4.10 → crapkit-0.4.12}/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.12
|
|
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
|
|
@@ -42,7 +42,7 @@ Dynamic: license-file
|
|
|
42
42
|
[](https://github.com/JeanFrancoisGagne/crapkit/actions/workflows/ci.yml)
|
|
43
43
|
[](https://pypi.org/project/crapkit/)
|
|
44
44
|
[](https://pypi.org/project/crapkit/)
|
|
45
|
-
[](LICENSE)
|
|
45
|
+
[](https://github.com/JeanFrancoisGagne/crapkit/blob/main/LICENSE)
|
|
46
46
|
|
|
47
47
|

|
|
48
48
|
|
|
@@ -58,6 +58,9 @@ because half the callers are coding agents.
|
|
|
58
58
|
CRAP = ccn^2 * (1 - cov)^3 + ccn
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
+
The name is not ours: C.R.A.P. (Change Risk Anti-Patterns) was coined for crap4j by
|
|
62
|
+
Alberto Savoia and Bob Evans in 2007.
|
|
63
|
+
|
|
61
64
|
`ccn` is the smaller of standard and modified cyclomatic complexity, both read off one
|
|
62
65
|
lizard pass. `cov` is branch coverage inside the function's span; with no branches it
|
|
63
66
|
falls back to statement coverage, and with no statements to invoked-or-not, so a
|
|
@@ -77,7 +80,12 @@ it.
|
|
|
77
80
|
```
|
|
78
81
|
pip install crapkit
|
|
79
82
|
cd your-repo
|
|
80
|
-
crapkit init #
|
|
83
|
+
crapkit init # crapkit.toml and .gitignore lines, plus a live coverage lane when it
|
|
84
|
+
# recognizes the runner and a scope speaks its language: pyproject.toml,
|
|
85
|
+
# pytest.ini or setup.cfg for pytest; a test script or vitest/jest in
|
|
86
|
+
# package.json for the JS side
|
|
87
|
+
# without one: the lane comes commented out, init says to declare one,
|
|
88
|
+
# and docs/lanes.md is how to fill it in
|
|
81
89
|
crapkit coverage # runs the lane, joins coverage, stores a scored run
|
|
82
90
|
crapkit worklist # the ranked risk map
|
|
83
91
|
crapkit ratchet seed && git add crapkit.toml crapkit-ratchet.tsv .gitignore
|
|
@@ -94,6 +102,9 @@ worklist @ fae4db93108 (run 1, floor ccn>=5, churn 12mo) — 1 active, 0 dormant
|
|
|
94
102
|
risk 0.0 ccn 14 ( 14 std) 1c/1a w 0.00 calc/grade.py:7 classify( score , attempts , late , bonus )
|
|
95
103
|
```
|
|
96
104
|
|
|
105
|
+
`risk 0.0` is what a one-commit repo scores, because churn needs a spread of commits to
|
|
106
|
+
rank and ccn order stands in until then ([Risk](#risk-what-ranks-the-worklist)).
|
|
107
|
+
|
|
97
108
|
`ratchet seed` signs today's debt at today's score. From then on marks only ever fall, so
|
|
98
109
|
the repo can get better and never worse while you burn it down.
|
|
99
110
|
|
|
@@ -106,7 +117,9 @@ command when `pytest_cov` is missing; `pip install "crapkit[py]"` pulls the plug
|
|
|
106
117
|
alongside crapkit when the two share a venv. On a Windows PATH holding only the `py`
|
|
107
118
|
launcher it writes `py`, not a `python3` the lane could never run, and when cmd.exe cannot
|
|
108
119
|
start the interpreter at all (exit 9009, the Store alias) it names that instead of guessing
|
|
109
|
-
at pytest-cov.
|
|
120
|
+
at pytest-cov. A repo that pins no lockfile and carries its own `.venv` gets that venv's
|
|
121
|
+
interpreter in the lane, when that interpreter can import pytest, rather than whichever
|
|
122
|
+
python the shell answers with. The two quickstarts below walk a real repo end to end.
|
|
110
123
|
|
|
111
124
|
**On Windows a lane command is read by cmd.exe**, the shell that will run it, not by sh.
|
|
112
125
|
Double quotes are the portable quoting. A single-quoted value is refused at config load
|
|
@@ -118,7 +131,7 @@ artifact:
|
|
|
118
131
|
command = "python -m pytest -m 'not live and not perf' --cov=calc --cov-branch --cov-report=json:.crapkit/cov/py.json"
|
|
119
132
|
|
|
120
133
|
$ crapkit doctor
|
|
121
|
-
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)
|
|
134
|
+
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); a suite whose testpaths cannot be collected in one process needs one lane per testpath, each with full_suite = false and its own artifact
|
|
122
135
|
```
|
|
123
136
|
|
|
124
137
|
Write it `-m "not live and not perf"`. Carets, `&&` and `|` segments, redirections and
|
|
@@ -145,9 +158,14 @@ mirror installs fine. Requires Python 3.11 or newer. The `pip install -e ".[dev]
|
|
|
145
158
|
[Development](#development) is a different thing: it adds the test extra, for people
|
|
146
159
|
changing crapkit.
|
|
147
160
|
|
|
161
|
+
Scoring runs your own test command on your own machine and reads the artifact it writes.
|
|
162
|
+
There is no network call anywhere in crapkit, so no source, no score and no telemetry
|
|
163
|
+
leaves the box
|
|
164
|
+
([SECURITY.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/SECURITY.md)).
|
|
165
|
+
|
|
148
166
|
```
|
|
149
167
|
$ crapkit --version
|
|
150
|
-
crapkit 0.4.
|
|
168
|
+
crapkit 0.4.12
|
|
151
169
|
```
|
|
152
170
|
|
|
153
171
|
`python -m crapkit` works identically to the console script and is what to use from a
|
|
@@ -222,9 +240,11 @@ claude plugin install crapkit@crapkit
|
|
|
222
240
|
```
|
|
223
241
|
|
|
224
242
|
Two commands, installed once per user, and every repo on the machine gets it. The plugin
|
|
225
|
-
ships three skills
|
|
226
|
-
|
|
227
|
-
|
|
243
|
+
ships three skills, the read-only MCP server, and one advisory PostToolUse hook that names
|
|
244
|
+
any function an edit pushed over its ceiling. Claude reaches two of the skills by itself,
|
|
245
|
+
`crapkit` and `crapkit-recover`; the third you type, as `/crapkit:crapkit-onboard`, because
|
|
246
|
+
wiring a repo up happens once and its description has no business in every turn's window.
|
|
247
|
+
It adds no files to your repo, and it needs the crapkit CLI on PATH.
|
|
228
248
|
|
|
229
249
|
A repo with no `crapkit.toml` costs a silent sub-50 ms no-op per edit. Other agent
|
|
230
250
|
runtimes have no marketplace: copy `plugin/skills/*` into their skills directory instead.
|
|
@@ -375,7 +395,7 @@ crapkit ships a `.pre-commit-hooks.yaml` declaring `id: crapkit-gate`. In your
|
|
|
375
395
|
repos:
|
|
376
396
|
- repo: https://github.com/JeanFrancoisGagne/crapkit
|
|
377
397
|
# crapkit's release step rewrites this line to the tag it just cut
|
|
378
|
-
rev: v0.4.
|
|
398
|
+
rev: v0.4.12
|
|
379
399
|
hooks:
|
|
380
400
|
- id: crapkit-gate
|
|
381
401
|
```
|
|
@@ -426,7 +446,7 @@ crapkit: baseline commit a74260f321f is not an ancestor of HEAD (rebase or amend
|
|
|
426
446
|
|
|
427
447
|
That is exit 4 on a `git clone --depth 1` of a repo whose baseline verifies at full depth.
|
|
428
448
|
Set `fetch-depth: 0` on the checkout step, which is what crapkit's own
|
|
429
|
-
[.github/workflows/ci.yml](.github/workflows/ci.yml) does.
|
|
449
|
+
[.github/workflows/ci.yml](https://github.com/JeanFrancoisGagne/crapkit/blob/main/.github/workflows/ci.yml) does.
|
|
430
450
|
|
|
431
451
|
The whole PR job, on GitHub Actions:
|
|
432
452
|
|
|
@@ -469,11 +489,11 @@ violation and 0 otherwise. The stderr block above is the same either way.
|
|
|
469
489
|
three-record audit: an alert line through `alert_command`, a ratchet entry staged into the
|
|
470
490
|
commit, and a row in the override log. All three land or nothing does, and an unset
|
|
471
491
|
`alert_command` refuses the override outright. See
|
|
472
|
-
[docs/ratchet.md](docs/ratchet.md#overrides-and-the-audit-trail).
|
|
492
|
+
[docs/ratchet.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/ratchet.md#overrides-and-the-audit-trail).
|
|
473
493
|
|
|
474
494
|
## The GitHub Action
|
|
475
495
|
|
|
476
|
-
[action.yml](action.yml) at this repository's root is a composite action, so a reviewer
|
|
496
|
+
[action.yml](https://github.com/JeanFrancoisGagne/crapkit/blob/main/action.yml) at this repository's root is a composite action, so a reviewer
|
|
477
497
|
sees crapkit's numbers on the pull request without installing anything. Four lines add it
|
|
478
498
|
to a workflow, and every input has a default:
|
|
479
499
|
|
|
@@ -481,7 +501,7 @@ to a workflow, and every input has a default:
|
|
|
481
501
|
- uses: actions/checkout@v4
|
|
482
502
|
with:
|
|
483
503
|
fetch-depth: 0
|
|
484
|
-
- uses: JeanFrancoisGagne/crapkit@v0.4.
|
|
504
|
+
- uses: JeanFrancoisGagne/crapkit@v0.4.12
|
|
485
505
|
```
|
|
486
506
|
|
|
487
507
|
The whole job those four lines sit in:
|
|
@@ -497,8 +517,11 @@ jobs:
|
|
|
497
517
|
- uses: actions/checkout@v4
|
|
498
518
|
with:
|
|
499
519
|
fetch-depth: 0 # the diff, and verify's baseline commit
|
|
520
|
+
- uses: actions/setup-python@v5
|
|
521
|
+
with:
|
|
522
|
+
python-version: "3.12" # the interpreter the install below lands in
|
|
500
523
|
- run: pip install -e ".[dev]" # whatever your lanes need to run
|
|
501
|
-
- uses: JeanFrancoisGagne/crapkit@v0.4.
|
|
524
|
+
- uses: JeanFrancoisGagne/crapkit@v0.4.12
|
|
502
525
|
with:
|
|
503
526
|
gate: "false"
|
|
504
527
|
```
|
|
@@ -513,8 +536,9 @@ baseline's commit. With a shallow clone the file list comes back empty and the c
|
|
|
513
536
|
ranks the whole repository instead of the diff.
|
|
514
537
|
|
|
515
538
|
The action installs crapkit from `$GITHUB_ACTION_PATH`, which is its own checkout of the
|
|
516
|
-
ref you pinned in `uses:`. So
|
|
517
|
-
with whatever released
|
|
539
|
+
ref you pinned in `uses:`. So a pin left at last month's tag scores your tree with last
|
|
540
|
+
month's crapkit rather than with whatever released since, and pinning a tag is the whole
|
|
541
|
+
version policy; the snippets above name the current release.
|
|
518
542
|
|
|
519
543
|
### What the comment looks like
|
|
520
544
|
|
|
@@ -564,7 +588,7 @@ behind the checkout to measure from.
|
|
|
564
588
|
| `gate` | `"false"` | `"true"` exits with `crapkit verify`'s own code, so a finding fails the check. Anything else exits 0 and the comment is the whole output |
|
|
565
589
|
| `delta` | `"true"` | scores the pull request's base commit first, so the verdict covers the commits the pull request adds. Costs a second lane run; `"false"` scores the checkout alone |
|
|
566
590
|
| `top` | `"5"` | worklist rows rendered in the table |
|
|
567
|
-
| `python-version` | `"3.12"` | the interpreter `actions/setup-python` installs crapkit into |
|
|
591
|
+
| `python-version` | `"3.12"` | the interpreter `actions/setup-python` installs crapkit into. Match it to the version your own setup-python step named, or the lanes run on an interpreter your dependencies never reached |
|
|
568
592
|
|
|
569
593
|
`gate: "false"` is the default on purpose. A team adopts the action before it has decided
|
|
570
594
|
which findings should stop a merge, and a check that fails on day one gets turned off on
|
|
@@ -641,22 +665,22 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
|
|
|
641
665
|
| Command | What it does |
|
|
642
666
|
|---|---|
|
|
643
667
|
| `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. |
|
|
644
|
-
| `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
|
|
668
|
+
| `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 or that the shell cannot start, naming the word to change; a bare name is looked for on PATH and a runner spelled as a path is looked for under the directory the lane runs in, so `.venv/bin/python` answers the same from any directory you run `doctor` in; 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](https://github.com/JeanFrancoisGagne/crapkit/tree/main/plugin) against the `crapkit` on PATH (the bare name its hooks and MCP server spawn) on both version and hook `--protocol`, and FAILs when PATH carries no `crapkit` at all, 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](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/agent-json.md#doctor---json). |
|
|
645
669
|
| `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. |
|
|
646
|
-
| `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). |
|
|
670
|
+
| `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](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md). |
|
|
647
671
|
| `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. |
|
|
648
672
|
| `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. |
|
|
649
673
|
| `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. |
|
|
650
674
|
| `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. |
|
|
651
675
|
| `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). |
|
|
652
|
-
| `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. |
|
|
676
|
+
| `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](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md#test-attribution-for-explain---tests)). `--json` emits the same content as one `schema` 1 object. |
|
|
653
677
|
| `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. |
|
|
654
|
-
| `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). |
|
|
678
|
+
| `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](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/ratchet.md). |
|
|
655
679
|
| `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. |
|
|
656
680
|
| `overrides [--json]` | The override audit trail: who granted what, when, and why. |
|
|
657
681
|
| `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). |
|
|
658
682
|
| `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. |
|
|
659
|
-
| `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. |
|
|
683
|
+
| `report [--out PATH]` | One self-contained HTML page written to `.crapkit/report.html` (or `--out PATH`, repo-relative, or an absolute path you name), 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. |
|
|
660
684
|
| `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. |
|
|
661
685
|
| `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. |
|
|
662
686
|
| `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. |
|
|
@@ -664,7 +688,8 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
|
|
|
664
688
|
| `hook-precommit` | The cc-only gate on staged blobs. No coverage, no snapshot, no repo-wide cache. Exit 6 on a violation. |
|
|
665
689
|
| `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, and `hook-precommit` stays the enforcement point. Exit 2 and an advisory on stderr is the only thing it ever says, one block per judged file (a head line, one line per breaching function, a closing line): 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. The root is the first `crapkit.toml` above the edited file; the walk stops at a `.git` entry, so a worktree never borrows its parent's config. A `Bash` event names no file, so it judges the working tree instead: the dirty or untracked `*.py` files touched in the last 12 seconds, 25 at most, each through the same ladder, and silence for a clean tree or a cwd outside any repo. That half fires only where you register a `Bash` matcher ([The Claude Code plugin](#the-claude-code-plugin)). It opens no snapshot and writes nothing. |
|
|
666
690
|
| `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. |
|
|
667
|
-
| `
|
|
691
|
+
| `help [TOPIC]` | The help git, npm and docker answer to. With no TOPIC it prints the command list; with one it prints that subcommand's own help, the same page as `crapkit TOPIC --help`. A TOPIC that names no subcommand exits 3. |
|
|
692
|
+
| `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](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/agent-json.md#mcp-server). |
|
|
668
693
|
|
|
669
694
|
## Reading the output
|
|
670
695
|
|
|
@@ -826,7 +851,7 @@ pip install pytest-cov
|
|
|
826
851
|
If your suite drives its own CLI through `subprocess.run`, add `[tool.coverage.run]
|
|
827
852
|
patch = ["subprocess"]` to `pyproject.toml` and keep `coverage>=7.10.6`: pytest-cov 7.0.0
|
|
828
853
|
dropped subprocess measurement, so without that key every entry point scores 0% and nothing
|
|
829
|
-
warns. [docs/lanes.md](docs/lanes.md) has the whole rule.
|
|
854
|
+
warns. [docs/lanes.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md) has the whole rule.
|
|
830
855
|
|
|
831
856
|
### 1. Scaffold the config
|
|
832
857
|
|
|
@@ -844,11 +869,11 @@ coverage lane from what the repo already has: a pytest marker file (`pyproject.t
|
|
|
844
869
|
`poetry.lock`, `pdm.lock` or `Pipfile.lock` makes the lane `uv run python -m pytest …` (and
|
|
845
870
|
the matching `run` for the rest), because a bare `python` binds to whichever venv the shell
|
|
846
871
|
has active rather than the one the repo pins — see
|
|
847
|
-
[The interpreter a lane binds to](docs/lanes.md#the-interpreter-a-lane-binds-to). Whatever
|
|
872
|
+
[The interpreter a lane binds to](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md#the-interpreter-a-lane-binds-to). Whatever
|
|
848
873
|
it detects, it also leaves commented templates for the runners it did not find, and those
|
|
849
874
|
carry the same launcher, so uncommenting one cannot hand the bare `python` back. Every lane
|
|
850
875
|
it writes reports into `.crapkit/cov/`, which is why the `.gitignore` list is so short: see
|
|
851
|
-
[Where artifacts live](docs/lanes.md#where-artifacts-live).
|
|
876
|
+
[Where artifacts live](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md#where-artifacts-live).
|
|
852
877
|
|
|
853
878
|
```toml
|
|
854
879
|
[crapkit]
|
|
@@ -860,11 +885,11 @@ paths = ["calc"]
|
|
|
860
885
|
languages = ["python"]
|
|
861
886
|
|
|
862
887
|
[exclude]
|
|
863
|
-
globs = ["**/node_modules/**", "**/dist/**", "**/build/**", "**/vendor/**", "**/*.test.*", "**/*.spec.*", "**/test_*.py", "**/*_test.py", "**/conftest.py", "**/*_test.go", "*.config.ts", "*.config.js", "*.config.mts", "**/*.config.ts", "**/*.config.js", "**/*.config.mts"]
|
|
888
|
+
globs = ["**/node_modules/**", "**/dist/**", "**/build/**", "**/vendor/**", "**/*.test.*", "**/*.spec.*", "**/test_*.py", "**/*_test.py", "**/conftest.py", "node_modules/**", "dist/**", "build/**", "vendor/**", "*.test.*", "*.spec.*", "test_*.py", "*_test.py", "conftest.py", "*_test.go", "**/*_test.go", "*.config.ts", "*.config.js", "*.config.mts", "**/*.config.ts", "**/*.config.js", "**/*.config.mts"]
|
|
864
889
|
|
|
865
890
|
[[lane]]
|
|
866
891
|
name = "py"
|
|
867
|
-
command = "python -m pytest --cov --cov-branch --cov-report=json:.crapkit/cov/py.json --junitxml=.crapkit/cov/junit-py.xml"
|
|
892
|
+
command = "python -m pytest --cov --cov-branch --cov-report=json:.crapkit/cov/py.json --junitxml=.crapkit/cov/junit-py.xml --continue-on-collection-errors"
|
|
868
893
|
artifact = ".crapkit/cov/py.json"
|
|
869
894
|
results_artifact = ".crapkit/cov/junit-py.xml"
|
|
870
895
|
parser = "coveragepy"
|
|
@@ -873,7 +898,7 @@ scopes = ["calc"]
|
|
|
873
898
|
# Declare one [[lane]] per coverage command, then run `crapkit coverage`.
|
|
874
899
|
# [[lane]]
|
|
875
900
|
# name = "js"
|
|
876
|
-
# command = "npx vitest run --coverage --coverage.reportsDirectory=.crapkit/cov/js --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml"
|
|
901
|
+
# command = "npx vitest run --coverage --coverage.reportsDirectory=.crapkit/cov/js --coverage.reportOnFailure --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml"
|
|
877
902
|
# artifact = ".crapkit/cov/js/coverage-final.json"
|
|
878
903
|
# results_artifact = ".crapkit/cov/js/junit.xml"
|
|
879
904
|
# parser = "istanbul"
|
|
@@ -886,16 +911,16 @@ calc = "python -m pytest {files} -q -p no:cacheprovider"
|
|
|
886
911
|
```
|
|
887
912
|
|
|
888
913
|
The last block is the one an agent loop needs. `crapkit test-scoped` exits 3 for a file
|
|
889
|
-
whose scope declares no template, and [AGENTS.md](AGENTS.md#4-run-the-owning-scopes-tests)
|
|
914
|
+
whose scope declares no template, and [AGENTS.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/AGENTS.md#4-run-the-owning-scopes-tests)
|
|
890
915
|
makes it step 4 of the burn-down loop. Every key is in
|
|
891
|
-
[docs/configuration.md](docs/configuration.md).
|
|
916
|
+
[docs/configuration.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/configuration.md).
|
|
892
917
|
|
|
893
918
|
### 2. Check the config against the repo
|
|
894
919
|
|
|
895
920
|
```
|
|
896
921
|
$ crapkit doctor
|
|
897
922
|
ok config keys all recognized
|
|
898
|
-
ok scope 'calc': 1
|
|
923
|
+
ok scope 'calc': 1 file
|
|
899
924
|
ok every tracked source file belongs to a scope
|
|
900
925
|
ok 1 lane(s) declared
|
|
901
926
|
ok lizard 1.24.0
|
|
@@ -934,7 +959,7 @@ $ crapkit next-item
|
|
|
934
959
|
`remedy: "decompose"`, `est_splits: 3` (this needs roughly three pieces to fit under 6),
|
|
935
960
|
and `uncovered_lines` naming the ten lines no test walks. `handle` is the name form to
|
|
936
961
|
pass back, and `stale: false` says the run still describes HEAD. Every field is in
|
|
937
|
-
[docs/agent-json.md](docs/agent-json.md).
|
|
962
|
+
[docs/agent-json.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/agent-json.md).
|
|
938
963
|
|
|
939
964
|
### 5. Seed the ratchet
|
|
940
965
|
|
|
@@ -967,11 +992,11 @@ against the trusted baseline: every function the diff touched sits at or under i
|
|
|
967
992
|
ceiling, no marked function got worse, and no test that passed in the baseline fails now.
|
|
968
993
|
Exit 0 advances the baseline and tightens `crapkit-ratchet.tsv` in place, so the repaid
|
|
969
994
|
mark leaves the file: follow up with `git commit -am "ratchet: classify repaid"`. The full
|
|
970
|
-
mark lifecycle is in [docs/ratchet.md](docs/ratchet.md).
|
|
995
|
+
mark lifecycle is in [docs/ratchet.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/ratchet.md).
|
|
971
996
|
|
|
972
997
|
`crapkit next-item` now comes back `empty: true` with a `reasons` object saying which
|
|
973
998
|
ending you got. That is most of the stop condition, not all of it:
|
|
974
|
-
[AGENTS.md](AGENTS.md#the-termination-rule) states the whole rule and reads the rest of
|
|
999
|
+
[AGENTS.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/AGENTS.md#the-termination-rule) states the whole rule and reads the rest of
|
|
975
1000
|
`reasons`.
|
|
976
1001
|
|
|
977
1002
|
## Quickstart: TypeScript
|
|
@@ -988,16 +1013,16 @@ added to .gitignore: .crapkit/
|
|
|
988
1013
|
```
|
|
989
1014
|
|
|
990
1015
|
The lane `init` wrote is
|
|
991
|
-
`npm run test -- --coverage --coverage.reportsDirectory=.crapkit/cov/js --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml`.
|
|
1016
|
+
`npm run test -- --coverage --coverage.reportsDirectory=.crapkit/cov/js --coverage.reportOnFailure --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml`.
|
|
992
1017
|
It reads vitest's `json` reporter from `.crapkit/cov/js/coverage-final.json`; the
|
|
993
1018
|
`reportsDirectory` flag is what keeps that report out of your root. The junit half is the
|
|
994
1019
|
lane's `results_artifact`, which the crashed-worker and no-new-failures checks read; both
|
|
995
1020
|
reporters are named because `--reporter=junit` alone would replace the console output you
|
|
996
1021
|
watch the suite through. Anything that produces
|
|
997
|
-
an istanbul `coverage-final.json` works; see [docs/lanes.md](docs/lanes.md) for the
|
|
998
|
-
[jest](docs/lanes.md#jest) and [pytest](docs/lanes.md#pytest) recipes, a package
|
|
999
|
-
[one directory down](docs/lanes.md#running-from-a-subdirectory), and a
|
|
1000
|
-
[crapkit root below the repo top](docs/lanes.md#a-crapkit-root-below-the-repo-top).
|
|
1022
|
+
an istanbul `coverage-final.json` works; see [docs/lanes.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md) for the
|
|
1023
|
+
[jest](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md#jest) and [pytest](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md#pytest) recipes, a package
|
|
1024
|
+
[one directory down](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md#running-from-a-subdirectory), and a
|
|
1025
|
+
[crapkit root below the repo top](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md#a-crapkit-root-below-the-repo-top).
|
|
1001
1026
|
|
|
1002
1027
|
### 2. Install a coverage provider
|
|
1003
1028
|
|
|
@@ -1007,12 +1032,12 @@ exit 5:
|
|
|
1007
1032
|
|
|
1008
1033
|
```
|
|
1009
1034
|
$ crapkit coverage
|
|
1010
|
-
crapkit: lane 'js' FAILED: lane 'js' produced no artifact at .crapkit/cov/js/coverage-final.json (command exit 1); full log: /repo/.crapkit/lane-js.log; last output: $ npm run test -- --coverage --coverage.reportsDirectory=.crapkit/cov/js --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml
|
|
1035
|
+
crapkit: lane 'js' FAILED: lane 'js' produced no artifact at .crapkit/cov/js/coverage-final.json (command exit 1); full log: /repo/.crapkit/lane-js.log; last output: $ npm run test -- --coverage --coverage.reportsDirectory=.crapkit/cov/js --coverage.reportOnFailure --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml
|
|
1011
1036
|
|
|
1012
1037
|
MISSING DEPENDENCY Cannot find dependency '@vitest/coverage-v8'
|
|
1013
1038
|
|
|
1014
1039
|
(exit 1)
|
|
1015
|
-
crapkit: every lane failed
|
|
1040
|
+
crapkit: every lane failed (1 of 1); the errors are above
|
|
1016
1041
|
```
|
|
1017
1042
|
|
|
1018
1043
|
That failure **writes no run**. Every lane failed, so `coverage` exits before it opens a
|
|
@@ -1035,10 +1060,12 @@ The artifact crapkit wants is `coverage-final.json`, written by vitest's `json`
|
|
|
1035
1060
|
reporter, which is on by default. If your vitest config sets `coverage.reporter`
|
|
1036
1061
|
explicitly, keep `"json"` in the list.
|
|
1037
1062
|
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1063
|
+
vitest writes **no coverage report at all when the run fails**. The lane `init` wrote
|
|
1064
|
+
already carries `--coverage.reportOnFailure`, so a red test still produces the artifact.
|
|
1065
|
+
If you write the lane by hand, or you would rather keep the switch beside your other
|
|
1066
|
+
coverage settings, `coverage.reportOnFailure = true` in the vitest config does the same
|
|
1067
|
+
job; either one is enough. The full block is in
|
|
1068
|
+
[docs/lanes.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md#reportonfailure).
|
|
1042
1069
|
|
|
1043
1070
|
### 3. Score the repo
|
|
1044
1071
|
|
|
@@ -1150,22 +1177,22 @@ dropped it once `classify` scored under the ceiling, rewriting the tracked
|
|
|
1150
1177
|
|
|
1151
1178
|
A verify may also print `warning: N changed line(s) have no coverage` above its verdict;
|
|
1152
1179
|
that block is advisory unless `diff_uncovered_max` is set
|
|
1153
|
-
([docs/configuration.md](docs/configuration.md)).
|
|
1180
|
+
([docs/configuration.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/configuration.md)).
|
|
1154
1181
|
|
|
1155
1182
|
## Documentation
|
|
1156
1183
|
|
|
1157
1184
|
| Page | Covers |
|
|
1158
1185
|
|---|---|
|
|
1159
|
-
| [The handbook](https://jeanfrancoisgagne.github.io/crapkit/handbook.html) | **Start here for anything deeper.** The illustrated handbook: what crapkit is, how every piece works, and where each command earns its keep. Also at [docs/handbook.html](
|
|
1160
|
-
| [docs/adoption.md](docs/adoption.md) | The judgment layer over the quickstarts: scope granularity, exclude vs lane, scoped_tests wiring, the first-verify taint hazard. |
|
|
1161
|
-
| [docs/configuration.md](docs/configuration.md) | Every `crapkit.toml` key: type, default, and what it does. |
|
|
1162
|
-
| [docs/lanes.md](docs/lanes.md) | The lane model, vitest and jest and pytest recipes, artifact reuse, flake retest, containers. |
|
|
1163
|
-
| [docs/ratchet.md](docs/ratchet.md) | Seeding, pruning, the git merge driver, metric stamps, debt policy, overrides. |
|
|
1164
|
-
| [docs/agent-json.md](docs/agent-json.md) | The machine surface: `schema`, every payload field, real captured examples. |
|
|
1165
|
-
| [AGENTS.md](AGENTS.md) | The burn-down loop an agent runs, and the rules for changing crapkit itself. |
|
|
1166
|
-
| [plugin/](plugin
|
|
1186
|
+
| [The handbook](https://jeanfrancoisgagne.github.io/crapkit/handbook.html) | **Start here for anything deeper.** The illustrated handbook: what crapkit is, how every piece works, and where each command earns its keep. Also at [docs/handbook.html](https://jeanfrancoisgagne.github.io/crapkit/handbook.html), self-contained, so it opens straight from a clone. |
|
|
1187
|
+
| [docs/adoption.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/adoption.md) | The judgment layer over the quickstarts: scope granularity, exclude vs lane, scoped_tests wiring, the first-verify taint hazard. |
|
|
1188
|
+
| [docs/configuration.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/configuration.md) | Every `crapkit.toml` key: type, default, and what it does. |
|
|
1189
|
+
| [docs/lanes.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md) | The lane model, vitest and jest and pytest recipes, artifact reuse, flake retest, containers. |
|
|
1190
|
+
| [docs/ratchet.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/ratchet.md) | Seeding, pruning, the git merge driver, metric stamps, debt policy, overrides. |
|
|
1191
|
+
| [docs/agent-json.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/agent-json.md) | The machine surface: `schema`, every payload field, real captured examples. |
|
|
1192
|
+
| [AGENTS.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/AGENTS.md) | The burn-down loop an agent runs, and the rules for changing crapkit itself. |
|
|
1193
|
+
| [plugin/](https://github.com/JeanFrancoisGagne/crapkit/tree/main/plugin) | The Claude Code plugin: three skills, the read-side MCP server, and the advisory PostToolUse hook. |
|
|
1167
1194
|
|
|
1168
|
-
[crapkit.schema.json](crapkit.schema.json) is the authority on the config file shape.
|
|
1195
|
+
[crapkit.schema.json](https://github.com/JeanFrancoisGagne/crapkit/blob/main/crapkit.schema.json) is the authority on the config file shape.
|
|
1169
1196
|
|
|
1170
1197
|
## Development
|
|
1171
1198
|
|
|
@@ -1179,8 +1206,8 @@ python -m pytest -q
|
|
|
1179
1206
|
`pytest-xdist` is not optional: `tests/fixtures/mini_repo` declares a lane that shells out
|
|
1180
1207
|
to `pytest ... -n 2`, and without it that subprocess dies on an unrecognized `-n`. The
|
|
1181
1208
|
`git config` line arms the complexity gate on your own commits. Same steps, with what each
|
|
1182
|
-
one buys, in [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
1209
|
+
one buys, in [CONTRIBUTING.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/CONTRIBUTING.md).
|
|
1183
1210
|
|
|
1184
1211
|
## License
|
|
1185
1212
|
|
|
1186
|
-
MIT. See [LICENSE](LICENSE).
|
|
1213
|
+
MIT. See [LICENSE](https://github.com/JeanFrancoisGagne/crapkit/blob/main/LICENSE).
|