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.
Files changed (236) hide show
  1. package/LICENSE +95 -0
  2. package/README.md +73 -2
  3. package/assets/fonts/NotoEmoji-OFL.txt +93 -0
  4. package/assets/fonts/NotoEmoji-Poop.ttf +0 -0
  5. package/assets/fonts/SpaceGrotesk-Bold.ttf +0 -0
  6. package/assets/fonts/SpaceGrotesk-OFL.txt +93 -0
  7. package/dist/bin.js +11 -0
  8. package/dist/card/previous.js +38 -0
  9. package/dist/card/render.js +71 -0
  10. package/dist/cli.js +138 -0
  11. package/dist/commands/card.js +69 -0
  12. package/dist/commands/common.js +1 -0
  13. package/dist/commands/diff.js +54 -0
  14. package/dist/commands/footer.js +28 -0
  15. package/dist/commands/report.js +49 -0
  16. package/dist/commands/route.js +62 -0
  17. package/dist/commands/submit.js +125 -0
  18. package/dist/commands/summary.js +77 -0
  19. package/dist/diff/compare.js +73 -0
  20. package/dist/diff/conditions.js +87 -0
  21. package/dist/diff/format.js +23 -0
  22. package/dist/exec/docker.js +158 -0
  23. package/dist/exec/findings.js +73 -0
  24. package/dist/exec/plan.js +50 -0
  25. package/dist/exec/reports.js +90 -0
  26. package/dist/exec/roots.js +49 -0
  27. package/dist/exec/run.js +182 -0
  28. package/dist/exec/steps/build.js +45 -0
  29. package/dist/exec/steps/coverage-install.js +12 -0
  30. package/dist/exec/steps/install.js +41 -0
  31. package/dist/exec/steps/knip.js +28 -0
  32. package/dist/exec/steps/lint.js +25 -0
  33. package/dist/exec/steps/lockfiles.js +25 -0
  34. package/dist/exec/steps/test.js +17 -0
  35. package/dist/exec/steps/typecheck.js +40 -0
  36. package/dist/exec/test-reports/coverage-py.js +23 -0
  37. package/dist/exec/test-reports/coverage-ranges.js +65 -0
  38. package/dist/exec/test-reports/coverage-summary.js +23 -0
  39. package/dist/exec/test-reports/jest-json.js +31 -0
  40. package/dist/exec/test-reports/junit.js +24 -0
  41. package/dist/exec/test-reports/mocha-json.js +18 -0
  42. package/dist/exec/test-reports/vitest-json.js +5 -0
  43. package/dist/exec/test-runners/detect.js +99 -0
  44. package/dist/exec/test-runners/index.js +6 -0
  45. package/dist/exec/test-runners/jest.js +27 -0
  46. package/dist/exec/test-runners/mocha.js +28 -0
  47. package/dist/exec/test-runners/node-test.js +24 -0
  48. package/dist/exec/test-runners/pytest.js +18 -0
  49. package/dist/exec/test-runners/script.js +48 -0
  50. package/dist/exec/test-runners/spec.js +1 -0
  51. package/dist/exec/test-runners/vitest.js +28 -0
  52. package/dist/exec/tests.js +120 -0
  53. package/dist/exec/types.js +1 -0
  54. package/dist/interactive/after-scan.js +121 -0
  55. package/dist/interactive/docker-setup.js +97 -0
  56. package/dist/interactive/prompt.js +17 -0
  57. package/dist/interactive/system.js +10 -0
  58. package/dist/manifest/package-json.js +36 -0
  59. package/dist/manifest/python.js +106 -0
  60. package/dist/measure/d3/index.js +45 -0
  61. package/dist/measure/d3/presets.js +100 -0
  62. package/dist/measure/d3/py/adapter.js +67 -0
  63. package/dist/measure/d3/py/config.js +80 -0
  64. package/dist/measure/d3/py/contracts.js +57 -0
  65. package/dist/measure/d3/py/domain.js +57 -0
  66. package/dist/measure/d3/py/graph.js +214 -0
  67. package/dist/measure/d3/py/interpreter.js +31 -0
  68. package/dist/measure/d3/py/layers.js +120 -0
  69. package/dist/measure/d3/py/roots.js +58 -0
  70. package/dist/measure/d3/py/types.js +1 -0
  71. package/dist/measure/d3/scc.js +64 -0
  72. package/dist/measure/d3/ts/adapter.js +371 -0
  73. package/dist/measure/d3/ts/classify.js +107 -0
  74. package/dist/measure/d3/ts/config.js +318 -0
  75. package/dist/measure/d3/ts/domain.js +23 -0
  76. package/dist/measure/d3/ts/rules.js +65 -0
  77. package/dist/measure/d3/types.js +5 -0
  78. package/dist/measure/deps/cvss.js +66 -0
  79. package/dist/measure/deps/declared.js +195 -0
  80. package/dist/measure/deps/files.js +40 -0
  81. package/dist/measure/deps/lockfiles.js +308 -0
  82. package/dist/measure/deps/osv.js +122 -0
  83. package/dist/measure/deps/registry.js +142 -0
  84. package/dist/measure/deps/resolved.js +78 -0
  85. package/dist/measure/deps/units.js +5 -0
  86. package/dist/measure/eslint/health-plugin.js +19 -0
  87. package/dist/measure/ops/docs-commands.js +73 -0
  88. package/dist/measure/ops/docs-resolve.js +217 -0
  89. package/dist/measure/ops/entry-points.js +49 -0
  90. package/dist/measure/ops/env-example.js +20 -0
  91. package/dist/measure/ops/env-reads.js +46 -0
  92. package/dist/measure/ops/runtime.js +73 -0
  93. package/dist/measure/p1-1.js +8 -0
  94. package/dist/measure/p1-2.js +8 -0
  95. package/dist/measure/p1-3.js +8 -0
  96. package/dist/measure/p1-4.js +41 -0
  97. package/dist/measure/p1-5.js +59 -0
  98. package/dist/measure/p2-1.js +8 -0
  99. package/dist/measure/p2-2.js +44 -0
  100. package/dist/measure/p2-3.js +81 -0
  101. package/dist/measure/p2-4.js +288 -0
  102. package/dist/measure/p2-5.js +8 -0
  103. package/dist/measure/p3-1.js +16 -0
  104. package/dist/measure/p3-2.js +26 -0
  105. package/dist/measure/p3-3.js +37 -0
  106. package/dist/measure/p3-4.js +26 -0
  107. package/dist/measure/p3-5.js +26 -0
  108. package/dist/measure/p4-1.js +184 -0
  109. package/dist/measure/p4-2.js +27 -0
  110. package/dist/measure/p4-3.js +106 -0
  111. package/dist/measure/p4-4.js +309 -0
  112. package/dist/measure/p5-1.js +117 -0
  113. package/dist/measure/p5-2.js +80 -0
  114. package/dist/measure/p5-3.js +21 -0
  115. package/dist/measure/p5-4.js +161 -0
  116. package/dist/measure/p5-5.js +158 -0
  117. package/dist/measure/p6-1.js +20 -0
  118. package/dist/measure/p6-2.js +20 -0
  119. package/dist/measure/p6-3.js +40 -0
  120. package/dist/measure/p6-4.js +31 -0
  121. package/dist/measure/p6-5.js +60 -0
  122. package/dist/measure/p6-6.js +54 -0
  123. package/dist/measure/py/typecheck-config.js +95 -0
  124. package/dist/measure/registry.js +31 -0
  125. package/dist/measure/safety/py-tests.js +124 -0
  126. package/dist/measure/safety/ts-barrels.js +68 -0
  127. package/dist/measure/safety/ts-cases.js +111 -0
  128. package/dist/measure/safety/ts-credits.js +101 -0
  129. package/dist/measure/safety/ts-imports.js +63 -0
  130. package/dist/measure/safety/ts-resolve.js +43 -0
  131. package/dist/measure/ts/directives.js +37 -0
  132. package/dist/measure/ts/exports.js +60 -0
  133. package/dist/measure/ts/tsconfig.js +179 -0
  134. package/dist/measure/types.js +1 -0
  135. package/dist/measure/units.js +41 -0
  136. package/dist/methodology/load.js +159 -0
  137. package/dist/methodology/schema.js +105 -0
  138. package/dist/python/deadcode.js +27 -0
  139. package/dist/python/env.js +13 -0
  140. package/dist/python/helper.js +24 -0
  141. package/dist/python/imports.js +40 -0
  142. package/dist/python/nox.js +16 -0
  143. package/dist/python/scope.js +35 -0
  144. package/dist/python/tests.js +19 -0
  145. package/dist/python/typing.js +27 -0
  146. package/dist/report/escape.js +5 -0
  147. package/dist/report/format.js +75 -0
  148. package/dist/report/render.js +213 -0
  149. package/dist/report/roadmap.js +58 -0
  150. package/dist/report/scares.js +83 -0
  151. package/dist/report/style.js +27 -0
  152. package/dist/report-only/hotspots.js +48 -0
  153. package/dist/route/check.js +37 -0
  154. package/dist/route/files.js +74 -0
  155. package/dist/route/order.js +22 -0
  156. package/dist/route/select.js +114 -0
  157. package/dist/route/write.js +13 -0
  158. package/dist/run/read.js +89 -0
  159. package/dist/run/recompute.js +19 -0
  160. package/dist/run/scan.js +196 -0
  161. package/dist/run/schema.js +1 -0
  162. package/dist/run/score-run.js +42 -0
  163. package/dist/run/write.js +4 -0
  164. package/dist/scope/build.js +173 -0
  165. package/dist/scope/packages.js +87 -0
  166. package/dist/scope/rules.js +21 -0
  167. package/dist/scope/tracked.js +34 -0
  168. package/dist/scope/ts-lines.js +63 -0
  169. package/dist/scope/types.js +7 -0
  170. package/dist/scoring/aggregate.js +38 -0
  171. package/dist/scoring/pool.js +65 -0
  172. package/dist/scoring/score.js +81 -0
  173. package/dist/scoring/what-if.js +38 -0
  174. package/dist/scripts/checks.js +53 -0
  175. package/dist/scripts/ci.js +139 -0
  176. package/dist/scripts/entry-points/find.js +31 -0
  177. package/dist/scripts/entry-points/index.js +18 -0
  178. package/dist/scripts/entry-points/justfile.js +62 -0
  179. package/dist/scripts/entry-points/makefile.js +66 -0
  180. package/dist/scripts/entry-points/nox.js +29 -0
  181. package/dist/scripts/entry-points/package-json.js +21 -0
  182. package/dist/scripts/entry-points/pyproject.js +73 -0
  183. package/dist/scripts/entry-points/tox.js +130 -0
  184. package/dist/scripts/expand.js +300 -0
  185. package/dist/scripts/triggers.js +151 -0
  186. package/dist/scripts/types.js +1 -0
  187. package/dist/submit/constants.js +10 -0
  188. package/dist/submit/payload.js +42 -0
  189. package/dist/submit/repo-identity.js +46 -0
  190. package/dist/submit/send.js +29 -0
  191. package/dist/tools/catalog.js +46 -0
  192. package/dist/tools/image.js +180 -0
  193. package/dist/tools/kit.js +71 -0
  194. package/dist/tools/knip.js +169 -0
  195. package/dist/tools/outcome.js +21 -0
  196. package/dist/tools/resolve.js +66 -0
  197. package/dist/tools/workcopy.js +27 -0
  198. package/dist/util/ini.js +37 -0
  199. package/dist/util/pep440.js +163 -0
  200. package/dist/util/toml.js +27 -0
  201. package/docker/Dockerfile +142 -0
  202. package/docker/bin/health-capture +5 -0
  203. package/docker/bin/health-coverage-install +5 -0
  204. package/docker/bin/health-eslint +66 -0
  205. package/docker/bin/health-pytest +10 -0
  206. package/docker/bin/health-tsc +10 -0
  207. package/docker/bin/health-venv +11 -0
  208. package/docker/config/eslint.config.mjs +4 -0
  209. package/docker/config/eslint.rules.mjs +65 -0
  210. package/docker/config/eslint.untyped.config.mjs +4 -0
  211. package/docker/config/knip.json +1 -0
  212. package/docker/config/ruff.toml +7 -0
  213. package/methodology/LICENSE +396 -0
  214. package/methodology/v1.0/METHODOLOGY.md +1176 -0
  215. package/methodology/v1.0/dimensions/1-safety-net.yaml +240 -0
  216. package/methodology/v1.0/dimensions/2-type-static-rigor.yaml +289 -0
  217. package/methodology/v1.0/dimensions/3-architecture-boundaries.yaml +266 -0
  218. package/methodology/v1.0/dimensions/4-complexity-maintainability.yaml +192 -0
  219. package/methodology/v1.0/dimensions/5-dependency-security-hygiene.yaml +223 -0
  220. package/methodology/v1.0/dimensions/6-operability-feedback-loop.yaml +279 -0
  221. package/methodology/v1.0/methodology.yaml +242 -0
  222. package/methodology/v1.0/presets/django.yaml +43 -0
  223. package/methodology/v1.0/presets/express.yaml +47 -0
  224. package/methodology/v1.0/presets/fastapi.yaml +52 -0
  225. package/methodology/v1.0/presets/generic.yaml +41 -0
  226. package/methodology/v1.0/presets/nextjs.yaml +50 -0
  227. package/methodology/v1.0/scope.yaml +115 -0
  228. package/package.json +61 -4
  229. package/python/dead_code_helper.py +265 -0
  230. package/python/env_helper.py +71 -0
  231. package/python/graph_helper.py +277 -0
  232. package/python/imports_helper.py +132 -0
  233. package/python/nox_helper.py +79 -0
  234. package/python/scope_helper.py +96 -0
  235. package/python/tests_helper.py +171 -0
  236. 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/"