crapkit 0.4.14__tar.gz → 0.5.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.4.14/src/crapkit.egg-info → crapkit-0.5.0}/PKG-INFO +175 -71
- {crapkit-0.4.14 → crapkit-0.5.0}/README.md +174 -70
- {crapkit-0.4.14 → crapkit-0.5.0}/pyproject.toml +1 -1
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/__init__.py +1 -1
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/analyze.py +32 -2
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/churn.py +13 -2
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/cli/__init__.py +3 -2
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/cli/_shared.py +82 -5
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/cli/admin.py +283 -28
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/cli/analyses.py +54 -17
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/cli/claude_hook.py +20 -17
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/cli/parser.py +63 -35
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/cli/queue.py +79 -31
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/cli/ratchet_cmds.py +7 -5
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/cli/reports.py +14 -17
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/cli/scoring.py +212 -61
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/cli/verifying.py +159 -30
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/config.py +167 -14
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/digest.py +41 -20
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/doctor.py +33 -0
- crapkit-0.5.0/src/crapkit/errors.py +30 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/gitio.py +16 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/hook.py +4 -5
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/lanes.py +123 -25
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/lizardcognitive.py +64 -16
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/mcp_server.py +213 -48
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/mutate.py +26 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/override.py +5 -2
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/ratchet.py +22 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/report.py +21 -9
- crapkit-0.5.0/src/crapkit/repotext.py +48 -0
- crapkit-0.5.0/src/crapkit/rootfind.py +35 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/scaffold.py +184 -58
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/store.py +49 -15
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/universe.py +40 -1
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/worklist.py +81 -22
- {crapkit-0.4.14 → crapkit-0.5.0/src/crapkit.egg-info}/PKG-INFO +175 -71
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit.egg-info/SOURCES.txt +2 -0
- crapkit-0.4.14/src/crapkit/errors.py +0 -18
- {crapkit-0.4.14 → crapkit-0.5.0}/LICENSE +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/setup.cfg +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/__main__.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/_pygdefer.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/cache.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/churn_cache.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/churn_log.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/coupling.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/coupling_cache.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/coverage_istanbul.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/coverage_py.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/covstream.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/diffparse.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/discover.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/dup.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/invocation.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/junitparse.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/keys.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/lizardpowershell.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/lizardrust.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/lizardshell.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/merge.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/mutate_pool.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/packet.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/procs.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/ratchet_report.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/sarif.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/sarifio.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/score.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/snapshot.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/uncovered.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/verify.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit/watch.py +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit.egg-info/dependency_links.txt +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit.egg-info/entry_points.txt +0 -0
- {crapkit-0.4.14 → crapkit-0.5.0}/src/crapkit.egg-info/requires.txt +0 -0
- {crapkit-0.4.14 → crapkit-0.5.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.5.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
|
|
@@ -39,7 +39,7 @@ Dynamic: license-file
|
|
|
39
39
|
|
|
40
40
|
# crapkit
|
|
41
41
|
|
|
42
|
-
<!-- mcp-name: io.github.
|
|
42
|
+
<!-- mcp-name: io.github.JeanFrancoisGagne/crapkit -->
|
|
43
43
|
|
|
44
44
|
[](https://github.com/JeanFrancoisGagne/crapkit/actions/workflows/ci.yml)
|
|
45
45
|
[](https://pypi.org/project/crapkit/)
|
|
@@ -98,15 +98,18 @@ crapkit ratchet seed && git add crapkit.toml crapkit-ratchet.tsv .gitignore
|
|
|
98
98
|
|
|
99
99
|
```
|
|
100
100
|
$ crapkit coverage
|
|
101
|
-
run 1 @ fae4db93108: 2 functions scored
|
|
101
|
+
run 1 @ fae4db93108: 2 functions scored: 2 measured, 1 over ceiling 6, CRAP load 41.0, grade F
|
|
102
|
+
-> next: crapkit worklist
|
|
102
103
|
|
|
103
104
|
$ crapkit worklist
|
|
104
|
-
worklist @ fae4db93108 (run 1, floor ccn>=5, churn 12mo)
|
|
105
|
-
risk
|
|
105
|
+
worklist @ fae4db93108 (run 1, floor ccn>=5, churn 12mo) - 1 of 1 active (worklist_top 50), 0 dormant
|
|
106
|
+
risk 14.0 ccn 14 crap 38.5 cov 50% 1c/1a calc/grade.py:7 classify( score , attempts , late , bonus )
|
|
106
107
|
```
|
|
107
108
|
|
|
108
|
-
`risk
|
|
109
|
-
|
|
109
|
+
`risk 14.0` is ccn times a churn weight of one: a one-commit repo has no spread of commits
|
|
110
|
+
to weight, so each commit counts once and the ranking is complexity order until the
|
|
111
|
+
history grows ([Risk](#risk-what-ranks-the-worklist)). `crap 38.5` and `cov 50%` are the
|
|
112
|
+
score and the coverage behind it.
|
|
110
113
|
|
|
111
114
|
`ratchet seed` signs today's debt at today's score. From then on marks only ever fall, so
|
|
112
115
|
the repo can get better and never worse while you burn it down.
|
|
@@ -168,12 +171,13 @@ leaves the box
|
|
|
168
171
|
|
|
169
172
|
```
|
|
170
173
|
$ crapkit --version
|
|
171
|
-
crapkit 0.
|
|
174
|
+
crapkit 0.5.0
|
|
172
175
|
```
|
|
173
176
|
|
|
174
177
|
`python -m crapkit` works identically to the console script and is what to use from a
|
|
175
|
-
source checkout. Every subcommand accepts `--repo PATH` (default: the
|
|
176
|
-
|
|
178
|
+
source checkout. Every subcommand accepts `--repo PATH` (default: the nearest `crapkit.toml`
|
|
179
|
+
at or above the current directory, so a monorepo workspace finds the root's), and with it
|
|
180
|
+
you never have to `cd` into the repo you are scoring; [Subcommands](#subcommands) shows
|
|
177
181
|
where the flag goes.
|
|
178
182
|
|
|
179
183
|
## Upgrading from 0.4.4
|
|
@@ -360,6 +364,19 @@ EOF
|
|
|
360
364
|
chmod +x .git/hooks/pre-commit
|
|
361
365
|
```
|
|
362
366
|
|
|
367
|
+
The same file from PowerShell. `Out-File` and `>` write a byte-order mark (UTF-16 on
|
|
368
|
+
5.1) in front of the shebang, and git then answers every commit with `cannot spawn
|
|
369
|
+
.git/hooks/pre-commit` and lets it through; `Set-Content -Encoding ascii` does not. Git
|
|
370
|
+
runs the hook with its own `sh`, so the interpreter is spelled with forward slashes and
|
|
371
|
+
quoted, and no `chmod` is needed on Windows:
|
|
372
|
+
|
|
373
|
+
```powershell
|
|
374
|
+
$python = (Get-Command python).Source -replace '\\', '/'
|
|
375
|
+
Set-Content -Path .git/hooks/pre-commit -Encoding ascii -NoNewline -Value "#!/bin/sh`nexec '$python' -m crapkit hook-precommit`n"
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
`crapkit doctor` warns when the hook file git would spawn starts with a byte-order mark.
|
|
379
|
+
|
|
363
380
|
### Route 2: a committed hooks directory
|
|
364
381
|
|
|
365
382
|
The whole route, from a repo that has no `githooks/` yet:
|
|
@@ -398,7 +415,7 @@ crapkit ships a `.pre-commit-hooks.yaml` declaring `id: crapkit-gate`. In your
|
|
|
398
415
|
repos:
|
|
399
416
|
- repo: https://github.com/JeanFrancoisGagne/crapkit
|
|
400
417
|
# crapkit's release step rewrites this line to the tag it just cut
|
|
401
|
-
rev: v0.
|
|
418
|
+
rev: v0.5.0
|
|
402
419
|
hooks:
|
|
403
420
|
- id: crapkit-gate
|
|
404
421
|
```
|
|
@@ -444,10 +461,12 @@ git, and a shallow clone does not have that commit:
|
|
|
444
461
|
|
|
445
462
|
```
|
|
446
463
|
$ crapkit verify --baseline-tsv crapkit-baseline.tsv
|
|
447
|
-
crapkit: baseline commit a74260f321f is not an ancestor of HEAD
|
|
464
|
+
crapkit: baseline commit a74260f321f is not an ancestor of HEAD in this shallow clone, which does not hold it; set fetch-depth: 0 on the checkout or run git fetch --unshallow
|
|
448
465
|
```
|
|
449
466
|
|
|
450
467
|
That is exit 4 on a `git clone --depth 1` of a repo whose baseline verifies at full depth.
|
|
468
|
+
On a full clone the same exit blames what it used to, a rebase or an amend that rewrote
|
|
469
|
+
history, and asks for a fresh baseline instead.
|
|
451
470
|
Set `fetch-depth: 0` on the checkout step, which is what crapkit's own
|
|
452
471
|
[.github/workflows/ci.yml](https://github.com/JeanFrancoisGagne/crapkit/blob/main/.github/workflows/ci.yml) does.
|
|
453
472
|
|
|
@@ -504,7 +523,7 @@ to a workflow, and every input has a default:
|
|
|
504
523
|
- uses: actions/checkout@v4
|
|
505
524
|
with:
|
|
506
525
|
fetch-depth: 0
|
|
507
|
-
- uses: JeanFrancoisGagne/crapkit@v0.
|
|
526
|
+
- uses: JeanFrancoisGagne/crapkit@v0.5.0
|
|
508
527
|
```
|
|
509
528
|
|
|
510
529
|
The whole job those four lines sit in:
|
|
@@ -524,7 +543,7 @@ jobs:
|
|
|
524
543
|
with:
|
|
525
544
|
python-version: "3.12" # the interpreter the install below lands in
|
|
526
545
|
- run: pip install -e ".[dev]" # whatever your lanes need to run
|
|
527
|
-
- uses: JeanFrancoisGagne/crapkit@v0.
|
|
546
|
+
- uses: JeanFrancoisGagne/crapkit@v0.5.0
|
|
528
547
|
with:
|
|
529
548
|
gate: "false"
|
|
530
549
|
```
|
|
@@ -550,33 +569,55 @@ One comment per pull request, edited in place on every push. A hidden
|
|
|
550
569
|
carries one comment and not fifteen. On a `push` event there is no pull request to carry
|
|
551
570
|
it, and the same text goes to the job log instead.
|
|
552
571
|
|
|
553
|
-
Rendered
|
|
554
|
-
|
|
572
|
+
Rendered from three saved payloads: a pull request that adds an untested `route()` (ccn 8)
|
|
573
|
+
beside a ratchet-marked `legacy_router()`, in a repository whose `diff_uncovered_max` is 3.
|
|
574
|
+
The payloads are under `tests/fixtures/action_comment/`, and the unit suite pins this block
|
|
575
|
+
to their render:
|
|
555
576
|
|
|
556
577
|
```markdown
|
|
557
578
|
<!-- crapkit-action -->
|
|
558
579
|
|
|
559
580
|
## crapkit
|
|
560
581
|
|
|
561
|
-
|
|
582
|
+
4 functions in 2 files, 2 over ceiling 6, CRAP load 149.59, grade F.
|
|
562
583
|
|
|
563
|
-
**verify
|
|
584
|
+
**verify failed, exit 6: complexity gate.**
|
|
564
585
|
|
|
565
|
-
|
|
586
|
+
- gate: `app/calc.py:34` `route( a , b , c , d )` ccn 8, cov 0%, crap 72.0 -> decompose
|
|
587
|
+
- uncovered lines in `app/calc.py`: 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45
|
|
588
|
+
|
|
589
|
+
Run 3 against baseline 1, 1 changed file: 1 gate violation, 0 ratchet regressions, 0 new test failures, 11 uncovered changed lines.
|
|
590
|
+
|
|
591
|
+
### Worklist: 1 changed file
|
|
566
592
|
|
|
567
593
|
| File | Function | ccn | risk | remedy |
|
|
568
594
|
|---|---|---:|---:|---|
|
|
569
|
-
| `
|
|
570
|
-
| `
|
|
571
|
-
| `src/crapkit/cli/admin.py:80` | `_next_step( scopes : dict , lanes : tuple )` | 5 | 9.159 | ok |
|
|
572
|
-
| `src/crapkit/cli/admin.py:239` | `_pytest_cov_probe( command : str )` | 5 | 9.159 | ok |
|
|
573
|
-
| `src/crapkit/cli/admin.py:505` | `_segment_problems( name : str , cwd : Path , tokens : list [ str ] )` | 5 | 9.159 | ok |
|
|
595
|
+
| `app/calc.py:34` | `route( a , b , c , d )` | 8 | 4.0 | decompose |
|
|
596
|
+
| `app/calc.py:19` | `legacy_router( a , b , c , d , e )` | 8 | 4.0 | decompose (accepted debt) |
|
|
574
597
|
```
|
|
575
598
|
|
|
599
|
+
The first line is the run `crapkit coverage` wrote: functions and files, how many sit over
|
|
600
|
+
the ceiling (`over ceiling 6`, or `over their ceilings (6; reports 12, util 4)` when scopes
|
|
601
|
+
set their own), CRAP load and grade, with a failed lane's first error line appended as
|
|
602
|
+
`; lane 'js' failed: ...`. When `coverage --json` died before a summary, the line quotes the
|
|
603
|
+
error object it printed instead: `` `crapkit coverage` exited 5: lane 'py' cannot import
|
|
604
|
+
pytest-cov; pip install pytest-cov. ``
|
|
605
|
+
|
|
606
|
+
The verdict opens with the exit code and the rule it stands for (`complexity gate`,
|
|
607
|
+
`ratchet regressions`, `new test failures`, `diff-coverage ceiling N`), then one bullet per
|
|
608
|
+
finding: each gate violation with its function, ccn, coverage, CRAP and remedy; each
|
|
609
|
+
ratchet regression as recorded -> fresh; each new test failure by id; and the first twenty
|
|
610
|
+
uncovered changed lines, one bullet per file, with a count of the rest. The counts line
|
|
611
|
+
closes it. A verify that passed is one line: `**verify passed.** Run 2 against baseline 1,
|
|
612
|
+
7 changed files.`
|
|
613
|
+
|
|
576
614
|
The rows are the ranked worklist for the files the pull request changed, worst first,
|
|
577
|
-
`top` of them. `risk` is ccn times churn
|
|
578
|
-
and `remedy` is the run's own verdict for
|
|
579
|
-
|
|
615
|
+
`top` of them, with the rows a finding names listed first. `risk` is ccn times churn
|
|
616
|
+
weight, the number `crapkit worklist` ranks on, and `remedy` is the run's own verdict for
|
|
617
|
+
that function: `decompose`, `add-tests` or `ok`. `(accepted debt)` marks a function the
|
|
618
|
+
committed ratchet carries a mark for, so an untouched `legacy_router` does not read like
|
|
619
|
+
the pull request's own new function. A pull request that touches no ranked function gets
|
|
620
|
+
the heading and no table.
|
|
580
621
|
|
|
581
622
|
The two file counts describe the same diff, counted twice. `39 changed files` is
|
|
582
623
|
`git diff --name-only base.sha...HEAD`, the branch's own commits, and it is what the
|
|
@@ -588,8 +629,8 @@ behind the checkout to measure from.
|
|
|
588
629
|
|
|
589
630
|
| Input | Default | What it does |
|
|
590
631
|
|---|---|---|
|
|
591
|
-
| `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 |
|
|
592
|
-
| `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 |
|
|
632
|
+
| `gate` | `"false"` | `"true"` exits with `crapkit verify`'s own code, so a finding fails the check. On a pull request with `delta` on it also exits 1 when the base run was not made, because a verdict with no base run judged no changed function. Anything else exits 0 and the comment is the whole output |
|
|
633
|
+
| `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, and the verdict then judges no changed function |
|
|
593
634
|
| `top` | `"5"` | worklist rows rendered in the table |
|
|
594
635
|
| `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 |
|
|
595
636
|
|
|
@@ -614,13 +655,26 @@ The base run happens in a detached worktree under `RUNNER_TEMP`, and its store i
|
|
|
614
655
|
over the checkout's so both runs sit in one place. The cost is **two lane runs on a pull
|
|
615
656
|
request**: your suite runs once at the fork point and once on the checkout. Set `delta:
|
|
616
657
|
"false"` to skip the base run, and the verdict falls back to the checkout against its own
|
|
617
|
-
run, which reports the tree's own health and judges no changed function.
|
|
658
|
+
run, which reports the tree's own health and judges no changed function. The comment says
|
|
659
|
+
so in place of `verify passed`:
|
|
618
660
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
661
|
+
```markdown
|
|
662
|
+
**verify judged no changed function:** the base run was not made (no base commit). Run 2 against baseline 2, 0 changed files.
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
Three things leave the base run unmade: a shallow clone that does not hold the fork
|
|
666
|
+
point, a fork point older than your `crapkit.toml`, and a lane that will not run against
|
|
667
|
+
that tree. The step logs `crapkit base scoring exited N` and writes the reason to
|
|
668
|
+
`crapkit-base.reason` in the words the comment then quotes, `shallow clone does not hold
|
|
669
|
+
the fork point of <sha>; set fetch-depth: 0 on the checkout`, `no usable crapkit.toml at
|
|
670
|
+
the fork point <sha>: ...`, or `lane failed at the fork point <sha>: ...` with the lane's
|
|
671
|
+
first error line. The verdict falls back the same way `delta: "false"` does, and the
|
|
672
|
+
ratchet still runs, so exit 7 there is a finding. What differs is the job's status. With
|
|
673
|
+
`gate: "true"` on a pull request whose base run was attempted and not made, the exit step
|
|
674
|
+
exits 1 and prints the reason, because `actions/checkout`'s default depth-1 clone would
|
|
675
|
+
otherwise turn every pull request into a green check that judged nothing. A `push` event
|
|
676
|
+
and `delta: "false"` never attempt the base run, so they keep verify's own code; a `push`
|
|
677
|
+
has no base commit and no pull request to comment on.
|
|
624
678
|
|
|
625
679
|
One requirement the base run adds: the lane has to measure the tree it runs in. A lane
|
|
626
680
|
that reaches an installed copy of your package instead of the checkout will measure the
|
|
@@ -633,6 +687,21 @@ worktree is the quiet one. Point the lane at the tree, or set `delta: "false"`.
|
|
|
633
687
|
ran the lanes moments earlier on that tree, and verify parses those artifacts rather than
|
|
634
688
|
running the whole suite a second time for the same numbers.
|
|
635
689
|
|
|
690
|
+
Which is why `crapkit coverage` has to exit 0 for there to be a verdict. When it exits
|
|
691
|
+
anything else (a lane that failed, an artifact it refused), the action does not run
|
|
692
|
+
`verify`: on a runner that keeps its workspace between jobs (`clean: false`), a
|
|
693
|
+
`verify --reuse-artifacts` over a failed measurement read the artifact the dead lane had
|
|
694
|
+
left from an earlier run, passed over it, and made that run the trusted baseline. The
|
|
695
|
+
comment then carries coverage's failure in place of the verdict:
|
|
696
|
+
|
|
697
|
+
```markdown
|
|
698
|
+
**no verdict: `crapkit coverage` exited 5 (lane 'py' failed: lane 'py' wrote no artifact on its last attempt; the .crapkit/cov/py.json on disk predates it); verify did not run.**
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
The parenthesis is the first line of the lane failure the summary carries. When every
|
|
702
|
+
lane failed, `coverage` prints no summary at all and the lane errors are only in the job
|
|
703
|
+
log, and the line says so. With `gate: "true"` the job exits with coverage's code.
|
|
704
|
+
|
|
636
705
|
The other gate that judges a delta is the portable baseline in [Route 4](#route-4-ci):
|
|
637
706
|
commit `crapkit-baseline.tsv` on the default branch and run `crapkit verify --baseline-tsv
|
|
638
707
|
crapkit-baseline.tsv` in a step of your own. It needs no second lane run, and it needs
|
|
@@ -645,14 +714,18 @@ on PATH fails that step. Both leave the rendered text in the job log.
|
|
|
645
714
|
|
|
646
715
|
## Subcommands
|
|
647
716
|
|
|
648
|
-
Every subcommand takes `--repo PATH
|
|
649
|
-
|
|
650
|
-
|
|
717
|
+
Every subcommand takes `--repo PATH`, and the flag goes **after** the subcommand. Without
|
|
718
|
+
it the root is the nearest `crapkit.toml` at or above the current directory
|
|
719
|
+
([ADR 0002](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/adr/0002-configuration-is-found-upward-nearest-wins.md)): from a
|
|
720
|
+
monorepo workspace `crapkit worklist` reads the root configuration that claims the
|
|
721
|
+
workspace, says `crapkit: using crapkit.toml at /repo` on stderr, and reads a relative path
|
|
722
|
+
argument from where you stand. `claude-hook` is the one exception: it has no `--repo`,
|
|
723
|
+
because it takes its root from the file named in the hook payload it reads.
|
|
651
724
|
|
|
652
725
|
```
|
|
653
726
|
$ crapkit worklist --repo /path/to/repo --scope util --top 1
|
|
654
|
-
worklist @ a7c5c85ac37 (run 1, floor ccn>=5, churn 12mo)
|
|
655
|
-
risk 5.4 ccn 5
|
|
727
|
+
worklist @ a7c5c85ac37 (run 1, floor ccn>=5, churn 12mo) - 1 of 3 active (--top 1), 0 dormant
|
|
728
|
+
risk 5.4 ccn 5 crap 30.0 cov 0% 5c/1a util/stats.py:1 bucket( value , low , high )
|
|
656
729
|
```
|
|
657
730
|
|
|
658
731
|
Before it, argparse reads the path as the subcommand name and exits 2 without ever
|
|
@@ -672,8 +745,8 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
|
|
|
672
745
|
| `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. |
|
|
673
746
|
| `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). |
|
|
674
747
|
| `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. |
|
|
675
|
-
| `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. |
|
|
676
|
-
| `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. |
|
|
748
|
+
| `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. Every row carries the function's `crap` and `cov` off the ranked run and its `ratchet_mark` when the committed marks file signs for it, and the header counts the active rows the cap hid: `50 of 3980 active (worklist_top 50)`. `--scope NAME` (repeatable) is exact, not a substring; a name no `[[scope]]` declares is a configuration error, exit 3, naming the declared scopes. `--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. |
|
|
749
|
+
| `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, and a name no `[[scope]]` declares is a configuration error, exit 3, naming the declared scopes. `--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. |
|
|
677
750
|
| `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. |
|
|
678
751
|
| `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). |
|
|
679
752
|
| `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. |
|
|
@@ -683,16 +756,16 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
|
|
|
683
756
|
| `overrides [--json]` | The override audit trail: who granted what, when, and why. |
|
|
684
757
|
| `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). |
|
|
685
758
|
| `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. |
|
|
686
|
-
| `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.
|
|
759
|
+
| `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. Every row carries the function's CRAP and coverage, and prints the `crapkit explain` call for the rest: dark lines, history, the mark. It reads the same per-run rollups `trend` does, and writes them on the same terms. |
|
|
687
760
|
| `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. |
|
|
688
761
|
| `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. |
|
|
689
|
-
| `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. |
|
|
762
|
+
| `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. 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. |
|
|
690
763
|
| `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. |
|
|
691
764
|
| `hook-precommit` | The cc-only gate on staged blobs. No coverage, no snapshot, no repo-wide cache. Exit 6 on a violation. |
|
|
692
765
|
| `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. |
|
|
693
766
|
| `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. |
|
|
694
767
|
| `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. |
|
|
695
|
-
| `mcp` | A dependency-free stdio MCP server (newline JSON-RPC 2.0) exposing
|
|
768
|
+
| `mcp` | A dependency-free stdio MCP server (newline JSON-RPC 2.0) exposing ten 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). |
|
|
696
769
|
|
|
697
770
|
## Reading the output
|
|
698
771
|
|
|
@@ -731,16 +804,17 @@ outrank fifty from two years ago. The window anchors on the newest commit, never
|
|
|
731
804
|
wall clock, so a fixed tree ranks identically forever.
|
|
732
805
|
|
|
733
806
|
Age is not the input, position in the log is. A log whose commits all share one timestamp
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
807
|
+
has no range to weight against, so each commit counts once: a one-commit repo weighs every
|
|
808
|
+
file 1.0, ranks by ccn, and promotes nothing under the floor, because a top 10% of equal
|
|
809
|
+
weights would be every file. Commits minutes apart already rank. This repo was eight
|
|
810
|
+
commits old, all made the same day:
|
|
737
811
|
|
|
738
812
|
```
|
|
739
813
|
$ crapkit worklist --scope util
|
|
740
|
-
worklist @ a7c5c85ac37 (run 1, floor ccn>=5, churn 12mo)
|
|
741
|
-
risk 5.4 ccn 5
|
|
742
|
-
risk 4.5 ccn 9
|
|
743
|
-
risk 4.3 ccn 4
|
|
814
|
+
worklist @ a7c5c85ac37 (run 1, floor ccn>=5, churn 12mo) - 3 of 3 active (worklist_top 50), 0 dormant
|
|
815
|
+
risk 5.4 ccn 5 crap 30.0 cov 0% 5c/1a util/stats.py:1 bucket( value , low , high )
|
|
816
|
+
risk 4.5 ccn 9 crap 90.0 cov 0% 1c/1a util/curve.py:1 curve( scores , mode , floor , ceiling , skip_none )
|
|
817
|
+
risk 4.3 ccn 4 crap 4.2 cov 75% 5c/1a util/stats.py:13 spread( values , cap ) ok
|
|
744
818
|
```
|
|
745
819
|
|
|
746
820
|
`bucket` at ccn 5 outranks `curve` at ccn 9 because five commits touched it and one
|
|
@@ -861,7 +935,7 @@ warns. [docs/lanes.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/do
|
|
|
861
935
|
```
|
|
862
936
|
$ crapkit init
|
|
863
937
|
wrote crapkit.toml with 1 scope(s): calc
|
|
864
|
-
detected 1 lane(s) from this repo's own files: py
|
|
938
|
+
detected 1 lane(s) from this repo's own files: py - next: run `crapkit coverage`
|
|
865
939
|
added to .gitignore: .crapkit/, .coverage, __pycache__/
|
|
866
940
|
```
|
|
867
941
|
|
|
@@ -888,7 +962,26 @@ paths = ["calc"]
|
|
|
888
962
|
languages = ["python"]
|
|
889
963
|
|
|
890
964
|
[exclude]
|
|
891
|
-
|
|
965
|
+
# A leading **/ matches zero or more directories, so each glob below reaches the
|
|
966
|
+
# repo root and every nested copy. Test directories leave the corpus on their own.
|
|
967
|
+
globs = [
|
|
968
|
+
"**/node_modules/**",
|
|
969
|
+
"**/dist/**",
|
|
970
|
+
"**/build/**",
|
|
971
|
+
"**/vendor/**",
|
|
972
|
+
"**/generated/**",
|
|
973
|
+
"**/__generated__/**",
|
|
974
|
+
"**/*.generated.*",
|
|
975
|
+
"**/*.test.*",
|
|
976
|
+
"**/*.spec.*",
|
|
977
|
+
"**/test_*.py",
|
|
978
|
+
"**/*_test.py",
|
|
979
|
+
"**/conftest.py",
|
|
980
|
+
"**/*_test.go",
|
|
981
|
+
"**/*.config.ts",
|
|
982
|
+
"**/*.config.js",
|
|
983
|
+
"**/*.config.mts",
|
|
984
|
+
]
|
|
892
985
|
|
|
893
986
|
[[lane]]
|
|
894
987
|
name = "py"
|
|
@@ -908,9 +1001,12 @@ scopes = ["calc"]
|
|
|
908
1001
|
# scopes = ["<your-scope>"]
|
|
909
1002
|
|
|
910
1003
|
# `crapkit test-scoped FILES` runs one command per scope, with {files}
|
|
911
|
-
# replaced by that scope's files, each quoted
|
|
1004
|
+
# replaced by that scope's files, each quoted; a template with no {files}
|
|
1005
|
+
# runs as written, which is how a scope whose tests live elsewhere runs them.
|
|
912
1006
|
[crapkit.scoped_tests]
|
|
913
|
-
calc
|
|
1007
|
+
# calc: no test file under calc/, so the whole suite runs, from tests/
|
|
1008
|
+
calc = "python -m pytest tests -q -p no:cacheprovider"
|
|
1009
|
+
|
|
914
1010
|
```
|
|
915
1011
|
|
|
916
1012
|
The last block is the one an agent loop needs. `crapkit test-scoped` exits 3 for a file
|
|
@@ -926,6 +1022,7 @@ ok config keys all recognized
|
|
|
926
1022
|
ok scope 'calc': 1 file
|
|
927
1023
|
ok every tracked source file belongs to a scope
|
|
928
1024
|
ok 1 lane(s) declared
|
|
1025
|
+
ok lane 'py': python -> /home/you/ledger/.venv/bin/python (pytest 8.3.3, pytest-cov 7.1.0)
|
|
929
1026
|
ok lizard 1.24.0
|
|
930
1027
|
doctor: no problems found
|
|
931
1028
|
```
|
|
@@ -937,16 +1034,20 @@ and exit 0.
|
|
|
937
1034
|
|
|
938
1035
|
```
|
|
939
1036
|
$ crapkit coverage
|
|
940
|
-
run 1 @ fae4db93108: 2 functions scored
|
|
1037
|
+
run 1 @ fae4db93108: 2 functions scored: 2 measured, 1 over ceiling 6, CRAP load 41.0, grade F
|
|
1038
|
+
-> next: crapkit worklist
|
|
941
1039
|
|
|
942
1040
|
$ crapkit worklist
|
|
943
|
-
worklist @ fae4db93108 (run 1, floor ccn>=5, churn 12mo)
|
|
944
|
-
risk
|
|
1041
|
+
worklist @ fae4db93108 (run 1, floor ccn>=5, churn 12mo) - 1 of 1 active (worklist_top 50), 0 dormant
|
|
1042
|
+
risk 14.0 ccn 14 crap 38.5 cov 50% 1c/1a calc/grade.py:7 classify( score , attempts , late , bonus )
|
|
945
1043
|
```
|
|
946
1044
|
|
|
947
|
-
Columns: `risk`, `ccn`
|
|
948
|
-
`<commits>c/<authors>a` in the churn window
|
|
949
|
-
long name, then a marker on rows the burn-down queue will not hand out (`ok`,
|
|
1045
|
+
Columns: `risk`, `ccn`, the function's `crap` and `cov` off the ranked run (`-` on an
|
|
1046
|
+
inventory-only run), `<commits>c/<authors>a` in the churn window, `path:line`, the
|
|
1047
|
+
function's long name, then a marker on rows the burn-down queue will not hand out (`ok`,
|
|
1048
|
+
`no-lane`). The header counts the active rows against their total, so `50 of 3980 active
|
|
1049
|
+
(worklist_top 50)` says what the cap hid, and reads `(--top N)` when the flag set the cap.
|
|
1050
|
+
`--json` also carries `ccn_std`, `weight` and `ratchet_mark`.
|
|
950
1051
|
|
|
951
1052
|
**`worklist` is the risk map, not a to-do list.** It ranks finished rows too, so it does
|
|
952
1053
|
not empty when the burn-down does. `next-item` is the other view of that run: it drops the
|
|
@@ -956,7 +1057,7 @@ not empty when the burn-down does. `next-item` is the other view of that run: it
|
|
|
956
1057
|
|
|
957
1058
|
```
|
|
958
1059
|
$ crapkit next-item
|
|
959
|
-
{"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":
|
|
1060
|
+
{"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}
|
|
960
1061
|
```
|
|
961
1062
|
|
|
962
1063
|
`remedy: "decompose"`, `est_splits: 3` (this needs roughly three pieces to fit under 6),
|
|
@@ -971,7 +1072,7 @@ function at its current score, and from then on nothing may get worse.
|
|
|
971
1072
|
|
|
972
1073
|
```
|
|
973
1074
|
$ crapkit ratchet seed
|
|
974
|
-
crapkit-ratchet.tsv: added 1, tightened 0
|
|
1075
|
+
crapkit-ratchet.tsv: added 1, tightened 0 - 1 mark(s) vs run 1 (fae4db93108)
|
|
975
1076
|
|
|
976
1077
|
$ git add crapkit.toml crapkit-ratchet.tsv .gitignore && git commit -m "adopt crapkit"
|
|
977
1078
|
```
|
|
@@ -987,7 +1088,8 @@ $ crapkit verify
|
|
|
987
1088
|
verify OK @ 8d10c13303d vs baseline fae4db93108 (5 changed files)
|
|
988
1089
|
|
|
989
1090
|
$ crapkit coverage
|
|
990
|
-
run 3 @ 8d10c13303d: 5 functions scored
|
|
1091
|
+
run 3 @ 8d10c13303d: 5 functions scored: 5 measured, 0 over ceiling 6, CRAP load 19.0, grade A+
|
|
1092
|
+
-> next: crapkit worklist
|
|
991
1093
|
```
|
|
992
1094
|
|
|
993
1095
|
CRAP load 41.0 to 19.0, grade F to A+. `verify` reruns the lanes and checks three things
|
|
@@ -1011,7 +1113,7 @@ A vitest repo with `src/grade.ts` and `test/grade.test.ts`.
|
|
|
1011
1113
|
```
|
|
1012
1114
|
$ crapkit init
|
|
1013
1115
|
wrote crapkit.toml with 1 scope(s): src
|
|
1014
|
-
detected 1 lane(s) from this repo's own files: js
|
|
1116
|
+
detected 1 lane(s) from this repo's own files: js - next: run `crapkit coverage`
|
|
1015
1117
|
added to .gitignore: .crapkit/
|
|
1016
1118
|
```
|
|
1017
1119
|
|
|
@@ -1074,11 +1176,12 @@ job; either one is enough. The full block is in
|
|
|
1074
1176
|
|
|
1075
1177
|
```
|
|
1076
1178
|
$ crapkit coverage
|
|
1077
|
-
run 1 @ 8bfbe613fcd: 2 functions scored
|
|
1179
|
+
run 1 @ 8bfbe613fcd: 2 functions scored: 2 measured, 1 over ceiling 6, CRAP load 56.68, grade F
|
|
1180
|
+
-> next: crapkit worklist
|
|
1078
1181
|
|
|
1079
1182
|
$ crapkit worklist
|
|
1080
|
-
worklist @ 8bfbe613fcd (run 1, floor ccn>=5, churn 12mo)
|
|
1081
|
-
risk
|
|
1183
|
+
worklist @ 8bfbe613fcd (run 1, floor ccn>=5, churn 12mo) - 1 of 1 active (worklist_top 50), 0 dormant
|
|
1184
|
+
risk 15.0 ccn 15 crap 52.4 cov 45% 1c/1a src/grade.ts:8 classify ( row Row )
|
|
1082
1185
|
```
|
|
1083
1186
|
|
|
1084
1187
|
`classify` is ccn 15 against a ceiling of 6: one function holding the late-and-retry
|
|
@@ -1091,7 +1194,7 @@ can get worse while you burn this one down.
|
|
|
1091
1194
|
|
|
1092
1195
|
```
|
|
1093
1196
|
$ crapkit ratchet seed
|
|
1094
|
-
crapkit-ratchet.tsv: added 1, tightened 0
|
|
1197
|
+
crapkit-ratchet.tsv: added 1, tightened 0 - 1 mark(s) vs run 1 (8bfbe613fcd)
|
|
1095
1198
|
|
|
1096
1199
|
$ git add crapkit.toml crapkit-ratchet.tsv .gitignore && git commit -m "adopt crapkit"
|
|
1097
1200
|
```
|
|
@@ -1171,7 +1274,8 @@ $ crapkit verify
|
|
|
1171
1274
|
verify OK @ 2af3433d979 vs baseline 8bfbe613fcd (3 changed files)
|
|
1172
1275
|
|
|
1173
1276
|
$ crapkit coverage
|
|
1174
|
-
run 3 @ 2af3433d979: 5 functions scored
|
|
1277
|
+
run 3 @ 2af3433d979: 5 functions scored: 5 measured, 0 over ceiling 6, CRAP load 22.0, grade A+
|
|
1278
|
+
-> next: crapkit worklist
|
|
1175
1279
|
```
|
|
1176
1280
|
|
|
1177
1281
|
CRAP load 56.68 to 22.0, grade F to A+, and the mark seeded in step 4 is gone: `verify`
|