crapkit 0.7.5__tar.gz → 0.8.0__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.7.5 → crapkit-0.8.0}/PKG-INFO +102 -39
- crapkit-0.7.5/src/crapkit.egg-info/PKG-INFO → crapkit-0.8.0/README.md +1398 -1376
- {crapkit-0.7.5 → crapkit-0.8.0}/pyproject.toml +11 -6
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/__init__.py +1 -1
- crapkit-0.8.0/src/crapkit/_process_owner.py +335 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/_windows_job.py +2 -2
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/analyze.py +177 -49
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/cache.py +4 -3
- crapkit-0.8.0/src/crapkit/churn.py +224 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/churn_cache.py +30 -10
- crapkit-0.8.0/src/crapkit/churn_commits.py +184 -0
- crapkit-0.8.0/src/crapkit/churn_log.py +458 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/cli/_shared.py +66 -18
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/cli/admin.py +173 -244
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/cli/analyses.py +11 -5
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/cli/claude_hook.py +32 -5
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/cli/maintenance.py +11 -10
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/cli/parser.py +14 -7
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/cli/queue.py +154 -156
- crapkit-0.8.0/src/crapkit/cli/ratchet_cmds.py +524 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/cli/reports.py +62 -18
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/cli/scoring.py +145 -30
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/cli/verifying.py +189 -108
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/config.py +38 -5
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/config_contract.py +24 -6
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/coupling.py +8 -1
- crapkit-0.8.0/src/crapkit/coverage_format.py +57 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/coverage_istanbul.py +138 -3
- crapkit-0.8.0/src/crapkit/coverage_py.py +287 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/covstream.py +17 -220
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/doctor.py +113 -18
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/dup.py +143 -48
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/gitio.py +58 -31
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/hook.py +9 -1
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/junitparse.py +50 -8
- crapkit-0.8.0/src/crapkit/keys.py +402 -0
- crapkit-0.8.0/src/crapkit/lane_changes.py +145 -0
- crapkit-0.8.0/src/crapkit/lane_command.py +187 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/lanes.py +375 -175
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/lizardcognitive.py +43 -9
- crapkit-0.8.0/src/crapkit/lizardpython.py +389 -0
- crapkit-0.8.0/src/crapkit/lizardtypescript.py +299 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/mcp_server.py +83 -66
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/merge.py +4 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/override.py +48 -22
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/packet.py +91 -116
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/procs.py +168 -257
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/ratchet.py +74 -30
- crapkit-0.8.0/src/crapkit/ratchetfile.py +160 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/report.py +24 -12
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/scaffold.py +14 -10
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/score.py +94 -27
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/snapshot.py +11 -3
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/store.py +471 -140
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/uncovered.py +14 -20
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/universe.py +3 -3
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/verify.py +49 -8
- crapkit-0.7.5/README.md → crapkit-0.8.0/src/crapkit.egg-info/PKG-INFO +1439 -1335
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit.egg-info/SOURCES.txt +4 -1
- crapkit-0.7.5/src/crapkit/_process_owner.py +0 -129
- crapkit-0.7.5/src/crapkit/churn.py +0 -86
- crapkit-0.7.5/src/crapkit/churn_log.py +0 -340
- crapkit-0.7.5/src/crapkit/cli/ratchet_cmds.py +0 -258
- crapkit-0.7.5/src/crapkit/coverage_py.py +0 -119
- crapkit-0.7.5/src/crapkit/keys.py +0 -171
- crapkit-0.7.5/src/crapkit/lizardpython.py +0 -262
- crapkit-0.7.5/src/crapkit/lizardtypescript.py +0 -142
- crapkit-0.7.5/src/crapkit/ratchetfile.py +0 -84
- crapkit-0.7.5/src/crapkit/retention.py +0 -204
- {crapkit-0.7.5 → crapkit-0.8.0}/LICENSE +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/setup.cfg +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/__main__.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/_analysis_pool.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/_mcp_stdio.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/_process_family.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/_pygdefer.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/cli/__init__.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/coupling_cache.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/diffparse.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/digest.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/errors.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/gitpaths.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/invocation.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/lizardpowershell.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/lizardrust.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/lizardshell.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/locks.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/logs.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/mutate.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/mutate_pool.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/ratchet_report.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/records.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/repotext.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/resources.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/rootfind.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/sarif.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/sarifio.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/watch.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit/worklist.py +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit.egg-info/dependency_links.txt +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit.egg-info/entry_points.txt +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit.egg-info/requires.txt +0 -0
- {crapkit-0.7.5 → crapkit-0.8.0}/src/crapkit.egg-info/top_level.txt +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: crapkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
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
|
|
7
7
|
Project-URL: Homepage, https://github.com/JeanFrancoisGagne/crapkit
|
|
8
|
-
Project-URL: Documentation, https://
|
|
8
|
+
Project-URL: Documentation, https://www.jfgagne.com/crapkit/handbook.html
|
|
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
|
|
@@ -111,6 +111,9 @@ crapkit ratchet seed
|
|
|
111
111
|
git add crapkit.toml crapkit-ratchet.tsv .gitignore
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
+
Not a Python repo? `uvx crapkit init` runs the same commands and adds nothing to your
|
|
115
|
+
manifest: see [A repo that is not Python](#a-repo-that-is-not-python).
|
|
116
|
+
|
|
114
117
|
`init` detects pytest, Vitest and Jest from the repository's own files. Review the
|
|
115
118
|
generated config before running its commands. When detection leaves a commented
|
|
116
119
|
lane, fill it in using the [lane recipes](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md).
|
|
@@ -182,6 +185,28 @@ pip install git+https://github.com/JeanFrancoisGagne/crapkit.git
|
|
|
182
185
|
pip install .
|
|
183
186
|
```
|
|
184
187
|
|
|
188
|
+
### A repo that is not Python
|
|
189
|
+
|
|
190
|
+
crapkit is a command-line tool, never a dependency of the code it scores. A TypeScript,
|
|
191
|
+
Go or Rust repo adds nothing to its own manifest. With [uv](https://docs.astral.sh/uv/)
|
|
192
|
+
on the machine, `uvx` fetches crapkit into a cache of its own and runs it:
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
$ uvx crapkit init
|
|
196
|
+
wrote crapkit.toml with 1 scope(s): src
|
|
197
|
+
detected 1 lane(s) from this repo's own files: js - next: run `crapkit coverage`
|
|
198
|
+
added to .gitignore: .crapkit/
|
|
199
|
+
|
|
200
|
+
$ uvx crapkit coverage
|
|
201
|
+
$ uvx crapkit worklist
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The lane still runs your own test runner, so Vitest or Jest and its coverage package come
|
|
205
|
+
from the repo's `node_modules` as they do today. `uv tool install crapkit` or
|
|
206
|
+
`pipx install crapkit` puts a `crapkit` command on PATH once, which is what the
|
|
207
|
+
[commit gate](#the-gate) and the Claude Code plugin call. uv brings its own Python when
|
|
208
|
+
the machine has none.
|
|
209
|
+
|
|
185
210
|
Requires Python 3.11 or newer and Git on PATH. The CLI has one runtime dependency,
|
|
186
211
|
`lizard>=1.24.0`; a package mirror needs both distributions. Install into the environment
|
|
187
212
|
you intend to use, then check `crapkit --version`. The `pip install -e ".[dev]"` under
|
|
@@ -199,7 +224,7 @@ files. Review those commands before running Crapkit in a repository you do not t
|
|
|
199
224
|
|
|
200
225
|
```
|
|
201
226
|
$ crapkit --version
|
|
202
|
-
crapkit 0.
|
|
227
|
+
crapkit 0.8.0
|
|
203
228
|
```
|
|
204
229
|
|
|
205
230
|
`python -m crapkit` works identically to the console script and is what to use from a
|
|
@@ -212,18 +237,22 @@ where the flag goes.
|
|
|
212
237
|
|
|
213
238
|
Keep the CLI and plugin versions aligned, measure fresh coverage after upgrading,
|
|
214
239
|
and review any ratchet identity refusal before reseeding. The current reader is
|
|
215
|
-
analysis version
|
|
216
|
-
|
|
240
|
+
analysis version 11. It renames once each Python def with a PEP 695 type parameter
|
|
241
|
+
list and each def nested three or more deep, and it lists a def whose body sits on
|
|
242
|
+
its colon line; `crapkit ratchet prune` drops the marks left under the old names.
|
|
243
|
+
Older JavaScript and TypeScript callback marks can require a reviewed mapping.
|
|
244
|
+
Follow the [upgrade guide](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/upgrading.md)
|
|
217
245
|
for saved state, portable records and Windows launcher locks.
|
|
218
246
|
|
|
219
247
|
### Upgrading from 0.4.4
|
|
220
248
|
|
|
221
249
|
This historical example describes the 0.4.4 to 0.4.5 transition, from analysis
|
|
222
|
-
version 7 to 8. It is retained to explain
|
|
250
|
+
version 7 to 8. It is retained to explain the refusal, quoted as crapkit prints it
|
|
251
|
+
today:
|
|
223
252
|
|
|
224
253
|
```
|
|
225
254
|
$ crapkit verify
|
|
226
|
-
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`
|
|
255
|
+
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; run `crapkit coverage`, then re-baseline with `crapkit ratchet seed`
|
|
227
256
|
```
|
|
228
257
|
|
|
229
258
|
That transition changed cognitive complexity, not `ccn` or the CRAP formula.
|
|
@@ -251,8 +280,10 @@ any function an edit pushed over its ceiling. Claude reaches two of the skills b
|
|
|
251
280
|
wiring a repo up happens once and its description has no business in every turn's window.
|
|
252
281
|
It adds no files to your repo, and it needs the crapkit CLI on PATH.
|
|
253
282
|
|
|
254
|
-
A repo with no `crapkit.toml` costs a silent
|
|
255
|
-
|
|
283
|
+
A repo with no `crapkit.toml` costs a silent no-op per edit: 68 ms on Windows through the
|
|
284
|
+
`crapkit.exe` launcher Claude Code starts, where a bare `python -c pass` took 32 ms on the
|
|
285
|
+
same machine. After upgrading the CLI, refresh the marketplace before updating the
|
|
286
|
+
installed plugin:
|
|
256
287
|
|
|
257
288
|
```
|
|
258
289
|
claude plugin marketplace update crapkit
|
|
@@ -285,8 +316,8 @@ command, matcher `Bash`:
|
|
|
285
316
|
```
|
|
286
317
|
|
|
287
318
|
The cost is one `git rev-parse --show-toplevel` and one `git status --porcelain -z -uall`
|
|
288
|
-
per shell call in any git repo, whether or not crapkit measures it: about
|
|
289
|
-
on crapkit's own checkout, and more on a bigger tree. What comes back is the dirty or
|
|
319
|
+
per shell call in any git repo, whether or not crapkit measures it: about 50 ms together
|
|
320
|
+
on crapkit's own checkout on Windows, and more on a bigger tree. What comes back is the dirty or
|
|
290
321
|
untracked `*.py` files written in the last 12 seconds, 25 at most, each judged the way an
|
|
291
322
|
edit is. Python only, so a TypeScript or Go repo pays the two spawns and hears nothing.
|
|
292
323
|
|
|
@@ -371,7 +402,7 @@ full verdict. The preview and hooks differ in what their available evidence can
|
|
|
371
402
|
| Surface | Fires | Power |
|
|
372
403
|
|---|---|---|
|
|
373
404
|
| `crapkit claude-hook` | after an agent's edit lands | **advisory.** Names the breach on stderr. Blocks nothing, because PostToolUse runs after the write |
|
|
374
|
-
| `crapkit rescore FILE --gate` | when you ask, after the first coverage run | **preview.**
|
|
405
|
+
| `crapkit rescore FILE --gate` | when you ask, after the first coverage run | **preview.** A stricter preview of the commit gate, sub-second, before you stage: a ratchet mark pardons a function only while its CRAP is at or under the mark. With no run behind it, exit 1 and `no snapshot` |
|
|
375
406
|
| `crapkit hook-precommit` | `git commit` | **blocks.** The hook exits 6; git reports 1. Staged blobs only, so it costs the size of the commit and needs no coverage |
|
|
376
407
|
| `crapkit verify` | before you push, and in CI | **the verdict.** Gate, ratchet, new test failures, diff coverage, against the trusted baseline |
|
|
377
408
|
|
|
@@ -405,8 +436,9 @@ chmod +x .git/hooks/pre-commit
|
|
|
405
436
|
```
|
|
406
437
|
|
|
407
438
|
The same file from PowerShell. `Out-File` and `>` write a byte-order mark (UTF-16 on
|
|
408
|
-
5.1) in front of the shebang, and git then
|
|
409
|
-
.git/hooks/pre-commit
|
|
439
|
+
5.1) in front of the shebang, and git then refuses every commit with `error: cannot spawn
|
|
440
|
+
.git/hooks/pre-commit: No such file or directory` (measured on git 2.43 for Windows)
|
|
441
|
+
without ever running the gate; `Set-Content -Encoding ascii` writes no mark. Git
|
|
410
442
|
runs the hook with its own `sh`, so the interpreter is spelled with forward slashes and
|
|
411
443
|
quoted, and no `chmod` is needed on Windows:
|
|
412
444
|
|
|
@@ -455,7 +487,7 @@ crapkit ships a `.pre-commit-hooks.yaml` declaring `id: crapkit-gate`. In your
|
|
|
455
487
|
repos:
|
|
456
488
|
- repo: https://github.com/JeanFrancoisGagne/crapkit
|
|
457
489
|
# crapkit's release step rewrites this line to the tag it just cut
|
|
458
|
-
rev: v0.
|
|
490
|
+
rev: v0.8.0
|
|
459
491
|
hooks:
|
|
460
492
|
- id: crapkit-gate
|
|
461
493
|
```
|
|
@@ -507,6 +539,16 @@ crapkit: baseline commit a74260f321f is not an ancestor of HEAD in this shallow
|
|
|
507
539
|
That is exit 4 on a `git clone --depth 1` of a repo whose baseline verifies at full depth.
|
|
508
540
|
On a full clone the same exit blames what it used to, a rebase or an amend that rewrote
|
|
509
541
|
history, and asks for a fresh baseline instead.
|
|
542
|
+
`verify --base` and `hook-precommit --base` look up the fork point with `git merge-base`,
|
|
543
|
+
and in the same clone they refuse with exit 4 and the same fix:
|
|
544
|
+
|
|
545
|
+
```
|
|
546
|
+
$ crapkit hook-precommit --base c47a37b1df69c434ba42eec5979ddad03d2bf1e4
|
|
547
|
+
crapkit: git merge-base c47a37b1df69c434ba42eec5979ddad03d2bf1e4 HEAD failed in /home/runner/work/app/app: fatal: Not a valid commit name c47a37b1df69c434ba42eec5979ddad03d2bf1e4; this shallow clone does not hold every commit: set fetch-depth: 0 on the checkout or run git fetch --unshallow
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
When the clone holds both commits but not the one they fork from, the line reads
|
|
551
|
+
`no merge base between REF and HEAD in ROOT`, followed by the same fix.
|
|
510
552
|
Set `fetch-depth: 0` on the checkout step, which is what crapkit's own
|
|
511
553
|
[.github/workflows/ci.yml](https://github.com/JeanFrancoisGagne/crapkit/blob/main/.github/workflows/ci.yml) does.
|
|
512
554
|
|
|
@@ -563,7 +605,7 @@ to a workflow, and every input has a default:
|
|
|
563
605
|
- uses: actions/checkout@v4
|
|
564
606
|
with:
|
|
565
607
|
fetch-depth: 0
|
|
566
|
-
- uses: JeanFrancoisGagne/crapkit@v0.
|
|
608
|
+
- uses: JeanFrancoisGagne/crapkit@v0.8.0
|
|
567
609
|
```
|
|
568
610
|
|
|
569
611
|
The whole job those four lines sit in:
|
|
@@ -583,7 +625,7 @@ jobs:
|
|
|
583
625
|
with:
|
|
584
626
|
python-version: "3.12" # the interpreter the install below lands in
|
|
585
627
|
- run: pip install -e ".[dev]" # whatever your lanes need to run
|
|
586
|
-
- uses: JeanFrancoisGagne/crapkit@v0.
|
|
628
|
+
- uses: JeanFrancoisGagne/crapkit@v0.8.0
|
|
587
629
|
with:
|
|
588
630
|
gate: "false"
|
|
589
631
|
```
|
|
@@ -654,7 +696,7 @@ closes it. A verify that passed is one line: `**verify passed.** Run 2 against b
|
|
|
654
696
|
The rows are the ranked worklist for the files the pull request changed, worst first,
|
|
655
697
|
`top` of them, with the rows a finding names listed first. `risk` is ccn times churn
|
|
656
698
|
weight, the number `crapkit worklist` ranks on, and `remedy` is the run's own verdict for
|
|
657
|
-
that function: `decompose`, `add-tests` or `ok`. `(accepted debt)` marks a function the
|
|
699
|
+
that function: `decompose`, `split-lines`, `add-tests` or `ok`. `(accepted debt)` marks a function the
|
|
658
700
|
committed ratchet carries a mark for, so an untouched `legacy_router` does not read like
|
|
659
701
|
the pull request's own new function. A pull request that touches no ranked function gets
|
|
660
702
|
the heading and no table.
|
|
@@ -739,8 +781,9 @@ comment then carries coverage's failure in place of the verdict:
|
|
|
739
781
|
```
|
|
740
782
|
|
|
741
783
|
The parenthesis is the first line of the lane failure the summary carries. When every
|
|
742
|
-
lane failed, `coverage` prints
|
|
743
|
-
log, and the line says so
|
|
784
|
+
lane failed, `coverage` prints an error object instead of a summary and the lane errors
|
|
785
|
+
are only in the job log, and the line says so: `(every lane failed (1 of 1); the lane
|
|
786
|
+
errors are in the job log)`. With `gate: "true"` the job exits with coverage's code.
|
|
744
787
|
|
|
745
788
|
The other gate that judges a delta is the portable baseline in [Route 4](#route-4-ci):
|
|
746
789
|
commit `crapkit-baseline.tsv` on the default branch and run `crapkit verify --baseline-tsv
|
|
@@ -754,9 +797,9 @@ on PATH fails that step. Both leave the rendered text in the job log.
|
|
|
754
797
|
|
|
755
798
|
## Subcommands
|
|
756
799
|
|
|
757
|
-
`crapkit clean --dry-run --json` previews
|
|
800
|
+
`crapkit clean --dry-run --json` previews abandoned temporary mutation recovery. See
|
|
758
801
|
[resource policies](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/resources.md)
|
|
759
|
-
for shared analysis workers, process lifetime
|
|
802
|
+
for shared analysis workers, process lifetime and bounded logs.
|
|
760
803
|
|
|
761
804
|
Every subcommand takes `--repo PATH`, and the flag goes **after** the subcommand. Without
|
|
762
805
|
it the root is the nearest `crapkit.toml` at or above the current directory
|
|
@@ -784,7 +827,7 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
|
|
|
784
827
|
|
|
785
828
|
| Command | What it does |
|
|
786
829
|
|---|---|
|
|
787
|
-
| `clean [--dry-run] [--json]` |
|
|
830
|
+
| `clean [--dry-run] [--json]` | Recovers abandoned temporary mutation worktrees. Preserves active runs and intentional mutation pools. `--dry-run` reports planned recoveries. It leaves test evidence alone: crapkit's own development runner, `tools/testing/run.py`, prunes `.crapkit/test-runs/` with `--retention-days` and `--retention-count`. `--json` keeps `test_runs` with every array empty. |
|
|
788
831
|
| `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. |
|
|
789
832
|
| `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). |
|
|
790
833
|
| `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. |
|
|
@@ -795,8 +838,8 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
|
|
|
795
838
|
| `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. |
|
|
796
839
|
| `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). |
|
|
797
840
|
| `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. |
|
|
798
|
-
| `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. |
|
|
799
|
-
| `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). |
|
|
841
|
+
| `rescore FILE ... [--gate] [--json]` | Fresh complexity for named files over the latest run's stale coverage, joined by name. A function on a line span another one shares, and a Python def whose body starts on the line its signature ends, scores untested, as the coverage run scores it. 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. |
|
|
842
|
+
| `ratchet seed \| prune \| merge \| move \| report [--baseline ID] [--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. `seed` and `prune` take `--baseline ID` to read a named run instead of verify's pick, refused for the reasons `verify --baseline` refuses one. See [docs/ratchet.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/ratchet.md). |
|
|
800
843
|
| `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. |
|
|
801
844
|
| `overrides [--json]` | The override audit trail: who granted what, when, and why. |
|
|
802
845
|
| `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). |
|
|
@@ -806,7 +849,7 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
|
|
|
806
849
|
| `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. |
|
|
807
850
|
| `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. Both lists pass through the scored corpus first, the same predicate `coverage` uses (scopes, excludes, the test-file cut, `max_file_bytes`): a test file, an excluded path, a file over `max_file_bytes` or a file no scope claims is named on stderr and never mutated, `--json` lists it under `outside_corpus`, and when nothing is left stdout says `nothing to mutate` at exit 0 without starting the suite. `--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. Every worker uses a kept worktree, including one; see [mutation worktrees](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/configuration.md#mutation-worktrees). `--drop-pool` removes them and exits. |
|
|
808
851
|
| `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. |
|
|
809
|
-
| `hook-precommit` | The cc-only gate on staged blobs. No coverage, no snapshot, no repo-wide cache. Exit 6 on a violation. |
|
|
852
|
+
| `hook-precommit [--base REF]` | The cc-only gate on staged blobs. No coverage, no snapshot, no repo-wide cache. Exit 6 on a violation. `--base REF` compares the index with the merge base of REF and HEAD, the form a CI checkout runs. |
|
|
810
853
|
| `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. |
|
|
811
854
|
| `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. |
|
|
812
855
|
| `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. |
|
|
@@ -830,6 +873,7 @@ The coverage summary counts all four as `measured` / `untested` / `no_lane` / `c
|
|
|
830
873
|
| Remedy | Condition | Action |
|
|
831
874
|
|---|---|---|
|
|
832
875
|
| `decompose` | `ccn > ceiling` | Split it. No amount of coverage clears this. |
|
|
876
|
+
| `split-lines` | `ccn <= ceiling`, `crap > ceiling`, and another function shares its source lines, or a Python def's body starts on the line its signature ends | Put each definition on its own lines, then measure again. Coverage cannot tell functions on one line apart, so the score stays at uncovered whatever the tests do. A one-line Python def shares its line with its `def` statement, which runs at import, so coverage.py cannot show a call. So does a body on the last line of a signature that spans several lines, or one that goes on from the colon's line inside brackets or after a backslash: move the body to its own line after the signature. |
|
|
833
877
|
| `add-tests` | `ccn <= ceiling` and `crap > ceiling` | Cover the branches. |
|
|
834
878
|
| `ok` | `crap <= ceiling` | Nothing. |
|
|
835
879
|
|
|
@@ -920,6 +964,11 @@ bypasses the rule, and the run history records which run the verdict used. Nothi
|
|
|
920
964
|
touches a repo that has never run `verify`: with no failure to protect, `coverage` alone
|
|
921
965
|
always advances the baseline.
|
|
922
966
|
|
|
967
|
+
`ratchet seed --baseline ID` and `ratchet prune --baseline ID` take the same name as
|
|
968
|
+
`verify --baseline ID`, admitted by the same rule. When a failed verify pins seed to a run
|
|
969
|
+
it cannot read or sign, that is the way out
|
|
970
|
+
([docs/ratchet.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/ratchet.md#naming-the-run-to-seed-from)).
|
|
971
|
+
|
|
923
972
|
**When the id you pass cannot serve.** A `--baseline ID` naming a real run that is not a
|
|
924
973
|
candidate says which run it is, why, and which ones can:
|
|
925
974
|
|
|
@@ -941,7 +990,7 @@ crapkit: run 3 is an inventory run (no coverage was measured) and cannot serve a
|
|
|
941
990
|
| 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. |
|
|
942
991
|
| 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. |
|
|
943
992
|
| 8 | New test failures against the baseline run. Failures the baseline already had do not count. |
|
|
944
|
-
| 9 | Diff-coverage ceiling breached: `diff_uncovered_max` is set and more changed lines than that never ran. |
|
|
993
|
+
| 9 | Diff-coverage ceiling breached: `diff_uncovered_max` is set and more changed lines than that never ran. A changed file no lane artifact mentions counts every line of its functions. |
|
|
945
994
|
|
|
946
995
|
### Exit 1 means one of three things
|
|
947
996
|
|
|
@@ -1063,6 +1112,7 @@ makes it step 4 of the burn-down loop. Every key is in
|
|
|
1063
1112
|
|
|
1064
1113
|
```
|
|
1065
1114
|
$ crapkit doctor
|
|
1115
|
+
resources: up to 8 analysis worker(s) per pool, 8 shared slot(s); lane log limit 16777216 bytes per file
|
|
1066
1116
|
ok config keys all recognized
|
|
1067
1117
|
ok scope 'calc': 1 file
|
|
1068
1118
|
ok every tracked source file belongs to a scope
|
|
@@ -1102,7 +1152,7 @@ not empty when the burn-down does. `next-item` is the other view of that run: it
|
|
|
1102
1152
|
|
|
1103
1153
|
```
|
|
1104
1154
|
$ crapkit next-item
|
|
1105
|
-
{"commit": "fae4db93108b4841a00959f9117430679e7250ca", "empty": false, "item": {"authors": 1, "ccn": 14, "ccn_std": 14, "cognitive": 13, "commits": 1, "cov": 0.5, "crap": 38.5, "end": 28, "est_splits": 3, "est_uncovered_paths": 7, "flag": "measured", "function": "classify( score , attempts , late , bonus )", "handle": "classify", "nesting": 3, "nloc": 22, "path": "calc/grade.py", "remedy": "decompose", "scope": "calc", "start": 7, "target": 6, "uncovered_lines": [9, 11, 15, 17, 19, 24, 25, 26, 27, 28]}, "run_id": 1, "schema": 1, "skipped_no_lane": 0, "stale": false}
|
|
1155
|
+
{"commit": "fae4db93108b4841a00959f9117430679e7250ca", "empty": false, "item": {"authors": 1, "ccn": 14, "ccn_std": 14, "cognitive": 13, "commits": 1, "cov": 0.5, "crap": 38.5, "end": 28, "est_splits": 3, "est_uncovered_paths": 7, "flag": "measured", "function": "classify( score , attempts , late , bonus )", "handle": "classify", "nesting": 3, "nloc": 22, "occurrence": 1, "path": "calc/grade.py", "remedy": "decompose", "scope": "calc", "start": 7, "target": 6, "uncovered_lines": [9, 11, 15, 17, 19, 24, 25, 26, 27, 28]}, "run_id": 1, "schema": 1, "skipped_no_lane": 0, "stale": false}
|
|
1106
1156
|
```
|
|
1107
1157
|
|
|
1108
1158
|
`remedy: "decompose"`, `est_splits: 3` (this needs roughly three pieces to fit under 6),
|
|
@@ -1130,7 +1180,7 @@ table of cases pushed into parametrized tests. Commit the fix, then:
|
|
|
1130
1180
|
|
|
1131
1181
|
```
|
|
1132
1182
|
$ crapkit verify
|
|
1133
|
-
verify OK @ 8d10c13303d vs baseline fae4db93108 (5 changed files)
|
|
1183
|
+
verify OK @ 8d10c13303d vs baseline fae4db93108 (5 changed files) ratchet: 1 dropped, 0 tightened -> git add crapkit-ratchet.tsv
|
|
1134
1184
|
|
|
1135
1185
|
$ crapkit coverage
|
|
1136
1186
|
run 3 @ 8d10c13303d: 5 functions scored: 5 measured, 0 over ceiling 6, CRAP load 19.0, grade A+
|
|
@@ -1174,6 +1224,12 @@ an istanbul `coverage-final.json` works; see [docs/lanes.md](https://github.com/
|
|
|
1174
1224
|
[one directory down](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md#running-from-a-subdirectory), and a
|
|
1175
1225
|
[crapkit root below the repo top](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md#a-crapkit-root-below-the-repo-top).
|
|
1176
1226
|
|
|
1227
|
+
`init` writes the vitest line under `[crapkit.scoped_tests]` commented out, because it
|
|
1228
|
+
cannot see which vitest config a file-scoped run needs. Until you uncomment that line or
|
|
1229
|
+
write your own, `crapkit doctor` WARNs `scope 'src' has a lane but no
|
|
1230
|
+
[crapkit.scoped_tests] template`. The WARN leaves doctor's exit at 0; it means `crapkit
|
|
1231
|
+
test-scoped` exits 3 on files under `src/`.
|
|
1232
|
+
|
|
1177
1233
|
### 2. Install a coverage provider
|
|
1178
1234
|
|
|
1179
1235
|
**This is the step that stops most TypeScript users.** vitest ships no coverage provider
|
|
@@ -1268,15 +1324,16 @@ export function classify(row: Row): string {
|
|
|
1268
1324
|
```
|
|
1269
1325
|
$ crapkit rescore src/grade.ts --gate
|
|
1270
1326
|
rescore vs run 1 @ 8bfbe613fcd (coverage STALE, complexity fresh)
|
|
1271
|
-
ccn cov crap remedy
|
|
1272
|
-
5 0% 30.0 add-tests
|
|
1273
|
-
5 0% 30.0 add-tests
|
|
1274
|
-
4 0% 20.0 add-tests
|
|
1275
|
-
4 45% 6.7 add-tests
|
|
1276
|
-
4 75% 4.2 ok
|
|
1327
|
+
ccn cov crap remedy function
|
|
1328
|
+
5 0% 30.0 add-tests src/grade.ts:22 band ( score )
|
|
1329
|
+
5 0% 30.0 add-tests src/grade.ts:38 demote ( letter , row Row )
|
|
1330
|
+
4 0% 20.0 add-tests src/grade.ts:8 penalty ( attempts , late )
|
|
1331
|
+
4 45% 6.7 add-tests src/grade.ts:48 classify ( row Row )
|
|
1332
|
+
4 75% 4.2 ok src/grade.ts:59 average ( scores Array )
|
|
1333
|
+
gate: 4 changed function(s) judged, 0 over ceiling 6
|
|
1277
1334
|
```
|
|
1278
1335
|
|
|
1279
|
-
Exit 0: every piece is at or under 6. The `crap` column is loud because its coverage half
|
|
1336
|
+
Exit 0 and the `gate:` line: every changed piece is at or under 6. The `crap` column is loud because its coverage half
|
|
1280
1337
|
is still run 1's, from before three of those functions existed, and `add-tests` is the
|
|
1281
1338
|
literal instruction for step 6.
|
|
1282
1339
|
|
|
@@ -1316,7 +1373,7 @@ verify FAILED @ 0296156ff21 vs baseline 0e646697946 (1 changed files)
|
|
|
1316
1373
|
|
|
1317
1374
|
```
|
|
1318
1375
|
$ crapkit verify
|
|
1319
|
-
verify OK @ 2af3433d979 vs baseline 8bfbe613fcd (3 changed files)
|
|
1376
|
+
verify OK @ 2af3433d979 vs baseline 8bfbe613fcd (3 changed files) ratchet: 1 dropped, 0 tightened -> git add crapkit-ratchet.tsv
|
|
1320
1377
|
|
|
1321
1378
|
$ crapkit coverage
|
|
1322
1379
|
run 3 @ 2af3433d979: 5 functions scored: 5 measured, 0 over ceiling 6, CRAP load 22.0, grade A+
|
|
@@ -1340,11 +1397,11 @@ with no debt.
|
|
|
1340
1397
|
|
|
1341
1398
|
| Page | Covers |
|
|
1342
1399
|
|---|---|
|
|
1343
|
-
| [The handbook](https://
|
|
1400
|
+
| [The handbook](https://www.jfgagne.com/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://www.jfgagne.com/crapkit/handbook.html), self-contained, so it opens straight from a clone. |
|
|
1344
1401
|
| [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. |
|
|
1345
1402
|
| [docs/configuration.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/configuration.md) | Every `crapkit.toml` key: type, default, and what it does. |
|
|
1346
1403
|
| [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. |
|
|
1347
|
-
| [docs/resources.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/resources.md) | Worker budgets, command cleanup, log rotation
|
|
1404
|
+
| [docs/resources.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/resources.md) | Worker budgets, command cleanup, log rotation and safe cleanup. |
|
|
1348
1405
|
| [docs/ratchet.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/ratchet.md) | Seeding, pruning, the git merge driver, metric stamps, debt policy, overrides. |
|
|
1349
1406
|
| [docs/upgrading.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/upgrading.md) | Existing installations: analysis and key versions, saved state, plugin alignment and Windows upgrades. |
|
|
1350
1407
|
| [docs/portable-records.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/portable-records.md) | Lossless exports, portable baselines and ratchets, including filenames with delimiters. |
|
|
@@ -1371,6 +1428,12 @@ line arms the complexity gate. See
|
|
|
1371
1428
|
for development and [the verified implementation report](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/architecture/2026-09-07-implementation/REPORT.md)
|
|
1372
1429
|
for complete Windows source and Linux wheel results, focused benchmarks and their limits.
|
|
1373
1430
|
|
|
1431
|
+
## Maintainer and project background
|
|
1432
|
+
|
|
1433
|
+
crapkit is created and maintained by [Jean-François Gagné](https://www.jfgagne.com/).
|
|
1434
|
+
Read the [project background](https://www.jfgagne.com/projects/crapkit/) for the
|
|
1435
|
+
problem it addresses and how it fits into his work on software and AI.
|
|
1436
|
+
|
|
1374
1437
|
## License
|
|
1375
1438
|
|
|
1376
1439
|
MIT. See [LICENSE](https://github.com/JeanFrancoisGagne/crapkit/blob/main/LICENSE).
|