crapkit 0.5.0__tar.gz → 0.6.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.5.0/src/crapkit.egg-info → crapkit-0.6.0}/PKG-INFO +21 -6
- {crapkit-0.5.0 → crapkit-0.6.0}/README.md +20 -5
- {crapkit-0.5.0 → crapkit-0.6.0}/pyproject.toml +1 -1
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/__init__.py +1 -1
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/cli/verifying.py +28 -4
- crapkit-0.6.0/src/crapkit/mcp_server.py +1951 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/verify.py +17 -0
- {crapkit-0.5.0 → crapkit-0.6.0/src/crapkit.egg-info}/PKG-INFO +21 -6
- crapkit-0.5.0/src/crapkit/mcp_server.py +0 -417
- {crapkit-0.5.0 → crapkit-0.6.0}/LICENSE +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/setup.cfg +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/__main__.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/_pygdefer.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/analyze.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/cache.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/churn.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/churn_cache.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/churn_log.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/cli/__init__.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/cli/_shared.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/cli/admin.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/cli/analyses.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/cli/claude_hook.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/cli/parser.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/cli/queue.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/cli/ratchet_cmds.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/cli/reports.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/cli/scoring.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/config.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/coupling.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/coupling_cache.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/coverage_istanbul.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/coverage_py.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/covstream.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/diffparse.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/digest.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/discover.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/doctor.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/dup.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/errors.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/gitio.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/hook.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/invocation.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/junitparse.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/keys.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/lanes.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/lizardcognitive.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/lizardpowershell.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/lizardrust.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/lizardshell.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/merge.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/mutate.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/mutate_pool.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/override.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/packet.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/procs.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/ratchet.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/ratchet_report.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/report.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/repotext.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/rootfind.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/sarif.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/sarifio.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/scaffold.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/score.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/snapshot.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/store.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/uncovered.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/universe.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/watch.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit/worklist.py +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit.egg-info/SOURCES.txt +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit.egg-info/dependency_links.txt +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit.egg-info/entry_points.txt +0 -0
- {crapkit-0.5.0 → crapkit-0.6.0}/src/crapkit.egg-info/requires.txt +0 -0
- {crapkit-0.5.0 → crapkit-0.6.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.6.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
|
|
@@ -73,6 +73,16 @@ half-executed straight-line function never reads as fully covered.
|
|
|
73
73
|
function at ccn 7 with 100% coverage still scores 7 and still fails the gate. The only
|
|
74
74
|
move that clears it is splitting the function.
|
|
75
75
|
|
|
76
|
+
**Why 6 and not 30.** crap4j's conventional threshold of 30 is a CRAP score: it lets an
|
|
77
|
+
untested `ccn 5` through (25 + 5 = 30) and a fully covered `ccn 30` too. crapkit's default
|
|
78
|
+
is a complexity ceiling, because coverage can at best collapse CRAP to `ccn`, and a
|
|
79
|
+
function you cannot cover past `ccn 6` is one you decompose. Set `target = 30` in
|
|
80
|
+
`crapkit.toml` if you want the crap4j number. A repo with existing debt does not need to:
|
|
81
|
+
`ratchet seed` marks today's over-ceiling functions at today's score, the gate then judges
|
|
82
|
+
only the functions a change touches, and marks may only fall, so adoption never starts with
|
|
83
|
+
a wall of red. Next to crap4py, radon, xenon, wily and SonarQube:
|
|
84
|
+
[docs/comparison.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/comparison.md).
|
|
85
|
+
|
|
76
86
|
crapkit scores **git-tracked files only**. Source you have not `git add`ed is invisible to
|
|
77
87
|
it.
|
|
78
88
|
|
|
@@ -171,7 +181,7 @@ leaves the box
|
|
|
171
181
|
|
|
172
182
|
```
|
|
173
183
|
$ crapkit --version
|
|
174
|
-
crapkit 0.
|
|
184
|
+
crapkit 0.6.0
|
|
175
185
|
```
|
|
176
186
|
|
|
177
187
|
`python -m crapkit` works identically to the console script and is what to use from a
|
|
@@ -415,7 +425,7 @@ crapkit ships a `.pre-commit-hooks.yaml` declaring `id: crapkit-gate`. In your
|
|
|
415
425
|
repos:
|
|
416
426
|
- repo: https://github.com/JeanFrancoisGagne/crapkit
|
|
417
427
|
# crapkit's release step rewrites this line to the tag it just cut
|
|
418
|
-
rev: v0.
|
|
428
|
+
rev: v0.6.0
|
|
419
429
|
hooks:
|
|
420
430
|
- id: crapkit-gate
|
|
421
431
|
```
|
|
@@ -523,7 +533,7 @@ to a workflow, and every input has a default:
|
|
|
523
533
|
- uses: actions/checkout@v4
|
|
524
534
|
with:
|
|
525
535
|
fetch-depth: 0
|
|
526
|
-
- uses: JeanFrancoisGagne/crapkit@v0.
|
|
536
|
+
- uses: JeanFrancoisGagne/crapkit@v0.6.0
|
|
527
537
|
```
|
|
528
538
|
|
|
529
539
|
The whole job those four lines sit in:
|
|
@@ -543,7 +553,7 @@ jobs:
|
|
|
543
553
|
with:
|
|
544
554
|
python-version: "3.12" # the interpreter the install below lands in
|
|
545
555
|
- run: pip install -e ".[dev]" # whatever your lanes need to run
|
|
546
|
-
- uses: JeanFrancoisGagne/crapkit@v0.
|
|
556
|
+
- uses: JeanFrancoisGagne/crapkit@v0.6.0
|
|
547
557
|
with:
|
|
548
558
|
gate: "false"
|
|
549
559
|
```
|
|
@@ -765,7 +775,7 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
|
|
|
765
775
|
| `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. |
|
|
766
776
|
| `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. |
|
|
767
777
|
| `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. |
|
|
768
|
-
| `mcp` | A dependency-free stdio MCP server (newline JSON-RPC 2.0) exposing
|
|
778
|
+
| `mcp` | A dependency-free stdio MCP server (newline JSON-RPC 2.0) exposing twelve read-only tools named `verb_noun`, each with a title and a documented output schema. 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). |
|
|
769
779
|
|
|
770
780
|
## Reading the output
|
|
771
781
|
|
|
@@ -1285,6 +1295,11 @@ dropped it once `classify` scored under the ceiling, rewriting the tracked
|
|
|
1285
1295
|
A verify may also print `warning: N changed line(s) have no coverage` above its verdict;
|
|
1286
1296
|
that block is advisory unless `diff_uncovered_max` is set
|
|
1287
1297
|
([docs/configuration.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/configuration.md)).
|
|
1298
|
+
It prints `warning: N function(s) over the ceiling carry no ratchet mark` when the tree
|
|
1299
|
+
holds debt `ratchet seed` never signed: the gate judges touched functions only and the
|
|
1300
|
+
ratchet check compares marks only, so coverage loss on such a function would pass unseen.
|
|
1301
|
+
The count is `unmarked_over_target` in `--json`, fires no exit code, and is zero on a repo
|
|
1302
|
+
with no debt.
|
|
1288
1303
|
|
|
1289
1304
|
## Documentation
|
|
1290
1305
|
|
|
@@ -34,6 +34,16 @@ half-executed straight-line function never reads as fully covered.
|
|
|
34
34
|
function at ccn 7 with 100% coverage still scores 7 and still fails the gate. The only
|
|
35
35
|
move that clears it is splitting the function.
|
|
36
36
|
|
|
37
|
+
**Why 6 and not 30.** crap4j's conventional threshold of 30 is a CRAP score: it lets an
|
|
38
|
+
untested `ccn 5` through (25 + 5 = 30) and a fully covered `ccn 30` too. crapkit's default
|
|
39
|
+
is a complexity ceiling, because coverage can at best collapse CRAP to `ccn`, and a
|
|
40
|
+
function you cannot cover past `ccn 6` is one you decompose. Set `target = 30` in
|
|
41
|
+
`crapkit.toml` if you want the crap4j number. A repo with existing debt does not need to:
|
|
42
|
+
`ratchet seed` marks today's over-ceiling functions at today's score, the gate then judges
|
|
43
|
+
only the functions a change touches, and marks may only fall, so adoption never starts with
|
|
44
|
+
a wall of red. Next to crap4py, radon, xenon, wily and SonarQube:
|
|
45
|
+
[docs/comparison.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/comparison.md).
|
|
46
|
+
|
|
37
47
|
crapkit scores **git-tracked files only**. Source you have not `git add`ed is invisible to
|
|
38
48
|
it.
|
|
39
49
|
|
|
@@ -132,7 +142,7 @@ leaves the box
|
|
|
132
142
|
|
|
133
143
|
```
|
|
134
144
|
$ crapkit --version
|
|
135
|
-
crapkit 0.
|
|
145
|
+
crapkit 0.6.0
|
|
136
146
|
```
|
|
137
147
|
|
|
138
148
|
`python -m crapkit` works identically to the console script and is what to use from a
|
|
@@ -376,7 +386,7 @@ crapkit ships a `.pre-commit-hooks.yaml` declaring `id: crapkit-gate`. In your
|
|
|
376
386
|
repos:
|
|
377
387
|
- repo: https://github.com/JeanFrancoisGagne/crapkit
|
|
378
388
|
# crapkit's release step rewrites this line to the tag it just cut
|
|
379
|
-
rev: v0.
|
|
389
|
+
rev: v0.6.0
|
|
380
390
|
hooks:
|
|
381
391
|
- id: crapkit-gate
|
|
382
392
|
```
|
|
@@ -484,7 +494,7 @@ to a workflow, and every input has a default:
|
|
|
484
494
|
- uses: actions/checkout@v4
|
|
485
495
|
with:
|
|
486
496
|
fetch-depth: 0
|
|
487
|
-
- uses: JeanFrancoisGagne/crapkit@v0.
|
|
497
|
+
- uses: JeanFrancoisGagne/crapkit@v0.6.0
|
|
488
498
|
```
|
|
489
499
|
|
|
490
500
|
The whole job those four lines sit in:
|
|
@@ -504,7 +514,7 @@ jobs:
|
|
|
504
514
|
with:
|
|
505
515
|
python-version: "3.12" # the interpreter the install below lands in
|
|
506
516
|
- run: pip install -e ".[dev]" # whatever your lanes need to run
|
|
507
|
-
- uses: JeanFrancoisGagne/crapkit@v0.
|
|
517
|
+
- uses: JeanFrancoisGagne/crapkit@v0.6.0
|
|
508
518
|
with:
|
|
509
519
|
gate: "false"
|
|
510
520
|
```
|
|
@@ -726,7 +736,7 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
|
|
|
726
736
|
| `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. |
|
|
727
737
|
| `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. |
|
|
728
738
|
| `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. |
|
|
729
|
-
| `mcp` | A dependency-free stdio MCP server (newline JSON-RPC 2.0) exposing
|
|
739
|
+
| `mcp` | A dependency-free stdio MCP server (newline JSON-RPC 2.0) exposing twelve read-only tools named `verb_noun`, each with a title and a documented output schema. 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). |
|
|
730
740
|
|
|
731
741
|
## Reading the output
|
|
732
742
|
|
|
@@ -1246,6 +1256,11 @@ dropped it once `classify` scored under the ceiling, rewriting the tracked
|
|
|
1246
1256
|
A verify may also print `warning: N changed line(s) have no coverage` above its verdict;
|
|
1247
1257
|
that block is advisory unless `diff_uncovered_max` is set
|
|
1248
1258
|
([docs/configuration.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/configuration.md)).
|
|
1259
|
+
It prints `warning: N function(s) over the ceiling carry no ratchet mark` when the tree
|
|
1260
|
+
holds debt `ratchet seed` never signed: the gate judges touched functions only and the
|
|
1261
|
+
ratchet check compares marks only, so coverage loss on such a function would pass unseen.
|
|
1262
|
+
The count is `unmarked_over_target` in `--json`, fires no exit code, and is zero on a repo
|
|
1263
|
+
with no debt.
|
|
1249
1264
|
|
|
1250
1265
|
## Documentation
|
|
1251
1266
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "crapkit"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.6.0"
|
|
8
8
|
description = "Scores every function on complexity times uncovered risk, ranks the worst, and blocks commits that add more."
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""crapkit: deterministic CRAP-score framework."""
|
|
2
|
-
__version__ = "0.
|
|
2
|
+
__version__ = "0.6.0"
|
|
@@ -483,9 +483,13 @@ def _verify_attribution(verdict) -> dict:
|
|
|
483
483
|
|
|
484
484
|
|
|
485
485
|
def _verify_result(verdict, overridden, run_id: int, baseline: dict, commit: str, ranges,
|
|
486
|
-
uncovered: list, diff_uncovered_max: int | None
|
|
486
|
+
uncovered: list, diff_uncovered_max: int | None,
|
|
487
|
+
unmarked_over_target: int) -> dict:
|
|
487
488
|
"""`diff_uncovered_max` travels with the count it judges: a reader of exit 9
|
|
488
|
-
(the Action's comment) can say which ceiling the lines went over.
|
|
489
|
+
(the Action's comment) can say which ceiling the lines went over.
|
|
490
|
+
`unmarked_over_target` is the standing debt no mark covers (see
|
|
491
|
+
`_warn_standing_debt`); it fires no exit code and is the one number that
|
|
492
|
+
says how much of the tree the ratchet is not holding."""
|
|
489
493
|
return {
|
|
490
494
|
"ok": verdict.ok,
|
|
491
495
|
"run_id": run_id,
|
|
@@ -500,10 +504,28 @@ def _verify_result(verdict, overridden, run_id: int, baseline: dict, commit: str
|
|
|
500
504
|
"diff_uncovered_count": len(uncovered),
|
|
501
505
|
"diff_uncovered": [{"path": p, "line": ln} for p, ln in uncovered[:50]],
|
|
502
506
|
"diff_uncovered_max": diff_uncovered_max,
|
|
507
|
+
"unmarked_over_target": unmarked_over_target,
|
|
503
508
|
**_verify_attribution(verdict),
|
|
504
509
|
}
|
|
505
510
|
|
|
506
511
|
|
|
512
|
+
def _warn_standing_debt(unmarked: list) -> None:
|
|
513
|
+
"""One stderr line for the over-ceiling functions no mark covers.
|
|
514
|
+
|
|
515
|
+
The gate never looks at an untouched function and the ratchet check compares
|
|
516
|
+
marks only, so a rise on these (coverage loss included) reaches no finding;
|
|
517
|
+
docs/ratchet.md says "seed once, early" for exactly this, and a header-only
|
|
518
|
+
marks file cannot say whether seed ever ran. stderr, because
|
|
519
|
+
`--json` prints one object on stdout. Silent at zero, which is the state
|
|
520
|
+
of a repo with no debt and of one seeded in full.
|
|
521
|
+
"""
|
|
522
|
+
if not unmarked:
|
|
523
|
+
return
|
|
524
|
+
print(f"warning: {len(unmarked)} function(s) over the ceiling carry no ratchet mark, so a "
|
|
525
|
+
f"rise on them (coverage loss included) passes unseen; record them with "
|
|
526
|
+
f"`{_self()} ratchet seed`", file=sys.stderr)
|
|
527
|
+
|
|
528
|
+
|
|
507
529
|
def _warn_diff_uncovered(uncovered: list) -> None:
|
|
508
530
|
if not uncovered:
|
|
509
531
|
return
|
|
@@ -576,7 +598,7 @@ def cmd_verify(args: argparse.Namespace) -> int:
|
|
|
576
598
|
from ..diffparse import changed_ranges
|
|
577
599
|
from ..gitio import GitFacts, diff_since
|
|
578
600
|
from ..uncovered import missing_by_path
|
|
579
|
-
from ..verify import diff_uncovered, evaluate
|
|
601
|
+
from ..verify import diff_uncovered, evaluate, unmarked_over_ceiling
|
|
580
602
|
|
|
581
603
|
root = _command_root(args.repo)
|
|
582
604
|
cfg = _load_repo_config(root)
|
|
@@ -616,6 +638,8 @@ def cmd_verify(args: argparse.Namespace) -> int:
|
|
|
616
638
|
# the whole cost of the post-commit verify on an unchanged tree.
|
|
617
639
|
uncovered = diff_uncovered(ranges, missing_by_path(root, cfg)) if ranges else []
|
|
618
640
|
_warn_diff_uncovered(uncovered)
|
|
641
|
+
unmarked = unmarked_over_ceiling(scored, ratchet, cfg.target, cfg.scope_targets)
|
|
642
|
+
_warn_standing_debt(unmarked)
|
|
619
643
|
breach = _diff_cover_breach(cfg, uncovered)
|
|
620
644
|
if breach:
|
|
621
645
|
verdict = verdict._replace(ok=False) # a breached run never advances the baseline
|
|
@@ -629,7 +653,7 @@ def cmd_verify(args: argparse.Namespace) -> int:
|
|
|
629
653
|
|
|
630
654
|
_report_verify(args.json,
|
|
631
655
|
{**_verify_result(verdict, overridden, run_id, baseline, commit, ranges,
|
|
632
|
-
uncovered, cfg.diff_uncovered_max),
|
|
656
|
+
uncovered, cfg.diff_uncovered_max, len(unmarked)),
|
|
633
657
|
**_receipt(tool_versions, ratchet_sha256, changes)},
|
|
634
658
|
verdict, overridden, cfg.ratchet_file)
|
|
635
659
|
_refuse_override(verdict, args.override)
|