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,240 @@
1
+ dimension:
2
+ id: 1
3
+ key: safety_net
4
+ name: Safety net
5
+ question: Will a change break something without anyone noticing?
6
+ agent_relevance: >-
7
+ An agent cannot remember how the system is supposed to behave. The test
8
+ suite is the only record of that behaviour it can check its change
9
+ against.
10
+
11
+ parameters:
12
+ - id: "1.1"
13
+ key: suite_pass_rate
14
+ name: Suite pass rate
15
+ layer: execution
16
+ weight: 20
17
+ requires: [install, test_run]
18
+ pooling: ratio
19
+ raw:
20
+ unit: percent
21
+ definition: >-
22
+ passed / (passed + failed + errored) test cases. Skipped and
23
+ expected-failure tests are excluded from both numerator and
24
+ denominator. A test file that fails to load or collect counts as one
25
+ failed test case.
26
+ when_empty: 0
27
+ scoring:
28
+ method: piecewise_linear
29
+ anchors:
30
+ - raw: 80
31
+ score: 0
32
+ descriptor: The suite is red. Nobody can use it to check a change.
33
+ - raw: 95
34
+ score: 50
35
+ descriptor: Mostly green, with known failures people have learned to ignore.
36
+ - raw: 100
37
+ score: 100
38
+ descriptor: Green. A new failure means the change broke something.
39
+ measure:
40
+ typescript_javascript: >-
41
+ vitest, jest, mocha or node:test, detected from package.json
42
+ dependencies and the test script. Run once with a JSON reporter.
43
+ Playwright and Cypress suites are not run.
44
+ python: >-
45
+ pytest (also runs unittest suites), with
46
+ --continue-on-collection-errors and a JSON or JUnit report.
47
+ not_applicable_when: null
48
+ rationale: >-
49
+ A suite with known failures stops working as a check: a new failure
50
+ looks the same as the old ones. The curve is steep on purpose. 95% of a
51
+ 200-test suite is 10 failing tests, which is already enough for people
52
+ to stop reading the output.
53
+ sources: []
54
+
55
+ - id: "1.2"
56
+ key: line_coverage
57
+ name: Line coverage
58
+ layer: execution
59
+ weight: 25
60
+ requires: [install, test_run]
61
+ pooling: ratio
62
+ raw:
63
+ unit: percent
64
+ definition: >-
65
+ Covered lines / coverable lines across all logic files, including
66
+ logic files no test loads. A file no test loads counts as 0%
67
+ covered. For TypeScript and JavaScript the coverable lines are
68
+ health's code lines (lines with at least one token). A line counts as
69
+ covered when the innermost statement or function containing it ran.
70
+ when_empty: 0
71
+ scoring:
72
+ method: piecewise_linear
73
+ anchors:
74
+ - raw: 10
75
+ score: 0
76
+ descriptor: No safety net.
77
+ - raw: 40
78
+ score: 50
79
+ descriptor: Happy paths only.
80
+ - raw: 60
81
+ score: 75
82
+ descriptor: Acceptable.
83
+ - raw: 80
84
+ score: 100
85
+ descriptor: Well covered. No extra credit above this.
86
+ measure:
87
+ typescript_javascript: >-
88
+ v8 or istanbul coverage with the json-summary and json reporters
89
+ (vitest coverage.include, jest collectCoverageFrom over logic files, c8
90
+ for mocha and node:test). Lines are counted by health from the json
91
+ report's statement and function ranges, not taken from the tool, so
92
+ the same code gives the same count under every runner. Repository
93
+ coverage thresholds are disabled so they cannot fail the run.
94
+ python: >-
95
+ coverage.py through pytest-cov, with --cov set to every first-party
96
+ package so files no test imports are included.
97
+ not_applicable_when: null
98
+ rationale: >-
99
+ Coverage shows which code no test executes. It does not show that the
100
+ executed code is checked, which is why 1.5 exists. The 60% anchor
101
+ matches Google's "acceptable" level. The 100 anchor is 80%, which is
102
+ the coverage SonarQube's default quality gate requires on new code and
103
+ sits between Google's "commendable" (75%) and "exemplary" (90%).
104
+ Above 80% the methodology gives no extra credit, because the remaining
105
+ code is often glue that costs more to test than it returns.
106
+ sources:
107
+ - "Arguelles, Ivanković, Bender. Code Coverage Best Practices. Google Testing Blog, 2020. https://testing.googleblog.com/2020/08/code-coverage-best-practices.html"
108
+ - "SonarQube default quality gate (Sonar way): coverage on new code at least 80%."
109
+
110
+ - id: "1.3"
111
+ key: branch_coverage
112
+ name: Branch coverage
113
+ layer: execution
114
+ weight: 15
115
+ requires: [install, test_run]
116
+ pooling: ratio
117
+ raw:
118
+ unit: percent
119
+ definition: Covered branches / total branches across all logic files, including files no test loads.
120
+ when_empty: 0
121
+ scoring:
122
+ method: piecewise_linear
123
+ anchors:
124
+ - raw: 5
125
+ score: 0
126
+ descriptor: Only one path through each decision is ever run.
127
+ - raw: 30
128
+ score: 50
129
+ descriptor: Error and edge paths are mostly untested.
130
+ - raw: 50
131
+ score: 75
132
+ descriptor: Main error paths are tested.
133
+ - raw: 70
134
+ score: 100
135
+ descriptor: Most decisions are tested both ways.
136
+ measure:
137
+ typescript_javascript: >-
138
+ Same coverage run as 1.2; branch totals from json-summary. They come
139
+ from the runner's coverage tool and can differ slightly between
140
+ runners on the same code.
141
+ python: Same run as 1.2 with --cov-branch.
142
+ not_applicable_when: null
143
+ rationale: >-
144
+ Tests written quickly cover the happy path. Line coverage can be high
145
+ while every error branch is untested. Branch coverage shows that gap.
146
+ Branch percentages are lower than line percentages on the same code,
147
+ so the anchors sit about 10 points lower than 1.2's.
148
+ sources: []
149
+
150
+ - id: "1.4"
151
+ key: modules_with_tests
152
+ name: Modules with tests
153
+ layer: static
154
+ weight: 25
155
+ requires: []
156
+ pooling: ratio
157
+ raw:
158
+ unit: percent
159
+ definition: >-
160
+ Logic files directly imported by at least one test file / all logic
161
+ files. Imports are followed through barrel files and Python
162
+ __init__.py re-exports to the file that defines the imported name.
163
+ Transitive imports do not count.
164
+ when_empty: 0
165
+ scoring:
166
+ method: piecewise_linear
167
+ anchors:
168
+ - raw: 10
169
+ score: 0
170
+ descriptor: Almost nothing has a test.
171
+ - raw: 40
172
+ score: 50
173
+ descriptor: Some modules have tests; most do not.
174
+ - raw: 70
175
+ score: 75
176
+ descriptor: Most modules have a test.
177
+ - raw: 90
178
+ score: 100
179
+ descriptor: Nearly every module has a test.
180
+ measure:
181
+ typescript_javascript: >-
182
+ AST import graph from test files (static imports, require and dynamic
183
+ import with a literal path).
184
+ python: AST import graph from test files (import and from-import statements).
185
+ not_applicable_when: null
186
+ rationale: >-
187
+ This is the static counterpart to coverage. It still works when the
188
+ suite does not run, so a repository with tests that are broken gets
189
+ partial credit, and "make the existing tests run" shows up as a gain
190
+ in 1.1 to 1.3. Only direct imports count; otherwise one test that
191
+ imports the app entry point would mark every file as tested.
192
+ sources: []
193
+
194
+ - id: "1.5"
195
+ key: asserting_tests
196
+ name: Tests that assert something
197
+ layer: static
198
+ weight: 15
199
+ requires: []
200
+ pooling: ratio
201
+ raw:
202
+ unit: percent
203
+ definition: >-
204
+ Test cases containing at least one assertion / all test cases. An
205
+ assertion inside a helper function defined in the same file and
206
+ called by the test counts (one level deep).
207
+ when_empty: 0
208
+ scoring:
209
+ method: piecewise_linear
210
+ anchors:
211
+ - raw: 50
212
+ score: 0
213
+ descriptor: Many tests run code without checking anything.
214
+ - raw: 80
215
+ score: 50
216
+ descriptor: A noticeable share of tests check nothing.
217
+ - raw: 95
218
+ score: 75
219
+ descriptor: A few tests check nothing.
220
+ - raw: 100
221
+ score: 100
222
+ descriptor: Every test checks something.
223
+ measure:
224
+ typescript_javascript: >-
225
+ Test case: a call to it, test or their .each/.only/.concurrent
226
+ variants. Assertion: expect(...), assert(...), assert.*,
227
+ expectTypeOf(...), t.assert* / t.is / t.deepEqual (node:test and
228
+ ava contexts).
229
+ python: >-
230
+ Test case: a test_* function in a test module, or a test_* method of
231
+ a unittest.TestCase subclass. Assertion: an assert statement,
232
+ self.assert*, pytest.raises, pytest.warns, or a mock method call
233
+ starting with assert_.
234
+ not_applicable_when: null
235
+ rationale: >-
236
+ A test without an assertion raises coverage and checks nothing. This is
237
+ the cheapest way to game 1.2 and 1.3, and it is common in generated
238
+ tests. The anchors are strict because writing a test without an
239
+ assertion is almost never intentional.
240
+ sources: []
@@ -0,0 +1,289 @@
1
+ dimension:
2
+ id: 2
3
+ key: type_static_rigor
4
+ name: Type & static rigor
5
+ question: Does the code tell you what it expects before you run it?
6
+ agent_relevance: >-
7
+ Type errors and lint findings are the fastest feedback an agent gets. A
8
+ strict typechecker catches a wrong change in seconds, before any test
9
+ runs, and the types document what each function accepts and returns.
10
+
11
+ parameters:
12
+ - id: "2.1"
13
+ key: typecheck_errors
14
+ name: Typecheck errors
15
+ layer: execution
16
+ weight: 25
17
+ requires: [install, typecheck_run]
18
+ pooling: ratio
19
+ raw:
20
+ unit: errors per 1,000 source lines
21
+ definition: >-
22
+ Typechecker errors located in in-scope files (logic and test), divided
23
+ by source lines / 1,000. Errors in excluded files and in dependencies
24
+ are ignored. Warnings are ignored.
25
+ when_empty: 100
26
+ scoring:
27
+ method: piecewise_linear
28
+ anchors:
29
+ - raw: 0
30
+ score: 100
31
+ descriptor: Clean. A new error means the change introduced it.
32
+ - raw: 1
33
+ score: 75
34
+ descriptor: A few known errors.
35
+ - raw: 5
36
+ score: 50
37
+ descriptor: Errors are routine and people work around them.
38
+ - raw: 20
39
+ score: 0
40
+ descriptor: The typechecker is ignored.
41
+ measure:
42
+ typescript_javascript: >-
43
+ tsc --noEmit with the repository's tsconfig.json (each tsconfig that
44
+ a workspace package declares). JavaScript-only repositories without a
45
+ tsconfig or jsconfig are checked with a default config: allowJs,
46
+ checkJs, strict false.
47
+ python: >-
48
+ pyright with the repository's configuration (pyrightconfig.json or
49
+ [tool.pyright]). If only mypy is configured, mypy with its
50
+ configuration. If both are configured, pyright. If neither,
51
+ pyright in basic mode.
52
+ not_applicable_when: null
53
+ rationale: >-
54
+ The repository's own config is used so the count reflects what its
55
+ developers see. A config error, or a checker that cannot start, is a
56
+ repository failure and scores 0. Untyped code is checked with a
57
+ default config on purpose, so "add types" is visible as a score change.
58
+ sources:
59
+ - "TypeScript: Type checking JavaScript files (checkJs). https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html"
60
+ - "Pyright configuration: typeCheckingMode. https://microsoft.github.io/pyright/#/configuration"
61
+
62
+ - id: "2.2"
63
+ key: declared_strictness
64
+ name: Declared strictness
65
+ layer: static
66
+ weight: 20
67
+ requires: []
68
+ pooling: unit_weighted
69
+ raw:
70
+ unit: level
71
+ definition: >-
72
+ The strictness the repository's typechecker configuration declares,
73
+ read from the config files without running anything.
74
+ when_empty: 0
75
+ scoring:
76
+ method: levels
77
+ levels:
78
+ typescript_javascript:
79
+ - level: none
80
+ score: 0
81
+ descriptor: >-
82
+ No strict-family flag is enabled, or JavaScript without
83
+ checkJs.
84
+ - level: partial
85
+ score: 40
86
+ descriptor: >-
87
+ Some strict-family flags are enabled, or strict is true with
88
+ one or more strict-family flags turned back off.
89
+ - level: strict
90
+ score: 75
91
+ descriptor: strict is true and no strict-family flag is turned off.
92
+ - level: strict_plus
93
+ score: 100
94
+ descriptor: strict plus noUncheckedIndexedAccess.
95
+ python:
96
+ - level: none
97
+ score: 0
98
+ descriptor: No pyright or mypy configuration.
99
+ - level: basic
100
+ score: 40
101
+ descriptor: pyright typeCheckingMode basic, or mypy with default settings.
102
+ - level: standard
103
+ score: 75
104
+ descriptor: >-
105
+ pyright typeCheckingMode standard (pyright's default when a
106
+ config exists), or mypy with disallow_untyped_defs and
107
+ check_untyped_defs.
108
+ - level: strict
109
+ score: 100
110
+ descriptor: pyright typeCheckingMode strict, or mypy strict = true.
111
+ measure:
112
+ typescript_javascript: >-
113
+ tsconfig.json with extends resolved. Strict-family flags: noImplicitAny,
114
+ strictNullChecks, strictFunctionTypes, strictBindCallApply,
115
+ strictPropertyInitialization, noImplicitThis, useUnknownInCatchVariables,
116
+ alwaysStrict.
117
+ python: >-
118
+ pyrightconfig.json, [tool.pyright] or [tool.basedpyright] in
119
+ pyproject.toml; mypy.ini, .mypy.ini, setup.cfg [mypy] or [tool.mypy]
120
+ in pyproject.toml. When both are configured, pyright's level is used.
121
+ not_applicable_when: null
122
+ rationale: >-
123
+ 2.1 counts errors under whatever config the repository chose. Without
124
+ 2.2, turning strictness off would raise the score. Strict null checks
125
+ catch access to null and undefined values, a common runtime error in
126
+ both languages. noUncheckedIndexedAccess
127
+ makes array and record lookups return possibly undefined values, which
128
+ is where strict TypeScript still lets runtime errors through.
129
+ sources:
130
+ - "TypeScript TSConfig reference: strict. https://www.typescriptlang.org/tsconfig#strict"
131
+ - "TypeScript TSConfig reference: noUncheckedIndexedAccess. https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess"
132
+ - "Pyright configuration: typeCheckingMode. https://microsoft.github.io/pyright/#/configuration"
133
+ - "mypy command line: --strict. https://mypy.readthedocs.io/en/stable/command_line.html"
134
+
135
+ - id: "2.3"
136
+ key: escape_hatches
137
+ name: Escape hatches
138
+ layer: static
139
+ weight: 20
140
+ requires: []
141
+ pooling: ratio
142
+ raw:
143
+ unit: escape hatches per 1,000 logic lines
144
+ definition: >-
145
+ Points where code opts out of the typechecker, counted in logic
146
+ files, divided by logic lines / 1,000. A file-level opt-out counts
147
+ as 10.
148
+ when_empty: 100
149
+ scoring:
150
+ method: piecewise_linear
151
+ anchors:
152
+ - raw: 0
153
+ score: 100
154
+ descriptor: The types are trusted everywhere.
155
+ - raw: 2
156
+ score: 75
157
+ descriptor: A few documented exceptions.
158
+ - raw: 5
159
+ score: 50
160
+ descriptor: Casts are a common way to make errors go away.
161
+ - raw: 15
162
+ score: 0
163
+ descriptor: The types say little about what the code does.
164
+ measure:
165
+ typescript_javascript: >-
166
+ Each counts 1: an explicit any type; an as assertion (as const is
167
+ excluded; each as in "as unknown as T" counts separately); an
168
+ angle-bracket assertion <T>x; a non-null assertion x!; a definite
169
+ assignment assertion x!: T; @ts-ignore; @ts-expect-error. Counts 10:
170
+ @ts-nocheck.
171
+ python: >-
172
+ Each counts 1: Any in an annotation; typing.cast(...); # type: ignore
173
+ on a line; # pyright: ignore on a line. Counts 10: # type: ignore
174
+ before the first statement of a module; # mypy: ignore-errors.
175
+ not_applicable_when: null
176
+ rationale: >-
177
+ Each escape hatch is a place where the typechecker cannot help the next
178
+ person. A change that breaks a contract passes the typechecker silently
179
+ at that point. The typescript-eslint no-unsafe-* rules are not used in
180
+ 2.5, so the same problem is not counted twice.
181
+ sources: []
182
+
183
+ - id: "2.4"
184
+ key: typed_public_surface
185
+ name: Typed public surface
186
+ layer: static
187
+ weight: 15
188
+ requires: []
189
+ pooling: ratio
190
+ raw:
191
+ unit: percent
192
+ definition: >-
193
+ Typed slots / all slots on the public surface of logic files. In
194
+ TypeScript and JavaScript a slot is a parameter. In Python a slot is
195
+ a parameter or a return type.
196
+ when_empty: 100
197
+ scoring:
198
+ method: piecewise_linear
199
+ anchors:
200
+ - raw: 20
201
+ score: 0
202
+ descriptor: The public surface is untyped.
203
+ - raw: 50
204
+ score: 50
205
+ descriptor: Half of what callers use is typed.
206
+ - raw: 80
207
+ score: 75
208
+ descriptor: Most of what callers use is typed.
209
+ - raw: 100
210
+ score: 100
211
+ descriptor: Every public function says what it accepts.
212
+ measure:
213
+ typescript_javascript: >-
214
+ Public surface: exported functions, exported arrow functions and
215
+ function expressions, default exports, and public methods of
216
+ exported classes. A parameter is typed if it has a type annotation,
217
+ a default value, a contextual type (for example a callback passed to
218
+ a typed function), or a JSDoc @param type in a file checked with
219
+ checkJs.
220
+ python: >-
221
+ Public surface: module-level functions and classes whose names do not
222
+ start with _, and their methods whose names do not start with _
223
+ (dunder methods are included). If __all__ is defined, it decides
224
+ what is public. Modules whose names start with _ are excluded. self
225
+ and cls are not slots. __init__ needs no return annotation.
226
+ not_applicable_when: null
227
+ rationale: >-
228
+ The public surface is what the next person calls without reading the
229
+ body. In TypeScript, inferred return types are reliable, so only
230
+ parameters are counted. In Python, an unannotated return is Any, so
231
+ both are counted. Untyped JavaScript and untyped Python score low on
232
+ purpose.
233
+ sources:
234
+ - "PEP 484: Type Hints. https://peps.python.org/pep-0484/"
235
+
236
+ - id: "2.5"
237
+ key: baseline_lint
238
+ name: Baseline lint findings
239
+ layer: execution
240
+ weight: 20
241
+ requires: [install, lint_run]
242
+ pooling: ratio
243
+ raw:
244
+ unit: findings per 1,000 source lines
245
+ definition: >-
246
+ Findings from the methodology's pinned correctness ruleset in
247
+ in-scope files (logic and test), divided by source lines / 1,000.
248
+ The repository's own lint config is not used. A file that cannot be
249
+ parsed counts as one finding.
250
+ when_empty: 100
251
+ scoring:
252
+ method: piecewise_linear
253
+ anchors:
254
+ - raw: 0
255
+ score: 100
256
+ descriptor: No known bug patterns.
257
+ - raw: 2
258
+ score: 75
259
+ descriptor: A few.
260
+ - raw: 5
261
+ score: 50
262
+ descriptor: Bug patterns are common.
263
+ - raw: 20
264
+ score: 0
265
+ descriptor: The code has never been linted for correctness.
266
+ measure:
267
+ typescript_javascript: >-
268
+ ESLint with the pinned ruleset. Core rules: no-unreachable,
269
+ no-dupe-keys, no-self-compare, no-unsafe-finally, no-fallthrough,
270
+ no-cond-assign, use-isnan, valid-typeof, no-unsafe-optional-chaining,
271
+ no-dupe-else-if, no-duplicate-case, no-self-assign, getter-return,
272
+ no-async-promise-executor, no-constant-binary-expression,
273
+ no-unused-vars (or @typescript-eslint/no-unused-vars on TypeScript
274
+ files). Type-aware rules on TypeScript files only:
275
+ @typescript-eslint/no-floating-promises, no-misused-promises,
276
+ await-thenable. TypeScript files outside every tsconfig are linted
277
+ without the type-aware rules. react-hooks/rules-of-hooks when react is
278
+ a dependency.
279
+ python: ruff with the rule sets F (Pyflakes), B (flake8-bugbear) and E9 (syntax and IO errors).
280
+ not_applicable_when: null
281
+ rationale: >-
282
+ Each rule in the set catches a real bug pattern, such as an
283
+ unawaited promise or an unreachable branch. Style rules are excluded,
284
+ so formatting choices do not affect the score. The ruleset is pinned
285
+ so every repository is judged by the same rules, and so turning rules
286
+ off in the repository's own config does not raise the score.
287
+ sources:
288
+ - "typescript-eslint rules. https://typescript-eslint.io/rules/"
289
+ - "Ruff rules. https://docs.astral.sh/ruff/rules/"