vibecodedshit 0.0.0-stage → 0.3.0
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.
- package/LICENSE +95 -0
- package/README.md +73 -2
- package/assets/fonts/NotoEmoji-OFL.txt +93 -0
- package/assets/fonts/NotoEmoji-Poop.ttf +0 -0
- package/assets/fonts/SpaceGrotesk-Bold.ttf +0 -0
- package/assets/fonts/SpaceGrotesk-OFL.txt +93 -0
- package/dist/bin.js +11 -0
- package/dist/card/previous.js +38 -0
- package/dist/card/render.js +71 -0
- package/dist/cli.js +138 -0
- package/dist/commands/card.js +69 -0
- package/dist/commands/common.js +1 -0
- package/dist/commands/diff.js +54 -0
- package/dist/commands/footer.js +28 -0
- package/dist/commands/report.js +49 -0
- package/dist/commands/route.js +62 -0
- package/dist/commands/submit.js +125 -0
- package/dist/commands/summary.js +77 -0
- package/dist/diff/compare.js +73 -0
- package/dist/diff/conditions.js +87 -0
- package/dist/diff/format.js +23 -0
- package/dist/exec/docker.js +158 -0
- package/dist/exec/findings.js +73 -0
- package/dist/exec/plan.js +50 -0
- package/dist/exec/reports.js +90 -0
- package/dist/exec/roots.js +49 -0
- package/dist/exec/run.js +182 -0
- package/dist/exec/steps/build.js +45 -0
- package/dist/exec/steps/coverage-install.js +12 -0
- package/dist/exec/steps/install.js +41 -0
- package/dist/exec/steps/knip.js +28 -0
- package/dist/exec/steps/lint.js +25 -0
- package/dist/exec/steps/lockfiles.js +25 -0
- package/dist/exec/steps/test.js +17 -0
- package/dist/exec/steps/typecheck.js +40 -0
- package/dist/exec/test-reports/coverage-py.js +23 -0
- package/dist/exec/test-reports/coverage-ranges.js +65 -0
- package/dist/exec/test-reports/coverage-summary.js +23 -0
- package/dist/exec/test-reports/jest-json.js +31 -0
- package/dist/exec/test-reports/junit.js +24 -0
- package/dist/exec/test-reports/mocha-json.js +18 -0
- package/dist/exec/test-reports/vitest-json.js +5 -0
- package/dist/exec/test-runners/detect.js +99 -0
- package/dist/exec/test-runners/index.js +6 -0
- package/dist/exec/test-runners/jest.js +27 -0
- package/dist/exec/test-runners/mocha.js +28 -0
- package/dist/exec/test-runners/node-test.js +24 -0
- package/dist/exec/test-runners/pytest.js +18 -0
- package/dist/exec/test-runners/script.js +48 -0
- package/dist/exec/test-runners/spec.js +1 -0
- package/dist/exec/test-runners/vitest.js +28 -0
- package/dist/exec/tests.js +120 -0
- package/dist/exec/types.js +1 -0
- package/dist/interactive/after-scan.js +121 -0
- package/dist/interactive/docker-setup.js +97 -0
- package/dist/interactive/prompt.js +17 -0
- package/dist/interactive/system.js +10 -0
- package/dist/manifest/package-json.js +36 -0
- package/dist/manifest/python.js +106 -0
- package/dist/measure/d3/index.js +45 -0
- package/dist/measure/d3/presets.js +100 -0
- package/dist/measure/d3/py/adapter.js +67 -0
- package/dist/measure/d3/py/config.js +80 -0
- package/dist/measure/d3/py/contracts.js +57 -0
- package/dist/measure/d3/py/domain.js +57 -0
- package/dist/measure/d3/py/graph.js +214 -0
- package/dist/measure/d3/py/interpreter.js +31 -0
- package/dist/measure/d3/py/layers.js +120 -0
- package/dist/measure/d3/py/roots.js +58 -0
- package/dist/measure/d3/py/types.js +1 -0
- package/dist/measure/d3/scc.js +64 -0
- package/dist/measure/d3/ts/adapter.js +371 -0
- package/dist/measure/d3/ts/classify.js +107 -0
- package/dist/measure/d3/ts/config.js +318 -0
- package/dist/measure/d3/ts/domain.js +23 -0
- package/dist/measure/d3/ts/rules.js +65 -0
- package/dist/measure/d3/types.js +5 -0
- package/dist/measure/deps/cvss.js +66 -0
- package/dist/measure/deps/declared.js +195 -0
- package/dist/measure/deps/files.js +40 -0
- package/dist/measure/deps/lockfiles.js +308 -0
- package/dist/measure/deps/osv.js +122 -0
- package/dist/measure/deps/registry.js +142 -0
- package/dist/measure/deps/resolved.js +78 -0
- package/dist/measure/deps/units.js +5 -0
- package/dist/measure/eslint/health-plugin.js +19 -0
- package/dist/measure/ops/docs-commands.js +73 -0
- package/dist/measure/ops/docs-resolve.js +217 -0
- package/dist/measure/ops/entry-points.js +49 -0
- package/dist/measure/ops/env-example.js +20 -0
- package/dist/measure/ops/env-reads.js +46 -0
- package/dist/measure/ops/runtime.js +73 -0
- package/dist/measure/p1-1.js +8 -0
- package/dist/measure/p1-2.js +8 -0
- package/dist/measure/p1-3.js +8 -0
- package/dist/measure/p1-4.js +41 -0
- package/dist/measure/p1-5.js +59 -0
- package/dist/measure/p2-1.js +8 -0
- package/dist/measure/p2-2.js +44 -0
- package/dist/measure/p2-3.js +81 -0
- package/dist/measure/p2-4.js +288 -0
- package/dist/measure/p2-5.js +8 -0
- package/dist/measure/p3-1.js +16 -0
- package/dist/measure/p3-2.js +26 -0
- package/dist/measure/p3-3.js +37 -0
- package/dist/measure/p3-4.js +26 -0
- package/dist/measure/p3-5.js +26 -0
- package/dist/measure/p4-1.js +184 -0
- package/dist/measure/p4-2.js +27 -0
- package/dist/measure/p4-3.js +106 -0
- package/dist/measure/p4-4.js +309 -0
- package/dist/measure/p5-1.js +117 -0
- package/dist/measure/p5-2.js +80 -0
- package/dist/measure/p5-3.js +21 -0
- package/dist/measure/p5-4.js +161 -0
- package/dist/measure/p5-5.js +158 -0
- package/dist/measure/p6-1.js +20 -0
- package/dist/measure/p6-2.js +20 -0
- package/dist/measure/p6-3.js +40 -0
- package/dist/measure/p6-4.js +31 -0
- package/dist/measure/p6-5.js +60 -0
- package/dist/measure/p6-6.js +54 -0
- package/dist/measure/py/typecheck-config.js +95 -0
- package/dist/measure/registry.js +31 -0
- package/dist/measure/safety/py-tests.js +124 -0
- package/dist/measure/safety/ts-barrels.js +68 -0
- package/dist/measure/safety/ts-cases.js +111 -0
- package/dist/measure/safety/ts-credits.js +101 -0
- package/dist/measure/safety/ts-imports.js +63 -0
- package/dist/measure/safety/ts-resolve.js +43 -0
- package/dist/measure/ts/directives.js +37 -0
- package/dist/measure/ts/exports.js +60 -0
- package/dist/measure/ts/tsconfig.js +179 -0
- package/dist/measure/types.js +1 -0
- package/dist/measure/units.js +41 -0
- package/dist/methodology/load.js +159 -0
- package/dist/methodology/schema.js +105 -0
- package/dist/python/deadcode.js +27 -0
- package/dist/python/env.js +13 -0
- package/dist/python/helper.js +24 -0
- package/dist/python/imports.js +40 -0
- package/dist/python/nox.js +16 -0
- package/dist/python/scope.js +35 -0
- package/dist/python/tests.js +19 -0
- package/dist/python/typing.js +27 -0
- package/dist/report/escape.js +5 -0
- package/dist/report/format.js +75 -0
- package/dist/report/render.js +213 -0
- package/dist/report/roadmap.js +58 -0
- package/dist/report/scares.js +83 -0
- package/dist/report/style.js +27 -0
- package/dist/report-only/hotspots.js +48 -0
- package/dist/route/check.js +37 -0
- package/dist/route/files.js +74 -0
- package/dist/route/order.js +22 -0
- package/dist/route/select.js +114 -0
- package/dist/route/write.js +13 -0
- package/dist/run/read.js +89 -0
- package/dist/run/recompute.js +19 -0
- package/dist/run/scan.js +196 -0
- package/dist/run/schema.js +1 -0
- package/dist/run/score-run.js +42 -0
- package/dist/run/write.js +4 -0
- package/dist/scope/build.js +173 -0
- package/dist/scope/packages.js +87 -0
- package/dist/scope/rules.js +21 -0
- package/dist/scope/tracked.js +34 -0
- package/dist/scope/ts-lines.js +63 -0
- package/dist/scope/types.js +7 -0
- package/dist/scoring/aggregate.js +38 -0
- package/dist/scoring/pool.js +65 -0
- package/dist/scoring/score.js +81 -0
- package/dist/scoring/what-if.js +38 -0
- package/dist/scripts/checks.js +53 -0
- package/dist/scripts/ci.js +139 -0
- package/dist/scripts/entry-points/find.js +31 -0
- package/dist/scripts/entry-points/index.js +18 -0
- package/dist/scripts/entry-points/justfile.js +62 -0
- package/dist/scripts/entry-points/makefile.js +66 -0
- package/dist/scripts/entry-points/nox.js +29 -0
- package/dist/scripts/entry-points/package-json.js +21 -0
- package/dist/scripts/entry-points/pyproject.js +73 -0
- package/dist/scripts/entry-points/tox.js +130 -0
- package/dist/scripts/expand.js +300 -0
- package/dist/scripts/triggers.js +151 -0
- package/dist/scripts/types.js +1 -0
- package/dist/submit/constants.js +10 -0
- package/dist/submit/payload.js +42 -0
- package/dist/submit/repo-identity.js +46 -0
- package/dist/submit/send.js +29 -0
- package/dist/tools/catalog.js +46 -0
- package/dist/tools/image.js +180 -0
- package/dist/tools/kit.js +71 -0
- package/dist/tools/knip.js +169 -0
- package/dist/tools/outcome.js +21 -0
- package/dist/tools/resolve.js +66 -0
- package/dist/tools/workcopy.js +27 -0
- package/dist/util/ini.js +37 -0
- package/dist/util/pep440.js +163 -0
- package/dist/util/toml.js +27 -0
- package/docker/Dockerfile +142 -0
- package/docker/bin/health-capture +5 -0
- package/docker/bin/health-coverage-install +5 -0
- package/docker/bin/health-eslint +66 -0
- package/docker/bin/health-pytest +10 -0
- package/docker/bin/health-tsc +10 -0
- package/docker/bin/health-venv +11 -0
- package/docker/config/eslint.config.mjs +4 -0
- package/docker/config/eslint.rules.mjs +65 -0
- package/docker/config/eslint.untyped.config.mjs +4 -0
- package/docker/config/knip.json +1 -0
- package/docker/config/ruff.toml +7 -0
- package/methodology/LICENSE +396 -0
- package/methodology/v1.0/METHODOLOGY.md +1176 -0
- package/methodology/v1.0/dimensions/1-safety-net.yaml +240 -0
- package/methodology/v1.0/dimensions/2-type-static-rigor.yaml +289 -0
- package/methodology/v1.0/dimensions/3-architecture-boundaries.yaml +266 -0
- package/methodology/v1.0/dimensions/4-complexity-maintainability.yaml +192 -0
- package/methodology/v1.0/dimensions/5-dependency-security-hygiene.yaml +223 -0
- package/methodology/v1.0/dimensions/6-operability-feedback-loop.yaml +279 -0
- package/methodology/v1.0/methodology.yaml +242 -0
- package/methodology/v1.0/presets/django.yaml +43 -0
- package/methodology/v1.0/presets/express.yaml +47 -0
- package/methodology/v1.0/presets/fastapi.yaml +52 -0
- package/methodology/v1.0/presets/generic.yaml +41 -0
- package/methodology/v1.0/presets/nextjs.yaml +50 -0
- package/methodology/v1.0/scope.yaml +115 -0
- package/package.json +61 -4
- package/python/dead_code_helper.py +265 -0
- package/python/env_helper.py +71 -0
- package/python/graph_helper.py +277 -0
- package/python/imports_helper.py +132 -0
- package/python/nox_helper.py +79 -0
- package/python/scope_helper.py +96 -0
- package/python/tests_helper.py +171 -0
- package/python/typing_helper.py +235 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
dimension:
|
|
2
|
+
id: 6
|
|
3
|
+
key: operability_feedback_loop
|
|
4
|
+
name: Operability & feedback loop
|
|
5
|
+
question: Can someone new set it up, check a change and know they are done?
|
|
6
|
+
agent_relevance: >-
|
|
7
|
+
An agent needs a command that tells it whether its change is correct,
|
|
8
|
+
and it needs to find that command without asking. A slow or missing
|
|
9
|
+
check means the agent stops early or guesses.
|
|
10
|
+
|
|
11
|
+
script_resolution: >-
|
|
12
|
+
Parameters 6.2, 6.4 and 6.6 follow package.json scripts, Makefile targets,
|
|
13
|
+
justfile recipes, tox and nox sessions, and pyproject.toml script tables up
|
|
14
|
+
to 3 levels deep. Recognised tools: typecheckers tsc, vue-tsc, pyright,
|
|
15
|
+
basedpyright, mypy; linters eslint, biome, oxlint, ruff, flake8, pylint;
|
|
16
|
+
test runners vitest, jest, mocha, node --test, pytest, python -m unittest,
|
|
17
|
+
tox, nox. The prefixes npx, bunx, pnpm exec, yarn, uv run, poetry run,
|
|
18
|
+
pdm run and python -m are removed before a command is matched.
|
|
19
|
+
|
|
20
|
+
parameters:
|
|
21
|
+
- id: "6.1"
|
|
22
|
+
key: install_and_build
|
|
23
|
+
name: Clean install and build
|
|
24
|
+
layer: execution
|
|
25
|
+
weight: 25
|
|
26
|
+
requires: []
|
|
27
|
+
pooling: unit_weighted
|
|
28
|
+
raw:
|
|
29
|
+
unit: level
|
|
30
|
+
definition: >-
|
|
31
|
+
Result of a clean install from the lockfile, followed by the build
|
|
32
|
+
step, in the pinned runner.
|
|
33
|
+
when_empty: 0
|
|
34
|
+
scoring:
|
|
35
|
+
method: levels
|
|
36
|
+
levels:
|
|
37
|
+
- level: install_fails
|
|
38
|
+
score: 0
|
|
39
|
+
descriptor: The project does not install from a clean checkout.
|
|
40
|
+
- level: build_fails
|
|
41
|
+
score: 40
|
|
42
|
+
descriptor: It installs, but the build fails.
|
|
43
|
+
- level: works
|
|
44
|
+
score: 100
|
|
45
|
+
descriptor: >-
|
|
46
|
+
It installs and builds from a clean checkout. A project with no
|
|
47
|
+
build step scores 100 when the install succeeds.
|
|
48
|
+
measure:
|
|
49
|
+
typescript_javascript: >-
|
|
50
|
+
Install: npm ci, pnpm install --frozen-lockfile, yarn install
|
|
51
|
+
--immutable or bun install --frozen-lockfile, chosen by lockfile;
|
|
52
|
+
npm install when there is no lockfile. Build: the build script in
|
|
53
|
+
package.json, when one exists.
|
|
54
|
+
python: >-
|
|
55
|
+
Install: uv sync --frozen, poetry install --sync, pdm sync, pipenv
|
|
56
|
+
sync, or pip install -r on the requirements files, chosen by
|
|
57
|
+
lockfile; pip install . when there is only a pyproject.toml. Build:
|
|
58
|
+
python manage.py check for Django projects; otherwise importing
|
|
59
|
+
each first-party top-level package.
|
|
60
|
+
not_applicable_when: null
|
|
61
|
+
rationale: >-
|
|
62
|
+
Everything in the execution layer depends on this step. A project that
|
|
63
|
+
does not install from a clean checkout depends on someone's machine.
|
|
64
|
+
sources: []
|
|
65
|
+
|
|
66
|
+
- id: "6.2"
|
|
67
|
+
key: one_command_verify
|
|
68
|
+
name: One-command verify
|
|
69
|
+
layer: static
|
|
70
|
+
weight: 15
|
|
71
|
+
requires: []
|
|
72
|
+
pooling: unit_weighted
|
|
73
|
+
raw:
|
|
74
|
+
unit: count of checks (0-3)
|
|
75
|
+
definition: >-
|
|
76
|
+
The largest number of the three checks (typecheck, lint, tests) that
|
|
77
|
+
a single entry point runs: one package.json script, Makefile target,
|
|
78
|
+
justfile recipe, tox or nox session, or pyproject script.
|
|
79
|
+
when_empty: 0
|
|
80
|
+
scoring:
|
|
81
|
+
method: levels
|
|
82
|
+
levels:
|
|
83
|
+
- level: "0"
|
|
84
|
+
score: 0
|
|
85
|
+
descriptor: No single command runs any check.
|
|
86
|
+
- level: "1"
|
|
87
|
+
score: 33
|
|
88
|
+
descriptor: One command runs one check.
|
|
89
|
+
- level: "2"
|
|
90
|
+
score: 66
|
|
91
|
+
descriptor: One command runs two checks.
|
|
92
|
+
- level: "3"
|
|
93
|
+
score: 100
|
|
94
|
+
descriptor: One command runs typecheck, lint and tests.
|
|
95
|
+
measure:
|
|
96
|
+
all_languages: Static resolution of entry points as described in script_resolution.
|
|
97
|
+
not_applicable_when: null
|
|
98
|
+
rationale: >-
|
|
99
|
+
"Run this and it tells you if you are done" is the most useful fact
|
|
100
|
+
an agent can learn about a repository. Without it, the agent has to
|
|
101
|
+
find and run each check separately, and often skips one.
|
|
102
|
+
sources: []
|
|
103
|
+
|
|
104
|
+
- id: "6.3"
|
|
105
|
+
key: feedback_loop_time
|
|
106
|
+
name: Feedback loop time
|
|
107
|
+
layer: execution
|
|
108
|
+
weight: 10
|
|
109
|
+
requires: [install, typecheck_run, lint_run, test_run]
|
|
110
|
+
pooling: unit_weighted
|
|
111
|
+
raw:
|
|
112
|
+
unit: seconds
|
|
113
|
+
definition: >-
|
|
114
|
+
Summed wall-clock time of the methodology's own typecheck (2.1),
|
|
115
|
+
lint (2.5) and test (1.1) runs on the pinned runner.
|
|
116
|
+
when_empty: 0
|
|
117
|
+
scoring:
|
|
118
|
+
method: piecewise_linear
|
|
119
|
+
anchors:
|
|
120
|
+
- raw: 60
|
|
121
|
+
score: 100
|
|
122
|
+
descriptor: A full check takes about a minute.
|
|
123
|
+
- raw: 180
|
|
124
|
+
score: 75
|
|
125
|
+
descriptor: A full check takes a few minutes.
|
|
126
|
+
- raw: 600
|
|
127
|
+
score: 50
|
|
128
|
+
descriptor: The ten-minute build.
|
|
129
|
+
- raw: 1200
|
|
130
|
+
score: 0
|
|
131
|
+
descriptor: Nobody runs the full check locally.
|
|
132
|
+
measure:
|
|
133
|
+
all_languages: Wall-clock time recorded by the orchestrator for the three steps.
|
|
134
|
+
not_applicable_when: null
|
|
135
|
+
rationale: >-
|
|
136
|
+
A slow check gets run less often, by people and by agents. This is
|
|
137
|
+
the only parameter whose raw value varies between identical runs, so
|
|
138
|
+
its weight is low and the runner hardware is fixed.
|
|
139
|
+
sources:
|
|
140
|
+
- "Beck, K. Extreme Programming Explained, 2nd ed. Addison-Wesley, 2004 (Ten-Minute Build)."
|
|
141
|
+
- "Fowler, M. Continuous Integration. https://martinfowler.com/articles/continuousIntegration.html"
|
|
142
|
+
|
|
143
|
+
- id: "6.4"
|
|
144
|
+
key: ci_runs_checks
|
|
145
|
+
name: CI runs the checks on pull requests
|
|
146
|
+
layer: static
|
|
147
|
+
weight: 20
|
|
148
|
+
requires: []
|
|
149
|
+
pooling: unit_weighted
|
|
150
|
+
raw:
|
|
151
|
+
unit: count of checks (0-3)
|
|
152
|
+
definition: >-
|
|
153
|
+
How many of the three checks (typecheck, lint, tests) CI runs on
|
|
154
|
+
pull requests or on every push. Checks may be spread across jobs
|
|
155
|
+
and workflows.
|
|
156
|
+
when_empty: 0
|
|
157
|
+
scoring:
|
|
158
|
+
method: levels
|
|
159
|
+
levels:
|
|
160
|
+
- level: "0"
|
|
161
|
+
score: 0
|
|
162
|
+
descriptor: CI runs no checks, or there is no CI.
|
|
163
|
+
- level: "1"
|
|
164
|
+
score: 33
|
|
165
|
+
descriptor: CI runs one check.
|
|
166
|
+
- level: "2"
|
|
167
|
+
score: 66
|
|
168
|
+
descriptor: CI runs two checks.
|
|
169
|
+
- level: "3"
|
|
170
|
+
score: 100
|
|
171
|
+
descriptor: CI runs typecheck, lint and tests on every pull request.
|
|
172
|
+
measure:
|
|
173
|
+
all_languages: >-
|
|
174
|
+
CI files as listed in 3.1. A workflow counts if it is triggered on
|
|
175
|
+
pull_request, pull_request_target, merge_request events, or on push
|
|
176
|
+
without a branch filter that limits it to the default branch.
|
|
177
|
+
Commands are resolved as described in script_resolution.
|
|
178
|
+
not_applicable_when: null
|
|
179
|
+
rationale: >-
|
|
180
|
+
Local checks are optional; CI checks are not. CI is what keeps the
|
|
181
|
+
score from dropping after the audit ends.
|
|
182
|
+
sources:
|
|
183
|
+
- "Fowler, M. Continuous Integration. https://martinfowler.com/articles/continuousIntegration.html"
|
|
184
|
+
|
|
185
|
+
- id: "6.5"
|
|
186
|
+
key: reproducible_environment
|
|
187
|
+
name: Reproducible environment
|
|
188
|
+
layer: static
|
|
189
|
+
weight: 15
|
|
190
|
+
requires: []
|
|
191
|
+
pooling: unit_weighted
|
|
192
|
+
raw:
|
|
193
|
+
unit: score
|
|
194
|
+
definition: Mean of two components.
|
|
195
|
+
when_empty: 0
|
|
196
|
+
scoring:
|
|
197
|
+
method: mean_of_components
|
|
198
|
+
components:
|
|
199
|
+
- key: runtime_pinned
|
|
200
|
+
name: Runtime version pinned
|
|
201
|
+
method: levels
|
|
202
|
+
levels:
|
|
203
|
+
- level: "no"
|
|
204
|
+
score: 0
|
|
205
|
+
- level: "yes"
|
|
206
|
+
score: 100
|
|
207
|
+
measure: >-
|
|
208
|
+
Any of .nvmrc, .node-version, .tool-versions, mise.toml,
|
|
209
|
+
engines.node in package.json, volta in package.json,
|
|
210
|
+
.python-version, requires-python in pyproject.toml, or
|
|
211
|
+
runtime.txt.
|
|
212
|
+
- key: env_documented
|
|
213
|
+
name: Environment variables documented
|
|
214
|
+
method: ratio
|
|
215
|
+
definition: >-
|
|
216
|
+
Variable names read in logic files that appear in an env example
|
|
217
|
+
file / all variable names read in logic files, times 100. No
|
|
218
|
+
variables read scores 100.
|
|
219
|
+
measure: >-
|
|
220
|
+
Example files: .env.example, .env.sample, .env.template,
|
|
221
|
+
.env.dist, example.env. Reads: process.env.X, process.env["X"],
|
|
222
|
+
destructuring from process.env, import.meta.env.X,
|
|
223
|
+
os.environ["X"], os.environ.get("X"), os.getenv("X"). Excluded
|
|
224
|
+
names: NODE_ENV, CI, HOME, PATH, PWD, TZ, NEXT_RUNTIME, and
|
|
225
|
+
names starting with VERCEL_, NETLIFY, RENDER_, RAILWAY_ or
|
|
226
|
+
GITHUB_.
|
|
227
|
+
measure:
|
|
228
|
+
all_languages: Static reads of the files and patterns listed in the components.
|
|
229
|
+
not_applicable_when: null
|
|
230
|
+
rationale: >-
|
|
231
|
+
A missing environment variable is a common reason a fresh checkout
|
|
232
|
+
does not start, and an agent cannot guess the names. Config read from the
|
|
233
|
+
environment, with the names documented, is the Twelve-Factor approach.
|
|
234
|
+
sources:
|
|
235
|
+
- "The Twelve-Factor App, III. Config. https://12factor.net/config"
|
|
236
|
+
|
|
237
|
+
- id: "6.6"
|
|
238
|
+
key: agent_instructions
|
|
239
|
+
name: Commands documented for agents
|
|
240
|
+
layer: static
|
|
241
|
+
weight: 15
|
|
242
|
+
requires: []
|
|
243
|
+
pooling: unit_weighted
|
|
244
|
+
raw:
|
|
245
|
+
unit: level
|
|
246
|
+
definition: >-
|
|
247
|
+
Whether the repository documents how to install, run and check it,
|
|
248
|
+
and whether the documented commands exist.
|
|
249
|
+
when_empty: 0
|
|
250
|
+
scoring:
|
|
251
|
+
method: levels
|
|
252
|
+
levels:
|
|
253
|
+
- level: none
|
|
254
|
+
score: 0
|
|
255
|
+
descriptor: No documented commands.
|
|
256
|
+
- level: readme
|
|
257
|
+
score: 50
|
|
258
|
+
descriptor: >-
|
|
259
|
+
README documents commands, or an agent instructions file exists
|
|
260
|
+
but at least one command in it does not resolve.
|
|
261
|
+
- level: agent_file
|
|
262
|
+
score: 100
|
|
263
|
+
descriptor: >-
|
|
264
|
+
AGENTS.md or CLAUDE.md at the repository root documents
|
|
265
|
+
commands, and every command in it resolves.
|
|
266
|
+
measure:
|
|
267
|
+
all_languages: >-
|
|
268
|
+
A command is an inline code span or a line in a fenced code block
|
|
269
|
+
that starts with a package manager (npm, pnpm, yarn, bun, npx, uv,
|
|
270
|
+
poetry, pdm, pip, pipenv, python, make, just) or a recognised tool.
|
|
271
|
+
It resolves when the script, target, recipe, session or tool it names
|
|
272
|
+
exists in the repository or in its declared dependencies.
|
|
273
|
+
not_applicable_when: null
|
|
274
|
+
rationale: >-
|
|
275
|
+
An agent reads AGENTS.md or CLAUDE.md before anything else. Commands
|
|
276
|
+
in that file that do not exist send it down the wrong path, which is
|
|
277
|
+
worse than no file, so they drop the level to README.
|
|
278
|
+
sources:
|
|
279
|
+
- "AGENTS.md. https://agents.md"
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# Codebase Health methodology, version 1.0
|
|
2
|
+
#
|
|
3
|
+
# This file holds the parts of the methodology that apply to every dimension:
|
|
4
|
+
# aggregation, bands, execution rules, delta rules and regression flags.
|
|
5
|
+
# Parameters and their rubrics live in dimensions/*.yaml. File scope lives in
|
|
6
|
+
# scope.yaml. Framework presets for dimension 3 live in presets/*.yaml.
|
|
7
|
+
#
|
|
8
|
+
# Any change to a weight, anchor, band, threshold or rule in this folder
|
|
9
|
+
# requires a new methodology version. Runs from different versions are never
|
|
10
|
+
# compared.
|
|
11
|
+
|
|
12
|
+
methodology: codebase-health
|
|
13
|
+
version: "1.0"
|
|
14
|
+
|
|
15
|
+
purpose: >-
|
|
16
|
+
Health = how safely and cheaply someone with no memory of this codebase,
|
|
17
|
+
whether a contractor or an agent, can make a correct change.
|
|
18
|
+
|
|
19
|
+
languages:
|
|
20
|
+
- typescript
|
|
21
|
+
- javascript
|
|
22
|
+
- python
|
|
23
|
+
|
|
24
|
+
scale:
|
|
25
|
+
min: 0
|
|
26
|
+
max: 100
|
|
27
|
+
|
|
28
|
+
dimensions:
|
|
29
|
+
- id: 1
|
|
30
|
+
key: safety_net
|
|
31
|
+
file: dimensions/1-safety-net.yaml
|
|
32
|
+
- id: 2
|
|
33
|
+
key: type_static_rigor
|
|
34
|
+
file: dimensions/2-type-static-rigor.yaml
|
|
35
|
+
- id: 3
|
|
36
|
+
key: architecture_boundaries
|
|
37
|
+
file: dimensions/3-architecture-boundaries.yaml
|
|
38
|
+
- id: 4
|
|
39
|
+
key: complexity_maintainability
|
|
40
|
+
file: dimensions/4-complexity-maintainability.yaml
|
|
41
|
+
- id: 5
|
|
42
|
+
key: dependency_security_hygiene
|
|
43
|
+
file: dimensions/5-dependency-security-hygiene.yaml
|
|
44
|
+
- id: 6
|
|
45
|
+
key: operability_feedback_loop
|
|
46
|
+
file: dimensions/6-operability-feedback-loop.yaml
|
|
47
|
+
|
|
48
|
+
scoring:
|
|
49
|
+
# How one parameter's raw value becomes a 0-100 score.
|
|
50
|
+
piecewise_linear: >-
|
|
51
|
+
Anchors are sorted by raw value. Between two anchors the score is
|
|
52
|
+
interpolated linearly. Below the lowest anchor the score equals the
|
|
53
|
+
lowest anchor's score; above the highest anchor it equals the highest
|
|
54
|
+
anchor's score. The same rule covers higher-is-better and
|
|
55
|
+
lower-is-better parameters; only the order of the scores differs.
|
|
56
|
+
levels: >-
|
|
57
|
+
The parameter's raw value is one of a fixed set of named levels, and each
|
|
58
|
+
level has a fixed score.
|
|
59
|
+
mean_of_components: >-
|
|
60
|
+
The parameter is the unweighted mean of its components, each scored by
|
|
61
|
+
its own rule.
|
|
62
|
+
|
|
63
|
+
aggregation:
|
|
64
|
+
parameter_to_dimension:
|
|
65
|
+
method: weighted_mean
|
|
66
|
+
weights: per parameter, declared in each dimension file, summing to 100
|
|
67
|
+
not_applicable:
|
|
68
|
+
method: redistribute_within_dimension
|
|
69
|
+
rule: >-
|
|
70
|
+
A parameter's weight is spread over the other parameters in the same
|
|
71
|
+
dimension, in proportion to their weights. A parameter may only be not
|
|
72
|
+
applicable when its precondition is a design choice the methodology does
|
|
73
|
+
not score. Each dimension file lists the allowed conditions. Missing
|
|
74
|
+
tests and failed execution always score 0, never not applicable.
|
|
75
|
+
dimension_to_health:
|
|
76
|
+
method: capped_mean
|
|
77
|
+
weights: equal
|
|
78
|
+
formula: health = min(mean(dimension scores), min(dimension scores) + cap_margin)
|
|
79
|
+
cap_margin: 30
|
|
80
|
+
precision:
|
|
81
|
+
stored: full
|
|
82
|
+
displayed:
|
|
83
|
+
health: integer, rounded half up
|
|
84
|
+
health_delta: one decimal
|
|
85
|
+
dimension_score: integer, rounded half up
|
|
86
|
+
dimension_delta: integer, rounded half up
|
|
87
|
+
band_is_taken_from: displayed health
|
|
88
|
+
|
|
89
|
+
bands:
|
|
90
|
+
- key: vibe_coded
|
|
91
|
+
name: Vibe-coded
|
|
92
|
+
min: 0
|
|
93
|
+
max: 39
|
|
94
|
+
meaning: >-
|
|
95
|
+
Changes break things without anyone noticing. Nobody, human or agent,
|
|
96
|
+
can work here safely.
|
|
97
|
+
- key: fragile
|
|
98
|
+
name: Fragile
|
|
99
|
+
min: 40
|
|
100
|
+
max: 59
|
|
101
|
+
meaning: It works, but every change is a gamble.
|
|
102
|
+
- key: stable
|
|
103
|
+
name: Stable
|
|
104
|
+
min: 60
|
|
105
|
+
max: 79
|
|
106
|
+
meaning: Changes are safe with care and review.
|
|
107
|
+
- key: agent_ready
|
|
108
|
+
name: Agent-ready
|
|
109
|
+
min: 80
|
|
110
|
+
max: 100
|
|
111
|
+
meaning: >-
|
|
112
|
+
An agent with no memory of the codebase can make a correct change and
|
|
113
|
+
verify it alone.
|
|
114
|
+
|
|
115
|
+
layers:
|
|
116
|
+
static:
|
|
117
|
+
description: >-
|
|
118
|
+
Reads tracked files only: source, configuration, manifests and
|
|
119
|
+
lockfiles. Parses ASTs and builds import graphs. Never runs repository
|
|
120
|
+
code. Always runs.
|
|
121
|
+
execution:
|
|
122
|
+
description: >-
|
|
123
|
+
Installs dependencies, then runs the typechecker, the pinned lint
|
|
124
|
+
ruleset, the build and the test suite with coverage. Attempted on every
|
|
125
|
+
run.
|
|
126
|
+
|
|
127
|
+
execution:
|
|
128
|
+
runner:
|
|
129
|
+
image: health:1.0
|
|
130
|
+
cpus: 4
|
|
131
|
+
memory_gb: 8
|
|
132
|
+
network: >-
|
|
133
|
+
Allowed during dependency install and while fetching the advisory and
|
|
134
|
+
registry snapshots. Off for build, typecheck, lint and tests.
|
|
135
|
+
environment: >-
|
|
136
|
+
Variables documented in the repository's env example file (see 6.5) are
|
|
137
|
+
loaded with their example values. No other secrets or variables are
|
|
138
|
+
provided. External services (databases, queues, browsers) are not
|
|
139
|
+
provided.
|
|
140
|
+
timeouts_seconds:
|
|
141
|
+
install: 900
|
|
142
|
+
build: 900
|
|
143
|
+
typecheck: 600
|
|
144
|
+
lint: 600
|
|
145
|
+
tests: 600
|
|
146
|
+
outcomes:
|
|
147
|
+
repository_failure: >-
|
|
148
|
+
An install, build, typecheck or test command fails or times out because
|
|
149
|
+
of the repository. Every parameter that requires that step scores 0.
|
|
150
|
+
A timeout counts as a failure. An install that fails in one install
|
|
151
|
+
root zeroes only the units beneath that root (same language group,
|
|
152
|
+
package or logic file under the root); the checks of the other roots
|
|
153
|
+
still run and their units are measured. If the install fails in every
|
|
154
|
+
root, the parameters that need it score 0 for the whole repository.
|
|
155
|
+
tool_error: >-
|
|
156
|
+
The tool itself fails (a crash in the orchestrator, a missing tool in
|
|
157
|
+
the image, coverage data missing after a test run that produced
|
|
158
|
+
results). The run is marked invalid and produces no score.
|
|
159
|
+
requirements:
|
|
160
|
+
install: dependencies installed from the lockfile, or from the manifest when no lockfile exists
|
|
161
|
+
build: install succeeded and the build step (see 6.1) exited 0
|
|
162
|
+
typecheck_run: install succeeded and the typechecker ran to completion and reported diagnostics
|
|
163
|
+
lint_run: install succeeded and the pinned lint ruleset ran to completion
|
|
164
|
+
test_run: install succeeded, a supported test runner was found, and it produced a results report
|
|
165
|
+
|
|
166
|
+
minimum_repository: >-
|
|
167
|
+
At least one logic file (see scope.yaml). Repositories below this are not
|
|
168
|
+
scored.
|
|
169
|
+
|
|
170
|
+
pooling:
|
|
171
|
+
# How parameters combine across languages and workspace packages in one
|
|
172
|
+
# repository. The repository always gets one headline score.
|
|
173
|
+
ratio: >-
|
|
174
|
+
Numerators and denominators are summed across languages and packages,
|
|
175
|
+
then the pooled ratio is scored once. Used for counts per 1,000 lines,
|
|
176
|
+
percentages and pass rates. A unit under an install root whose install
|
|
177
|
+
failed takes no part in the sums; the pooled score is multiplied by the
|
|
178
|
+
share of the logic lines that is not failed.
|
|
179
|
+
unit_weighted: >-
|
|
180
|
+
The parameter is scored separately for each language or package, then
|
|
181
|
+
the scores are combined by logic-line weight. Used for configuration and
|
|
182
|
+
yes/no parameters. Example: 2.2 scores 100 for a TypeScript frontend with
|
|
183
|
+
8,000 logic lines and 0 for a Python backend with 2,000; the pooled score
|
|
184
|
+
is 80.
|
|
185
|
+
report: Every dimension is also reported per package and per language.
|
|
186
|
+
|
|
187
|
+
delta:
|
|
188
|
+
description: >-
|
|
189
|
+
health diff compares two runs only when all conditions match. Otherwise
|
|
190
|
+
it refuses and names the conditions that differ.
|
|
191
|
+
conditions:
|
|
192
|
+
- key: layers
|
|
193
|
+
rule: Both runs attempted the same layers.
|
|
194
|
+
- key: tool_versions
|
|
195
|
+
rule: Both runs used the same tool versions (the same image digest).
|
|
196
|
+
- key: methodology_version
|
|
197
|
+
rule: Both runs used the same methodology version.
|
|
198
|
+
- key: as_of_date
|
|
199
|
+
rule: >-
|
|
200
|
+
Both runs were scored at the same as-of date. For a contract, this is
|
|
201
|
+
the baseline's as-of date. The report also shows today's score for
|
|
202
|
+
the newer run.
|
|
203
|
+
- key: ignore_set
|
|
204
|
+
rule: >-
|
|
205
|
+
Both runs used the same .healthignore and .gitleaksignore contents
|
|
206
|
+
(compared by hash).
|
|
207
|
+
rescoring: >-
|
|
208
|
+
run.json stores raw values and the resolved dependency list, so health
|
|
209
|
+
diff can rescore parameters 5.1 and 5.2 of both runs against one advisory
|
|
210
|
+
and registry snapshot.
|
|
211
|
+
|
|
212
|
+
regression:
|
|
213
|
+
description: Used by sprint-loop. Flags a regression; never blocks.
|
|
214
|
+
flag_when:
|
|
215
|
+
- Any dimension score drops by 3.0 or more (unrounded).
|
|
216
|
+
- The band drops.
|
|
217
|
+
pr_label: health-regression
|
|
218
|
+
telegram_format: "Health {health} {band} ({health_delta}) · {dimension_deltas} · ⚠ {regressions or 'none'}"
|
|
219
|
+
telegram_example: "Health 62 Stable (+1.4) · Safety net +6 · Complexity −1 · ⚠ none"
|
|
220
|
+
baseline: >-
|
|
221
|
+
The base branch commit, cached by tree hash, methodology version and
|
|
222
|
+
as-of date. Both runs use the as-of date of the day sprint-loop runs.
|
|
223
|
+
|
|
224
|
+
report_only:
|
|
225
|
+
# Signals that appear in the report and never change the score.
|
|
226
|
+
- key: hotspots
|
|
227
|
+
description: >-
|
|
228
|
+
Files ranked by churn (commits touching the file in the last 12 months)
|
|
229
|
+
multiplied by cognitive complexity. This is the "fix these first" list.
|
|
230
|
+
- key: secrets_in_history
|
|
231
|
+
description: Secrets found in git history. The fix is rotating the key.
|
|
232
|
+
- key: dev_dependency_vulnerabilities
|
|
233
|
+
description: Known vulnerabilities in packages reachable only from development dependencies.
|
|
234
|
+
- key: top_scares
|
|
235
|
+
description: >-
|
|
236
|
+
The first three of a fixed list of plain-English consequences that the
|
|
237
|
+
scan's findings trigger, for example a Supabase service-role key in the
|
|
238
|
+
tree. Copy lives in code.
|
|
239
|
+
- key: excluded_lines
|
|
240
|
+
description: Percentage of lines excluded by .healthignore.
|
|
241
|
+
- key: per_package_breakdown
|
|
242
|
+
description: Every dimension per workspace package and per language.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
preset:
|
|
2
|
+
key: django
|
|
3
|
+
name: Django
|
|
4
|
+
languages: [python]
|
|
5
|
+
detect: The project declares django as a dependency.
|
|
6
|
+
|
|
7
|
+
conventions: >-
|
|
8
|
+
Layers are matched by module name inside each Django app, either as a
|
|
9
|
+
module (views.py) or a package (views/__init__.py and its submodules).
|
|
10
|
+
|
|
11
|
+
modules:
|
|
12
|
+
roots: Django apps (directories that contain apps.py).
|
|
13
|
+
public_entry: [__init__.py, models, services, selectors]
|
|
14
|
+
|
|
15
|
+
layers:
|
|
16
|
+
models: [models]
|
|
17
|
+
http: [views, urls, forms, serializers, admin]
|
|
18
|
+
views: [views]
|
|
19
|
+
urls: [urls]
|
|
20
|
+
services: [services, selectors]
|
|
21
|
+
|
|
22
|
+
rules:
|
|
23
|
+
- name: models-do-not-import-http
|
|
24
|
+
description: Models do not depend on views, urls, forms, serializers or admin.
|
|
25
|
+
from: { in: [models] }
|
|
26
|
+
to: { in: [http] }
|
|
27
|
+
severity: error
|
|
28
|
+
type_only_imports: included
|
|
29
|
+
- name: services-do-not-import-views
|
|
30
|
+
description: Services and selectors do not depend on views or urls.
|
|
31
|
+
from: { in: [services] }
|
|
32
|
+
to: { in: [views, urls] }
|
|
33
|
+
severity: error
|
|
34
|
+
type_only_imports: included
|
|
35
|
+
- name: only-urls-import-views
|
|
36
|
+
description: Views are imported only by urls modules.
|
|
37
|
+
from: { not_in: [urls] }
|
|
38
|
+
to: { in: [views] }
|
|
39
|
+
severity: error
|
|
40
|
+
type_only_imports: included
|
|
41
|
+
|
|
42
|
+
sources:
|
|
43
|
+
- "HackSoft Django Styleguide. https://github.com/HackSoftware/Django-Styleguide"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
preset:
|
|
2
|
+
key: express
|
|
3
|
+
name: Express
|
|
4
|
+
languages: [typescript, javascript]
|
|
5
|
+
detect: The package declares express as a dependency and not next.
|
|
6
|
+
|
|
7
|
+
conventions: >-
|
|
8
|
+
Globs are relative to the package root and match with or without a src/
|
|
9
|
+
prefix. When a file matches several layer globs, the innermost matching
|
|
10
|
+
directory decides its layer.
|
|
11
|
+
|
|
12
|
+
layers:
|
|
13
|
+
http: ["routes/**", "controllers/**", "middleware/**", "middlewares/**"]
|
|
14
|
+
services: ["services/**"]
|
|
15
|
+
data: ["models/**", "repositories/**", "db/**", "dal/**"]
|
|
16
|
+
shared: ["utils/**", "lib/**", "helpers/**"]
|
|
17
|
+
|
|
18
|
+
modules:
|
|
19
|
+
roots: ["features/*", "modules/*", "packages/*"]
|
|
20
|
+
public_entry: "index.{ts,js,mts,mjs,cjs}"
|
|
21
|
+
|
|
22
|
+
rules:
|
|
23
|
+
- name: services-do-not-import-http
|
|
24
|
+
description: Services do not depend on the HTTP layer or on express.
|
|
25
|
+
from: { in: [services] }
|
|
26
|
+
to:
|
|
27
|
+
in: [http]
|
|
28
|
+
packages: [express]
|
|
29
|
+
severity: error
|
|
30
|
+
type_only_imports: included
|
|
31
|
+
- name: data-does-not-import-up
|
|
32
|
+
description: The data layer does not depend on services, the HTTP layer or express.
|
|
33
|
+
from: { in: [data] }
|
|
34
|
+
to:
|
|
35
|
+
in: [services, http]
|
|
36
|
+
packages: [express]
|
|
37
|
+
severity: error
|
|
38
|
+
type_only_imports: included
|
|
39
|
+
- name: shared-does-not-import-up
|
|
40
|
+
description: Shared helpers do not depend on the HTTP layer or services.
|
|
41
|
+
from: { in: [shared] }
|
|
42
|
+
to: { in: [http, services] }
|
|
43
|
+
severity: error
|
|
44
|
+
type_only_imports: included
|
|
45
|
+
|
|
46
|
+
sources:
|
|
47
|
+
- "Cockburn, A. Hexagonal Architecture (Ports and Adapters), 2005. https://alistair.cockburn.us/hexagonal-architecture/"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
preset:
|
|
2
|
+
key: fastapi
|
|
3
|
+
name: FastAPI
|
|
4
|
+
languages: [python]
|
|
5
|
+
detect: The project declares fastapi as a dependency and not django.
|
|
6
|
+
|
|
7
|
+
conventions: >-
|
|
8
|
+
Layers are matched by module or package name anywhere under a first-party
|
|
9
|
+
package. When a module matches several layers, the innermost match
|
|
10
|
+
decides its layer.
|
|
11
|
+
|
|
12
|
+
modules:
|
|
13
|
+
roots: >-
|
|
14
|
+
Subpackages that contain router.py, and the direct subpackages of any
|
|
15
|
+
features or modules package.
|
|
16
|
+
public_entry: [__init__.py]
|
|
17
|
+
|
|
18
|
+
layers:
|
|
19
|
+
http: [routers, router, routes, api, endpoints]
|
|
20
|
+
services: [services, service]
|
|
21
|
+
data: [models, repositories, repository, db, crud]
|
|
22
|
+
schemas: [schemas, schema]
|
|
23
|
+
app_entry: [main, app]
|
|
24
|
+
|
|
25
|
+
rules:
|
|
26
|
+
- name: services-do-not-import-http
|
|
27
|
+
description: Services do not depend on routers, fastapi or starlette.
|
|
28
|
+
from: { in: [services] }
|
|
29
|
+
to:
|
|
30
|
+
in: [http]
|
|
31
|
+
packages: [fastapi, starlette]
|
|
32
|
+
severity: error
|
|
33
|
+
type_only_imports: included
|
|
34
|
+
- name: data-does-not-import-up
|
|
35
|
+
description: >-
|
|
36
|
+
The data layer does not depend on the HTTP layer, services or fastapi.
|
|
37
|
+
Importing schemas is allowed.
|
|
38
|
+
from: { in: [data] }
|
|
39
|
+
to:
|
|
40
|
+
in: [http, services]
|
|
41
|
+
packages: [fastapi, starlette]
|
|
42
|
+
severity: error
|
|
43
|
+
type_only_imports: included
|
|
44
|
+
- name: routers-are-leaves
|
|
45
|
+
description: Only main.py and app.py import routers.
|
|
46
|
+
from: { not_in: [http, app_entry] }
|
|
47
|
+
to: { in: [http] }
|
|
48
|
+
severity: error
|
|
49
|
+
type_only_imports: included
|
|
50
|
+
|
|
51
|
+
sources:
|
|
52
|
+
- "FastAPI: Bigger Applications - Multiple Files. https://fastapi.tiangolo.com/tutorial/bigger-applications/"
|