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.
@@ -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.