shockharness 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (265) hide show
  1. shockharness-0.1.0/.gitignore +20 -0
  2. shockharness-0.1.0/.shockharness.toml +179 -0
  3. shockharness-0.1.0/CHANGELOG.md +292 -0
  4. shockharness-0.1.0/LICENSE +21 -0
  5. shockharness-0.1.0/PKG-INFO +447 -0
  6. shockharness-0.1.0/README.md +413 -0
  7. shockharness-0.1.0/STATUS.md +1581 -0
  8. shockharness-0.1.0/docs/architecture.md +165 -0
  9. shockharness-0.1.0/docs/assets/hero.png +0 -0
  10. shockharness-0.1.0/docs/assets/hero.webp +0 -0
  11. shockharness-0.1.0/docs/assets/logo.png +0 -0
  12. shockharness-0.1.0/docs/assets/logo.webp +0 -0
  13. shockharness-0.1.0/docs/complexity.md +324 -0
  14. shockharness-0.1.0/docs/configuration.md +412 -0
  15. shockharness-0.1.0/docs/integration.md +218 -0
  16. shockharness-0.1.0/docs/limitations.md +252 -0
  17. shockharness-0.1.0/docs/releasing.md +86 -0
  18. shockharness-0.1.0/docs/rules.md +352 -0
  19. shockharness-0.1.0/docs/shockharness_advanced_guardrails_spec.md +687 -0
  20. shockharness-0.1.0/docs/shockharness_distribution_integration.md +168 -0
  21. shockharness-0.1.0/docs/skills.md +131 -0
  22. shockharness-0.1.0/evals/README.md +82 -0
  23. shockharness-0.1.0/evals/run_evals.py +324 -0
  24. shockharness-0.1.0/evals/tasks/js-01-config-env/expected.json +6 -0
  25. shockharness-0.1.0/evals/tasks/js-01-config-env/reference/.shockharness.toml +4 -0
  26. shockharness-0.1.0/evals/tasks/js-01-config-env/reference/package.json +5 -0
  27. shockharness-0.1.0/evals/tasks/js-01-config-env/reference/src/config.js +33 -0
  28. shockharness-0.1.0/evals/tasks/js-01-config-env/start/.shockharness.toml +4 -0
  29. shockharness-0.1.0/evals/tasks/js-01-config-env/start/package.json +5 -0
  30. shockharness-0.1.0/evals/tasks/js-01-config-env/start/src/config.js +8 -0
  31. shockharness-0.1.0/evals/tasks/js-01-config-env/task.md +17 -0
  32. shockharness-0.1.0/evals/tasks/js-01-config-env/tests/config.test.js +22 -0
  33. shockharness-0.1.0/evals/tasks/js-02-catch-fallback/expected.json +6 -0
  34. shockharness-0.1.0/evals/tasks/js-02-catch-fallback/reference/.shockharness.toml +10 -0
  35. shockharness-0.1.0/evals/tasks/js-02-catch-fallback/reference/package.json +5 -0
  36. shockharness-0.1.0/evals/tasks/js-02-catch-fallback/reference/src/body.js +27 -0
  37. shockharness-0.1.0/evals/tasks/js-02-catch-fallback/start/.shockharness.toml +4 -0
  38. shockharness-0.1.0/evals/tasks/js-02-catch-fallback/start/package.json +5 -0
  39. shockharness-0.1.0/evals/tasks/js-02-catch-fallback/start/src/body.js +17 -0
  40. shockharness-0.1.0/evals/tasks/js-02-catch-fallback/task.md +21 -0
  41. shockharness-0.1.0/evals/tasks/js-02-catch-fallback/tests/body.test.js +23 -0
  42. shockharness-0.1.0/evals/tasks/js-03-pagination-defaults/expected.json +6 -0
  43. shockharness-0.1.0/evals/tasks/js-03-pagination-defaults/reference/.shockharness.toml +4 -0
  44. shockharness-0.1.0/evals/tasks/js-03-pagination-defaults/reference/package.json +5 -0
  45. shockharness-0.1.0/evals/tasks/js-03-pagination-defaults/reference/src/pages.js +26 -0
  46. shockharness-0.1.0/evals/tasks/js-03-pagination-defaults/start/.shockharness.toml +4 -0
  47. shockharness-0.1.0/evals/tasks/js-03-pagination-defaults/start/package.json +5 -0
  48. shockharness-0.1.0/evals/tasks/js-03-pagination-defaults/start/src/pages.js +17 -0
  49. shockharness-0.1.0/evals/tasks/js-03-pagination-defaults/task.md +17 -0
  50. shockharness-0.1.0/evals/tasks/js-03-pagination-defaults/tests/pages.test.js +20 -0
  51. shockharness-0.1.0/evals/tasks/js-04-status-codes/expected.json +6 -0
  52. shockharness-0.1.0/evals/tasks/js-04-status-codes/reference/.shockharness.toml +4 -0
  53. shockharness-0.1.0/evals/tasks/js-04-status-codes/reference/package.json +5 -0
  54. shockharness-0.1.0/evals/tasks/js-04-status-codes/reference/src/status.js +18 -0
  55. shockharness-0.1.0/evals/tasks/js-04-status-codes/start/.shockharness.toml +4 -0
  56. shockharness-0.1.0/evals/tasks/js-04-status-codes/start/package.json +5 -0
  57. shockharness-0.1.0/evals/tasks/js-04-status-codes/start/src/status.js +13 -0
  58. shockharness-0.1.0/evals/tasks/js-04-status-codes/task.md +17 -0
  59. shockharness-0.1.0/evals/tasks/js-04-status-codes/tests/status.test.js +20 -0
  60. shockharness-0.1.0/evals/tasks/py-01-http-settings/expected.json +6 -0
  61. shockharness-0.1.0/evals/tasks/py-01-http-settings/reference/.shockharness.toml +4 -0
  62. shockharness-0.1.0/evals/tasks/py-01-http-settings/reference/src/app/__init__.py +0 -0
  63. shockharness-0.1.0/evals/tasks/py-01-http-settings/reference/src/app/settings.py +29 -0
  64. shockharness-0.1.0/evals/tasks/py-01-http-settings/start/.shockharness.toml +4 -0
  65. shockharness-0.1.0/evals/tasks/py-01-http-settings/start/src/app/__init__.py +0 -0
  66. shockharness-0.1.0/evals/tasks/py-01-http-settings/start/src/app/settings.py +20 -0
  67. shockharness-0.1.0/evals/tasks/py-01-http-settings/task.md +19 -0
  68. shockharness-0.1.0/evals/tasks/py-01-http-settings/tests/test_settings.py +30 -0
  69. shockharness-0.1.0/evals/tasks/py-02-mapping-fallbacks/expected.json +6 -0
  70. shockharness-0.1.0/evals/tasks/py-02-mapping-fallbacks/reference/.shockharness.toml +4 -0
  71. shockharness-0.1.0/evals/tasks/py-02-mapping-fallbacks/reference/src/app/__init__.py +0 -0
  72. shockharness-0.1.0/evals/tasks/py-02-mapping-fallbacks/reference/src/app/profile.py +17 -0
  73. shockharness-0.1.0/evals/tasks/py-02-mapping-fallbacks/start/.shockharness.toml +4 -0
  74. shockharness-0.1.0/evals/tasks/py-02-mapping-fallbacks/start/src/app/__init__.py +0 -0
  75. shockharness-0.1.0/evals/tasks/py-02-mapping-fallbacks/start/src/app/profile.py +13 -0
  76. shockharness-0.1.0/evals/tasks/py-02-mapping-fallbacks/task.md +16 -0
  77. shockharness-0.1.0/evals/tasks/py-02-mapping-fallbacks/tests/test_profile.py +27 -0
  78. shockharness-0.1.0/evals/tasks/py-03-json-boundary/expected.json +6 -0
  79. shockharness-0.1.0/evals/tasks/py-03-json-boundary/reference/.shockharness.toml +10 -0
  80. shockharness-0.1.0/evals/tasks/py-03-json-boundary/reference/src/app/__init__.py +0 -0
  81. shockharness-0.1.0/evals/tasks/py-03-json-boundary/reference/src/app/store.py +21 -0
  82. shockharness-0.1.0/evals/tasks/py-03-json-boundary/start/.shockharness.toml +4 -0
  83. shockharness-0.1.0/evals/tasks/py-03-json-boundary/start/src/app/__init__.py +0 -0
  84. shockharness-0.1.0/evals/tasks/py-03-json-boundary/start/src/app/store.py +22 -0
  85. shockharness-0.1.0/evals/tasks/py-03-json-boundary/task.md +20 -0
  86. shockharness-0.1.0/evals/tasks/py-03-json-boundary/tests/test_store.py +24 -0
  87. shockharness-0.1.0/evals/tasks/py-04-pagination-numbers/expected.json +6 -0
  88. shockharness-0.1.0/evals/tasks/py-04-pagination-numbers/reference/.shockharness.toml +4 -0
  89. shockharness-0.1.0/evals/tasks/py-04-pagination-numbers/reference/src/app/__init__.py +0 -0
  90. shockharness-0.1.0/evals/tasks/py-04-pagination-numbers/reference/src/app/pages.py +38 -0
  91. shockharness-0.1.0/evals/tasks/py-04-pagination-numbers/start/.shockharness.toml +4 -0
  92. shockharness-0.1.0/evals/tasks/py-04-pagination-numbers/start/src/app/__init__.py +0 -0
  93. shockharness-0.1.0/evals/tasks/py-04-pagination-numbers/start/src/app/pages.py +16 -0
  94. shockharness-0.1.0/evals/tasks/py-04-pagination-numbers/task.md +17 -0
  95. shockharness-0.1.0/evals/tasks/py-04-pagination-numbers/tests/test_pages.py +30 -0
  96. shockharness-0.1.0/examples/js/.shockharness.toml +12 -0
  97. shockharness-0.1.0/examples/js/README.md +28 -0
  98. shockharness-0.1.0/examples/python/.shockharness.toml +12 -0
  99. shockharness-0.1.0/examples/python/README.md +22 -0
  100. shockharness-0.1.0/examples/python/tests/__init__.py +0 -0
  101. shockharness-0.1.0/examples/python/tests/test_example_service.py +24 -0
  102. shockharness-0.1.0/pyproject.toml +85 -0
  103. shockharness-0.1.0/schemas/baseline-v1.schema.json +64 -0
  104. shockharness-0.1.0/schemas/diff-v1.schema.json +656 -0
  105. shockharness-0.1.0/schemas/report-v1.schema.json +948 -0
  106. shockharness-0.1.0/src/shockharness/__init__.py +13 -0
  107. shockharness-0.1.0/src/shockharness/__main__.py +5 -0
  108. shockharness-0.1.0/src/shockharness/analysis.py +447 -0
  109. shockharness-0.1.0/src/shockharness/api.py +114 -0
  110. shockharness-0.1.0/src/shockharness/cli.py +596 -0
  111. shockharness-0.1.0/src/shockharness/complexity/__init__.py +8 -0
  112. shockharness-0.1.0/src/shockharness/complexity/assemble.py +119 -0
  113. shockharness-0.1.0/src/shockharness/complexity/callgraph.py +754 -0
  114. shockharness-0.1.0/src/shockharness/complexity/cognitive.py +96 -0
  115. shockharness-0.1.0/src/shockharness/complexity/coupling.py +350 -0
  116. shockharness-0.1.0/src/shockharness/complexity/cyclomatic.py +45 -0
  117. shockharness-0.1.0/src/shockharness/complexity/metrics.py +148 -0
  118. shockharness-0.1.0/src/shockharness/complexity/nesting.py +50 -0
  119. shockharness-0.1.0/src/shockharness/complexity/npath.py +110 -0
  120. shockharness-0.1.0/src/shockharness/complexity/regressions.py +176 -0
  121. shockharness-0.1.0/src/shockharness/complexity/size.py +28 -0
  122. shockharness-0.1.0/src/shockharness/config.py +502 -0
  123. shockharness-0.1.0/src/shockharness/constants.py +77 -0
  124. shockharness-0.1.0/src/shockharness/defaults.toml +102 -0
  125. shockharness-0.1.0/src/shockharness/discovery.py +143 -0
  126. shockharness-0.1.0/src/shockharness/errors.py +42 -0
  127. shockharness-0.1.0/src/shockharness/findings.py +87 -0
  128. shockharness-0.1.0/src/shockharness/gate.py +232 -0
  129. shockharness-0.1.0/src/shockharness/gating/__init__.py +1 -0
  130. shockharness-0.1.0/src/shockharness/gating/baseline.py +349 -0
  131. shockharness-0.1.0/src/shockharness/gating/diff.py +260 -0
  132. shockharness-0.1.0/src/shockharness/globs.py +142 -0
  133. shockharness-0.1.0/src/shockharness/history/__init__.py +1 -0
  134. shockharness-0.1.0/src/shockharness/history/cochange.py +73 -0
  135. shockharness-0.1.0/src/shockharness/history/git.py +182 -0
  136. shockharness-0.1.0/src/shockharness/history/hotspots.py +163 -0
  137. shockharness-0.1.0/src/shockharness/ir/__init__.py +1 -0
  138. shockharness-0.1.0/src/shockharness/ir/javascript.py +181 -0
  139. shockharness-0.1.0/src/shockharness/ir/javascript_catches.py +570 -0
  140. shockharness-0.1.0/src/shockharness/ir/javascript_defaults.py +874 -0
  141. shockharness-0.1.0/src/shockharness/ir/javascript_functions.py +1135 -0
  142. shockharness-0.1.0/src/shockharness/ir/lines.py +54 -0
  143. shockharness-0.1.0/src/shockharness/ir/model.py +411 -0
  144. shockharness-0.1.0/src/shockharness/ir/python.py +185 -0
  145. shockharness-0.1.0/src/shockharness/ir/python_catches.py +441 -0
  146. shockharness-0.1.0/src/shockharness/ir/python_defaults.py +917 -0
  147. shockharness-0.1.0/src/shockharness/ir/python_functions.py +865 -0
  148. shockharness-0.1.0/src/shockharness/output/__init__.py +1 -0
  149. shockharness-0.1.0/src/shockharness/output/json.py +15 -0
  150. shockharness-0.1.0/src/shockharness/output/sarif.py +203 -0
  151. shockharness-0.1.0/src/shockharness/output/text.py +248 -0
  152. shockharness-0.1.0/src/shockharness/profiles/__init__.py +1 -0
  153. shockharness-0.1.0/src/shockharness/profiles/minimal.toml +20 -0
  154. shockharness-0.1.0/src/shockharness/profiles/recommended.toml +7 -0
  155. shockharness-0.1.0/src/shockharness/profiles/strict.toml +5 -0
  156. shockharness-0.1.0/src/shockharness/report.py +188 -0
  157. shockharness-0.1.0/src/shockharness/rules/__init__.py +1 -0
  158. shockharness-0.1.0/src/shockharness/rules/complexity.py +207 -0
  159. shockharness-0.1.0/src/shockharness/rules/defaults.py +285 -0
  160. shockharness-0.1.0/src/shockharness/rules/exceptions.py +318 -0
  161. shockharness-0.1.0/src/shockharness/rules/magic_numbers.py +59 -0
  162. shockharness-0.1.0/src/shockharness/rules/registry.py +917 -0
  163. shockharness-0.1.0/src/shockharness/rules/severity.py +23 -0
  164. shockharness-0.1.0/src/shockharness/schema.py +271 -0
  165. shockharness-0.1.0/src/shockharness/skills/__init__.py +91 -0
  166. shockharness-0.1.0/src/shockharness/skills/adapters.py +106 -0
  167. shockharness-0.1.0/src/shockharness/skills/fix-violations/SKILL.md +393 -0
  168. shockharness-0.1.0/src/shockharness/skills/install.py +336 -0
  169. shockharness-0.1.0/src/shockharness/skills/pre-commit-review/SKILL.md +304 -0
  170. shockharness-0.1.0/src/shockharness/skills/refactor-complexity/SKILL.md +413 -0
  171. shockharness-0.1.0/src/shockharness/skills/write-compliant-code/SKILL.md +400 -0
  172. shockharness-0.1.0/src/shockharness/source.py +52 -0
  173. shockharness-0.1.0/src/shockharness/suppressions.py +195 -0
  174. shockharness-0.1.0/src/shockharness/version.py +3 -0
  175. shockharness-0.1.0/tests/__init__.py +0 -0
  176. shockharness-0.1.0/tests/cli/__init__.py +0 -0
  177. shockharness-0.1.0/tests/cli/test_cli.py +962 -0
  178. shockharness-0.1.0/tests/conftest.py +139 -0
  179. shockharness-0.1.0/tests/fixtures/expected.json +3074 -0
  180. shockharness-0.1.0/tests/fixtures/javascript/clean.js +3 -0
  181. shockharness-0.1.0/tests/fixtures/javascript/common.cjs +1 -0
  182. shockharness-0.1.0/tests/fixtures/javascript/def_destructuring.js +21 -0
  183. shockharness-0.1.0/tests/fixtures/javascript/def_exception_default.js +74 -0
  184. shockharness-0.1.0/tests/fixtures/javascript/def_logical_or.js +43 -0
  185. shockharness-0.1.0/tests/fixtures/javascript/def_nullish.js +40 -0
  186. shockharness-0.1.0/tests/fixtures/javascript/def_parameters.js +38 -0
  187. shockharness-0.1.0/tests/fixtures/javascript/def_process_env.js +15 -0
  188. shockharness-0.1.0/tests/fixtures/javascript/exc_async_await.js +35 -0
  189. shockharness-0.1.0/tests/fixtures/javascript/exc_boundary_catch.js +87 -0
  190. shockharness-0.1.0/tests/fixtures/javascript/exc_nested_catches.js +28 -0
  191. shockharness-0.1.0/tests/fixtures/javascript/exc_promise_catch.js +32 -0
  192. shockharness-0.1.0/tests/fixtures/javascript/exc_rethrow.js +40 -0
  193. shockharness-0.1.0/tests/fixtures/javascript/exc_return_fallback.js +58 -0
  194. shockharness-0.1.0/tests/fixtures/javascript/exc_untyped_catch.js +32 -0
  195. shockharness-0.1.0/tests/fixtures/javascript/jsx.js +2 -0
  196. shockharness-0.1.0/tests/fixtures/javascript/magic_basic.js +7 -0
  197. shockharness-0.1.0/tests/fixtures/javascript/magic_encodings.js +3 -0
  198. shockharness-0.1.0/tests/fixtures/javascript/magic_negatives.js +10 -0
  199. shockharness-0.1.0/tests/fixtures/javascript/module.mjs +2 -0
  200. shockharness-0.1.0/tests/fixtures/javascript/scopes.js +9 -0
  201. shockharness-0.1.0/tests/fixtures/javascript/strings_regex.js +6 -0
  202. shockharness-0.1.0/tests/fixtures/javascript/suppressions.js +4 -0
  203. shockharness-0.1.0/tests/fixtures/javascript/syntax_error.js +3 -0
  204. shockharness-0.1.0/tests/fixtures/javascript/unicode.js +2 -0
  205. shockharness-0.1.0/tests/fixtures/python/clean.py +15 -0
  206. shockharness-0.1.0/tests/fixtures/python/def_dataclasses.py +30 -0
  207. shockharness-0.1.0/tests/fixtures/python/def_environment.py +23 -0
  208. shockharness-0.1.0/tests/fixtures/python/def_exception_default.py +78 -0
  209. shockharness-0.1.0/tests/fixtures/python/def_mapping_get.py +55 -0
  210. shockharness-0.1.0/tests/fixtures/python/def_nullish.py +28 -0
  211. shockharness-0.1.0/tests/fixtures/python/def_parameters.py +59 -0
  212. shockharness-0.1.0/tests/fixtures/python/def_truthiness.py +42 -0
  213. shockharness-0.1.0/tests/fixtures/python/exc_bare.py +12 -0
  214. shockharness-0.1.0/tests/fixtures/python/exc_broad.py +26 -0
  215. shockharness-0.1.0/tests/fixtures/python/exc_except_star.py +10 -0
  216. shockharness-0.1.0/tests/fixtures/python/exc_logging_only.py +49 -0
  217. shockharness-0.1.0/tests/fixtures/python/exc_multiple_operations.py +60 -0
  218. shockharness-0.1.0/tests/fixtures/python/exc_nested.py +29 -0
  219. shockharness-0.1.0/tests/fixtures/python/exc_rethrow.py +44 -0
  220. shockharness-0.1.0/tests/fixtures/python/exc_rethrow_transparent.py +30 -0
  221. shockharness-0.1.0/tests/fixtures/python/exc_return_fallback.py +62 -0
  222. shockharness-0.1.0/tests/fixtures/python/exc_scoped_timeout.py +55 -0
  223. shockharness-0.1.0/tests/fixtures/python/exc_tuple.py +22 -0
  224. shockharness-0.1.0/tests/fixtures/python/exc_unresolved.py +91 -0
  225. shockharness-0.1.0/tests/fixtures/python/magic_annotations.py +8 -0
  226. shockharness-0.1.0/tests/fixtures/python/magic_basic.py +13 -0
  227. shockharness-0.1.0/tests/fixtures/python/magic_booleans.py +5 -0
  228. shockharness-0.1.0/tests/fixtures/python/magic_encodings.py +3 -0
  229. shockharness-0.1.0/tests/fixtures/python/magic_negatives.py +9 -0
  230. shockharness-0.1.0/tests/fixtures/python/magic_scopes.py +32 -0
  231. shockharness-0.1.0/tests/fixtures/python/magic_strings_not_numbers.py +5 -0
  232. shockharness-0.1.0/tests/fixtures/python/suppressions.py +9 -0
  233. shockharness-0.1.0/tests/fixtures/python/syntax_error.py +2 -0
  234. shockharness-0.1.0/tests/fixtures/python/unicode.py +3 -0
  235. shockharness-0.1.0/tests/property/__init__.py +0 -0
  236. shockharness-0.1.0/tests/property/test_complexity_fuzz.py +175 -0
  237. shockharness-0.1.0/tests/property/test_properties.py +159 -0
  238. shockharness-0.1.0/tests/test_self_compliance.py +31 -0
  239. shockharness-0.1.0/tests/unit/__init__.py +0 -0
  240. shockharness-0.1.0/tests/unit/test_analysis.py +132 -0
  241. shockharness-0.1.0/tests/unit/test_baseline.py +274 -0
  242. shockharness-0.1.0/tests/unit/test_callgraph.py +357 -0
  243. shockharness-0.1.0/tests/unit/test_complexity_diff.py +182 -0
  244. shockharness-0.1.0/tests/unit/test_complexity_metrics.py +589 -0
  245. shockharness-0.1.0/tests/unit/test_config.py +250 -0
  246. shockharness-0.1.0/tests/unit/test_consumer_script.py +84 -0
  247. shockharness-0.1.0/tests/unit/test_defaults.py +628 -0
  248. shockharness-0.1.0/tests/unit/test_discovery.py +110 -0
  249. shockharness-0.1.0/tests/unit/test_evals.py +136 -0
  250. shockharness-0.1.0/tests/unit/test_exceptions.py +478 -0
  251. shockharness-0.1.0/tests/unit/test_gate_and_report.py +247 -0
  252. shockharness-0.1.0/tests/unit/test_globs.py +76 -0
  253. shockharness-0.1.0/tests/unit/test_golden_fixtures.py +49 -0
  254. shockharness-0.1.0/tests/unit/test_history.py +274 -0
  255. shockharness-0.1.0/tests/unit/test_javascript_adapter.py +311 -0
  256. shockharness-0.1.0/tests/unit/test_magic001.py +109 -0
  257. shockharness-0.1.0/tests/unit/test_profiles.py +249 -0
  258. shockharness-0.1.0/tests/unit/test_python_adapter.py +280 -0
  259. shockharness-0.1.0/tests/unit/test_registry.py +83 -0
  260. shockharness-0.1.0/tests/unit/test_sarif.py +231 -0
  261. shockharness-0.1.0/tests/unit/test_schema.py +82 -0
  262. shockharness-0.1.0/tests/unit/test_skills.py +560 -0
  263. shockharness-0.1.0/tests/unit/test_source.py +38 -0
  264. shockharness-0.1.0/tests/unit/test_suppressions.py +99 -0
  265. shockharness-0.1.0/tests/unit/test_workflows.py +111 -0
@@ -0,0 +1,20 @@
1
+ # Environments
2
+ .venv/
3
+ venv/
4
+
5
+ # IDE
6
+ .idea/
7
+ .vscode/
8
+
9
+ # Python
10
+ __pycache__/
11
+ *.py[cod]
12
+ *.egg-info/
13
+ dist/
14
+ build/
15
+ .pytest_cache/
16
+ .hypothesis/
17
+ .ruff_cache/
18
+
19
+ # macOS
20
+ .DS_Store
@@ -0,0 +1,179 @@
1
+ # ShockHarness analyzing itself. Every exemption below is deliberate and reported.
2
+ # See agent_code_integrity_project_spec.md section 15.5 for the bootstrapping policy.
3
+
4
+ [tool.shockharness]
5
+ profile = "strict"
6
+ include = ["src/**/*.py", "tests/**/*.py", "scripts/**/*.py", "evals/*.py"]
7
+ exclude = ["**/.venv/**", "**/node_modules/**", "**/dist/**", "**/build/**", "tests/fixtures/**", "evals/tasks/**"]
8
+ parse_errors = "fail"
9
+
10
+ [tool.shockharness.rules.magic_numbers]
11
+ enabled = true
12
+ allowed_values = [0, 1]
13
+ allow_unary_negative_one = false
14
+ exclude_configuration_files = false
15
+
16
+ [tool.shockharness.rules.exceptions]
17
+ enabled = true
18
+ mode = "boundary_whitelist"
19
+
20
+ # Declared boundaries where the tool meets the outside world. Each converts the
21
+ # named failure into a typed analyzer diagnostic; none may swallow it.
22
+ [[tool.shockharness.rules.exceptions.boundaries]]
23
+ path = "src/shockharness/config.py"
24
+ language = "python"
25
+ exception = "OSError"
26
+ reason = "Reading a configuration file is I/O; failure becomes a ConfigError (exit 2)"
27
+
28
+ [[tool.shockharness.rules.exceptions.boundaries]]
29
+ path = "src/shockharness/config.py"
30
+ language = "python"
31
+ exception = "tomllib.TOMLDecodeError"
32
+ reason = "Config file content is external input; decode failure becomes a ConfigError (exit 2)"
33
+
34
+ [[tool.shockharness.rules.exceptions.boundaries]]
35
+ path = "src/shockharness/config.py"
36
+ language = "python"
37
+ exception = "UnicodeDecodeError"
38
+ reason = "Config file bytes are external input; non-UTF-8 becomes a ConfigError (exit 2)"
39
+
40
+ [[tool.shockharness.rules.exceptions.boundaries]]
41
+ path = "src/shockharness/analysis.py"
42
+ language = "python"
43
+ exception = "OSError"
44
+ reason = "Reading analyzed files is I/O; failure becomes a per-file read_error status, never a pass"
45
+
46
+ [[tool.shockharness.rules.exceptions.boundaries]]
47
+ path = "src/shockharness/analysis.py"
48
+ language = "python"
49
+ exception = "UnicodeDecodeError"
50
+ reason = "Analyzed source is external bytes; undecodable input becomes a decode_error file status"
51
+
52
+ [[tool.shockharness.rules.exceptions.boundaries]]
53
+ path = "src/shockharness/ir/python.py"
54
+ language = "python"
55
+ exception = "SyntaxError"
56
+ reason = "ast.parse and tokenize on external source; failure becomes a ParseFailure fact feeding the parse_errors gate"
57
+
58
+ [[tool.shockharness.rules.exceptions.boundaries]]
59
+ path = "src/shockharness/ir/python.py"
60
+ language = "python"
61
+ exception = "ValueError"
62
+ reason = "ast.parse raises ValueError for NUL bytes on 3.11 and tokenize raises UnicodeDecodeError (a ValueError) on some CR sequences; both become a ParseFailure"
63
+
64
+ [[tool.shockharness.rules.exceptions.boundaries]]
65
+ path = "src/shockharness/ir/python.py"
66
+ language = "python"
67
+ exception = "tokenize.TokenError"
68
+ reason = "Comment extraction tokenizes external source; failure becomes a ParseFailure fact"
69
+
70
+ [[tool.shockharness.rules.exceptions.boundaries]]
71
+ path = "src/shockharness/cli.py"
72
+ language = "python"
73
+ exception = "OSError"
74
+ reason = "Writing the report, baseline, or init file to a user path is I/O; failure becomes a UsageError (exit 2)"
75
+
76
+ [[tool.shockharness.rules.exceptions.boundaries]]
77
+ path = "src/shockharness/gating/baseline.py"
78
+ language = "python"
79
+ exception = "OSError"
80
+ reason = "Reading a user-supplied baseline file is I/O; failure becomes a UsageError (exit 2)"
81
+
82
+ [[tool.shockharness.rules.exceptions.boundaries]]
83
+ path = "src/shockharness/gating/baseline.py"
84
+ language = "python"
85
+ exception = "UnicodeDecodeError"
86
+ reason = "Baseline file bytes are external input; non-UTF-8 becomes a UsageError (exit 2)"
87
+
88
+ [[tool.shockharness.rules.exceptions.boundaries]]
89
+ path = "src/shockharness/gating/baseline.py"
90
+ language = "python"
91
+ exception = "json.JSONDecodeError"
92
+ reason = "Baseline file content is external input; malformed JSON becomes a UsageError (exit 2)"
93
+
94
+ [[tool.shockharness.rules.exceptions.boundaries]]
95
+ path = "src/shockharness/history/git.py"
96
+ language = "python"
97
+ exception = "subprocess.CalledProcessError"
98
+ reason = "The git subprocess is the outside world; a non-zero exit (not a repository, unknown revision) becomes a GitResult error: diff/gate turn it into a UsageError (exit 2), history metrics into status = unavailable"
99
+
100
+ [[tool.shockharness.rules.exceptions.boundaries]]
101
+ path = "src/shockharness/history/git.py"
102
+ language = "python"
103
+ exception = "OSError"
104
+ reason = "Launching git can fail when the binary is absent (FileNotFoundError) or cannot be executed; the failure becomes a GitResult error, never a silent empty history"
105
+
106
+ [[tool.shockharness.rules.exceptions.boundaries]]
107
+ path = "src/shockharness/history/git.py"
108
+ language = "python"
109
+ exception = "UnicodeDecodeError"
110
+ reason = "git output is external bytes; undecodable output becomes a GitResult error"
111
+
112
+ [[tool.shockharness.rules.exceptions.boundaries]]
113
+ path = "src/shockharness/skills/install.py"
114
+ language = "python"
115
+ exception = "OSError"
116
+ reason = "Reading and writing skill files and the manifest in the user's chosen destination is I/O; failure becomes a UsageError (exit 2)"
117
+
118
+ [[tool.shockharness.rules.exceptions.boundaries]]
119
+ path = "src/shockharness/skills/install.py"
120
+ language = "python"
121
+ exception = "UnicodeDecodeError"
122
+ reason = "Skills manifest bytes are external input; non-UTF-8 becomes a UsageError (exit 2)"
123
+
124
+ [[tool.shockharness.rules.exceptions.boundaries]]
125
+ path = "src/shockharness/skills/install.py"
126
+ language = "python"
127
+ exception = "json.JSONDecodeError"
128
+ reason = "Skills manifest content is external input; malformed JSON becomes a UsageError (exit 2)"
129
+
130
+ [[tool.shockharness.rules.exceptions.boundaries]]
131
+ path = "src/shockharness/cli.py"
132
+ language = "python"
133
+ exception = "shockharness.errors.ShockHarnessError"
134
+ reason = "Process boundary: typed configuration and usage errors print one line and exit 2"
135
+
136
+ [[tool.shockharness.rules.exceptions.boundaries]]
137
+ path = "src/shockharness/cli.py"
138
+ language = "python"
139
+ exception = "Exception"
140
+ reason = "Process boundary: an unexpected internal failure prints its full traceback and exits 3 instead of Python's default 1; nothing is hidden"
141
+
142
+ [tool.shockharness.rules.defaults]
143
+ enabled = true
144
+ function_parameters = "forbid"
145
+ dataclass_fields = "forbid"
146
+ missing_value_fallbacks = "forbid"
147
+ truthiness_fallbacks = "review"
148
+ nullish_fallbacks = "forbid"
149
+ configuration_fallbacks = "forbid"
150
+
151
+ [tool.shockharness.rules.magic_strings]
152
+ enabled = true
153
+ mode = "review"
154
+
155
+ [tool.shockharness.complexity]
156
+ enabled = true
157
+ cognitive_warn = 15
158
+ cyclomatic_warn = 15
159
+ max_nesting_warn = 4
160
+
161
+ [tool.shockharness.gates]
162
+ new_hard_violations = "fail"
163
+ legacy = "baseline"
164
+ complexity_regressions = "warn"
165
+
166
+ [[tool.shockharness.overrides]]
167
+ paths = ["src/shockharness/constants.py"]
168
+ disable = ["MAGIC001"]
169
+ reason = "Single designated module holding every numeric constant the tool needs (exit codes, schema version, caps)"
170
+
171
+ [[tool.shockharness.overrides]]
172
+ paths = ["tests/**"]
173
+ disable = ["MAGIC001", "DEF001"]
174
+ reason = "Test code encodes literal expected outputs and pytest fixture signatures"
175
+
176
+ [[tool.shockharness.overrides]]
177
+ paths = ["evals/run_evals.py"]
178
+ disable = ["MAGIC001"]
179
+ reason = "The eval runner is stdlib-only by design (it scores the analyzer from outside), so it cannot import shockharness.constants and holds its one JSON indent constant itself"
@@ -0,0 +1,292 @@
1
+ # Changelog
2
+
3
+ All notable changes to ShockHarness. Format follows Keep a Changelog; versions
4
+ follow Semantic Versioning. The report `schema_version` is tracked separately.
5
+
6
+ ## [Unreleased]
7
+
8
+ ## [0.1.0] - 2026-10-14
9
+
10
+ First stable release. Every milestone of the specification (A through G) is
11
+ implemented: the three default-on hard policies (`MAGIC001`, `EXC001`-`EXC003`,
12
+ `DEF001`-`DEF006`), inline suppressions, baselines, revision diff and gate,
13
+ SARIF, git history metrics, advisory complexity metrics and call graph
14
+ (`CPLX001`-`CPLX005`), three profiles, the `check` command, `--exit-zero`,
15
+ tag-triggered publishing, a reusable GitHub Actions workflow, consumer
16
+ examples, four agent skills with `skills install`/`update`, and the eval
17
+ corpus. From this release the CLI surface, exit codes, rule IDs and report
18
+ `schema_version` 1 are stable; changes to them are documented here first.
19
+
20
+ ### Fixed
21
+ - Skills: `SKILL.md` content is normalized to LF when read from the package, so
22
+ frontmatter checks, installs and manifest hashes are identical on checkouts
23
+ that use CRLF (Windows runners with `core.autocrlf`). A `.gitattributes` now
24
+ pins LF on checkout for every text file in the repository.
25
+ - History metrics: commit dates are now normalized to one ISO 8601 spelling
26
+ (`+00:00`) regardless of the host's git version. Git 2.45+ prints UTC as `Z`,
27
+ which made `metrics.history` differ between machines and broke two tests on
28
+ CI runners with newer git than the development machine.
29
+ - Dependabot no longer proposes minor or major `tree-sitter` bumps; the 0.25
30
+ pin is deliberate (0.26.0 segfaults on CPython 3.14).
31
+
32
+ ### Added
33
+ - Milestone E: complexity metrics, call graph, hotspots. `CPLX001`-`CPLX005`
34
+ are implemented as advisory warnings that never fail a plain `check`. Every
35
+ module body, class body, function, method, lambda and arrow is a *unit*
36
+ measured on its own (nested units are excluded from their parent) with
37
+ versioned conventions stated in every message and in
38
+ `metrics.complexity.conventions`: `cyclomatic/1` (boolean operators counted
39
+ under `complexity.cyclomatic_counts_boolean_operators`, optional chaining
40
+ never), `cognitive/1` (published-style nesting and interruption increments,
41
+ explicitly not a SonarSource score, Python/JavaScript parity enforced by a
42
+ table and a Hypothesis property), `nesting/1`, `npath/1` (bounded by
43
+ `complexity.npath_cap`, reported as `>= cap` when saturated, never
44
+ enumerated) and `size/1`. New keys `npath_cap`, `npath_warn`, `fan_out_warn`,
45
+ `cyclomatic_counts_boolean_operators`, `abstraction_displacement`;
46
+ `cyclomatic_counts_boolean_operators = false` is a reported deviation.
47
+ `docs/complexity.md` defines everything.
48
+ - `metrics.graph`: a call graph whose edges are `resolved` (direct call of a
49
+ module-level, nested or imported definition), `probable` (module alias
50
+ attribute, `self`/`this` method the class defines, static class call,
51
+ function passed as an argument) or `unresolved` (computed callee, unknown
52
+ receiver, unbound name, undefined `self` attribute); counts per confidence,
53
+ fan-in/fan-out per unit (builtins excluded, unresolved never counted),
54
+ module import edges for Python (absolute and relative, canonical
55
+ package-rooted names, never suffix matching) and JavaScript (`import`,
56
+ `export ... from`, `export *`, `require`), strongly connected components
57
+ as `cycles`, and `shared_mutable_state` notes. `CPLX005` fires per cycle
58
+ member and on fan-out above `fan_out_warn`.
59
+ - `metrics.history.hotspots`: `log1p(churn) * normalized_complexity` per file
60
+ (summed cognitive complexity over the repository maximum), with
61
+ `hotspots_formula`; `null` with a reason when history or complexity is
62
+ unavailable.
63
+ - Revision diffs list `metrics.complexity.regressions` (head units above a
64
+ threshold whose value grew since base) and honour
65
+ `gates.complexity_regressions` (`warn` default, `fail`, `ignore`);
66
+ `gate.complexity_regressions` and `summary.complexity_regressions` are new
67
+ fields. `complexity.abstraction_displacement = true` adds the experimental
68
+ `metrics.graph.abstraction_displacement` (every entry approximate).
69
+ - The analysis pipeline now has a cross-file phase between the per-file rules
70
+ and suppressions, so graph findings are suppressible and fingerprinted;
71
+ `build_report` takes the metrics, `collect_history` takes the complexity
72
+ section, `gate.evaluate` takes the regression count. Report and diff schemas
73
+ gain the new sections (additive); SARIF carries CPLX results.
74
+ - The `refactor-complexity` skill now quotes the real rules, thresholds,
75
+ report fields and commands.
76
+ - Milestone G: four vendor-neutral agent skills shipped as package data
77
+ (`shockharness/skills/<name>/SKILL.md`, open Agent Skills format with
78
+ `name`/`description` frontmatter): `write-compliant-code`,
79
+ `fix-violations`, `refactor-complexity`, `pre-commit-review`. Each has
80
+ compliant and non-compliant Python and JavaScript examples, exact CLI
81
+ invocations, a corrective workflow, a checklist, and an "Adopting
82
+ ShockHarness" section identical to the README's (enforced by a test).
83
+ `refactor-complexity` states that `CPLX001`-`CPLX005` are not implemented.
84
+ - `shockharness skills list|show|install|update`. `install --agent
85
+ claude|codex|generic [--project PATH] [--user] [--force]` writes the skills
86
+ (claude: `.claude/skills`, codex: `.agents/skills`, generic: `--project DIR`
87
+ itself plus a printed `AGENTS.md` pointer line) and a manifest
88
+ `.shockharness-skills.json` (schema version 1: agent, package version,
89
+ SHA-256 per file); it refuses to overwrite without `--force` (exit 2) and
90
+ touches nothing outside the destination. `update` rewrites files whose hash
91
+ still equals the manifest, restores missing ones, adds new skills, reports
92
+ removed ones, leaves edited files alone and reports each as a conflict with
93
+ the remediation (exit 1 while a conflict remains); `update --check` writes
94
+ nothing and exits 1 on any drift. Three new declared exception boundaries in
95
+ `skills/install.py` (`OSError`, `UnicodeDecodeError`,
96
+ `json.JSONDecodeError`).
97
+ - Skill evaluation corpus under `evals/`: eight paired tasks (four Python,
98
+ four JavaScript) with prompt, starting files, behavioral tests, expected
99
+ outcome and a clean reference solution; `evals/run_evals.py` (standard
100
+ library only, one declared `MAGIC001` override) scores candidate directories
101
+ by unsuppressed findings per rule, test exit code and suppressions added,
102
+ and writes a JSON scorecard. CI scores the reference solutions and
103
+ round-trips `skills install` / `update --check` for all three agents on
104
+ ubuntu. The sdist carries `evals/`.
105
+ - README "Adopting ShockHarness": a self-contained guide for an agent reading
106
+ the README over HTTP (pinned `uv tool install` / `uvx` / `pip` forms,
107
+ `init`, the four onboarding paths, direct and reusable CI steps with the
108
+ Dependabot snippet, `skills install`, and a verification table), with
109
+ absolute links only. `docs/skills.md` is new; `docs/integration.md` gains a
110
+ skills section.
111
+ - `--exit-zero` on `check` (and the `scan` alias) and `gate`: an advisory
112
+ run. The report, severities, gate verdict text and every count are
113
+ unchanged; only the process exit code is 0 where the verdict would have
114
+ returned 1. Exit 2 (configuration, usage) and exit 3 (tool failure, strict
115
+ parse failure) are never masked. The run is visible as `gate.advisory`
116
+ (JSON and diff reports, both schemas, additive), a `shockharness: advisory
117
+ run (--exit-zero): gate verdict FAIL reported as exit 0` line in text, and
118
+ the `advisory` SARIF run property; the SARIF invocation `exitCode` is the
119
+ process exit code while `properties.gate.exit_code` keeps the verdict's.
120
+ Deliberately a CLI-only flag, not a configuration key: the configuration
121
+ form is `gates.new_hard_violations = "warn"`. `docs/configuration.md` gains
122
+ "Adopting ShockHarness on an existing codebase" comparing the four onboarding
123
+ paths (baseline, `gates.new_hard_violations = "warn"`, `profile =
124
+ "minimal"`, `--exit-zero`).
125
+ - Milestone F (distribution half). `shockharness check` is the canonical
126
+ analysis command (identical arguments and behaviour to `scan`, which stays as
127
+ a hidden alias); `shockharness version` prints the same string as
128
+ `--version`.
129
+ - Built-in profiles shipped as package data (`shockharness/profiles/*.toml`),
130
+ selected with `profile = "strict" | "recommended" | "minimal"`: `strict` is
131
+ the bundled defaults; `recommended` reports `DEF003`/`DEF004` at review;
132
+ `minimal` caps `MAGIC001`, `EXC001`-`EXC003` and `DEF001`-`DEF006` at
133
+ review, sets `parse_errors = "warn"` and disables complexity. Precedence:
134
+ bundled defaults < profile < repository configuration < command-line
135
+ overrides. Every profile below `strict` is a policy deviation in every
136
+ report and in `config validate`; `config effective` records profile keys
137
+ with origin `profile:<name>`.
138
+ - `rules.magic_numbers.severity`, `rules.exceptions.severity`,
139
+ `rules.defaults.severity` (`"error"` | `"review"`): a per-section cap that
140
+ turns error findings into review findings; each cap is a reported deviation.
141
+ - The only two command-line overrides, on every configuration-loading command:
142
+ `--profile NAME` (replaces the profile layer; the repository's other keys
143
+ still apply) and `--parse-errors fail|warn`. Both are recorded with origin
144
+ `cli_override`, noted in the report, and part of the configuration digest.
145
+ - `shockharness.toml` is accepted alongside `.shockharness.toml` at the
146
+ dedicated tier; both present in one root is a configuration error naming
147
+ both files.
148
+ - `init --profile recommended|minimal` writes a short file selecting the profile.
149
+ - Release automation: `.github/workflows/publish.yml` (tag `v*` -> full CI
150
+ workflow -> build wheel and sdist -> tag equals `version.py` -> PyPI trusted
151
+ publishing with `id-token: write`, environment `pypi`, no stored token);
152
+ `docs/releasing.md` with the one-time PyPI steps and the per-release steps.
153
+ - Reusable consumer workflow `.github/workflows/check.yml` (`workflow_call`;
154
+ inputs `shockharness-version` (required), `config`, `base`,
155
+ `python-version`; SARIF upload to code scanning; permissions
156
+ `contents: read`, `security-events: write` only); `.github/dependabot.yml`
157
+ for this repository; `docs/integration.md` (pinning to a tag and to a SHA,
158
+ Dependabot snippet, where the pin may live).
159
+ - Consumer examples `examples/python` (dev dependency pinned to `0.1.0a1`)
160
+ and `examples/js` (analyzer in an isolated tool environment, never an npm
161
+ dependency), each with a policy file, a workflow and one deliberate
162
+ suppressed finding; `scripts/consumer_upgrade_check.py` (stdlib only)
163
+ copies each into a throwaway git repository, installs the wheel built from
164
+ the checkout, and asserts `check` exits 0, then 1 after one injected
165
+ violation, and `gate --base HEAD~1 --head HEAD` exits 1; run by CI on ubuntu.
166
+ - `effective_profile` is also in SARIF run properties, next to
167
+ `config_deviations`.
168
+ - Milestone F (analysis half): stable finding fingerprints (`findings[].fingerprint`;
169
+ rule ID, path, enclosing scope, whitespace-free evidence, occurrence index)
170
+ that survive line moves and reformatting; `shockharness baseline create PATH...
171
+ --output FILE` writing a reviewed, byte-stable baseline
172
+ (`schemas/baseline-v1.schema.json`) that is never updated automatically;
173
+ `--baseline FILE` on `scan` and `gate` with `gates.legacy` semantics
174
+ (`baseline`: matched findings listed and counted but not gating; `ignore`:
175
+ listed, not counted; `fail`: baseline recorded but not applied); stale
176
+ baseline entries reported under `gate.baseline.stale`; `evaluate_gate` and
177
+ `apply_baseline` in the library API.
178
+ - `shockharness diff --base REF --head REF` (reporting only) and
179
+ `shockharness gate --base REF --head REF` (exit 1 only on new hard
180
+ violations): both revisions resolved and exported with local git only, full
181
+ trees analyzed, findings classified new / fixed / unchanged by fingerprint,
182
+ provenance (both commits, config digest, tool version) in the report
183
+ (`schemas/diff-v1.schema.json`).
184
+ - SARIF 2.1.0 output (`--format sarif`) for `scan`, `diff`, and `gate`: one
185
+ run, every registered rule with full descriptions, results with level mapping
186
+ (error, warning, review/note -> note), root-relative URIs, exclusive end
187
+ columns, `partialFingerprints` carrying the baseline fingerprint, in-source
188
+ suppressions, and `baselineState` when a baseline or base revision applies.
189
+ - `metrics.history` in every CLI report: per-file commit count, author count,
190
+ first and last change, and co-change pairs with support and directional
191
+ confidence, read from local `git log` over `history.horizon_days` anchored
192
+ on the head commit date; `history.enabled`, `history.horizon_days`,
193
+ `history.min_support` configuration keys; explicit `unavailable`,
194
+ `disabled`, and `not_collected` statuses with reasons; `hotspots` is `null`
195
+ with a reason until Milestone E provides complexity.
196
+ - Six declared exception boundaries: reading a baseline file (`OSError`,
197
+ `UnicodeDecodeError`, `json.JSONDecodeError`) and the git subprocess
198
+ (`subprocess.CalledProcessError`, `OSError`, `UnicodeDecodeError`).
199
+
200
+ ### Changed
201
+ - **`policy_profile` is renamed to `effective_profile`** in the JSON report,
202
+ the diff report, both JSON schemas and SARIF run properties (specification
203
+ section 20, decision 13). `schema_version` stays `"1"`: no tagged release
204
+ has shipped the old field (0.1.0a1 was published only to claim the name and
205
+ predates the schema's first tagged release), so the rename is made before
206
+ schema v1 is published rather than versioned around.
207
+ - The text report's `config:` line names the effective profile; `config
208
+ validate` prints it.
209
+ - CI, the pre-commit hook, the README and the docs invoke `shockharness
210
+ check`; the self-gate and Ruff also cover `scripts/`.
211
+ - Report schema (still `schema_version` 1, additive): `findings[].fingerprint`,
212
+ `findings[].baseline`, `gate.baselined`, `gate.new_hard_violations`,
213
+ `gate.baseline`, `execution_summary.baselined`, `metrics.history`. The gate
214
+ verdict is driven by `new_hard_violations`; without a baseline it equals
215
+ `hard_violations` as before.
216
+ - `--format sarif` no longer exits 2; `evaluate_gate` no longer raises
217
+ `UnsupportedFeatureError` for a baseline (it rejects anything that is not a
218
+ `Baseline` with a `UsageError`).
219
+
220
+ ## [0.1.0a1] - 2026-10-14
221
+
222
+ First public alpha, published to PyPI to claim the `shockharness` name.
223
+ Contains Milestones A through D: MAGIC001, EXC001-EXC003, DEF001-DEF006,
224
+ SUPP001/SUPP002, configuration validation, text and JSON reports, and the
225
+ `scan`, `config`, `rules`, and `init` commands. No baselines, diff, SARIF,
226
+ complexity metrics, or skills yet; see `coverage.rules_not_implemented` in
227
+ every report.
228
+
229
+ Everything listed under "Added" below shipped in this version.
230
+
231
+ ### Added
232
+ - Milestone A: package skeleton, MIT license, configuration schema with full
233
+ shape and semantic validation, precedence (`--config` > `.shockharness.toml` >
234
+ `pyproject.toml` > bundled defaults), rule registry with every specified rule
235
+ ID, diagnostic and report models, `scan`, `config validate`, `config effective`,
236
+ `rules list`, `rules explain`, `init`, exit-code contract, GitHub Actions matrix.
237
+ - Milestone B: Python (`ast`) and JavaScript (tree-sitter) adapters with
238
+ byte-to-character column mapping, explicit parse-failure propagation,
239
+ `MAGIC001` (value-based 0/1, folded unary sign, annotations flagged),
240
+ `SUPP001`/`SUPP002` inline suppression checks, per-path overrides,
241
+ configuration-file exemption, JSON report (`schema_version` 1) and text output.
242
+ - Milestone C: catch facts in the IR (exception identities with
243
+ exact/approximate/unresolved confidence, handler body classification,
244
+ try-body operation attribution) for Python `except`/`except*` and JavaScript
245
+ `catch`, `.catch(handler)`, `.then(ok, handler)`; `EXC001` forbidden catch
246
+ (boundary whitelist, fail-closed on bare/unresolved identities, JavaScript
247
+ instanceof-guard convention), `EXC002` suppressed or converted exception,
248
+ `EXC003` broad try scope; `rules.exceptions.transparent_rethrow` (default
249
+ false); boundary `exception` names validated as dotted class names; every
250
+ boundary and `transparent_rethrow` reported under `config.deviations`.
251
+ - Milestone D: default facts in the IR (`DefaultFact`: family, kind, subject,
252
+ fallback, constant-shapedness, syntactic context, confidence) extracted by
253
+ `ir/python_defaults.py` and `ir/javascript_defaults.py`; `DEF001` parameter
254
+ and dataclass-field defaults, `DEF002` mapping/attribute/property fallbacks
255
+ (plus the implicit `None` of `.get(key)` as a review finding), `DEF003`
256
+ truthiness fallbacks (review by default; Boolean logic excluded), `DEF004`
257
+ nullish fallbacks, `DEF005` exception-to-default (literal returns and
258
+ one-step static traces to module constants and one-return helpers), `DEF006`
259
+ configuration fallbacks (`os.getenv`/`os.environ`/`process.env` in every
260
+ spelling); new `rules.defaults` keys `dataclass_fields`, `nullish_fallbacks`,
261
+ `configuration_fallbacks` (all `forbid`); loosened modes reported under
262
+ `config.deviations`; 13 golden fixtures and 25 unit tests; `safe_autofix` is
263
+ false for every finding.
264
+
265
+ ### Changed
266
+ - `EXC002` no longer reports the `return_fallback` classification while `DEF005`
267
+ is active: the handler is reported once, as `DEF005`, with the EXC002
268
+ classification named in the message. Disabling `DEF005` restores the EXC002
269
+ finding. Golden expectations for the eleven affected handlers changed rule ID
270
+ only.
271
+ - The tool's own code was brought under `DEF002`/`DEF004`: three `setdefault`
272
+ accumulators became explicit membership checks, the report's
273
+ `discovery`/`explicit_targets_excluded` fields are built with an explicit
274
+ branch, and nineteen `x if x is not None else v` expressions on parser fields
275
+ became either explicit branches (genuinely optional values: import aliases,
276
+ `from . import`, source segments) or assertions of grammar invariants (fields
277
+ tree-sitter and `ast` always populate). A Python `SyntaxError` position is now
278
+ recorded only when the interpreter reports both a line and a column; before,
279
+ a missing column was silently replaced by column 1.
280
+ - The arities that distinguish `getattr(o, a, v)` and `m.get(k, v)` from plain
281
+ lookups are named in `constants.py`.
282
+ - The tool's own boundaries were narrowed to satisfy `EXC003`: config decoding
283
+ and TOML parsing are two `try` blocks, analyzed-file decoding is bound before
284
+ the result is returned, and comment tokenization materializes the token list
285
+ in the one protected call. Behaviour is unchanged.
286
+
287
+ ### Dependencies
288
+ - `tree-sitter` 0.25.x (MIT) and `tree-sitter-javascript` 0.25.x (MIT). Pinned
289
+ as a tested pair; language ABI 15. `tree-sitter` 0.26.0 is excluded: with
290
+ CPython 3.14 it segfaults when walking trees nested deeper than about 200
291
+ levels (CPython 3.13 with 0.26.0 and CPython 3.14 with 0.25.2 are both fine).
292
+ The 300-deep nesting test in `tests/unit/test_javascript_adapter.py` guards this.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Thomas Lahore
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.