vibecodedshit 0.0.0-stage → 0.2.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 (232) hide show
  1. package/LICENSE +95 -0
  2. package/README.md +64 -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 +8 -0
  8. package/dist/card/previous.js +38 -0
  9. package/dist/card/render.js +71 -0
  10. package/dist/cli.js +121 -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 +25 -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/manifest/package-json.js +36 -0
  55. package/dist/manifest/python.js +106 -0
  56. package/dist/measure/d3/index.js +45 -0
  57. package/dist/measure/d3/presets.js +100 -0
  58. package/dist/measure/d3/py/adapter.js +67 -0
  59. package/dist/measure/d3/py/config.js +80 -0
  60. package/dist/measure/d3/py/contracts.js +57 -0
  61. package/dist/measure/d3/py/domain.js +57 -0
  62. package/dist/measure/d3/py/graph.js +214 -0
  63. package/dist/measure/d3/py/interpreter.js +31 -0
  64. package/dist/measure/d3/py/layers.js +120 -0
  65. package/dist/measure/d3/py/roots.js +58 -0
  66. package/dist/measure/d3/py/types.js +1 -0
  67. package/dist/measure/d3/scc.js +64 -0
  68. package/dist/measure/d3/ts/adapter.js +371 -0
  69. package/dist/measure/d3/ts/classify.js +107 -0
  70. package/dist/measure/d3/ts/config.js +318 -0
  71. package/dist/measure/d3/ts/domain.js +23 -0
  72. package/dist/measure/d3/ts/rules.js +65 -0
  73. package/dist/measure/d3/types.js +5 -0
  74. package/dist/measure/deps/cvss.js +66 -0
  75. package/dist/measure/deps/declared.js +195 -0
  76. package/dist/measure/deps/files.js +40 -0
  77. package/dist/measure/deps/lockfiles.js +308 -0
  78. package/dist/measure/deps/osv.js +122 -0
  79. package/dist/measure/deps/registry.js +142 -0
  80. package/dist/measure/deps/resolved.js +78 -0
  81. package/dist/measure/deps/units.js +5 -0
  82. package/dist/measure/eslint/health-plugin.js +19 -0
  83. package/dist/measure/ops/docs-commands.js +73 -0
  84. package/dist/measure/ops/docs-resolve.js +217 -0
  85. package/dist/measure/ops/entry-points.js +49 -0
  86. package/dist/measure/ops/env-example.js +20 -0
  87. package/dist/measure/ops/env-reads.js +46 -0
  88. package/dist/measure/ops/runtime.js +73 -0
  89. package/dist/measure/p1-1.js +8 -0
  90. package/dist/measure/p1-2.js +8 -0
  91. package/dist/measure/p1-3.js +8 -0
  92. package/dist/measure/p1-4.js +41 -0
  93. package/dist/measure/p1-5.js +59 -0
  94. package/dist/measure/p2-1.js +8 -0
  95. package/dist/measure/p2-2.js +44 -0
  96. package/dist/measure/p2-3.js +81 -0
  97. package/dist/measure/p2-4.js +288 -0
  98. package/dist/measure/p2-5.js +8 -0
  99. package/dist/measure/p3-1.js +16 -0
  100. package/dist/measure/p3-2.js +26 -0
  101. package/dist/measure/p3-3.js +37 -0
  102. package/dist/measure/p3-4.js +26 -0
  103. package/dist/measure/p3-5.js +26 -0
  104. package/dist/measure/p4-1.js +184 -0
  105. package/dist/measure/p4-2.js +27 -0
  106. package/dist/measure/p4-3.js +106 -0
  107. package/dist/measure/p4-4.js +309 -0
  108. package/dist/measure/p5-1.js +117 -0
  109. package/dist/measure/p5-2.js +80 -0
  110. package/dist/measure/p5-3.js +21 -0
  111. package/dist/measure/p5-4.js +161 -0
  112. package/dist/measure/p5-5.js +158 -0
  113. package/dist/measure/p6-1.js +20 -0
  114. package/dist/measure/p6-2.js +20 -0
  115. package/dist/measure/p6-3.js +40 -0
  116. package/dist/measure/p6-4.js +31 -0
  117. package/dist/measure/p6-5.js +60 -0
  118. package/dist/measure/p6-6.js +54 -0
  119. package/dist/measure/py/typecheck-config.js +95 -0
  120. package/dist/measure/registry.js +31 -0
  121. package/dist/measure/safety/py-tests.js +124 -0
  122. package/dist/measure/safety/ts-barrels.js +68 -0
  123. package/dist/measure/safety/ts-cases.js +111 -0
  124. package/dist/measure/safety/ts-credits.js +101 -0
  125. package/dist/measure/safety/ts-imports.js +63 -0
  126. package/dist/measure/safety/ts-resolve.js +43 -0
  127. package/dist/measure/ts/directives.js +37 -0
  128. package/dist/measure/ts/exports.js +60 -0
  129. package/dist/measure/ts/tsconfig.js +179 -0
  130. package/dist/measure/types.js +1 -0
  131. package/dist/measure/units.js +41 -0
  132. package/dist/methodology/load.js +159 -0
  133. package/dist/methodology/schema.js +105 -0
  134. package/dist/python/deadcode.js +27 -0
  135. package/dist/python/env.js +13 -0
  136. package/dist/python/helper.js +24 -0
  137. package/dist/python/imports.js +40 -0
  138. package/dist/python/nox.js +16 -0
  139. package/dist/python/scope.js +35 -0
  140. package/dist/python/tests.js +19 -0
  141. package/dist/python/typing.js +27 -0
  142. package/dist/report/escape.js +5 -0
  143. package/dist/report/format.js +75 -0
  144. package/dist/report/render.js +213 -0
  145. package/dist/report/roadmap.js +58 -0
  146. package/dist/report/scares.js +83 -0
  147. package/dist/report/style.js +27 -0
  148. package/dist/report-only/hotspots.js +48 -0
  149. package/dist/route/check.js +37 -0
  150. package/dist/route/files.js +74 -0
  151. package/dist/route/order.js +22 -0
  152. package/dist/route/select.js +114 -0
  153. package/dist/route/write.js +13 -0
  154. package/dist/run/read.js +89 -0
  155. package/dist/run/recompute.js +19 -0
  156. package/dist/run/scan.js +196 -0
  157. package/dist/run/schema.js +1 -0
  158. package/dist/run/score-run.js +42 -0
  159. package/dist/run/write.js +4 -0
  160. package/dist/scope/build.js +173 -0
  161. package/dist/scope/packages.js +87 -0
  162. package/dist/scope/rules.js +21 -0
  163. package/dist/scope/tracked.js +34 -0
  164. package/dist/scope/ts-lines.js +63 -0
  165. package/dist/scope/types.js +7 -0
  166. package/dist/scoring/aggregate.js +38 -0
  167. package/dist/scoring/pool.js +65 -0
  168. package/dist/scoring/score.js +81 -0
  169. package/dist/scoring/what-if.js +38 -0
  170. package/dist/scripts/checks.js +53 -0
  171. package/dist/scripts/ci.js +139 -0
  172. package/dist/scripts/entry-points/find.js +31 -0
  173. package/dist/scripts/entry-points/index.js +18 -0
  174. package/dist/scripts/entry-points/justfile.js +62 -0
  175. package/dist/scripts/entry-points/makefile.js +66 -0
  176. package/dist/scripts/entry-points/nox.js +29 -0
  177. package/dist/scripts/entry-points/package-json.js +21 -0
  178. package/dist/scripts/entry-points/pyproject.js +73 -0
  179. package/dist/scripts/entry-points/tox.js +130 -0
  180. package/dist/scripts/expand.js +300 -0
  181. package/dist/scripts/triggers.js +151 -0
  182. package/dist/scripts/types.js +1 -0
  183. package/dist/submit/constants.js +9 -0
  184. package/dist/submit/payload.js +42 -0
  185. package/dist/submit/repo-identity.js +46 -0
  186. package/dist/submit/send.js +29 -0
  187. package/dist/tools/catalog.js +46 -0
  188. package/dist/tools/image.js +180 -0
  189. package/dist/tools/kit.js +71 -0
  190. package/dist/tools/knip.js +169 -0
  191. package/dist/tools/outcome.js +21 -0
  192. package/dist/tools/resolve.js +66 -0
  193. package/dist/tools/workcopy.js +27 -0
  194. package/dist/util/ini.js +37 -0
  195. package/dist/util/pep440.js +163 -0
  196. package/dist/util/toml.js +27 -0
  197. package/docker/Dockerfile +142 -0
  198. package/docker/bin/health-capture +5 -0
  199. package/docker/bin/health-coverage-install +5 -0
  200. package/docker/bin/health-eslint +66 -0
  201. package/docker/bin/health-pytest +10 -0
  202. package/docker/bin/health-tsc +10 -0
  203. package/docker/bin/health-venv +11 -0
  204. package/docker/config/eslint.config.mjs +4 -0
  205. package/docker/config/eslint.rules.mjs +65 -0
  206. package/docker/config/eslint.untyped.config.mjs +4 -0
  207. package/docker/config/knip.json +1 -0
  208. package/docker/config/ruff.toml +7 -0
  209. package/methodology/LICENSE +396 -0
  210. package/methodology/v1.0/METHODOLOGY.md +1176 -0
  211. package/methodology/v1.0/dimensions/1-safety-net.yaml +240 -0
  212. package/methodology/v1.0/dimensions/2-type-static-rigor.yaml +289 -0
  213. package/methodology/v1.0/dimensions/3-architecture-boundaries.yaml +266 -0
  214. package/methodology/v1.0/dimensions/4-complexity-maintainability.yaml +192 -0
  215. package/methodology/v1.0/dimensions/5-dependency-security-hygiene.yaml +223 -0
  216. package/methodology/v1.0/dimensions/6-operability-feedback-loop.yaml +279 -0
  217. package/methodology/v1.0/methodology.yaml +242 -0
  218. package/methodology/v1.0/presets/django.yaml +43 -0
  219. package/methodology/v1.0/presets/express.yaml +47 -0
  220. package/methodology/v1.0/presets/fastapi.yaml +52 -0
  221. package/methodology/v1.0/presets/generic.yaml +41 -0
  222. package/methodology/v1.0/presets/nextjs.yaml +50 -0
  223. package/methodology/v1.0/scope.yaml +115 -0
  224. package/package.json +61 -4
  225. package/python/dead_code_helper.py +265 -0
  226. package/python/env_helper.py +71 -0
  227. package/python/graph_helper.py +277 -0
  228. package/python/imports_helper.py +132 -0
  229. package/python/nox_helper.py +79 -0
  230. package/python/scope_helper.py +96 -0
  231. package/python/tests_helper.py +171 -0
  232. package/python/typing_helper.py +235 -0
@@ -0,0 +1,266 @@
1
+ dimension:
2
+ id: 3
3
+ key: architecture_boundaries
4
+ name: Architecture & boundaries
5
+ question: Can you change one part without reading the rest?
6
+ agent_relevance: >-
7
+ An agent works inside a limited context window. Enforced boundaries
8
+ limit how much of the codebase one change can affect, so the agent only
9
+ needs to read the module it is changing and that module's public entry
10
+ points.
11
+
12
+ presets:
13
+ description: >-
14
+ When a repository declares no import rules, the scan uses the preset for
15
+ the detected framework (presets/*.yaml). When it declares rules (see
16
+ 3.1), its rules replace the preset. Detection order: nextjs, express,
17
+ django, fastapi, generic. Each workspace package gets its own preset.
18
+ files:
19
+ - presets/nextjs.yaml
20
+ - presets/express.yaml
21
+ - presets/django.yaml
22
+ - presets/fastapi.yaml
23
+ - presets/generic.yaml
24
+
25
+ parameters:
26
+ - id: "3.1"
27
+ key: boundaries_declared
28
+ name: Boundaries declared and enforced
29
+ layer: static
30
+ weight: 15
31
+ requires: []
32
+ pooling: unit_weighted
33
+ raw:
34
+ unit: level
35
+ definition: >-
36
+ Whether the repository declares import rules in a supported rules
37
+ file, and whether CI runs them.
38
+ when_empty: 0
39
+ scoring:
40
+ method: levels
41
+ levels:
42
+ - level: none
43
+ score: 0
44
+ descriptor: No import rules are declared.
45
+ - level: declared
46
+ score: 50
47
+ descriptor: >-
48
+ A rules file declares at least one direction rule, but CI does
49
+ not run it.
50
+ - level: enforced
51
+ score: 100
52
+ descriptor: The rules are declared and CI runs them on pull requests.
53
+ measure:
54
+ typescript_javascript: >-
55
+ Rules file: .dependency-cruiser.{js,cjs,mjs,json}. Declared means at
56
+ least one forbidden or allowed rule whose from and to paths name
57
+ different directories. Enforced means a CI file invokes depcruise,
58
+ directly or through a package script or make target (up to 3 levels).
59
+ python: >-
60
+ Rules file: .importlinter, [importlinter] in setup.cfg, or
61
+ [tool.importlinter] in pyproject.toml. Declared means at least one
62
+ layers, forbidden or independence contract. Enforced means a CI file
63
+ invokes lint-imports, directly or through a script or make target.
64
+ ci_files: >-
65
+ .github/workflows/*, .gitlab-ci.yml, .circleci/config.yml,
66
+ bitbucket-pipelines.yml, azure-pipelines.yml.
67
+ not_applicable_when: null
68
+ rationale: >-
69
+ An architecture that exists only in people's heads is not visible to
70
+ an agent and erodes with each change. A rules file states the
71
+ architecture in a form a tool can check. Running it in CI is what stops
72
+ new violations.
73
+ sources:
74
+ - "dependency-cruiser rules reference. https://github.com/sverweij/dependency-cruiser/blob/main/doc/rules-reference.md"
75
+ - "import-linter contract types. https://import-linter.readthedocs.io/en/stable/contract_types.html"
76
+
77
+ - id: "3.2"
78
+ key: boundary_violations
79
+ name: Boundary violations
80
+ layer: static
81
+ weight: 30
82
+ requires: []
83
+ pooling: ratio
84
+ raw:
85
+ unit: violations per 1,000 imports
86
+ definition: >-
87
+ Import edges that break a rule, divided by all internal import edges
88
+ / 1,000. Rules come from the repository's declared rules file, or
89
+ from the preset when none is declared. Rules with severity error or
90
+ warn count; info is ignored. Each violating edge counts once, even
91
+ when it breaks several rules. Imports from test files are excluded.
92
+ Type-only imports are included, except where a preset rule says
93
+ otherwise.
94
+ when_empty: 100
95
+ scoring:
96
+ method: piecewise_linear
97
+ anchors:
98
+ - raw: 0
99
+ score: 100
100
+ descriptor: Every import respects the architecture.
101
+ - raw: 2
102
+ score: 75
103
+ descriptor: A few known shortcuts.
104
+ - raw: 10
105
+ score: 50
106
+ descriptor: The layers exist in name only in places.
107
+ - raw: 30
108
+ score: 0
109
+ descriptor: Anything imports anything.
110
+ measure:
111
+ typescript_javascript: dependency-cruiser with the declared rules or the preset converted to dependency-cruiser rules.
112
+ python: grimp builds the import graph; import-linter checks the declared contracts or the preset converted to contracts.
113
+ not_applicable_when: null
114
+ rationale: >-
115
+ Each violation is a dependency a reader would not expect from the
116
+ directory structure. The count is normalized per 1,000 imports so
117
+ repositories of different sizes are comparable.
118
+ sources:
119
+ - "Martin, R. C. Clean Architecture. Prentice Hall, 2017 (the Dependency Rule)."
120
+ - "Cockburn, A. Hexagonal Architecture (Ports and Adapters), 2005. https://alistair.cockburn.us/hexagonal-architecture/"
121
+
122
+ - id: "3.3"
123
+ key: import_cycles
124
+ name: Import cycles
125
+ layer: static
126
+ weight: 25
127
+ requires: []
128
+ pooling: ratio
129
+ raw:
130
+ unit: percent
131
+ definition: >-
132
+ Logic files that belong to an import cycle / all logic files. A cycle
133
+ is a strongly connected component of two or more files in the graph
134
+ of module-level static imports. Type-only imports, dynamic imports,
135
+ imports inside functions and imports under
136
+ "if TYPE_CHECKING:" are excluded.
137
+ when_empty: 100
138
+ scoring:
139
+ method: piecewise_linear
140
+ anchors:
141
+ - raw: 0
142
+ score: 100
143
+ descriptor: The import graph has no cycles.
144
+ - raw: 2
145
+ score: 75
146
+ descriptor: One or two small cycles.
147
+ - raw: 5
148
+ score: 50
149
+ descriptor: Cycles connect several areas.
150
+ - raw: 15
151
+ score: 0
152
+ descriptor: Large parts of the code depend on each other.
153
+ measure:
154
+ typescript_javascript: dependency-cruiser cycle detection on the filtered graph.
155
+ python: grimp import graph, filtered as defined, then strongly connected components.
156
+ not_applicable_when: null
157
+ rationale: >-
158
+ Files in a cycle cannot be understood, tested or changed one at a time.
159
+ Cycles also cause import-order bugs at runtime, especially in Python.
160
+ Type-only imports are excluded because they have no runtime effect.
161
+ sources:
162
+ - "Martin, R. C. Agile Software Development: Principles, Patterns, and Practices. Prentice Hall, 2002 (the Acyclic Dependencies Principle)."
163
+
164
+ - id: "3.4"
165
+ key: public_entry_discipline
166
+ name: Public-entry discipline
167
+ layer: static
168
+ weight: 15
169
+ requires: []
170
+ pooling: ratio
171
+ raw:
172
+ unit: percent
173
+ definition: >-
174
+ Imports that cross from one module into another and land on that
175
+ module's public entry / all imports that cross between modules.
176
+ Modules are defined by the preset (or by the independence or layer
177
+ contracts in the declared rules). The public entry is index.* in
178
+ TypeScript and JavaScript, and __init__.py in Python.
179
+ when_empty: 100
180
+ scoring:
181
+ method: piecewise_linear
182
+ anchors:
183
+ - raw: 50
184
+ score: 0
185
+ descriptor: Other modules reach into internals as often as not.
186
+ - raw: 75
187
+ score: 50
188
+ descriptor: Entry points exist and are often bypassed.
189
+ - raw: 90
190
+ score: 75
191
+ descriptor: Entry points are mostly used.
192
+ - raw: 100
193
+ score: 100
194
+ descriptor: Every cross-module import goes through the entry point.
195
+ measure:
196
+ typescript_javascript: Import graph from dependency-cruiser; module roots from the preset.
197
+ python: >-
198
+ grimp import graph; module roots from the preset. In Django apps,
199
+ models, services and selectors are also public entries.
200
+ not_applicable_when: >-
201
+ The repository has fewer than two modules as defined by the preset or
202
+ the declared rules. Splitting code into modules is a design choice the
203
+ methodology does not score.
204
+ rationale: >-
205
+ A public entry point is a small interface the next person can read
206
+ instead of the whole module. When other modules import internals
207
+ directly, every internal file becomes part of the interface and cannot
208
+ be changed safely.
209
+ sources:
210
+ - "HackSoft Django Styleguide. https://github.com/HackSoftware/Django-Styleguide"
211
+
212
+ - id: "3.5"
213
+ key: domain_purity
214
+ name: Domain purity
215
+ layer: static
216
+ weight: 15
217
+ requires: []
218
+ pooling: ratio
219
+ raw:
220
+ unit: percent
221
+ definition: >-
222
+ Files in the domain layer that import a framework, database, network
223
+ or filesystem package, or an infrastructure directory / all files in
224
+ the domain layer.
225
+ when_empty: 100
226
+ scoring:
227
+ method: piecewise_linear
228
+ anchors:
229
+ - raw: 0
230
+ score: 100
231
+ descriptor: Domain logic is plain code that runs and tests without infrastructure.
232
+ - raw: 5
233
+ score: 75
234
+ descriptor: A few domain files touch infrastructure.
235
+ - raw: 15
236
+ score: 50
237
+ descriptor: Domain logic regularly reaches for the database or the network.
238
+ - raw: 40
239
+ score: 0
240
+ descriptor: The domain layer is a directory name.
241
+ measure:
242
+ domain_layer: >-
243
+ Files under a directory named domain, or files in a layer named
244
+ domain in the declared rules.
245
+ typescript_javascript: >-
246
+ Forbidden imports: next, react, react-dom, express, @prisma/client,
247
+ drizzle-orm, pg, mysql2, mongoose, redis, ioredis, axios, node-fetch,
248
+ fs, node:fs, http, node:http, https, node:https. A call to the global
249
+ fetch also counts.
250
+ python: >-
251
+ Forbidden imports: django, fastapi, flask, starlette, sqlalchemy,
252
+ requests, httpx, aiohttp, boto3, psycopg, psycopg2, redis, pymongo.
253
+ A call to the builtin open also counts.
254
+ infrastructure_directories: infrastructure, infra, db, adapters.
255
+ not_applicable_when: >-
256
+ The repository declares no domain layer. Choosing a layered or
257
+ domain-driven design is a design choice the methodology does not score.
258
+ rationale: >-
259
+ This is the deterministic part of domain-driven design alignment. Pure
260
+ domain code can be tested without a database or network and changed
261
+ without reading infrastructure code. The methodology does not judge
262
+ whether the domain model is good; it checks that the repository keeps
263
+ the boundary it declared.
264
+ sources:
265
+ - "Evans, E. Domain-Driven Design. Addison-Wesley, 2003."
266
+ - "Cockburn, A. Hexagonal Architecture (Ports and Adapters), 2005. https://alistair.cockburn.us/hexagonal-architecture/"
@@ -0,0 +1,192 @@
1
+ dimension:
2
+ id: 4
3
+ key: complexity_maintainability
4
+ name: Complexity & maintainability
5
+ question: How much does someone have to read to make one change correctly?
6
+ agent_relevance: >-
7
+ Every line an agent reads uses context. Long files, complex functions,
8
+ duplicated logic and dead code all increase how much it must read, and
9
+ duplicated logic is where an agent fixes one copy and misses the other.
10
+
11
+ parameters:
12
+ - id: "4.1"
13
+ key: complex_functions
14
+ name: Complex functions
15
+ layer: static
16
+ weight: 30
17
+ requires: []
18
+ pooling: ratio
19
+ raw:
20
+ unit: percent
21
+ definition: >-
22
+ Functions with cognitive complexity strictly greater than 15 / all
23
+ functions in logic files, as enumerated by the tool.
24
+ when_empty: 100
25
+ scoring:
26
+ method: piecewise_linear
27
+ anchors:
28
+ - raw: 0
29
+ score: 100
30
+ descriptor: Every function can be read in one pass.
31
+ - raw: 3
32
+ score: 75
33
+ descriptor: A few hard functions.
34
+ - raw: 8
35
+ score: 50
36
+ descriptor: Hard functions in most areas.
37
+ - raw: 20
38
+ score: 0
39
+ descriptor: Logic is concentrated in functions nobody wants to touch.
40
+ measure:
41
+ typescript_javascript: eslint-plugin-sonarjs cognitive-complexity rule, threshold 15.
42
+ python: complexipy, threshold 15.
43
+ not_applicable_when: null
44
+ rationale: >-
45
+ Cognitive complexity counts nesting and breaks in linear flow, which
46
+ is closer to how hard a function is to read than cyclomatic
47
+ complexity. 15 is the default threshold in SonarQube, eslint-plugin-sonarjs
48
+ and complexipy. Counting the share of functions over the threshold,
49
+ instead of the mean, keeps many small functions from hiding a few
50
+ large ones.
51
+ sources:
52
+ - "Campbell, G. A. Cognitive Complexity: A new way of measuring understandability. SonarSource, 2018. https://www.sonarsource.com/docs/CognitiveComplexity.pdf"
53
+ - "SonarQube rule S3776 (Cognitive Complexity of functions should not be too high), default threshold 15."
54
+ - "McCabe, T. J. A Complexity Measure. IEEE Transactions on Software Engineering, 1976 (cyclomatic complexity, threshold 10)."
55
+
56
+ - id: "4.2"
57
+ key: large_files
58
+ name: Lines in large files
59
+ layer: static
60
+ weight: 20
61
+ requires: []
62
+ pooling: ratio
63
+ raw:
64
+ unit: percent
65
+ definition: >-
66
+ Logic lines in logic files with more than 500 logic lines / all logic
67
+ lines.
68
+ when_empty: 100
69
+ scoring:
70
+ method: piecewise_linear
71
+ anchors:
72
+ - raw: 0
73
+ score: 100
74
+ descriptor: Every file fits in one read.
75
+ - raw: 10
76
+ score: 75
77
+ descriptor: A few large files.
78
+ - raw: 25
79
+ score: 50
80
+ descriptor: A quarter of the code lives in large files.
81
+ - raw: 50
82
+ score: 0
83
+ descriptor: Most of the code lives in files too large to read whole.
84
+ measure:
85
+ typescript_javascript: Logic line count per file (scope.yaml).
86
+ python: Logic line count per file (scope.yaml); docstrings do not count.
87
+ not_applicable_when: null
88
+ rationale: >-
89
+ A large file has to be read in parts, so changes to it are made with
90
+ partial context. The threshold of 500 logic lines is more lenient than
91
+ ESLint's max-lines default (300 physical lines) and stricter than
92
+ Pylint's max-module-lines default (1,000). The raw value is a share of
93
+ lines, not of files, so one 5,000-line file weighs more than one
94
+ 501-line file.
95
+ sources:
96
+ - "ESLint max-lines rule, default 300. https://eslint.org/docs/latest/rules/max-lines"
97
+ - "Pylint too-many-lines (C0302), max-module-lines default 1000."
98
+
99
+ - id: "4.3"
100
+ key: duplication
101
+ name: Duplication
102
+ layer: static
103
+ weight: 25
104
+ requires: []
105
+ pooling: ratio
106
+ raw:
107
+ unit: percent
108
+ definition: Duplicated lines / all lines in logic files, as reported by jscpd.
109
+ when_empty: 100
110
+ scoring:
111
+ method: piecewise_linear
112
+ anchors:
113
+ - raw: 3
114
+ score: 100
115
+ descriptor: Duplication is incidental.
116
+ - raw: 5
117
+ score: 75
118
+ descriptor: Some copied blocks.
119
+ - raw: 10
120
+ score: 50
121
+ descriptor: Copy and paste is a common way to add features.
122
+ - raw: 20
123
+ score: 0
124
+ descriptor: Large parts of the logic exist in several copies.
125
+ measure:
126
+ typescript_javascript: jscpd, min-tokens 50, min-lines 5, logic files only.
127
+ python: jscpd, same settings.
128
+ not_applicable_when: null
129
+ rationale: >-
130
+ A bug fixed in one copy stays in the others. This is a common agent
131
+ mistake: the agent finds one copy, fixes it, and its tests pass. 3% is
132
+ the default duplication limit in SonarQube's quality gate.
133
+ sources:
134
+ - "SonarQube default quality gate (Sonar way): duplicated lines on new code at most 3%."
135
+ - "jscpd. https://github.com/kucherenko/jscpd"
136
+
137
+ - id: "4.4"
138
+ key: dead_code
139
+ name: Unused exports and files
140
+ layer: static
141
+ weight: 25
142
+ requires: []
143
+ pooling: ratio
144
+ raw:
145
+ unit: percent
146
+ definition: >-
147
+ (Unused exported symbols + exported symbols in unused files) / all
148
+ exported symbols in logic files. Exported types count. Library entry
149
+ points (package.json exports and main, Python __all__ of a
150
+ distributed package) count as used.
151
+ when_empty: 100
152
+ scoring:
153
+ method: piecewise_linear
154
+ anchors:
155
+ - raw: 0
156
+ score: 100
157
+ descriptor: Everything exported is used.
158
+ - raw: 3
159
+ score: 75
160
+ descriptor: A little dead code.
161
+ - raw: 8
162
+ score: 50
163
+ descriptor: Dead code is common.
164
+ - raw: 20
165
+ score: 0
166
+ descriptor: A large share of the code is never called.
167
+ measure:
168
+ typescript_javascript: knip (unused files, exports and types), with framework plugins enabled.
169
+ python: >-
170
+ vulture with min-confidence 60 for unused functions, classes and
171
+ methods; decorated definitions are treated as used. A method is
172
+ also treated as used when its class, directly or through base
173
+ classes defined in the repository, has a base class defined
174
+ outside the repository (standard library or third party), or when
175
+ a base class defined in the repository has a method of the same
176
+ name; object and Generic do not count as bases. Unused modules:
177
+ modules no other module imports, excluding entry modules (__main__,
178
+ manage.py, wsgi, asgi, settings, conftest, targets of
179
+ [project.scripts], Django convention modules such as models, admin,
180
+ apps, urls, views, and FastAPI main.py).
181
+ not_applicable_when: null
182
+ rationale: >-
183
+ Dead code costs reading time and looks like a place to make a change.
184
+ An agent that edits an unused function gets no test failure and no
185
+ effect. Decorated Python definitions are treated as used because
186
+ frameworks register them by decorator (routes, fixtures, signals).
187
+ Methods that override a base class are treated as used for the same
188
+ reason: frameworks and the standard library call hooks such as
189
+ do_GET, setUp or emit by name, which vulture cannot see.
190
+ sources:
191
+ - "knip. https://knip.dev"
192
+ - "vulture. https://github.com/jendrikseipp/vulture"
@@ -0,0 +1,223 @@
1
+ dimension:
2
+ id: 5
3
+ key: dependency_security_hygiene
4
+ name: Dependency & security hygiene
5
+ question: Is the code built on parts that are known, current and safe?
6
+ agent_relevance: >-
7
+ An agent adds dependencies and copies patterns from what it finds. An
8
+ undeclared dependency, a stale major version or a committed secret gets
9
+ copied into new code the same way good patterns do.
10
+
11
+ as_of_date: >-
12
+ Parameters 5.1 and 5.2 are evaluated at the run's as-of date: advisories
13
+ published on or before that date count, and the latest version of a
14
+ package is the latest published on or before that date.
15
+
16
+ parameters:
17
+ - id: "5.1"
18
+ key: known_vulnerabilities
19
+ name: Known vulnerabilities in production dependencies
20
+ layer: static
21
+ weight: 30
22
+ requires: []
23
+ pooling: ratio
24
+ raw:
25
+ unit: severity points
26
+ definition: >-
27
+ Sum of severity points over advisories affecting resolved production
28
+ dependencies (direct and transitive). Critical 10, high 5, moderate 2,
29
+ low 0.5. Each advisory counts once per package; aliases are
30
+ deduplicated. Withdrawn advisories are excluded.
31
+ when_empty: 100
32
+ scoring:
33
+ method: piecewise_linear
34
+ anchors:
35
+ - raw: 0
36
+ score: 100
37
+ descriptor: No known vulnerabilities.
38
+ - raw: 2
39
+ score: 75
40
+ descriptor: One moderate, or a few low.
41
+ - raw: 10
42
+ score: 50
43
+ descriptor: A critical, or several highs.
44
+ - raw: 30
45
+ score: 0
46
+ descriptor: Several criticals. Nobody is watching.
47
+ measure:
48
+ typescript_javascript: osv-scanner on the lockfile, against the OSV snapshot for the as-of date. Production dependencies only.
49
+ python: osv-scanner on the lockfile, against the OSV snapshot for the as-of date. Production dependencies only.
50
+ severity: >-
51
+ The GitHub Security Advisory severity label when present. Otherwise
52
+ the CVSS v3.1 base score band (critical 9.0-10.0, high 7.0-8.9,
53
+ moderate 4.0-6.9, low 0.1-3.9). Otherwise moderate.
54
+ not_applicable_when: null
55
+ rationale: >-
56
+ Severity points stop one critical advisory from scoring the same as
57
+ one low one. The count is not normalized by repository size, because
58
+ a vulnerability's risk does not shrink as the codebase grows.
59
+ Development-only dependencies are reported separately and not scored.
60
+ sources:
61
+ - "OSV schema (published, withdrawn, aliases). https://ossf.github.io/osv-schema/"
62
+ - "FIRST. CVSS v3.1 Specification, qualitative severity rating scale. https://www.first.org/cvss/v3.1/specification-document"
63
+
64
+ - id: "5.2"
65
+ key: major_versions_behind
66
+ name: Dependencies a major version behind
67
+ layer: static
68
+ weight: 20
69
+ requires: []
70
+ pooling: ratio
71
+ raw:
72
+ unit: percent
73
+ definition: >-
74
+ Direct dependencies (production and development) whose resolved
75
+ version is at least one major version behind the highest stable,
76
+ non-yanked version published on or before the as-of date / all direct
77
+ dependencies. For 0.x versions, a minor version bump counts as major.
78
+ when_empty: 100
79
+ scoring:
80
+ method: piecewise_linear
81
+ anchors:
82
+ - raw: 0
83
+ score: 100
84
+ descriptor: Every direct dependency is on its current major.
85
+ - raw: 10
86
+ score: 75
87
+ descriptor: A few dependencies are behind.
88
+ - raw: 25
89
+ score: 50
90
+ descriptor: A quarter of dependencies are behind.
91
+ - raw: 50
92
+ score: 0
93
+ descriptor: Upgrades have stopped.
94
+ measure:
95
+ typescript_javascript: npm registry publish times from the registry snapshot.
96
+ python: PyPI release upload times from the registry snapshot.
97
+ not_applicable_when: null
98
+ rationale: >-
99
+ Each major version behind is a migration someone will have to do, and
100
+ an agent writing code against current documentation will use APIs the
101
+ installed version does not have. Development dependencies are included
102
+ because outdated tooling affects every change.
103
+ sources:
104
+ - "Semantic Versioning 2.0.0, item 4 (major version zero). https://semver.org"
105
+
106
+ - id: "5.3"
107
+ key: lockfile
108
+ name: Lockfile
109
+ layer: static
110
+ weight: 15
111
+ requires: []
112
+ pooling: unit_weighted
113
+ raw:
114
+ unit: level
115
+ definition: Whether a lockfile exists and matches the manifest.
116
+ when_empty: 0
117
+ scoring:
118
+ method: levels
119
+ levels:
120
+ - level: none
121
+ score: 0
122
+ descriptor: No lockfile. Every install can resolve different versions.
123
+ - level: out_of_sync
124
+ score: 40
125
+ descriptor: A lockfile exists but does not match the manifest.
126
+ - level: in_sync
127
+ score: 100
128
+ descriptor: The lockfile matches the manifest.
129
+ measure:
130
+ typescript_javascript: >-
131
+ package-lock.json, npm-shrinkwrap.json, pnpm-lock.yaml, yarn.lock or
132
+ bun.lock. In sync means every dependency declared in package.json is
133
+ present in the lockfile with a version that satisfies its range.
134
+ python: >-
135
+ uv.lock, poetry.lock, pdm.lock, Pipfile.lock, or requirements files
136
+ with every requirement pinned with == and --hash. In sync means
137
+ every dependency declared in pyproject.toml (or Pipfile) is in the
138
+ lockfile with a version that satisfies its specifier, or for
139
+ uv.lock and poetry.lock, the lockfile's recorded manifest hash
140
+ matches.
141
+ not_applicable_when: null
142
+ rationale: >-
143
+ Without a lockfile, two installs of the same commit can produce
144
+ different code. Results stop being reproducible, including this
145
+ methodology's results.
146
+ sources:
147
+ - "npm ci documentation. https://docs.npmjs.com/cli/commands/npm-ci"
148
+
149
+ - id: "5.4"
150
+ key: secrets_in_tree
151
+ name: Secrets in the working tree
152
+ layer: static
153
+ weight: 20
154
+ requires: []
155
+ pooling: unit_weighted
156
+ raw:
157
+ unit: count
158
+ definition: >-
159
+ Secrets found by the pinned gitleaks default ruleset in every
160
+ tracked file at the scanned commit. .healthignore does not apply.
161
+ .gitleaksignore applies and is part of the ignore set.
162
+ when_empty: 100
163
+ scoring:
164
+ method: piecewise_linear
165
+ anchors:
166
+ - raw: 0
167
+ score: 100
168
+ descriptor: No secrets in the code.
169
+ - raw: 1
170
+ score: 0
171
+ descriptor: At least one secret is in the code.
172
+ measure:
173
+ all_languages: gitleaks dir on the checkout, default ruleset of the pinned version.
174
+ not_applicable_when: null
175
+ rationale: >-
176
+ One committed secret is enough to cause a breach, so the parameter is
177
+ binary. Secrets in git history are reported but not scored, because
178
+ the fix is rotating the key, which cannot be seen in the code.
179
+ sources:
180
+ - "CWE-798: Use of Hard-coded Credentials. https://cwe.mitre.org/data/definitions/798.html"
181
+ - "gitleaks. https://github.com/gitleaks/gitleaks"
182
+
183
+ - id: "5.5"
184
+ key: declaration_errors
185
+ name: Dependency declaration errors
186
+ layer: static
187
+ weight: 15
188
+ requires: []
189
+ pooling: ratio
190
+ raw:
191
+ unit: percent
192
+ definition: >-
193
+ (Declared dependencies that are never imported + imported packages
194
+ that are not declared) / declared direct dependencies.
195
+ when_empty: 100
196
+ scoring:
197
+ method: piecewise_linear
198
+ anchors:
199
+ - raw: 0
200
+ score: 100
201
+ descriptor: The manifest matches the code.
202
+ - raw: 5
203
+ score: 75
204
+ descriptor: A few stale or missing entries.
205
+ - raw: 15
206
+ score: 50
207
+ descriptor: The manifest and the code have drifted apart.
208
+ - raw: 30
209
+ score: 0
210
+ descriptor: Nobody maintains the manifest.
211
+ measure:
212
+ typescript_javascript: knip unused dependencies plus unlisted dependencies.
213
+ python: >-
214
+ deptry DEP001 (missing), DEP002 (unused) and DEP003 (transitive
215
+ imported directly). DEP004 (development dependency used in
216
+ production code) is not counted.
217
+ not_applicable_when: null
218
+ rationale: >-
219
+ An undeclared import works until the transitive dependency that
220
+ provides it changes. An unused declaration adds install time and
221
+ attack surface. Both mislead a reader about what the code depends on.
222
+ sources:
223
+ - "deptry rules. https://deptry.com/rules-violations/"