crapkit 0.7.6__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.6 → crapkit-0.8.0}/PKG-INFO +62 -31
- crapkit-0.7.6/src/crapkit.egg-info/PKG-INFO → crapkit-0.8.0/README.md +1398 -1408
- {crapkit-0.7.6 → crapkit-0.8.0}/pyproject.toml +10 -5
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/__init__.py +1 -1
- crapkit-0.8.0/src/crapkit/_process_owner.py +335 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/_windows_job.py +2 -2
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/analyze.py +177 -49
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/cache.py +4 -3
- crapkit-0.8.0/src/crapkit/churn.py +224 -0
- {crapkit-0.7.6 → 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.6 → crapkit-0.8.0}/src/crapkit/cli/_shared.py +66 -18
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/cli/admin.py +173 -244
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/cli/analyses.py +11 -5
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/cli/claude_hook.py +32 -5
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/cli/maintenance.py +11 -10
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/cli/parser.py +14 -7
- {crapkit-0.7.6 → 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.6 → crapkit-0.8.0}/src/crapkit/cli/reports.py +62 -18
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/cli/scoring.py +143 -28
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/cli/verifying.py +189 -108
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/config.py +38 -5
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/config_contract.py +24 -6
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/coupling.py +8 -1
- crapkit-0.8.0/src/crapkit/coverage_format.py +57 -0
- {crapkit-0.7.6 → 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.6 → crapkit-0.8.0}/src/crapkit/covstream.py +17 -220
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/doctor.py +113 -18
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/dup.py +143 -48
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/gitio.py +58 -31
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/hook.py +9 -1
- {crapkit-0.7.6 → 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.6 → crapkit-0.8.0}/src/crapkit/lanes.py +375 -175
- {crapkit-0.7.6 → 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.6 → crapkit-0.8.0}/src/crapkit/mcp_server.py +70 -43
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/merge.py +4 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/override.py +48 -22
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/packet.py +91 -116
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/procs.py +168 -257
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/ratchet.py +74 -30
- crapkit-0.8.0/src/crapkit/ratchetfile.py +160 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/report.py +24 -12
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/scaffold.py +14 -10
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/score.py +68 -17
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/snapshot.py +11 -3
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/store.py +470 -139
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/uncovered.py +14 -20
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/universe.py +3 -3
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/verify.py +49 -8
- crapkit-0.7.6/README.md → crapkit-0.8.0/src/crapkit.egg-info/PKG-INFO +1439 -1367
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit.egg-info/SOURCES.txt +4 -1
- crapkit-0.7.6/src/crapkit/_process_owner.py +0 -129
- crapkit-0.7.6/src/crapkit/churn.py +0 -86
- crapkit-0.7.6/src/crapkit/churn_log.py +0 -340
- crapkit-0.7.6/src/crapkit/cli/ratchet_cmds.py +0 -258
- crapkit-0.7.6/src/crapkit/coverage_py.py +0 -119
- crapkit-0.7.6/src/crapkit/keys.py +0 -171
- crapkit-0.7.6/src/crapkit/lizardpython.py +0 -262
- crapkit-0.7.6/src/crapkit/lizardtypescript.py +0 -142
- crapkit-0.7.6/src/crapkit/ratchetfile.py +0 -84
- crapkit-0.7.6/src/crapkit/retention.py +0 -204
- {crapkit-0.7.6 → crapkit-0.8.0}/LICENSE +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/setup.cfg +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/__main__.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/_analysis_pool.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/_mcp_stdio.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/_process_family.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/_pygdefer.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/cli/__init__.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/coupling_cache.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/diffparse.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/digest.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/errors.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/gitpaths.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/invocation.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/lizardpowershell.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/lizardrust.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/lizardshell.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/locks.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/logs.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/mutate.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/mutate_pool.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/ratchet_report.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/records.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/repotext.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/resources.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/rootfind.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/sarif.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/sarifio.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/watch.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit/worklist.py +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit.egg-info/dependency_links.txt +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit.egg-info/entry_points.txt +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/src/crapkit.egg-info/requires.txt +0 -0
- {crapkit-0.7.6 → crapkit-0.8.0}/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.
|
|
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
|
|
@@ -224,7 +224,7 @@ files. Review those commands before running Crapkit in a repository you do not t
|
|
|
224
224
|
|
|
225
225
|
```
|
|
226
226
|
$ crapkit --version
|
|
227
|
-
crapkit 0.
|
|
227
|
+
crapkit 0.8.0
|
|
228
228
|
```
|
|
229
229
|
|
|
230
230
|
`python -m crapkit` works identically to the console script and is what to use from a
|
|
@@ -237,18 +237,22 @@ where the flag goes.
|
|
|
237
237
|
|
|
238
238
|
Keep the CLI and plugin versions aligned, measure fresh coverage after upgrading,
|
|
239
239
|
and review any ratchet identity refusal before reseeding. The current reader is
|
|
240
|
-
analysis version
|
|
241
|
-
|
|
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)
|
|
242
245
|
for saved state, portable records and Windows launcher locks.
|
|
243
246
|
|
|
244
247
|
### Upgrading from 0.4.4
|
|
245
248
|
|
|
246
249
|
This historical example describes the 0.4.4 to 0.4.5 transition, from analysis
|
|
247
|
-
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:
|
|
248
252
|
|
|
249
253
|
```
|
|
250
254
|
$ crapkit verify
|
|
251
|
-
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`
|
|
252
256
|
```
|
|
253
257
|
|
|
254
258
|
That transition changed cognitive complexity, not `ccn` or the CRAP formula.
|
|
@@ -276,8 +280,10 @@ any function an edit pushed over its ceiling. Claude reaches two of the skills b
|
|
|
276
280
|
wiring a repo up happens once and its description has no business in every turn's window.
|
|
277
281
|
It adds no files to your repo, and it needs the crapkit CLI on PATH.
|
|
278
282
|
|
|
279
|
-
A repo with no `crapkit.toml` costs a silent
|
|
280
|
-
|
|
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:
|
|
281
287
|
|
|
282
288
|
```
|
|
283
289
|
claude plugin marketplace update crapkit
|
|
@@ -310,8 +316,8 @@ command, matcher `Bash`:
|
|
|
310
316
|
```
|
|
311
317
|
|
|
312
318
|
The cost is one `git rev-parse --show-toplevel` and one `git status --porcelain -z -uall`
|
|
313
|
-
per shell call in any git repo, whether or not crapkit measures it: about
|
|
314
|
-
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
|
|
315
321
|
untracked `*.py` files written in the last 12 seconds, 25 at most, each judged the way an
|
|
316
322
|
edit is. Python only, so a TypeScript or Go repo pays the two spawns and hears nothing.
|
|
317
323
|
|
|
@@ -396,7 +402,7 @@ full verdict. The preview and hooks differ in what their available evidence can
|
|
|
396
402
|
| Surface | Fires | Power |
|
|
397
403
|
|---|---|---|
|
|
398
404
|
| `crapkit claude-hook` | after an agent's edit lands | **advisory.** Names the breach on stderr. Blocks nothing, because PostToolUse runs after the write |
|
|
399
|
-
| `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` |
|
|
400
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 |
|
|
401
407
|
| `crapkit verify` | before you push, and in CI | **the verdict.** Gate, ratchet, new test failures, diff coverage, against the trusted baseline |
|
|
402
408
|
|
|
@@ -430,8 +436,9 @@ chmod +x .git/hooks/pre-commit
|
|
|
430
436
|
```
|
|
431
437
|
|
|
432
438
|
The same file from PowerShell. `Out-File` and `>` write a byte-order mark (UTF-16 on
|
|
433
|
-
5.1) in front of the shebang, and git then
|
|
434
|
-
.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
|
|
435
442
|
runs the hook with its own `sh`, so the interpreter is spelled with forward slashes and
|
|
436
443
|
quoted, and no `chmod` is needed on Windows:
|
|
437
444
|
|
|
@@ -480,7 +487,7 @@ crapkit ships a `.pre-commit-hooks.yaml` declaring `id: crapkit-gate`. In your
|
|
|
480
487
|
repos:
|
|
481
488
|
- repo: https://github.com/JeanFrancoisGagne/crapkit
|
|
482
489
|
# crapkit's release step rewrites this line to the tag it just cut
|
|
483
|
-
rev: v0.
|
|
490
|
+
rev: v0.8.0
|
|
484
491
|
hooks:
|
|
485
492
|
- id: crapkit-gate
|
|
486
493
|
```
|
|
@@ -532,6 +539,16 @@ crapkit: baseline commit a74260f321f is not an ancestor of HEAD in this shallow
|
|
|
532
539
|
That is exit 4 on a `git clone --depth 1` of a repo whose baseline verifies at full depth.
|
|
533
540
|
On a full clone the same exit blames what it used to, a rebase or an amend that rewrote
|
|
534
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.
|
|
535
552
|
Set `fetch-depth: 0` on the checkout step, which is what crapkit's own
|
|
536
553
|
[.github/workflows/ci.yml](https://github.com/JeanFrancoisGagne/crapkit/blob/main/.github/workflows/ci.yml) does.
|
|
537
554
|
|
|
@@ -588,7 +605,7 @@ to a workflow, and every input has a default:
|
|
|
588
605
|
- uses: actions/checkout@v4
|
|
589
606
|
with:
|
|
590
607
|
fetch-depth: 0
|
|
591
|
-
- uses: JeanFrancoisGagne/crapkit@v0.
|
|
608
|
+
- uses: JeanFrancoisGagne/crapkit@v0.8.0
|
|
592
609
|
```
|
|
593
610
|
|
|
594
611
|
The whole job those four lines sit in:
|
|
@@ -608,7 +625,7 @@ jobs:
|
|
|
608
625
|
with:
|
|
609
626
|
python-version: "3.12" # the interpreter the install below lands in
|
|
610
627
|
- run: pip install -e ".[dev]" # whatever your lanes need to run
|
|
611
|
-
- uses: JeanFrancoisGagne/crapkit@v0.
|
|
628
|
+
- uses: JeanFrancoisGagne/crapkit@v0.8.0
|
|
612
629
|
with:
|
|
613
630
|
gate: "false"
|
|
614
631
|
```
|
|
@@ -764,8 +781,9 @@ comment then carries coverage's failure in place of the verdict:
|
|
|
764
781
|
```
|
|
765
782
|
|
|
766
783
|
The parenthesis is the first line of the lane failure the summary carries. When every
|
|
767
|
-
lane failed, `coverage` prints
|
|
768
|
-
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.
|
|
769
787
|
|
|
770
788
|
The other gate that judges a delta is the portable baseline in [Route 4](#route-4-ci):
|
|
771
789
|
commit `crapkit-baseline.tsv` on the default branch and run `crapkit verify --baseline-tsv
|
|
@@ -779,9 +797,9 @@ on PATH fails that step. Both leave the rendered text in the job log.
|
|
|
779
797
|
|
|
780
798
|
## Subcommands
|
|
781
799
|
|
|
782
|
-
`crapkit clean --dry-run --json` previews
|
|
800
|
+
`crapkit clean --dry-run --json` previews abandoned temporary mutation recovery. See
|
|
783
801
|
[resource policies](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/resources.md)
|
|
784
|
-
for shared analysis workers, process lifetime
|
|
802
|
+
for shared analysis workers, process lifetime and bounded logs.
|
|
785
803
|
|
|
786
804
|
Every subcommand takes `--repo PATH`, and the flag goes **after** the subcommand. Without
|
|
787
805
|
it the root is the nearest `crapkit.toml` at or above the current directory
|
|
@@ -809,7 +827,7 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
|
|
|
809
827
|
|
|
810
828
|
| Command | What it does |
|
|
811
829
|
|---|---|
|
|
812
|
-
| `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. |
|
|
813
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. |
|
|
814
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). |
|
|
815
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. |
|
|
@@ -820,8 +838,8 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
|
|
|
820
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. |
|
|
821
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). |
|
|
822
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. |
|
|
823
|
-
| `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. |
|
|
824
|
-
| `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). |
|
|
825
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. |
|
|
826
844
|
| `overrides [--json]` | The override audit trail: who granted what, when, and why. |
|
|
827
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). |
|
|
@@ -831,7 +849,7 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
|
|
|
831
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. |
|
|
832
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. |
|
|
833
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. |
|
|
834
|
-
| `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. |
|
|
835
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. |
|
|
836
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. |
|
|
837
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. |
|
|
@@ -855,7 +873,7 @@ The coverage summary counts all four as `measured` / `untested` / `no_lane` / `c
|
|
|
855
873
|
| Remedy | Condition | Action |
|
|
856
874
|
|---|---|---|
|
|
857
875
|
| `decompose` | `ccn > ceiling` | Split it. No amount of coverage clears this. |
|
|
858
|
-
| `split-lines` | `ccn <= ceiling`, `crap > ceiling`, and another function shares its source lines | 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. |
|
|
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. |
|
|
859
877
|
| `add-tests` | `ccn <= ceiling` and `crap > ceiling` | Cover the branches. |
|
|
860
878
|
| `ok` | `crap <= ceiling` | Nothing. |
|
|
861
879
|
|
|
@@ -946,6 +964,11 @@ bypasses the rule, and the run history records which run the verdict used. Nothi
|
|
|
946
964
|
touches a repo that has never run `verify`: with no failure to protect, `coverage` alone
|
|
947
965
|
always advances the baseline.
|
|
948
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
|
+
|
|
949
972
|
**When the id you pass cannot serve.** A `--baseline ID` naming a real run that is not a
|
|
950
973
|
candidate says which run it is, why, and which ones can:
|
|
951
974
|
|
|
@@ -967,7 +990,7 @@ crapkit: run 3 is an inventory run (no coverage was measured) and cannot serve a
|
|
|
967
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. |
|
|
968
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. |
|
|
969
992
|
| 8 | New test failures against the baseline run. Failures the baseline already had do not count. |
|
|
970
|
-
| 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. |
|
|
971
994
|
|
|
972
995
|
### Exit 1 means one of three things
|
|
973
996
|
|
|
@@ -1089,6 +1112,7 @@ makes it step 4 of the burn-down loop. Every key is in
|
|
|
1089
1112
|
|
|
1090
1113
|
```
|
|
1091
1114
|
$ crapkit doctor
|
|
1115
|
+
resources: up to 8 analysis worker(s) per pool, 8 shared slot(s); lane log limit 16777216 bytes per file
|
|
1092
1116
|
ok config keys all recognized
|
|
1093
1117
|
ok scope 'calc': 1 file
|
|
1094
1118
|
ok every tracked source file belongs to a scope
|
|
@@ -1128,7 +1152,7 @@ not empty when the burn-down does. `next-item` is the other view of that run: it
|
|
|
1128
1152
|
|
|
1129
1153
|
```
|
|
1130
1154
|
$ crapkit next-item
|
|
1131
|
-
{"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}
|
|
1132
1156
|
```
|
|
1133
1157
|
|
|
1134
1158
|
`remedy: "decompose"`, `est_splits: 3` (this needs roughly three pieces to fit under 6),
|
|
@@ -1156,7 +1180,7 @@ table of cases pushed into parametrized tests. Commit the fix, then:
|
|
|
1156
1180
|
|
|
1157
1181
|
```
|
|
1158
1182
|
$ crapkit verify
|
|
1159
|
-
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
|
|
1160
1184
|
|
|
1161
1185
|
$ crapkit coverage
|
|
1162
1186
|
run 3 @ 8d10c13303d: 5 functions scored: 5 measured, 0 over ceiling 6, CRAP load 19.0, grade A+
|
|
@@ -1200,6 +1224,12 @@ an istanbul `coverage-final.json` works; see [docs/lanes.md](https://github.com/
|
|
|
1200
1224
|
[one directory down](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md#running-from-a-subdirectory), and a
|
|
1201
1225
|
[crapkit root below the repo top](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md#a-crapkit-root-below-the-repo-top).
|
|
1202
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
|
+
|
|
1203
1233
|
### 2. Install a coverage provider
|
|
1204
1234
|
|
|
1205
1235
|
**This is the step that stops most TypeScript users.** vitest ships no coverage provider
|
|
@@ -1300,9 +1330,10 @@ rescore vs run 1 @ 8bfbe613fcd (coverage STALE, complexity fresh)
|
|
|
1300
1330
|
4 0% 20.0 add-tests src/grade.ts:8 penalty ( attempts , late )
|
|
1301
1331
|
4 45% 6.7 add-tests src/grade.ts:48 classify ( row Row )
|
|
1302
1332
|
4 75% 4.2 ok src/grade.ts:59 average ( scores Array )
|
|
1333
|
+
gate: 4 changed function(s) judged, 0 over ceiling 6
|
|
1303
1334
|
```
|
|
1304
1335
|
|
|
1305
|
-
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
|
|
1306
1337
|
is still run 1's, from before three of those functions existed, and `add-tests` is the
|
|
1307
1338
|
literal instruction for step 6.
|
|
1308
1339
|
|
|
@@ -1342,7 +1373,7 @@ verify FAILED @ 0296156ff21 vs baseline 0e646697946 (1 changed files)
|
|
|
1342
1373
|
|
|
1343
1374
|
```
|
|
1344
1375
|
$ crapkit verify
|
|
1345
|
-
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
|
|
1346
1377
|
|
|
1347
1378
|
$ crapkit coverage
|
|
1348
1379
|
run 3 @ 2af3433d979: 5 functions scored: 5 measured, 0 over ceiling 6, CRAP load 22.0, grade A+
|
|
@@ -1370,7 +1401,7 @@ with no debt.
|
|
|
1370
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. |
|
|
1371
1402
|
| [docs/configuration.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/configuration.md) | Every `crapkit.toml` key: type, default, and what it does. |
|
|
1372
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. |
|
|
1373
|
-
| [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. |
|
|
1374
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. |
|
|
1375
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. |
|
|
1376
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. |
|