riskratchet 0.1.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.
- riskratchet-0.1.0/.gitignore +225 -0
- riskratchet-0.1.0/LICENSE +21 -0
- riskratchet-0.1.0/PKG-INFO +75 -0
- riskratchet-0.1.0/PLAN.md +399 -0
- riskratchet-0.1.0/README.md +27 -0
- riskratchet-0.1.0/TODO.md +155 -0
- riskratchet-0.1.0/pyproject.toml +107 -0
- riskratchet-0.1.0/src/riskratchet/__init__.py +50 -0
- riskratchet-0.1.0/src/riskratchet/analysis.py +195 -0
- riskratchet-0.1.0/src/riskratchet/baseline.py +171 -0
- riskratchet-0.1.0/src/riskratchet/cli.py +264 -0
- riskratchet-0.1.0/src/riskratchet/complexity.py +82 -0
- riskratchet-0.1.0/src/riskratchet/coverage.py +144 -0
- riskratchet-0.1.0/src/riskratchet/engine.py +114 -0
- riskratchet-0.1.0/src/riskratchet/git.py +66 -0
- riskratchet-0.1.0/src/riskratchet/models.py +153 -0
- riskratchet-0.1.0/src/riskratchet/reporting.py +301 -0
- riskratchet-0.1.0/src/riskratchet/scoring.py +140 -0
- riskratchet-0.1.0/tests/test_analysis.py +147 -0
- riskratchet-0.1.0/tests/test_baseline.py +141 -0
- riskratchet-0.1.0/tests/test_cli.py +158 -0
- riskratchet-0.1.0/tests/test_complexity.py +133 -0
- riskratchet-0.1.0/tests/test_coverage.py +98 -0
- riskratchet-0.1.0/tests/test_engine.py +92 -0
- riskratchet-0.1.0/tests/test_scoring.py +177 -0
- riskratchet-0.1.0/uv.lock +843 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
coverage.json
|
|
49
|
+
*.cover
|
|
50
|
+
*.py.cover
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
cover/
|
|
54
|
+
|
|
55
|
+
# riskratchet artifacts
|
|
56
|
+
.riskratchet.json
|
|
57
|
+
|
|
58
|
+
# Local-only operational scripts (not for the package)
|
|
59
|
+
scripts/
|
|
60
|
+
|
|
61
|
+
# Translations
|
|
62
|
+
*.mo
|
|
63
|
+
*.pot
|
|
64
|
+
|
|
65
|
+
# Django stuff:
|
|
66
|
+
*.log
|
|
67
|
+
local_settings.py
|
|
68
|
+
db.sqlite3
|
|
69
|
+
db.sqlite3-journal
|
|
70
|
+
|
|
71
|
+
# Flask stuff:
|
|
72
|
+
instance/
|
|
73
|
+
.webassets-cache
|
|
74
|
+
|
|
75
|
+
# Scrapy stuff:
|
|
76
|
+
.scrapy
|
|
77
|
+
|
|
78
|
+
# Sphinx documentation
|
|
79
|
+
docs/_build/
|
|
80
|
+
|
|
81
|
+
# PyBuilder
|
|
82
|
+
.pybuilder/
|
|
83
|
+
target/
|
|
84
|
+
|
|
85
|
+
# Jupyter Notebook
|
|
86
|
+
.ipynb_checkpoints
|
|
87
|
+
|
|
88
|
+
# IPython
|
|
89
|
+
profile_default/
|
|
90
|
+
ipython_config.py
|
|
91
|
+
|
|
92
|
+
# pyenv
|
|
93
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
94
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
95
|
+
# .python-version
|
|
96
|
+
|
|
97
|
+
# pipenv
|
|
98
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
99
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
100
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
101
|
+
# install all needed dependencies.
|
|
102
|
+
# Pipfile.lock
|
|
103
|
+
|
|
104
|
+
# UV
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
106
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
107
|
+
# commonly ignored for libraries.
|
|
108
|
+
# uv.lock
|
|
109
|
+
|
|
110
|
+
# poetry
|
|
111
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
112
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
113
|
+
# commonly ignored for libraries.
|
|
114
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
115
|
+
# poetry.lock
|
|
116
|
+
# poetry.toml
|
|
117
|
+
|
|
118
|
+
# pdm
|
|
119
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
120
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
121
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
122
|
+
# pdm.lock
|
|
123
|
+
# pdm.toml
|
|
124
|
+
.pdm-python
|
|
125
|
+
.pdm-build/
|
|
126
|
+
|
|
127
|
+
# pixi
|
|
128
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
129
|
+
# pixi.lock
|
|
130
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
131
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
132
|
+
.pixi
|
|
133
|
+
|
|
134
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
135
|
+
__pypackages__/
|
|
136
|
+
|
|
137
|
+
# Celery stuff
|
|
138
|
+
celerybeat-schedule
|
|
139
|
+
celerybeat.pid
|
|
140
|
+
|
|
141
|
+
# Redis
|
|
142
|
+
*.rdb
|
|
143
|
+
*.aof
|
|
144
|
+
*.pid
|
|
145
|
+
|
|
146
|
+
# RabbitMQ
|
|
147
|
+
mnesia/
|
|
148
|
+
rabbitmq/
|
|
149
|
+
rabbitmq-data/
|
|
150
|
+
|
|
151
|
+
# ActiveMQ
|
|
152
|
+
activemq-data/
|
|
153
|
+
|
|
154
|
+
# SageMath parsed files
|
|
155
|
+
*.sage.py
|
|
156
|
+
|
|
157
|
+
# Environments
|
|
158
|
+
.env
|
|
159
|
+
.envrc
|
|
160
|
+
.venv
|
|
161
|
+
env/
|
|
162
|
+
venv/
|
|
163
|
+
ENV/
|
|
164
|
+
env.bak/
|
|
165
|
+
venv.bak/
|
|
166
|
+
|
|
167
|
+
# Spyder project settings
|
|
168
|
+
.spyderproject
|
|
169
|
+
.spyproject
|
|
170
|
+
|
|
171
|
+
# Rope project settings
|
|
172
|
+
.ropeproject
|
|
173
|
+
|
|
174
|
+
# mkdocs documentation
|
|
175
|
+
/site
|
|
176
|
+
|
|
177
|
+
# mypy
|
|
178
|
+
.mypy_cache/
|
|
179
|
+
.dmypy.json
|
|
180
|
+
dmypy.json
|
|
181
|
+
|
|
182
|
+
# Pyre type checker
|
|
183
|
+
.pyre/
|
|
184
|
+
|
|
185
|
+
# pytype static type analyzer
|
|
186
|
+
.pytype/
|
|
187
|
+
|
|
188
|
+
# Cython debug symbols
|
|
189
|
+
cython_debug/
|
|
190
|
+
|
|
191
|
+
# PyCharm
|
|
192
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
193
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
194
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
195
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
196
|
+
# .idea/
|
|
197
|
+
|
|
198
|
+
# Abstra
|
|
199
|
+
# Abstra is an AI-powered process automation framework.
|
|
200
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
201
|
+
# Learn more at https://abstra.io/docs
|
|
202
|
+
.abstra/
|
|
203
|
+
|
|
204
|
+
# Visual Studio Code
|
|
205
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
206
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
207
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
208
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
209
|
+
# .vscode/
|
|
210
|
+
# Temporary file for partial code execution
|
|
211
|
+
tempCodeRunnerFile.py
|
|
212
|
+
|
|
213
|
+
# Ruff stuff:
|
|
214
|
+
.ruff_cache/
|
|
215
|
+
|
|
216
|
+
# PyPI configuration file
|
|
217
|
+
.pypirc
|
|
218
|
+
|
|
219
|
+
# Marimo
|
|
220
|
+
marimo/_static/
|
|
221
|
+
marimo/_lsp/
|
|
222
|
+
__marimo__/
|
|
223
|
+
|
|
224
|
+
# Streamlit
|
|
225
|
+
.streamlit/secrets.toml
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kayhan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: riskratchet
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A maintainability ratchet for AI-assisted Python.
|
|
5
|
+
Project-URL: Homepage, https://github.com/KayhanB21/riskratchet
|
|
6
|
+
Project-URL: Source, https://github.com/KayhanB21/riskratchet
|
|
7
|
+
Author-email: Kayhan Babaee <me@kayhan.dev>
|
|
8
|
+
License: MIT License
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2026 Kayhan
|
|
11
|
+
|
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
in the Software without restriction, including without limitation the rights
|
|
15
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
furnished to do so, subject to the following conditions:
|
|
18
|
+
|
|
19
|
+
The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
copies or substantial portions of the Software.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
SOFTWARE.
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Keywords: code-quality,complexity,coverage,crap,maintainability
|
|
31
|
+
Classifier: Development Status :: 3 - Alpha
|
|
32
|
+
Classifier: Intended Audience :: Developers
|
|
33
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
34
|
+
Classifier: Programming Language :: Python :: 3
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
39
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
40
|
+
Classifier: Typing :: Typed
|
|
41
|
+
Requires-Python: >=3.10
|
|
42
|
+
Requires-Dist: coverage>=7.4
|
|
43
|
+
Requires-Dist: radon>=6.0.1
|
|
44
|
+
Requires-Dist: rich>=13.7
|
|
45
|
+
Requires-Dist: tomli>=2.0; python_version < '3.11'
|
|
46
|
+
Requires-Dist: typer>=0.12
|
|
47
|
+
Description-Content-Type: text/markdown
|
|
48
|
+
|
|
49
|
+
# riskratchet
|
|
50
|
+
|
|
51
|
+
A maintainability ratchet for AI-assisted Python.
|
|
52
|
+
|
|
53
|
+
riskratchet computes a function-level risk score from coverage gaps,
|
|
54
|
+
cyclomatic complexity, churn, public surface, and sprawl signals, then fails
|
|
55
|
+
when a change pushes risk upward without adding tests. Use it as a CLI, in
|
|
56
|
+
pre-commit, or in CI so AI-assisted commits can land working code without
|
|
57
|
+
ratcheting up untested complexity.
|
|
58
|
+
|
|
59
|
+
## Quickstart
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
uvx riskratchet scan src --coverage coverage.json
|
|
63
|
+
uvx riskratchet baseline src --coverage coverage.json --output .riskratchet.json
|
|
64
|
+
uvx riskratchet check src --coverage coverage.json --baseline .riskratchet.json
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Local development
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
uv sync
|
|
71
|
+
uv run pytest
|
|
72
|
+
uv run riskratchet scan src --coverage coverage.json
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This package is in early development. See PLAN.md for the v1 roadmap.
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
# riskratchet Package Plan
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
Build `riskratchet` as a Python maintainability ratchet for AI-assisted development: agents can change code, but CI/pre-commit/pytest should fail when a change increases untested complexity or maintainability risk.
|
|
6
|
+
|
|
7
|
+
V1 will be a standalone CLI with pre-commit and pytest integrations. The core will compute a full function-level risk score using complexity, coverage gaps, churn, public surface area, and sprawl signals. It will also expose the classic CRAP score for familiarity, but the product framing is broader: "do not let AI-assisted changes ratchet risk upward."
|
|
8
|
+
|
|
9
|
+
Use **uv as the package manager and developer workflow**. The repo should commit `uv.lock` for reproducible development and CI.
|
|
10
|
+
|
|
11
|
+
## Key Design
|
|
12
|
+
|
|
13
|
+
- Package name: `riskratchet`
|
|
14
|
+
- Tagline: `A maintainability ratchet for AI-assisted Python.`
|
|
15
|
+
- Python support: `>=3.10`
|
|
16
|
+
- Packaging: `pyproject.toml` with `hatchling`
|
|
17
|
+
- Package/dependency workflow: `uv`
|
|
18
|
+
- Commit:
|
|
19
|
+
- `pyproject.toml`
|
|
20
|
+
- `uv.lock`
|
|
21
|
+
- `.python-version` only if the project wants to pin local dev Python
|
|
22
|
+
- Runtime dependencies:
|
|
23
|
+
- `radon` for cyclomatic complexity
|
|
24
|
+
- `coverage` JSON input compatibility, parsed directly
|
|
25
|
+
- `typer` for CLI
|
|
26
|
+
- `rich` for terminal output
|
|
27
|
+
- Dev dependencies:
|
|
28
|
+
- `pytest`
|
|
29
|
+
- `pytest-cov`
|
|
30
|
+
- `ruff`
|
|
31
|
+
- `mypy`
|
|
32
|
+
- `pre-commit`
|
|
33
|
+
- `hypothesis`
|
|
34
|
+
|
|
35
|
+
Developer commands:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
uv sync
|
|
39
|
+
uv run riskratchet --help
|
|
40
|
+
uv run pytest
|
|
41
|
+
uv run ruff check .
|
|
42
|
+
uv run mypy src tests
|
|
43
|
+
uv build
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Public commands:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
riskratchet scan src --coverage coverage.json
|
|
50
|
+
riskratchet baseline src --coverage coverage.json --output .riskratchet.json
|
|
51
|
+
riskratchet check src --coverage coverage.json --baseline .riskratchet.json
|
|
52
|
+
riskratchet explain src/pkg/file.py::function_name --coverage coverage.json
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
V1 outputs:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
--format table
|
|
59
|
+
--format json
|
|
60
|
+
--format markdown
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
V1 integrations:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pytest --riskratchet --riskratchet-baseline .riskratchet.json
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```yaml
|
|
70
|
+
repos:
|
|
71
|
+
- repo: https://github.com/KayhanB21/riskratchet
|
|
72
|
+
rev: v0.1.0
|
|
73
|
+
hooks:
|
|
74
|
+
- id: riskratchet
|
|
75
|
+
args: ["check", "src", "--coverage", "coverage.json", "--baseline", ".riskratchet.json"]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Scoring Model
|
|
79
|
+
|
|
80
|
+
Analyze at function/method level. Each finding includes file, qualified function name, line span, risk score, CRAP score, component scores, and concrete remediation hints.
|
|
81
|
+
|
|
82
|
+
Core inputs:
|
|
83
|
+
|
|
84
|
+
- Function spans from Python AST
|
|
85
|
+
- Cyclomatic complexity from `radon`
|
|
86
|
+
- Coverage from `coverage json`
|
|
87
|
+
- Branch coverage mapped to function line spans
|
|
88
|
+
- Git churn from local git history when available
|
|
89
|
+
- Public surface detection from exported/importable names and underscore naming
|
|
90
|
+
- File sprawl from file length and function length
|
|
91
|
+
|
|
92
|
+
For each function, compute:
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
risk_score =
|
|
96
|
+
0.30 * coverage_gap_score +
|
|
97
|
+
0.25 * structural_complexity_score +
|
|
98
|
+
0.15 * branch_gap_score +
|
|
99
|
+
0.10 * churn_score +
|
|
100
|
+
0.10 * public_surface_score +
|
|
101
|
+
0.10 * sprawl_score
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Also compute classic CRAP:
|
|
105
|
+
|
|
106
|
+
```text
|
|
107
|
+
CRAP = CC^2 * (1 - line_coverage)^3 + CC
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Default severity:
|
|
111
|
+
|
|
112
|
+
```text
|
|
113
|
+
0-24 low
|
|
114
|
+
25-49 medium
|
|
115
|
+
50-74 high
|
|
116
|
+
75-100 critical
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Default check behavior:
|
|
120
|
+
|
|
121
|
+
- `scan`: never fails; reports current risk.
|
|
122
|
+
- `baseline`: writes full baseline JSON.
|
|
123
|
+
- `check`: fails if any function's risk increases beyond tolerance, or if a new function appears above the configured high-risk threshold.
|
|
124
|
+
- Default tolerance: `+5` risk points to avoid noise.
|
|
125
|
+
- Default new-function threshold: `50`.
|
|
126
|
+
- Existing high-risk code is allowed if it does not get worse.
|
|
127
|
+
|
|
128
|
+
Measured thermo-nuclear signals in v1:
|
|
129
|
+
|
|
130
|
+
- File over 1000 lines
|
|
131
|
+
- Function over 80 logical lines
|
|
132
|
+
- High nesting depth
|
|
133
|
+
- High boolean-condition density
|
|
134
|
+
- Many exception handlers or returns
|
|
135
|
+
- Churn-heavy functions
|
|
136
|
+
- Public functions with weak coverage
|
|
137
|
+
- Complex functions with missing branch coverage
|
|
138
|
+
|
|
139
|
+
Keep subjective "strict review" prose out of the numeric score in v1. Use direct remediation text like:
|
|
140
|
+
|
|
141
|
+
```text
|
|
142
|
+
High risk because complexity=14, branch coverage=38%, churn=7 commits.
|
|
143
|
+
Add tests for missing branches or split this function before changing it further.
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Implementation Breakdown
|
|
147
|
+
|
|
148
|
+
### Phase 1: uv Scaffold And Core Contracts
|
|
149
|
+
|
|
150
|
+
Create a typed Python package using uv:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
uv init --package --name riskratchet
|
|
154
|
+
uv add radon coverage typer rich
|
|
155
|
+
uv add --dev pytest pytest-cov ruff mypy pre-commit hypothesis
|
|
156
|
+
uv lock
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Use this project layout:
|
|
160
|
+
|
|
161
|
+
```text
|
|
162
|
+
src/riskratchet/
|
|
163
|
+
__init__.py
|
|
164
|
+
cli.py
|
|
165
|
+
analysis.py
|
|
166
|
+
coverage.py
|
|
167
|
+
complexity.py
|
|
168
|
+
git.py
|
|
169
|
+
scoring.py
|
|
170
|
+
baseline.py
|
|
171
|
+
reporting.py
|
|
172
|
+
pytest_plugin.py
|
|
173
|
+
tests/
|
|
174
|
+
fixtures/
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Core boundaries:
|
|
178
|
+
|
|
179
|
+
- `analysis`: AST function discovery, file metrics, public surface detection
|
|
180
|
+
- `coverage`: parse `coverage.json` and map line/branch data to function spans
|
|
181
|
+
- `complexity`: wrap `radon` and normalize complexity signals
|
|
182
|
+
- `git`: optional churn provider with a no-git fallback
|
|
183
|
+
- `scoring`: pure risk and CRAP calculations
|
|
184
|
+
- `baseline`: read/write/compare baseline JSON
|
|
185
|
+
- `reporting`: table, JSON, and markdown renderers
|
|
186
|
+
- `cli`: Typer commands only; no business logic
|
|
187
|
+
- `pytest_plugin`: pytest entrypoint that delegates to the same core check path
|
|
188
|
+
|
|
189
|
+
Core dataclasses:
|
|
190
|
+
|
|
191
|
+
- `FunctionId`
|
|
192
|
+
- `FunctionSpan`
|
|
193
|
+
- `CoverageStats`
|
|
194
|
+
- `ComplexityStats`
|
|
195
|
+
- `RiskComponents`
|
|
196
|
+
- `FunctionRisk`
|
|
197
|
+
- `RiskReport`
|
|
198
|
+
- `Baseline`
|
|
199
|
+
- `Regression`
|
|
200
|
+
|
|
201
|
+
All scoring functions must be pure and deterministic.
|
|
202
|
+
|
|
203
|
+
### Phase 2: CLI
|
|
204
|
+
|
|
205
|
+
Implement:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
riskratchet scan PATH...
|
|
209
|
+
riskratchet baseline PATH...
|
|
210
|
+
riskratchet check PATH...
|
|
211
|
+
riskratchet explain TARGET
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Required options:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
--coverage coverage.json
|
|
218
|
+
--config pyproject.toml
|
|
219
|
+
--format table|json|markdown
|
|
220
|
+
--output path
|
|
221
|
+
--fail-new-above 50
|
|
222
|
+
--fail-regression-above 5
|
|
223
|
+
--include PATTERN
|
|
224
|
+
--exclude PATTERN
|
|
225
|
+
--no-git
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Config under `pyproject.toml`:
|
|
229
|
+
|
|
230
|
+
```toml
|
|
231
|
+
[tool.riskratchet]
|
|
232
|
+
paths = ["src"]
|
|
233
|
+
coverage = "coverage.json"
|
|
234
|
+
baseline = ".riskratchet.json"
|
|
235
|
+
fail_new_above = 50
|
|
236
|
+
fail_regression_above = 5
|
|
237
|
+
exclude = ["tests/**", "migrations/**", "*/generated/**"]
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Phase 3: Integrations
|
|
241
|
+
|
|
242
|
+
Pre-commit:
|
|
243
|
+
|
|
244
|
+
- Add `.pre-commit-hooks.yaml`
|
|
245
|
+
- Hook runs `riskratchet check`
|
|
246
|
+
- Document that users should generate coverage before the hook in CI, or use pre-commit mainly for local ratchet checks against an existing coverage artifact.
|
|
247
|
+
|
|
248
|
+
Pytest:
|
|
249
|
+
|
|
250
|
+
- Add a thin pytest plugin.
|
|
251
|
+
- It should not duplicate the CLI logic.
|
|
252
|
+
- It should read pytest/coverage output after tests finish, run the same core check, and fail pytest on regressions.
|
|
253
|
+
- Keep plugin options minimal:
|
|
254
|
+
- `--riskratchet`
|
|
255
|
+
- `--riskratchet-baseline`
|
|
256
|
+
- `--riskratchet-fail-new-above`
|
|
257
|
+
- `--riskratchet-fail-regression-above`
|
|
258
|
+
|
|
259
|
+
### Phase 4: Docs And Post Support
|
|
260
|
+
|
|
261
|
+
README must include:
|
|
262
|
+
|
|
263
|
+
- Why CRAP alone is useful but incomplete
|
|
264
|
+
- Why AI-agent workflows need a maintainability ratchet
|
|
265
|
+
- Quickstart using uv:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
uvx riskratchet scan src --coverage coverage.json
|
|
269
|
+
uvx riskratchet baseline src --coverage coverage.json --output .riskratchet.json
|
|
270
|
+
uvx riskratchet check src --coverage coverage.json --baseline .riskratchet.json
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
- Local development using uv:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
uv sync
|
|
277
|
+
uv run pytest
|
|
278
|
+
uv run riskratchet scan src --coverage coverage.json
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
- pre-commit example
|
|
282
|
+
- pytest example
|
|
283
|
+
- JSON and markdown output examples
|
|
284
|
+
- Comparison table versus `pytest-crap`, `radon`, `coverage.py`, and `xenon`
|
|
285
|
+
|
|
286
|
+
Post angle:
|
|
287
|
+
|
|
288
|
+
```text
|
|
289
|
+
Let Agents Write Code. Don't Let Them Ratchet Up Risk.
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
The post should show:
|
|
293
|
+
|
|
294
|
+
- An AI agent adds working code.
|
|
295
|
+
- Tests pass.
|
|
296
|
+
- Coverage percentage looks acceptable.
|
|
297
|
+
- `riskratchet` fails because branch/path risk or maintainability risk increased.
|
|
298
|
+
- The fix is either adding meaningful tests or simplifying the function.
|
|
299
|
+
|
|
300
|
+
## Test Plan
|
|
301
|
+
|
|
302
|
+
Testability is a first-class design constraint. The implementation should be built around pure functions and fixture projects.
|
|
303
|
+
|
|
304
|
+
Unit tests:
|
|
305
|
+
|
|
306
|
+
- CRAP formula matches known examples.
|
|
307
|
+
- Risk score component weighting is deterministic.
|
|
308
|
+
- Severity thresholds classify correctly.
|
|
309
|
+
- Function span discovery handles:
|
|
310
|
+
- nested functions
|
|
311
|
+
- methods
|
|
312
|
+
- async functions
|
|
313
|
+
- decorators
|
|
314
|
+
- class methods/static methods
|
|
315
|
+
- multiline signatures
|
|
316
|
+
- Coverage mapping handles:
|
|
317
|
+
- fully covered functions
|
|
318
|
+
- uncovered functions
|
|
319
|
+
- partial line coverage
|
|
320
|
+
- missing branch coverage
|
|
321
|
+
- files absent from coverage JSON
|
|
322
|
+
- Complexity mapping handles:
|
|
323
|
+
- radon blocks matched to AST spans
|
|
324
|
+
- syntax errors reported cleanly
|
|
325
|
+
- unsupported files skipped with warnings
|
|
326
|
+
- Churn provider handles:
|
|
327
|
+
- normal git repo
|
|
328
|
+
- shallow/no-history repo
|
|
329
|
+
- no-git directory
|
|
330
|
+
- renamed/deleted files gracefully
|
|
331
|
+
- Baseline comparison handles:
|
|
332
|
+
- new risky function
|
|
333
|
+
- improved risky function
|
|
334
|
+
- worsened existing function
|
|
335
|
+
- deleted function
|
|
336
|
+
- changed line numbers with same qualified function name
|
|
337
|
+
- tolerance boundary exactly
|
|
338
|
+
|
|
339
|
+
CLI tests:
|
|
340
|
+
|
|
341
|
+
- `scan` exits 0.
|
|
342
|
+
- `baseline` writes stable JSON.
|
|
343
|
+
- `check` exits 0 with no regression.
|
|
344
|
+
- `check` exits non-zero with risk regression.
|
|
345
|
+
- `--format json` validates against snapshot/schema.
|
|
346
|
+
- `--format markdown` is stable enough for PR comments.
|
|
347
|
+
- Invalid coverage path gives actionable error.
|
|
348
|
+
- Invalid baseline gives actionable error.
|
|
349
|
+
|
|
350
|
+
Integration tests:
|
|
351
|
+
|
|
352
|
+
- Fixture package with pytest + coverage JSON.
|
|
353
|
+
- pre-commit hook smoke test using a small fixture repo.
|
|
354
|
+
- pytest plugin smoke test proving pytest exits non-zero on risk regression.
|
|
355
|
+
|
|
356
|
+
Property-style tests:
|
|
357
|
+
|
|
358
|
+
- Risk score is bounded 0-100.
|
|
359
|
+
- Increasing coverage cannot increase coverage-gap score.
|
|
360
|
+
- Increasing branch coverage cannot increase branch-gap score.
|
|
361
|
+
- Increasing complexity cannot decrease structural-complexity score.
|
|
362
|
+
- Baseline comparison is deterministic regardless of input ordering.
|
|
363
|
+
|
|
364
|
+
Golden fixtures:
|
|
365
|
+
|
|
366
|
+
- `simple_clean`
|
|
367
|
+
- `complex_uncovered`
|
|
368
|
+
- `covered_but_branchy`
|
|
369
|
+
- `large_file_sprawl`
|
|
370
|
+
- `public_api_regression`
|
|
371
|
+
- `agent_generated_spaghetti`
|
|
372
|
+
|
|
373
|
+
CI checks, all through uv:
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
uv sync --locked
|
|
377
|
+
uv run ruff check .
|
|
378
|
+
uv run mypy src tests
|
|
379
|
+
uv run pytest
|
|
380
|
+
uv run pytest --cov=riskratchet --cov-report=term-missing --cov-report=json
|
|
381
|
+
uv run riskratchet scan src --coverage coverage.json --format json
|
|
382
|
+
uv build
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Target internal coverage for `riskratchet`: at least 90%, with focused branch coverage around scoring and baseline comparison.
|
|
386
|
+
|
|
387
|
+
## Assumptions And Defaults
|
|
388
|
+
|
|
389
|
+
- V1 is named `riskratchet`.
|
|
390
|
+
- uv is the package manager and local dev workflow.
|
|
391
|
+
- `uv.lock` is committed.
|
|
392
|
+
- V1 is a standalone CLI first, but includes pre-commit and pytest integrations.
|
|
393
|
+
- V1 implements the full risk score, not pure CRAP.
|
|
394
|
+
- V1 keeps CRAP as a reported compatibility metric.
|
|
395
|
+
- V1 supports table, JSON, and markdown output. SARIF is deferred.
|
|
396
|
+
- V1 uses measured maintainability signals from the thermo-nuclear review idea, but does not ship subjective review prose as scoring logic.
|
|
397
|
+
- V1 uses local git churn when available and falls back cleanly when git is unavailable.
|
|
398
|
+
- V1 is strict about regressions, not existing legacy risk.
|
|
399
|
+
- V1 should be small, typed, and heavily tested before optimizing for broad framework support.
|