hecks 3.5.0 → 3.6.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 (1088) hide show
  1. checksums.yaml +4 -4
  2. data/examples/pizzas/bluebook/hecksagon/mock_stripe_payment_adapter.rb +1 -1
  3. data/lib/hecks/adapters/driven/claude_code.rb +11 -7
  4. data/lib/hecks/adapters/driven/d1/connection.rb +90 -0
  5. data/lib/hecks/adapters/driven/d1.rb +48 -314
  6. data/lib/hecks/adapters/driven/folder.rb +20 -10
  7. data/lib/hecks/adapters/driven/google_authentication.rb +18 -4
  8. data/lib/hecks/adapters/driven/governance_authorization.rb +2 -2
  9. data/lib/hecks/adapters/driven/heki/journal.rb +11 -10
  10. data/lib/hecks/adapters/driven/heki/saga_store.rb +4 -6
  11. data/lib/hecks/adapters/driven/heki/sagas.rb +55 -0
  12. data/lib/hecks/adapters/driven/heki.rb +21 -66
  13. data/lib/hecks/adapters/driven/in_memory_ordering.rb +37 -12
  14. data/lib/hecks/adapters/driven/lambda/client.rb +1 -1
  15. data/lib/hecks/adapters/driven/lambda.rb +1 -8
  16. data/lib/hecks/adapters/driven/memory/outbox_rows.rb +76 -0
  17. data/lib/hecks/adapters/driven/memory/shared_elements/values.rb +117 -0
  18. data/lib/hecks/adapters/driven/memory/shared_elements.rb +10 -97
  19. data/lib/hecks/adapters/driven/memory.rb +3 -65
  20. data/lib/hecks/adapters/driven/postgres/codec.rb +29 -26
  21. data/lib/hecks/adapters/driven/postgres/ddl.rb +50 -0
  22. data/lib/hecks/adapters/driven/postgres/dialect.rb +93 -0
  23. data/lib/hecks/adapters/driven/postgres/events.rb +57 -0
  24. data/lib/hecks/adapters/driven/postgres/journal.rb +127 -0
  25. data/lib/hecks/adapters/driven/postgres/outbox.rb +39 -31
  26. data/lib/hecks/adapters/driven/postgres/repository.rb +190 -0
  27. data/lib/hecks/adapters/driven/postgres/sagas.rb +72 -0
  28. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +13 -41
  29. data/lib/hecks/adapters/driven/postgres.rb +61 -528
  30. data/lib/hecks/adapters/driven/prism.rb +1 -1
  31. data/lib/hecks/adapters/driven/sql_query_builder.rb +45 -53
  32. data/lib/hecks/adapters/driven/sql_value_members.rb +57 -0
  33. data/lib/hecks/adapters/driven/sqlite/codec.rb +32 -29
  34. data/lib/hecks/adapters/driven/sqlite/ddl.rb +68 -0
  35. data/lib/hecks/adapters/driven/sqlite/dialect.rb +55 -0
  36. data/lib/hecks/adapters/driven/sqlite/events.rb +61 -0
  37. data/lib/hecks/adapters/driven/sqlite/journal.rb +125 -0
  38. data/lib/hecks/adapters/driven/sqlite/outbox.rb +98 -0
  39. data/lib/hecks/adapters/driven/sqlite/projection.rb +82 -63
  40. data/lib/hecks/adapters/driven/sqlite/repository.rb +146 -0
  41. data/lib/hecks/adapters/driven/sqlite/sagas.rb +69 -0
  42. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +8 -55
  43. data/lib/hecks/adapters/driven/sqlite.rb +36 -513
  44. data/lib/hecks/adapters/driven/tenant_provisioner.rb +13 -7
  45. data/lib/hecks/adapters/driving/github_webhook.rb +11 -8
  46. data/lib/hecks/behaviors/dsl.rb +17 -12
  47. data/lib/hecks/behaviors/expectations/boot.rb +81 -0
  48. data/lib/hecks/behaviors/expectations/checks.rb +178 -0
  49. data/lib/hecks/behaviors/expectations/dispatching.rb +93 -0
  50. data/lib/hecks/behaviors/expectations/qualify.rb +81 -0
  51. data/lib/hecks/behaviors/expectations.rb +39 -355
  52. data/lib/hecks/behaviors/rspec.rb +12 -12
  53. data/lib/hecks/behaviors/runner.rb +20 -11
  54. data/lib/hecks/bench/cli.rb +16 -13
  55. data/lib/hecks/bench/environment.rb +1 -1
  56. data/lib/hecks/bench/report.rb +11 -7
  57. data/lib/hecks/bench/ruby_runner.rb +13 -8
  58. data/lib/hecks/bench/rust_runner.rb +19 -11
  59. data/lib/hecks/bench/suite.rb +48 -20
  60. data/lib/hecks/bench/workload.rb +42 -31
  61. data/lib/hecks/bluebook/aggregate.rb +15 -21
  62. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +18 -21
  63. data/lib/hecks/bluebook/assembly/build.rb +12 -7
  64. data/lib/hecks/bluebook/assembly/contracts/read_models.rb +70 -0
  65. data/lib/hecks/bluebook/assembly/contracts/rules.rb +84 -0
  66. data/lib/hecks/bluebook/assembly/contracts/sagas.rb +109 -0
  67. data/lib/hecks/bluebook/assembly/contracts/structure.rb +91 -0
  68. data/lib/hecks/bluebook/assembly/contracts.rb +9 -319
  69. data/lib/hecks/bluebook/assembly/marks.rb +9 -8
  70. data/lib/hecks/bluebook/attribute.rb +13 -10
  71. data/lib/hecks/bluebook/behaviour/aggregate.rb +10 -3
  72. data/lib/hecks/bluebook/behaviour/read_model.rb +26 -13
  73. data/lib/hecks/bluebook/chapter.rb +22 -14
  74. data/lib/hecks/bluebook/command.rb +14 -18
  75. data/lib/hecks/bluebook/domain_port.rb +11 -8
  76. data/lib/hecks/bluebook/dsl/aggregate_builder/correction_sealing.rb +78 -0
  77. data/lib/hecks/bluebook/dsl/aggregate_builder/draining.rb +74 -0
  78. data/lib/hecks/bluebook/dsl/aggregate_builder/query_field_sealing.rb +105 -0
  79. data/lib/hecks/bluebook/dsl/aggregate_builder/query_sealing.rb +121 -0
  80. data/lib/hecks/bluebook/dsl/aggregate_builder/relationships.rb +128 -0
  81. data/lib/hecks/bluebook/dsl/aggregate_builder/rules.rb +84 -0
  82. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +61 -249
  83. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +51 -285
  84. data/lib/hecks/bluebook/dsl/attribute_collector/identity_resolution.rb +113 -0
  85. data/lib/hecks/bluebook/dsl/attribute_collector.rb +58 -117
  86. data/lib/hecks/bluebook/dsl/binding_proxy.rb +24 -19
  87. data/lib/hecks/bluebook/dsl/bluebook_builder/pending_givens.rb +109 -0
  88. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/correlation_keys.rb +105 -0
  89. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/event_shapes.rb +126 -0
  90. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/hop_tails.rb +79 -0
  91. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/projected_fields.rb +76 -0
  92. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/provisions.rb +65 -0
  93. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/query_hops.rb +123 -0
  94. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/references.rb +112 -0
  95. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/with_projections.rb +126 -0
  96. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +34 -595
  97. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +18 -107
  98. data/lib/hecks/bluebook/dsl/command_builder/implicit_attributes.rb +132 -0
  99. data/lib/hecks/bluebook/dsl/command_builder/mutations.rb +190 -0
  100. data/lib/hecks/bluebook/dsl/command_builder/owner.rb +22 -0
  101. data/lib/hecks/bluebook/dsl/command_builder/rules.rb +57 -0
  102. data/lib/hecks/bluebook/dsl/command_builder.rb +60 -360
  103. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +19 -13
  104. data/lib/hecks/bluebook/dsl/entity_builder/rules.rb +96 -0
  105. data/lib/hecks/bluebook/dsl/entity_builder/scope.rb +25 -0
  106. data/lib/hecks/bluebook/dsl/entity_builder/sealing.rb +94 -0
  107. data/lib/hecks/bluebook/dsl/entity_builder.rb +35 -169
  108. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +53 -24
  109. data/lib/hecks/bluebook/dsl/hecksagon_builder/chapter_scope.rb +133 -0
  110. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +3 -97
  111. data/lib/hecks/bluebook/dsl/identity_declaration.rb +73 -73
  112. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +7 -3
  113. data/lib/hecks/bluebook/dsl/need_word.rb +1 -1
  114. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +34 -29
  115. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +57 -41
  116. data/lib/hecks/bluebook/dsl/query_builder.rb +12 -15
  117. data/lib/hecks/bluebook/dsl/read_model_builder/sealing.rb +116 -0
  118. data/lib/hecks/bluebook/dsl/read_model_builder.rb +25 -102
  119. data/lib/hecks/bluebook/dsl/rule_reference.rb +26 -12
  120. data/lib/hecks/bluebook/dsl/translation_builder.rb +24 -14
  121. data/lib/hecks/bluebook/dsl/value_object_builder.rb +29 -19
  122. data/lib/hecks/bluebook/dsl/word_gate.rb +34 -27
  123. data/lib/hecks/bluebook/entity.rb +13 -17
  124. data/lib/hecks/bluebook/expression/ast_json/emitters.rb +93 -0
  125. data/lib/hecks/bluebook/expression/ast_json/emitters.rb-e +88 -0
  126. data/lib/hecks/bluebook/expression/ast_json.rb +49 -76
  127. data/lib/hecks/bluebook/expression/ast_reader.rb +95 -28
  128. data/lib/hecks/bluebook/expression/canonical_form.rb +6 -26
  129. data/lib/hecks/bluebook/expression/evaluator/parser.rb +62 -0
  130. data/lib/hecks/bluebook/expression/evaluator/scanning.rb +122 -0
  131. data/lib/hecks/bluebook/expression/evaluator.rb +11 -111
  132. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +28 -44
  133. data/lib/hecks/bluebook/expression/resolver/lookups.rb +77 -0
  134. data/lib/hecks/bluebook/expression/resolver/operations.rb +78 -0
  135. data/lib/hecks/bluebook/expression/resolver/parsing.rb +110 -0
  136. data/lib/hecks/bluebook/expression/resolver/scanning.rb +139 -0
  137. data/lib/hecks/bluebook/expression/resolver/values.rb +115 -0
  138. data/lib/hecks/bluebook/expression/resolver.rb +16 -414
  139. data/lib/hecks/bluebook/{hexagon.rb → hecksagon.rb} +22 -14
  140. data/lib/hecks/bluebook/keyword_fields.rb +42 -0
  141. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +7 -6
  142. data/lib/hecks/bluebook/meta_validator/judge/cells.rb +99 -0
  143. data/lib/hecks/bluebook/meta_validator/judge/dispatching.rb +111 -0
  144. data/lib/hecks/bluebook/meta_validator/judge/identity.rb +108 -0
  145. data/lib/hecks/bluebook/meta_validator/judge/walking.rb +141 -0
  146. data/lib/hecks/bluebook/meta_validator/judge.rb +31 -376
  147. data/lib/hecks/bluebook/meta_validator/modes.rb +137 -0
  148. data/lib/hecks/bluebook/meta_validator/plan/category.rb +44 -0
  149. data/lib/hecks/bluebook/meta_validator/plan/verbs.rb +73 -0
  150. data/lib/hecks/bluebook/meta_validator/plan.rb +38 -122
  151. data/lib/hecks/bluebook/meta_validator/readings/fields.rb +100 -0
  152. data/lib/hecks/bluebook/meta_validator/readings.rb +10 -95
  153. data/lib/hecks/bluebook/meta_validator/reconstruction/heads.rb +89 -0
  154. data/lib/hecks/bluebook/meta_validator/reconstruction/reading.rb +96 -0
  155. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +25 -151
  156. data/lib/hecks/bluebook/meta_validator/shapes/helpers.rb +83 -0
  157. data/lib/hecks/bluebook/meta_validator/shapes.rb +8 -75
  158. data/lib/hecks/bluebook/meta_validator/syntax_boot/disk_cache.rb +77 -0
  159. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +31 -95
  160. data/lib/hecks/bluebook/meta_validator/translation_judge/rules.rb +74 -0
  161. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +15 -65
  162. data/lib/hecks/bluebook/meta_validator/verdict_cache/tagging.rb +63 -0
  163. data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +22 -43
  164. data/lib/hecks/bluebook/meta_validator/world_judge.rb +8 -4
  165. data/lib/hecks/bluebook/meta_validator.rb +59 -175
  166. data/lib/hecks/bluebook/model_check/lifecycle_checks.rb +122 -0
  167. data/lib/hecks/bluebook/model_check/policy_checks.rb +129 -0
  168. data/lib/hecks/bluebook/model_check/saga_checks.rb +134 -0
  169. data/lib/hecks/bluebook/model_check/verbs.rb +69 -0
  170. data/lib/hecks/bluebook/model_check.rb +73 -413
  171. data/lib/hecks/bluebook/pattern_subset/refusals.rb +54 -0
  172. data/lib/hecks/bluebook/pattern_subset/scan.rb +58 -0
  173. data/lib/hecks/bluebook/pattern_subset.rb +93 -134
  174. data/lib/hecks/bluebook/process_manager.rb +10 -9
  175. data/lib/hecks/bluebook/project_discovery.rb +10 -7
  176. data/lib/hecks/bluebook/project_register.rb +42 -41
  177. data/lib/hecks/bluebook/query.rb +19 -12
  178. data/lib/hecks/bluebook/read_model.rb +41 -39
  179. data/lib/hecks/bluebook/smoke_test.rb +51 -36
  180. data/lib/hecks/bluebook/synthesizer.rb +13 -5
  181. data/lib/hecks/bluebook/translation.rb +12 -12
  182. data/lib/hecks/bluebook.rb +1 -1
  183. data/lib/hecks/chapters.rb +32 -16
  184. data/lib/hecks/cli/behaviors.rb +22 -15
  185. data/lib/hecks/cli/check_era.rb +2 -2
  186. data/lib/hecks/cli/command_table.rb +72 -0
  187. data/lib/hecks/cli/console.rb +10 -7
  188. data/lib/hecks/cli/deploy_template_diff.rb +22 -9
  189. data/lib/hecks/cli/document.rb +14 -10
  190. data/lib/hecks/cli/domain_stub.rb +1 -1
  191. data/lib/hecks/cli/domain_writer.rb +1 -1
  192. data/lib/hecks/cli/expression_projection.rb +8 -6
  193. data/lib/hecks/cli/follow.rb +10 -4
  194. data/lib/hecks/cli/interview_aggregate.rb +132 -0
  195. data/lib/hecks/cli/interview_draft.rb +16 -200
  196. data/lib/hecks/cli/interview_record.rb +130 -0
  197. data/lib/hecks/cli/interview_run.rb +19 -8
  198. data/lib/hecks/cli/interview_session/agent_state.rb +70 -0
  199. data/lib/hecks/cli/interview_session/catalog.rb +76 -0
  200. data/lib/hecks/cli/interview_session/manual_findings.rb +48 -0
  201. data/lib/hecks/cli/interview_session/proposals.rb +90 -0
  202. data/lib/hecks/cli/interview_session.rb +8 -205
  203. data/lib/hecks/cli/interview_shape.rb +13 -6
  204. data/lib/hecks/cli/ir.rb +8 -11
  205. data/lib/hecks/cli/launcher_words.rb +168 -0
  206. data/lib/hecks/cli/model_check/examination.rb +93 -0
  207. data/lib/hecks/cli/model_check/sources.rb +57 -0
  208. data/lib/hecks/cli/model_check.rb +28 -134
  209. data/lib/hecks/cli/project_cli/launcher_source.rb +177 -0
  210. data/lib/hecks/cli/project_cli/templates.rb +106 -0
  211. data/lib/hecks/cli/project_cli.rb +57 -236
  212. data/lib/hecks/cli/project_diagrams.rb +26 -4
  213. data/lib/hecks/cli/project_glossary.rb +19 -9
  214. data/lib/hecks/cli/refresh_rspec_runtime_baseline.rb +29 -13
  215. data/lib/hecks/cli/release.rb +29 -22
  216. data/lib/hecks/cli/release_gem.rb +12 -6
  217. data/lib/hecks/cli/rspec_io_parallel_files.rb +87 -30
  218. data/lib/hecks/cli/rspec_shard_files.rb +24 -9
  219. data/lib/hecks/cli/run.rb +25 -44
  220. data/lib/hecks/cli/run_expectations.rb +63 -0
  221. data/lib/hecks/cli/seed_semantics_corpus.rb +37 -17
  222. data/lib/hecks/cli/smoke_http/checks.rb +66 -0
  223. data/lib/hecks/cli/smoke_http/settings.rb +63 -0
  224. data/lib/hecks/cli/smoke_http/transport.rb +45 -0
  225. data/lib/hecks/cli/smoke_http.rb +8 -142
  226. data/lib/hecks/cli/smoke_test.rb +18 -4
  227. data/lib/hecks/cli/stores.rb +7 -4
  228. data/lib/hecks/cli/stress_concurrency_specs/reporting.rb +44 -0
  229. data/lib/hecks/cli/stress_concurrency_specs/usage.rb +28 -0
  230. data/lib/hecks/cli/stress_concurrency_specs.rb +82 -80
  231. data/lib/hecks/cli.rb +32 -208
  232. data/lib/hecks/codemod/runner.rb +92 -0
  233. data/lib/hecks/codemod/sweep.rb +94 -0
  234. data/lib/hecks/codemod.rb +22 -165
  235. data/lib/hecks/corpus/routes.rb +34 -0
  236. data/lib/hecks/corpus/rust.rb +193 -0
  237. data/lib/hecks/corpus/sources.rb +46 -0
  238. data/lib/hecks/corpus.rb +28 -265
  239. data/lib/hecks/doc/reference/coverage.rb +70 -0
  240. data/lib/hecks/doc/reference/harvester.rb +55 -0
  241. data/lib/hecks/doc/reference/readme.rb +89 -0
  242. data/lib/hecks/doc/reference/rendering.rb +149 -0
  243. data/lib/hecks/doc/reference.rb +14 -273
  244. data/lib/hecks/doors/cli_door/nesting.rb +34 -0
  245. data/lib/hecks/doors/cli_door/shorthand.rb +85 -0
  246. data/lib/hecks/doors/cli_door.rb +23 -95
  247. data/lib/hecks/doors/cli_runner/answers.rb +138 -0
  248. data/lib/hecks/doors/cli_runner/resolution.rb +146 -0
  249. data/lib/hecks/doors/cli_runner/settling.rb +115 -0
  250. data/lib/hecks/doors/cli_runner/streaming.rb +90 -0
  251. data/lib/hecks/doors/cli_runner/suggestions.rb +43 -0
  252. data/lib/hecks/doors/cli_runner.rb +12 -336
  253. data/lib/hecks/doors/command_request.rb +42 -31
  254. data/lib/hecks/doors/handle.rb +32 -30
  255. data/lib/hecks/doors/launcher_options.rb +62 -7
  256. data/lib/hecks/doors/mcp_door/properties.rb +47 -0
  257. data/lib/hecks/doors/mcp_door/tool_calls.rb +113 -0
  258. data/lib/hecks/doors/mcp_door/tool_definitions/auditing.rb +51 -0
  259. data/lib/hecks/doors/mcp_door/tool_definitions/calling.rb +69 -0
  260. data/lib/hecks/doors/mcp_door/tool_definitions/reading.rb +97 -0
  261. data/lib/hecks/doors/mcp_door/tool_definitions.rb +15 -0
  262. data/lib/hecks/doors/mcp_door.rb +55 -301
  263. data/lib/hecks/doors/mcp_door_scope/argument_policy.rb +92 -0
  264. data/lib/hecks/doors/mcp_door_scope/env_problems.rb +55 -0
  265. data/lib/hecks/doors/mcp_door_scope/presenter.rb +78 -0
  266. data/lib/hecks/doors/mcp_door_scope.rb +16 -172
  267. data/lib/hecks/doors/ruby_door/aggregate_door.rb +1 -1
  268. data/lib/hecks/doors/ruby_door/chapter.rb +33 -16
  269. data/lib/hecks/doors/ruby_door.rb +14 -11
  270. data/lib/hecks/doors/usage_cache.rb +14 -8
  271. data/lib/hecks/doors.rb +10 -4
  272. data/lib/hecks/embryonaut_bluebook/manifest.rb +11 -3
  273. data/lib/hecks/embryonaut_bluebook/registry/changelog.rb +26 -0
  274. data/lib/hecks/embryonaut_bluebook/registry/versions.rb +27 -0
  275. data/lib/hecks/embryonaut_bluebook/registry.rb +27 -37
  276. data/lib/hecks/embryonaut_bluebook/vendor/acceptance.rb +37 -0
  277. data/lib/hecks/embryonaut_bluebook/vendor.rb +22 -36
  278. data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +9 -6
  279. data/lib/hecks/embryonaut_bluebook.rb +16 -11
  280. data/lib/hecks/engine_agreement.rb +24 -17
  281. data/lib/hecks/forms/app/commands.rb +98 -0
  282. data/lib/hecks/forms/app/queries.rb +62 -0
  283. data/lib/hecks/forms/app/records.rb +67 -0
  284. data/lib/hecks/forms/app/responses.rb +46 -0
  285. data/lib/hecks/forms/app.rb +33 -221
  286. data/lib/hecks/forms/banking_presentation.rb +5 -0
  287. data/lib/hecks/forms/command_form_renderer.rb +55 -14
  288. data/lib/hecks/forms/field_controls.rb +95 -0
  289. data/lib/hecks/forms/field_renderer.rb +34 -88
  290. data/lib/hecks/forms/field_shape.rb +9 -34
  291. data/lib/hecks/forms/html.rb +1 -1
  292. data/lib/hecks/forms/index_renderer.rb +14 -9
  293. data/lib/hecks/forms/page.rb +2 -106
  294. data/lib/hecks/forms/page_assets.rb +88 -0
  295. data/lib/hecks/forms/page_script.rb +28 -0
  296. data/lib/hecks/forms/params.rb +17 -8
  297. data/lib/hecks/forms/query_form_renderer.rb +11 -7
  298. data/lib/hecks/forms/record_renderer.rb +28 -19
  299. data/lib/hecks/forms/reference_options.rb +1 -1
  300. data/lib/hecks/forms/text_shape.rb +40 -0
  301. data/lib/hecks/fqn.rb +64 -32
  302. data/lib/hecks/framework.rb +10 -3
  303. data/lib/hecks/freezer.rb +10 -1
  304. data/lib/hecks/fuzzing/bounded_exhaustive_expressions/fragments.rb +61 -0
  305. data/lib/hecks/fuzzing/bounded_exhaustive_expressions/productions.rb +217 -0
  306. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +5 -194
  307. data/lib/hecks/fuzzing/combination_miner.rb +23 -12
  308. data/lib/hecks/fuzzing/concurrent_dispatch/racing.rb +64 -0
  309. data/lib/hecks/fuzzing/concurrent_dispatch/world_copy.rb +66 -0
  310. data/lib/hecks/fuzzing/concurrent_dispatch.rb +31 -107
  311. data/lib/hecks/fuzzing/coverage_campaign.rb +7 -7
  312. data/lib/hecks/fuzzing/differential/wire_comparison.rb +74 -0
  313. data/lib/hecks/fuzzing/differential.rb +59 -84
  314. data/lib/hecks/fuzzing/domain_generator/block_rendering.rb +70 -0
  315. data/lib/hecks/fuzzing/domain_generator/builder/attribute_shapes.rb +105 -0
  316. data/lib/hecks/fuzzing/domain_generator/builder/shapes.rb +97 -0
  317. data/lib/hecks/fuzzing/domain_generator/builder/wiring.rb +128 -0
  318. data/lib/hecks/fuzzing/domain_generator/builder.rb +105 -0
  319. data/lib/hecks/fuzzing/domain_generator/pruning.rb +90 -0
  320. data/lib/hecks/fuzzing/domain_generator/removals.rb +62 -0
  321. data/lib/hecks/fuzzing/domain_generator/rendering.rb +94 -0
  322. data/lib/hecks/fuzzing/domain_generator/tokens.rb +38 -0
  323. data/lib/hecks/fuzzing/domain_generator.rb +7 -555
  324. data/lib/hecks/fuzzing/era_boundary.rb +34 -24
  325. data/lib/hecks/fuzzing/form_census.rb +18 -8
  326. data/lib/hecks/fuzzing/generated_domain_check.rb +23 -11
  327. data/lib/hecks/fuzzing/isolated_boot/copying.rb +62 -0
  328. data/lib/hecks/fuzzing/isolated_boot/postgres_scratch.rb +133 -0
  329. data/lib/hecks/fuzzing/isolated_boot/worlds.rb +102 -0
  330. data/lib/hecks/fuzzing/isolated_boot.rb +25 -252
  331. data/lib/hecks/fuzzing/persistence_parity.rb +5 -5
  332. data/lib/hecks/fuzzing/properties/aggregation_recompute.rb +121 -0
  333. data/lib/hecks/fuzzing/properties/catalog.rb +138 -0
  334. data/lib/hecks/fuzzing/properties/corrections.rb +23 -20
  335. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +64 -185
  336. data/lib/hecks/fuzzing/properties/group_by_recompute.rb +69 -0
  337. data/lib/hecks/fuzzing/properties/guards.rb +76 -108
  338. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +66 -227
  339. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +46 -35
  340. data/lib/hecks/fuzzing/properties/mutation_recompute.rb +162 -0
  341. data/lib/hecks/fuzzing/properties/outbox.rb +41 -24
  342. data/lib/hecks/fuzzing/properties/piece_invariants.rb +61 -0
  343. data/lib/hecks/fuzzing/properties/policy_wiring.rb +140 -0
  344. data/lib/hecks/fuzzing/properties/query_rows.rb +67 -0
  345. data/lib/hecks/fuzzing/properties/querying.rb +69 -90
  346. data/lib/hecks/fuzzing/properties/references.rb +100 -0
  347. data/lib/hecks/fuzzing/properties/tenant_scope.rb +84 -0
  348. data/lib/hecks/fuzzing/properties.rb +6 -119
  349. data/lib/hecks/fuzzing/qa_settings.rb +23 -17
  350. data/lib/hecks/fuzzing/replay/fan_out.rb +78 -0
  351. data/lib/hecks/fuzzing/replay/filters.rb +72 -0
  352. data/lib/hecks/fuzzing/replay/guard_check.rb +88 -0
  353. data/lib/hecks/fuzzing/replay/mutation_trace.rb +117 -0
  354. data/lib/hecks/fuzzing/replay/observation.rb +60 -0
  355. data/lib/hecks/fuzzing/replay/read_only_steps.rb +84 -0
  356. data/lib/hecks/fuzzing/replay/session.rb +151 -0
  357. data/lib/hecks/fuzzing/replay.rb +35 -442
  358. data/lib/hecks/fuzzing/rust_gap_manifest.rb +3 -4
  359. data/lib/hecks/fuzzing/self_consistency/saga_redelivery.rb +133 -0
  360. data/lib/hecks/fuzzing/self_consistency/sagas.rb +93 -0
  361. data/lib/hecks/fuzzing/self_consistency/value_objects.rb +87 -0
  362. data/lib/hecks/fuzzing/self_consistency.rb +8 -213
  363. data/lib/hecks/fuzzing/sequence_generator/adversary/argument_mutations.rb +116 -0
  364. data/lib/hecks/fuzzing/sequence_generator/adversary/caller_draw.rb +68 -0
  365. data/lib/hecks/fuzzing/sequence_generator/adversary/identity_mutations.rb +62 -0
  366. data/lib/hecks/fuzzing/sequence_generator/adversary/precedence_mutations.rb +126 -0
  367. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +23 -283
  368. data/lib/hecks/fuzzing/sequence_generator/argument_drawing.rb +78 -0
  369. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +69 -53
  370. data/lib/hecks/fuzzing/sequence_generator/identity_shaping.rb +85 -0
  371. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +20 -16
  372. data/lib/hecks/fuzzing/sequence_generator/picker.rb +12 -6
  373. data/lib/hecks/fuzzing/sequence_generator/prefixing.rb +45 -0
  374. data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +19 -8
  375. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +43 -144
  376. data/lib/hecks/fuzzing/sequence_generator.rb +65 -63
  377. data/lib/hecks/fuzzing/shrinker.rb +33 -29
  378. data/lib/hecks/fuzzing/target_capabilities.rb +10 -4
  379. data/lib/hecks/fuzzing/value_generator.rb +27 -17
  380. data/lib/hecks/gate/stages.yml +10 -0
  381. data/lib/hecks/grammar/evolve/arguments.rb +90 -0
  382. data/lib/hecks/grammar/evolve/keywords.rb +81 -0
  383. data/lib/hecks/grammar/evolve/seeds.rb +172 -0
  384. data/lib/hecks/grammar/evolve.rb +22 -263
  385. data/lib/hecks/grammar/operators.rb +111 -0
  386. data/lib/hecks/grammar.rb +53 -126
  387. data/lib/hecks/hecks/adapters/bluebook_report.rb +6 -6
  388. data/lib/hecks/hecks/adapters/codebase/codemods.rb +1 -1
  389. data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +23 -9
  390. data/lib/hecks/hecks/adapters/codebase/language/edits.rb +75 -0
  391. data/lib/hecks/hecks/adapters/codebase/language.rb +30 -72
  392. data/lib/hecks/hecks/adapters/codebase/promotion/candidates.rb +54 -0
  393. data/lib/hecks/hecks/adapters/codebase/promotion/gate.rb +50 -0
  394. data/lib/hecks/hecks/adapters/codebase/promotion/move.rb +70 -0
  395. data/lib/hecks/hecks/adapters/codebase/promotion/standing.rb +88 -0
  396. data/lib/hecks/hecks/adapters/codebase/promotion/watch.rb +83 -0
  397. data/lib/hecks/hecks/adapters/codebase/promotion.rb +115 -0
  398. data/lib/hecks/hecks/adapters/codebase/publishing.rb +31 -12
  399. data/lib/hecks/hecks/adapters/codebase/regeneration.rb +43 -6
  400. data/lib/hecks/hecks/adapters/codebase/release_facts.rb +16 -5
  401. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +9 -1
  402. data/lib/hecks/hecks/adapters/codebase/sqlite_fixture.rb +1 -1
  403. data/lib/hecks/hecks/adapters/codebase/style.rb +1 -1
  404. data/lib/hecks/hecks/adapters/codebase/test_runner.rb +11 -5
  405. data/lib/hecks/hecks/adapters/codebase/test_suite.rb +27 -8
  406. data/lib/hecks/hecks/adapters/console_capture.rb +21 -17
  407. data/lib/hecks/hecks/adapters/cost_explorer.rb +11 -6
  408. data/lib/hecks/hecks/adapters/deploy_plans.rb +3 -3
  409. data/lib/hecks/hecks/adapters/deploy_toolchain/answers.rb +141 -0
  410. data/lib/hecks/hecks/adapters/deploy_toolchain/scripts.rb +95 -0
  411. data/lib/hecks/hecks/adapters/deploy_toolchain/statuses.rb +74 -0
  412. data/lib/hecks/hecks/adapters/deploy_toolchain.rb +21 -239
  413. data/lib/hecks/hecks/adapters/finding_github.rb +2 -2
  414. data/lib/hecks/hecks/adapters/first_admin.rb +22 -8
  415. data/lib/hecks/hecks/adapters/git.rb +18 -5
  416. data/lib/hecks/hecks/adapters/git_refs.rb +144 -0
  417. data/lib/hecks/hecks/adapters/github_rulesets.rb +126 -0
  418. data/lib/hecks/hecks/adapters/host_http.rb +16 -6
  419. data/lib/hecks/hecks/adapters/in_process_boot.rb +9 -5
  420. data/lib/hecks/hecks/adapters/in_process_operations/tailing.rb +61 -0
  421. data/lib/hecks/hecks/adapters/in_process_operations.rb +33 -35
  422. data/lib/hecks/hecks/adapters/journal_store/changes.rb +21 -15
  423. data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +29 -12
  424. data/lib/hecks/hecks/adapters/journal_store/readings/attestation.rb +52 -0
  425. data/lib/hecks/hecks/adapters/journal_store/readings/scaffolding.rb +52 -0
  426. data/lib/hecks/hecks/adapters/journal_store/readings.rb +5 -62
  427. data/lib/hecks/hecks/adapters/local_files.rb +27 -10
  428. data/lib/hecks/hecks/adapters/pg_admin/ownership.rb +115 -0
  429. data/lib/hecks/hecks/adapters/pg_admin.rb +14 -87
  430. data/lib/hecks/hecks/adapters/process_pool.rb +54 -22
  431. data/lib/hecks/hecks/adapters/rust_toolchain.rb +12 -4
  432. data/lib/hecks/hecks/adapters/site_toolchain.rb +16 -7
  433. data/lib/hecks/hecks/adapters/terminal.rb +13 -9
  434. data/lib/hecks/hecks/codebase.bluebook +322 -5
  435. data/lib/hecks/hecks/hecks.hecksagon +16 -0
  436. data/lib/hecks/hecks/hecks.world +10 -2
  437. data/lib/hecks/kernel_capabilities/templates.rb +92 -0
  438. data/lib/hecks/kernel_capabilities.rb +18 -85
  439. data/lib/hecks/language/bluebook/vocabulary.bluebook +43 -1
  440. data/lib/hecks/literal/item_splitter.rb +50 -0
  441. data/lib/hecks/literal.rb +29 -38
  442. data/lib/hecks/mcp_stdio_guard.rb +8 -8
  443. data/lib/hecks/naming.rb +9 -3
  444. data/lib/hecks/persistence_legacy_fixture/postgres_stores.rb +81 -0
  445. data/lib/hecks/persistence_legacy_fixture/regenerate.rb +26 -66
  446. data/lib/hecks/persistence_legacy_fixture.rb +13 -10
  447. data/lib/hecks/ports/access_control.rb +13 -10
  448. data/lib/hecks/ports/agent/answers.rb +2 -2
  449. data/lib/hecks/ports/agent.rb +13 -10
  450. data/lib/hecks/ports/authentication.rb +13 -10
  451. data/lib/hecks/ports/authorization.rb +13 -10
  452. data/lib/hecks/ports/clock.rb +13 -10
  453. data/lib/hecks/ports/extraction.rb +13 -12
  454. data/lib/hecks/ports/identity_assignment.rb +13 -10
  455. data/lib/hecks/ports/identity_generation.rb +13 -10
  456. data/lib/hecks/ports/identity_resolution.rb +13 -10
  457. data/lib/hecks/ports/key_vault.rb +13 -10
  458. data/lib/hecks/ports/persistence/append_only.rb +5 -5
  459. data/lib/hecks/ports/persistence/binding_policy.rb +33 -9
  460. data/lib/hecks/ports/persistence/codec_boundary.rb +22 -11
  461. data/lib/hecks/ports/persistence/constants.rb +10 -0
  462. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +18 -16
  463. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +27 -18
  464. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +52 -23
  465. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +14 -8
  466. data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +17 -6
  467. data/lib/hecks/ports/persistence/plugins/era/lineage/coverage.rb +58 -0
  468. data/lib/hecks/ports/persistence/plugins/era/lineage/rewrite.rb +130 -0
  469. data/lib/hecks/ports/persistence/plugins/era/lineage/rules.rb +25 -0
  470. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +20 -180
  471. data/lib/hecks/ports/persistence/plugins/era/postgres_era/boot.rb +41 -0
  472. data/lib/hecks/ports/persistence/plugins/era/postgres_era/class_methods.rb +105 -0
  473. data/lib/hecks/ports/persistence/plugins/era/postgres_era/dialect.rb +137 -0
  474. data/lib/hecks/ports/persistence/plugins/era/postgres_era/events.rb +25 -28
  475. data/lib/hecks/ports/persistence/plugins/era/postgres_era/head_writes.rb +62 -0
  476. data/lib/hecks/ports/persistence/plugins/era/postgres_era/journal.rb +128 -0
  477. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/base_sql.rb +74 -0
  478. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/chain_sql.rb +169 -0
  479. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/domain_rename.rb +91 -0
  480. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_integrity.rb +99 -0
  481. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +0 -73
  482. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +55 -39
  483. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +51 -222
  484. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/merge_writes.rb +120 -0
  485. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +85 -60
  486. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +97 -181
  487. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +19 -19
  488. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/sql_templates.rb +88 -0
  489. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +77 -118
  490. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +3 -117
  491. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_sql.rb +116 -0
  492. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +3 -1
  493. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/context.rb +38 -0
  494. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +43 -25
  495. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +62 -38
  496. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +21 -16
  497. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +66 -86
  498. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/refusals.rb +75 -0
  499. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +27 -9
  500. data/lib/hecks/ports/persistence/plugins/era/postgres_era/query_phases.rb +63 -0
  501. data/lib/hecks/ports/persistence/plugins/era/postgres_era/sagas.rb +78 -0
  502. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +28 -482
  503. data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +40 -12
  504. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +22 -15
  505. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +39 -19
  506. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -16
  507. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +48 -25
  508. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +60 -99
  509. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/path_diff.rb +57 -0
  510. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +21 -11
  511. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +13 -4
  512. data/lib/hecks/ports/persistence/postgres_dump/connection.rb +4 -2
  513. data/lib/hecks/ports/persistence/postgres_dump.rb +13 -7
  514. data/lib/hecks/ports/persistence/repository_factory.rb +35 -11
  515. data/lib/hecks/ports/persistence/state_codec.rb +10 -0
  516. data/lib/hecks/ports/persistence.rb +1 -10
  517. data/lib/hecks/ports/projection.rb +34 -21
  518. data/lib/hecks/ports/query/in_memory.rb +16 -8
  519. data/lib/hecks/projection_files/expression_tables.rb +64 -0
  520. data/lib/hecks/projection_files/texts.rb +67 -0
  521. data/lib/hecks/projection_files.rb +19 -103
  522. data/lib/hecks/projections/bootstrap_table.rb +43 -29
  523. data/lib/hecks/projections/deploy/bastion_lines.rb +32 -0
  524. data/lib/hecks/projections/deploy/box/access.rb +73 -0
  525. data/lib/hecks/projections/deploy/box/caddy.rb +87 -0
  526. data/lib/hecks/projections/deploy/box/compose.rb +70 -0
  527. data/lib/hecks/projections/deploy/box/hosting.rb +4 -105
  528. data/lib/hecks/projections/deploy/box/hosting_parts.rb +97 -0
  529. data/lib/hecks/projections/deploy/box/hosting_settings.rb +1 -1
  530. data/lib/hecks/projections/deploy/box/roll_recipe.rb +1 -1
  531. data/lib/hecks/projections/deploy/box/roll_scripts.rb +135 -0
  532. data/lib/hecks/projections/deploy/box/settings/checks.rb +38 -0
  533. data/lib/hecks/projections/deploy/box/settings/containers.rb +123 -0
  534. data/lib/hecks/projections/deploy/box/settings/origin.rb +56 -0
  535. data/lib/hecks/projections/deploy/box/settings/patterns.rb +43 -0
  536. data/lib/hecks/projections/deploy/box/settings/types.rb +72 -0
  537. data/lib/hecks/projections/deploy/box/settings.rb +58 -263
  538. data/lib/hecks/projections/deploy/box/stack_yaml.rb +107 -0
  539. data/lib/hecks/projections/deploy/box/templates/MIGRATION.md.tmpl +39 -0
  540. data/lib/hecks/projections/deploy/box/templates/Makefile.tmpl +24 -0
  541. data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +41 -1
  542. data/lib/hecks/projections/deploy/box/templates/deploy-service.services-roll.part +2 -1
  543. data/lib/hecks/projections/deploy/box/templates/deploy-service.taskdef-roll.part +2 -1
  544. data/lib/hecks/projections/deploy/box/templates/deploy-usage-services.tmpl +4 -0
  545. data/lib/hecks/projections/deploy/box/templates/deploy-usage-taskdef.tmpl +5 -0
  546. data/lib/hecks/projections/deploy/box/templates/ecr-repository.yaml.tmpl +9 -0
  547. data/lib/hecks/projections/deploy/box/templates/mode-summary-services.tmpl +2 -0
  548. data/lib/hecks/projections/deploy/box/templates/mode-summary-taskdef.tmpl +4 -0
  549. data/lib/hecks/projections/deploy/box/templates/services-block.tmpl +9 -0
  550. data/lib/hecks/projections/deploy/box/templates/tunnel-egress.yaml.tmpl +11 -0
  551. data/lib/hecks/projections/deploy/box/templates/writable-secrets.yaml.tmpl +7 -0
  552. data/lib/hecks/projections/deploy/box.rb +33 -493
  553. data/lib/hecks/projections/deploy/fargate/assembly.rb +13 -142
  554. data/lib/hecks/projections/deploy/fargate/cdn/behaviors.rb +76 -0
  555. data/lib/hecks/projections/deploy/fargate/cdn/lines.rb +112 -0
  556. data/lib/hecks/projections/deploy/fargate/cdn/options.rb +85 -0
  557. data/lib/hecks/projections/deploy/fargate/cdn.rb +23 -215
  558. data/lib/hecks/projections/deploy/fargate/check.rb +16 -6
  559. data/lib/hecks/projections/deploy/fargate/containers/definition_lines.rb +61 -0
  560. data/lib/hecks/projections/deploy/fargate/containers/reading.rb +86 -0
  561. data/lib/hecks/projections/deploy/fargate/containers/rendering.rb +132 -0
  562. data/lib/hecks/projections/deploy/fargate/containers/routing.rb +93 -0
  563. data/lib/hecks/projections/deploy/fargate/containers.rb +10 -286
  564. data/lib/hecks/projections/deploy/fargate/extras/reading.rb +90 -0
  565. data/lib/hecks/projections/deploy/fargate/extras/rendering.rb +81 -0
  566. data/lib/hecks/projections/deploy/fargate/extras.rb +9 -158
  567. data/lib/hecks/projections/deploy/fargate/monitoring/alarms.rb +115 -0
  568. data/lib/hecks/projections/deploy/fargate/monitoring/reading.rb +84 -0
  569. data/lib/hecks/projections/deploy/fargate/monitoring/topic.rb +59 -0
  570. data/lib/hecks/projections/deploy/fargate/monitoring/warmer.rb +90 -0
  571. data/lib/hecks/projections/deploy/fargate/monitoring/warmer_roles.rb +76 -0
  572. data/lib/hecks/projections/deploy/fargate/monitoring.rb +14 -348
  573. data/lib/hecks/projections/deploy/fargate/sections.rb +115 -0
  574. data/lib/hecks/projections/deploy/fargate/settings/ids.rb +55 -0
  575. data/lib/hecks/projections/deploy/fargate/settings/tuning.rb +77 -0
  576. data/lib/hecks/projections/deploy/fargate/settings.rb +17 -104
  577. data/lib/hecks/projections/deploy/fargate/stack/blocks.rb +91 -0
  578. data/lib/hecks/projections/deploy/fargate/stack/readers.rb +87 -0
  579. data/lib/hecks/projections/deploy/fargate/stack/recipes.rb +106 -0
  580. data/lib/hecks/projections/deploy/fargate/stack.rb +146 -0
  581. data/lib/hecks/projections/deploy/fargate/task_sections.rb +56 -0
  582. data/lib/hecks/projections/deploy/fargate/yaml.rb +1 -1
  583. data/lib/hecks/projections/deploy/fargate.rb +51 -756
  584. data/lib/hecks/projections/deploy/lambda/pii.rb +49 -0
  585. data/lib/hecks/projections/deploy/lambda/stack/blocks.rb +118 -0
  586. data/lib/hecks/projections/deploy/lambda/stack/checks.rb +64 -0
  587. data/lib/hecks/projections/deploy/lambda/stack/deploy_chain.rb +119 -0
  588. data/lib/hecks/projections/deploy/lambda/stack/readers.rb +62 -0
  589. data/lib/hecks/projections/deploy/lambda/stack/reading.rb +170 -0
  590. data/lib/hecks/projections/deploy/lambda/stack/reading_web.rb +53 -0
  591. data/lib/hecks/projections/deploy/lambda/stack/recipes.rb +112 -0
  592. data/lib/hecks/projections/deploy/lambda/stack/web.rb +57 -0
  593. data/lib/hecks/projections/deploy/lambda/stack.rb +54 -0
  594. data/lib/hecks/projections/deploy/lambda.rb +77 -1885
  595. data/lib/hecks/projections/deploy/preview/access_yaml.rb +8 -74
  596. data/lib/hecks/projections/deploy/preview/containers.rb +18 -7
  597. data/lib/hecks/projections/deploy/preview/routing_yaml.rb +15 -87
  598. data/lib/hecks/projections/deploy/preview/settings.rb +7 -4
  599. data/lib/hecks/projections/deploy/preview/task_yaml.rb +42 -106
  600. data/lib/hecks/projections/deploy/preview/template.rb +11 -106
  601. data/lib/hecks/projections/deploy/preview/yaml_text.rb +1 -1
  602. data/lib/hecks/projections/deploy/scripts/hosting.mk.tmpl +1 -1
  603. data/lib/hecks/projections/deploy/scripts/settings.rb +14 -6
  604. data/lib/hecks/projections/deploy/scripts.rb +11 -9
  605. data/lib/hecks/projections/deploy/shared/rows.rb +48 -0
  606. data/lib/hecks/projections/deploy/shared.rb +89 -449
  607. data/lib/hecks/projections/deploy/smoke.rb +15 -10
  608. data/lib/hecks/projections/deploy/template_diff/comparer.rb +21 -147
  609. data/lib/hecks/projections/deploy/template_diff/normalizer.rb +60 -0
  610. data/lib/hecks/projections/deploy/template_diff/value_diff.rb +136 -0
  611. data/lib/hecks/projections/deploy/template_diff.rb +8 -3
  612. data/lib/hecks/projections/deploy/templates/fargate/Dockerfile.tmpl +28 -0
  613. data/lib/hecks/projections/deploy/templates/fargate/Makefile.tmpl +63 -0
  614. data/lib/hecks/projections/deploy/templates/fargate/always_params.tmpl +4 -0
  615. data/lib/hecks/projections/deploy/templates/fargate/deploy_plain.tmpl +1 -0
  616. data/lib/hecks/projections/deploy/templates/fargate/deploy_shared.tmpl +11 -0
  617. data/lib/hecks/projections/deploy/templates/fargate/mint_era_own.tmpl +44 -0
  618. data/lib/hecks/projections/deploy/templates/fargate/mint_era_shared.tmpl +8 -0
  619. data/lib/hecks/projections/deploy/templates/fargate/missing_owner.tmpl +9 -0
  620. data/lib/hecks/projections/deploy/templates/fargate/oauth_params.tmpl +7 -0
  621. data/lib/hecks/projections/deploy/templates/fargate/oauth_task_env.tmpl +4 -0
  622. data/lib/hecks/projections/deploy/templates/fargate/oauth_task_policy.tmpl +7 -0
  623. data/lib/hecks/projections/deploy/templates/fargate/owning_params.tmpl +33 -0
  624. data/lib/hecks/projections/deploy/templates/fargate/template.yaml.tmpl +281 -0
  625. data/lib/hecks/projections/deploy/templates/lambda/Makefile.tmpl +218 -0
  626. data/lib/hecks/projections/deploy/templates/lambda/aurora_depends.tmpl +18 -0
  627. data/lib/hecks/projections/deploy/templates/lambda/build_dispatch_none.tmpl +10 -0
  628. data/lib/hecks/projections/deploy/templates/lambda/build_pg.tmpl +15 -0
  629. data/lib/hecks/projections/deploy/templates/lambda/build_plain.tmpl +2 -0
  630. data/lib/hecks/projections/deploy/templates/lambda/build_web.tmpl +11 -0
  631. data/lib/hecks/projections/deploy/templates/lambda/env_blind_guard.tmpl +4 -0
  632. data/lib/hecks/projections/deploy/templates/lambda/mint_era_own.tmpl +122 -0
  633. data/lib/hecks/projections/deploy/templates/lambda/mint_era_shared.tmpl +47 -0
  634. data/lib/hecks/projections/deploy/templates/lambda/missing_owner.tmpl +9 -0
  635. data/lib/hecks/projections/deploy/templates/lambda/nat_comment_oauth.tmpl +14 -0
  636. data/lib/hecks/projections/deploy/templates/lambda/nat_comment_plain.tmpl +3 -0
  637. data/lib/hecks/projections/deploy/templates/lambda/oauth_note.tmpl +8 -0
  638. data/lib/hecks/projections/deploy/templates/lambda/params_oauth.tmpl +13 -0
  639. data/lib/hecks/projections/deploy/templates/lambda/params_shared.tmpl +33 -0
  640. data/lib/hecks/projections/deploy/templates/lambda/pg_native.tmpl +71 -0
  641. data/lib/hecks/projections/deploy/templates/lambda/pii_oac.tmpl +8 -0
  642. data/lib/hecks/projections/deploy/templates/lambda/pii_permission.tmpl +8 -0
  643. data/lib/hecks/projections/deploy/templates/lambda/pii_resources.tmpl +116 -0
  644. data/lib/hecks/projections/deploy/templates/lambda/predeploy_bridge.tmpl +30 -0
  645. data/lib/hecks/projections/deploy/templates/lambda/predeploy_bridge_oauth.tmpl +12 -0
  646. data/lib/hecks/projections/deploy/templates/lambda/predeploy_bridge_plain.tmpl +7 -0
  647. data/lib/hecks/projections/deploy/templates/lambda/rename_schema_own.tmpl +93 -0
  648. data/lib/hecks/projections/deploy/templates/lambda/rename_schema_shared.tmpl +7 -0
  649. data/lib/hecks/projections/deploy/templates/lambda/rust_oauth_env.tmpl +17 -0
  650. data/lib/hecks/projections/deploy/templates/lambda/rust_oauth_policy.tmpl +16 -0
  651. data/lib/hecks/projections/deploy/templates/lambda/rust_session_secret.tmpl +13 -0
  652. data/lib/hecks/projections/deploy/templates/lambda/samconfig.toml.tmpl +12 -0
  653. data/lib/hecks/projections/deploy/templates/lambda/shared_note.tmpl +6 -0
  654. data/lib/hecks/projections/deploy/templates/lambda/sync_oauth.tmpl +23 -0
  655. data/lib/hecks/projections/deploy/templates/lambda/template.yaml.tmpl +187 -0
  656. data/lib/hecks/projections/deploy/templates/lambda/translation_own.tmpl +46 -0
  657. data/lib/hecks/projections/deploy/templates/lambda/translation_shared.tmpl +2 -0
  658. data/lib/hecks/projections/deploy/templates/lambda/web_function.tmpl +207 -0
  659. data/lib/hecks/projections/deploy/templates/lambda/web_google_oauth_env.tmpl +12 -0
  660. data/lib/hecks/projections/deploy/templates/lambda/web_no_oauth_env.tmpl +7 -0
  661. data/lib/hecks/projections/deploy/templates/lambda/web_oauth_policy.tmpl +4 -0
  662. data/lib/hecks/projections/deploy/templates/lambda/webhook_secret_resource.tmpl +20 -0
  663. data/lib/hecks/projections/deploy/templates/preview/alb.tmpl +8 -0
  664. data/lib/hecks/projections/deploy/templates/preview/alb_security_group.tmpl +11 -0
  665. data/lib/hecks/projections/deploy/templates/preview/assume_role.tmpl +6 -0
  666. data/lib/hecks/projections/deploy/templates/preview/base_parameters.tmpl +24 -0
  667. data/lib/hecks/projections/deploy/templates/preview/cluster.tmpl +4 -0
  668. data/lib/hecks/projections/deploy/templates/preview/container_secret.tmpl +9 -0
  669. data/lib/hecks/projections/deploy/templates/preview/db_init_script.sh.tmpl +23 -0
  670. data/lib/hecks/projections/deploy/templates/preview/db_init_secrets.tmpl +5 -0
  671. data/lib/hecks/projections/deploy/templates/preview/db_init_task.tmpl +39 -0
  672. data/lib/hecks/projections/deploy/templates/preview/desired_count.tmpl +5 -0
  673. data/lib/hecks/projections/deploy/templates/preview/distribution.tmpl +24 -0
  674. data/lib/hecks/projections/deploy/templates/preview/execution_role.tmpl +16 -0
  675. data/lib/hecks/projections/deploy/templates/preview/header.tmpl +16 -0
  676. data/lib/hecks/projections/deploy/templates/preview/ingress.tmpl +8 -0
  677. data/lib/hecks/projections/deploy/templates/preview/listener.tmpl +9 -0
  678. data/lib/hecks/projections/deploy/templates/preview/listener_rule.tmpl +11 -0
  679. data/lib/hecks/projections/deploy/templates/preview/log_group.tmpl +5 -0
  680. data/lib/hecks/projections/deploy/templates/preview/log_options.tmpl +5 -0
  681. data/lib/hecks/projections/deploy/templates/preview/outputs.tmpl +14 -0
  682. data/lib/hecks/projections/deploy/templates/preview/service.tmpl +17 -0
  683. data/lib/hecks/projections/deploy/templates/preview/session_secret.tmpl +11 -0
  684. data/lib/hecks/projections/deploy/templates/preview/target_group.tmpl +9 -0
  685. data/lib/hecks/projections/deploy/templates/preview/task_definition.tmpl +15 -0
  686. data/lib/hecks/projections/deploy/templates/preview/task_role.tmpl +22 -0
  687. data/lib/hecks/projections/deploy/templates/shared/aurora_db.tmpl +52 -0
  688. data/lib/hecks/projections/deploy/templates/shared/bastion.tmpl +74 -0
  689. data/lib/hecks/projections/deploy/templates/shared/bastion_own_public.tmpl +41 -0
  690. data/lib/hecks/projections/deploy/templates/shared/bastion_reuse_public.tmpl +10 -0
  691. data/lib/hecks/projections/deploy/templates/shared/nat_gateway.tmpl +102 -0
  692. data/lib/hecks/projections/deploy/templates/shared/oauth_egress.tmpl +17 -0
  693. data/lib/hecks/projections/deploy/templates/shared/plain_db.tmpl +16 -0
  694. data/lib/hecks/projections/deploy/templates/shared/vpc_and_database.tmpl +67 -0
  695. data/lib/hecks/projections/deploy/text_template.rb +35 -0
  696. data/lib/hecks/projections/diagrams/dispatch.rb +114 -0
  697. data/lib/hecks/projections/diagrams/statecharts.rb +59 -0
  698. data/lib/hecks/projections/diagrams/structure.rb +114 -0
  699. data/lib/hecks/projections/diagrams.rb +33 -256
  700. data/lib/hecks/projections/glossary/entries.rb +140 -0
  701. data/lib/hecks/projections/glossary/html/page.rb +96 -0
  702. data/lib/hecks/projections/glossary/html/parser.rb +66 -0
  703. data/lib/hecks/projections/glossary/html/sections.rb +78 -0
  704. data/lib/hecks/projections/glossary/html.rb +7 -179
  705. data/lib/hecks/projections/glossary/index.rb +45 -0
  706. data/lib/hecks/projections/glossary/markdown.rb +22 -11
  707. data/lib/hecks/projections/glossary/mermaid.rb +44 -31
  708. data/lib/hecks/projections/glossary/sensitivity.rb +7 -4
  709. data/lib/hecks/projections/glossary/sentences/value_objects.rb +77 -0
  710. data/lib/hecks/projections/glossary/sentences.rb +42 -81
  711. data/lib/hecks/projections/glossary/slugs.rb +33 -0
  712. data/lib/hecks/projections/glossary.rb +22 -190
  713. data/lib/hecks/projections/model.rb +20 -14
  714. data/lib/hecks/projections/rust_vocabulary/arguments/support.rb +33 -0
  715. data/lib/hecks/projections/rust_vocabulary/arguments.rb +171 -0
  716. data/lib/hecks/projections/rust_vocabulary/enum_body.rb +114 -0
  717. data/lib/hecks/projections/rust_vocabulary/tests.rb +144 -0
  718. data/lib/hecks/projections/rust_vocabulary.rb +24 -293
  719. data/lib/hecks/projections/site/admin.rb +15 -9
  720. data/lib/hecks/projections/site/admin_cms/templates/membership.rb +115 -0
  721. data/lib/hecks/projections/site/admin_cms/templates/session_strategy.rb +90 -0
  722. data/lib/hecks/projections/site/admin_cms/templates/sso.rb +101 -0
  723. data/lib/hecks/projections/site/admin_cms/templates/sso_tail.rb +54 -0
  724. data/lib/hecks/projections/site/admin_cms/templates/users.rb +83 -0
  725. data/lib/hecks/projections/site/admin_cms/templates.rb +14 -369
  726. data/lib/hecks/projections/site/admin_cms.rb +15 -9
  727. data/lib/hecks/projections/site/edge/behaviours.rb +10 -6
  728. data/lib/hecks/projections/site/edge/checks.rb +30 -80
  729. data/lib/hecks/projections/site/edge/findings.rb +28 -0
  730. data/lib/hecks/projections/site/edge/route_checks.rb +110 -0
  731. data/lib/hecks/projections/site/edge/row_reader.rb +52 -0
  732. data/lib/hecks/projections/site/edge/rules.rb +28 -26
  733. data/lib/hecks/projections/site/edge.rb +20 -31
  734. data/lib/hecks/projections/site/live_edge.rb +9 -7
  735. data/lib/hecks/projections/site/payload/fields/definitions.rb +90 -0
  736. data/lib/hecks/projections/site/payload/fields/readers.rb +69 -0
  737. data/lib/hecks/projections/site/payload/fields.rb +14 -111
  738. data/lib/hecks/projections/site/payload/lifecycle/text.rb +84 -0
  739. data/lib/hecks/projections/site/payload/lifecycle/text_tail.rb +106 -0
  740. data/lib/hecks/projections/site/payload/lifecycle.rb +8 -166
  741. data/lib/hecks/projections/site/payload/model.rb +22 -22
  742. data/lib/hecks/projections/site/payload/specs/wire.rb +69 -0
  743. data/lib/hecks/projections/site/payload/specs.rb +24 -68
  744. data/lib/hecks/projections/site/payload_driver.rb +19 -17
  745. data/lib/hecks/projections/site/root_rows.rb +12 -4
  746. data/lib/hecks/projections/site/routes_ts/helpers.rb +79 -0
  747. data/lib/hecks/projections/site/routes_ts/listings.rb +75 -0
  748. data/lib/hecks/projections/site/routes_ts/navigation.rb +79 -0
  749. data/lib/hecks/projections/site/routes_ts.rb +27 -175
  750. data/lib/hecks/projections/site/site_admin/access.rb +104 -0
  751. data/lib/hecks/projections/site/site_admin/session.rb +127 -0
  752. data/lib/hecks/projections/site/site_admin.rb +12 -219
  753. data/lib/hecks/projections/site/site_cdn.rb +12 -7
  754. data/lib/hecks/projections/site/site_host/dockerfile.rb +75 -0
  755. data/lib/hecks/projections/site/site_host/templates.rb +7 -63
  756. data/lib/hecks/projections/site/site_root/workflow.rb +65 -0
  757. data/lib/hecks/projections/site/site_root.rb +3 -54
  758. data/lib/hecks/projections/site/table/checks.rb +28 -57
  759. data/lib/hecks/projections/site/table/link_builder.rb +14 -5
  760. data/lib/hecks/projections/site/table/navigation_checks.rb +75 -0
  761. data/lib/hecks/projections/site/table/row_builder.rb +31 -53
  762. data/lib/hecks/projections/site/table/row_defaults.rb +65 -0
  763. data/lib/hecks/projections/site/table.rb +9 -5
  764. data/lib/hecks/projections/vocabulary.rb +34 -31
  765. data/lib/hecks/projector/cli_audience.rb +42 -0
  766. data/lib/hecks/projector/cli_projector/claims.rb +149 -0
  767. data/lib/hecks/projector/cli_projector/command_help.rb +65 -0
  768. data/lib/hecks/projector/cli_projector/command_specs.rb +133 -0
  769. data/lib/hecks/projector/cli_projector/listing.rb +100 -0
  770. data/lib/hecks/projector/cli_projector/option_specs.rb +116 -0
  771. data/lib/hecks/projector/cli_projector/query_specs.rb +67 -0
  772. data/lib/hecks/projector/cli_projector/usage.rb +101 -0
  773. data/lib/hecks/projector/cli_projector.rb +15 -532
  774. data/lib/hecks/projector/docs_projector/attributes.rb +78 -0
  775. data/lib/hecks/projector/docs_projector/behaviour.rb +127 -0
  776. data/lib/hecks/projector/docs_projector/closing.rb +40 -0
  777. data/lib/hecks/projector/docs_projector.rb +58 -192
  778. data/lib/hecks/projector/exporter/bindings.rb +174 -0
  779. data/lib/hecks/projector/exporter/translations.rb +127 -0
  780. data/lib/hecks/projector/exporter.rb +8 -279
  781. data/lib/hecks/projector/narrate_projector/commands.rb +108 -0
  782. data/lib/hecks/projector/narrate_projector/reactions.rb +33 -0
  783. data/lib/hecks/projector/narrate_projector.rb +60 -160
  784. data/lib/hecks/projector.rb +22 -13
  785. data/lib/hecks/quality_control/adapters/agent_profile.rb +25 -16
  786. data/lib/hecks/quality_control/adapters/github_checks.rb +42 -6
  787. data/lib/hecks/quality_control/adapters/github_ci_webhook.rb +1 -1
  788. data/lib/hecks/quality_control/adapters/github_issues.rb +22 -10
  789. data/lib/hecks/quality_control/cli/qa_discover_external_domains/parsing.rb +54 -0
  790. data/lib/hecks/quality_control/cli/qa_discover_external_domains/reporting.rb +44 -0
  791. data/lib/hecks/quality_control/cli/qa_discover_external_domains/scanning.rb +119 -0
  792. data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +28 -155
  793. data/lib/hecks/quality_control/cli/qa_domain_novelty/verdict.rb +76 -0
  794. data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +33 -66
  795. data/lib/hecks/quality_control/cli/qa_generated_domains/arguments.rb +62 -0
  796. data/lib/hecks/quality_control/cli/qa_generated_domains/blueprints.rb +45 -0
  797. data/lib/hecks/quality_control/cli/qa_generated_domains/evaluation.rb +107 -0
  798. data/lib/hecks/quality_control/cli/qa_generated_domains/promotion.rb +107 -0
  799. data/lib/hecks/quality_control/cli/qa_generated_domains/reporting.rb +97 -0
  800. data/lib/hecks/quality_control/cli/qa_generated_domains/shrinking.rb +70 -0
  801. data/lib/hecks/quality_control/cli/qa_generated_domains.rb +43 -327
  802. data/lib/hecks/quality_control/cli/qa_log_bug/arguments.rb +55 -0
  803. data/lib/hecks/quality_control/cli/qa_log_bug/proof.rb +65 -0
  804. data/lib/hecks/quality_control/cli/qa_log_bug.rb +18 -81
  805. data/lib/hecks/quality_control/cli/qa_mine_combinations/arguments.rb +54 -0
  806. data/lib/hecks/quality_control/cli/qa_mine_combinations/booting.rb +96 -0
  807. data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +45 -109
  808. data/lib/hecks/quality_control/cli/qa_open_pr/arguments.rb +55 -0
  809. data/lib/hecks/quality_control/cli/qa_open_pr/recording.rb +79 -0
  810. data/lib/hecks/quality_control/cli/qa_open_pr/rules.rb +122 -0
  811. data/lib/hecks/quality_control/cli/qa_open_pr.rb +41 -200
  812. data/lib/hecks/quality_control/cli/qa_postgres_migrate/copying.rb +45 -0
  813. data/lib/hecks/quality_control/cli/qa_postgres_migrate/reporting.rb +33 -0
  814. data/lib/hecks/quality_control/cli/qa_postgres_migrate.rb +37 -63
  815. data/lib/hecks/quality_control/cli/qa_postgres_role/ownership.rb +57 -0
  816. data/lib/hecks/quality_control/cli/qa_postgres_role.rb +66 -71
  817. data/lib/hecks/quality_control/cli/qa_pr_check/checking.rb +118 -0
  818. data/lib/hecks/quality_control/cli/qa_pr_check/reporting.rb +54 -0
  819. data/lib/hecks/quality_control/cli/qa_pr_check.rb +28 -145
  820. data/lib/hecks/quality_control/cli/qa_seed_angles/seed.rb +80 -0
  821. data/lib/hecks/quality_control/cli/qa_seed_angles.rb +13 -86
  822. data/lib/hecks/quality_control/cli/qa_sweep/all_mode/children.rb +80 -0
  823. data/lib/hecks/quality_control/cli/qa_sweep/all_mode/report.rb +125 -0
  824. data/lib/hecks/quality_control/cli/qa_sweep/all_mode.rb +63 -193
  825. data/lib/hecks/quality_control/cli/qa_sweep/checks/boundary.rb +92 -0
  826. data/lib/hecks/quality_control/cli/qa_sweep/checks/engines.rb +92 -0
  827. data/lib/hecks/quality_control/cli/qa_sweep/checks/seeds.rb +86 -0
  828. data/lib/hecks/quality_control/cli/qa_sweep/checks/shrinking.rb +69 -0
  829. data/lib/hecks/quality_control/cli/qa_sweep/checks.rb +10 -248
  830. data/lib/hecks/quality_control/cli/qa_sweep/conclusion.rb +60 -0
  831. data/lib/hecks/quality_control/cli/qa_sweep/depth.rb +61 -0
  832. data/lib/hecks/quality_control/cli/qa_sweep/dials.rb +26 -13
  833. data/lib/hecks/quality_control/cli/qa_sweep/finding_report/shrunk.rb +42 -0
  834. data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +40 -41
  835. data/lib/hecks/quality_control/cli/qa_sweep/parsing/validation.rb +70 -0
  836. data/lib/hecks/quality_control/cli/qa_sweep/parsing.rb +35 -65
  837. data/lib/hecks/quality_control/cli/qa_sweep/release_mode.rb +41 -22
  838. data/lib/hecks/quality_control/cli/qa_sweep/scratch.rb +67 -0
  839. data/lib/hecks/quality_control/cli/qa_sweep/seat.rb +94 -0
  840. data/lib/hecks/quality_control/cli/qa_sweep/seed_loop.rb +149 -0
  841. data/lib/hecks/quality_control/cli/qa_sweep/target_setup.rb +115 -0
  842. data/lib/hecks/quality_control/cli/qa_sweep.rb +22 -445
  843. data/lib/hecks/quality_control/cli/qa_tick/output.rb +107 -0
  844. data/lib/hecks/quality_control/cli/qa_tick/preflight.rb +95 -0
  845. data/lib/hecks/quality_control/cli/qa_tick.rb +42 -175
  846. data/lib/hecks/query_ir/formatting.rb +86 -0
  847. data/lib/hecks/query_ir/rules.rb +132 -0
  848. data/lib/hecks/query_ir/touchpoints.rb +104 -0
  849. data/lib/hecks/query_ir.rb +33 -281
  850. data/lib/hecks/query_ir_mcp/tools.rb +68 -0
  851. data/lib/hecks/query_ir_mcp.rb +27 -71
  852. data/lib/hecks/query_specification/common/comparison.rb +25 -5
  853. data/lib/hecks/query_specification/common/dsl.rb +1 -1
  854. data/lib/hecks/query_specification/common/null_policy.rb +9 -6
  855. data/lib/hecks/query_specification/common/options.rb +21 -14
  856. data/lib/hecks/query_specification/field_path.rb +5 -2
  857. data/lib/hecks/query_specification/hop_path.rb +15 -18
  858. data/lib/hecks/release/lane.rb +22 -0
  859. data/lib/hecks/release/runner/clean_tree.rb +2 -2
  860. data/lib/hecks/release/runner/commands.rb +1 -1
  861. data/lib/hecks/release/runner/options.rb +82 -0
  862. data/lib/hecks/release/runner/preflight.rb +12 -7
  863. data/lib/hecks/release/runner/publishing.rb +86 -0
  864. data/lib/hecks/release/runner/tagger.rb +8 -3
  865. data/lib/hecks/release/runner.rb +40 -156
  866. data/lib/hecks/rendering.rb +13 -9
  867. data/lib/hecks/router/door.rb +37 -0
  868. data/lib/hecks/router/namespace_installer.rb +45 -83
  869. data/lib/hecks/router/options_proxy.rb +44 -0
  870. data/lib/hecks/runtime/adapter_lookup.rb +45 -24
  871. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -8
  872. data/lib/hecks/runtime/command_interpreter/delegation.rb +116 -0
  873. data/lib/hecks/runtime/command_interpreter/enforcement.rb +38 -0
  874. data/lib/hecks/runtime/command_interpreter/hydration.rb +139 -0
  875. data/lib/hecks/runtime/command_interpreter/list_mutations.rb +98 -0
  876. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +67 -114
  877. data/lib/hecks/runtime/command_interpreter/persisting.rb +96 -0
  878. data/lib/hecks/runtime/command_interpreter.rb +57 -352
  879. data/lib/hecks/runtime/command_rules/admissibility.rb +64 -147
  880. data/lib/hecks/runtime/command_rules/arithmetic.rb +13 -72
  881. data/lib/hecks/runtime/command_rules/authorization.rb +9 -9
  882. data/lib/hecks/runtime/command_rules/emission.rb +13 -14
  883. data/lib/hecks/runtime/command_rules/guard_state.rb +49 -0
  884. data/lib/hecks/runtime/command_rules/invariants.rb +84 -0
  885. data/lib/hecks/runtime/command_rules/operands.rb +88 -0
  886. data/lib/hecks/runtime/command_rules/references.rb +59 -57
  887. data/lib/hecks/runtime/command_rules/tenant_boundary.rb +69 -0
  888. data/lib/hecks/runtime/dependency_planning/analyzer.rb +90 -0
  889. data/lib/hecks/runtime/dependency_planning/expression_reads.rb +73 -0
  890. data/lib/hecks/runtime/dependency_planning/mutation_analysis.rb +89 -0
  891. data/lib/hecks/runtime/dependency_planning/rule_analysis.rb +82 -0
  892. data/lib/hecks/runtime/dependency_planning.rb +3 -250
  893. data/lib/hecks/runtime/dispatcher/queries.rb +62 -0
  894. data/lib/hecks/runtime/dispatcher/result.rb +55 -0
  895. data/lib/hecks/runtime/dispatcher/routing.rb +107 -0
  896. data/lib/hecks/runtime/dispatcher.rb +64 -209
  897. data/lib/hecks/runtime/entity_element/location.rb +124 -0
  898. data/lib/hecks/runtime/entity_element/mutations.rb +150 -0
  899. data/lib/hecks/runtime/entity_element.rb +5 -177
  900. data/lib/hecks/runtime/entity_interpreter/enforcement.rb +51 -0
  901. data/lib/hecks/runtime/entity_interpreter/locating.rb +62 -0
  902. data/lib/hecks/runtime/entity_interpreter/resolution.rb +42 -0
  903. data/lib/hecks/runtime/entity_interpreter/saving.rb +49 -0
  904. data/lib/hecks/runtime/entity_interpreter.rb +45 -164
  905. data/lib/hecks/runtime/identity.rb +15 -9
  906. data/lib/hecks/runtime/instance.rb +11 -7
  907. data/lib/hecks/runtime/interpreting.rb +16 -8
  908. data/lib/hecks/runtime/invocation.rb +3 -155
  909. data/lib/hecks/runtime/invocation_builder.rb +132 -0
  910. data/lib/hecks/runtime/invocation_facts.rb +87 -0
  911. data/lib/hecks/runtime/loader/boot_steps.rb +96 -0
  912. data/lib/hecks/runtime/loader/described.rb +27 -0
  913. data/lib/hecks/runtime/loader.rb +27 -114
  914. data/lib/hecks/runtime/outbox/consumer_runner.rb +40 -0
  915. data/lib/hecks/runtime/outbox/fanout.rb +82 -0
  916. data/lib/hecks/runtime/outbox/relay.rb +149 -0
  917. data/lib/hecks/runtime/outbox.rb +3 -214
  918. data/lib/hecks/runtime/policy_interpreter/arguments.rb +67 -0
  919. data/lib/hecks/runtime/policy_interpreter/fan_out.rb +96 -0
  920. data/lib/hecks/runtime/policy_interpreter.rb +22 -137
  921. data/lib/hecks/runtime/port_operation_interpreter.rb +18 -17
  922. data/lib/hecks/runtime/query_interpreter/arguments.rb +51 -0
  923. data/lib/hecks/runtime/query_interpreter/entity_rows.rb +82 -0
  924. data/lib/hecks/runtime/query_interpreter/outside_answer.rb +89 -0
  925. data/lib/hecks/runtime/query_interpreter.rb +43 -191
  926. data/lib/hecks/runtime/reaction_invocation/identities.rb +92 -0
  927. data/lib/hecks/runtime/reaction_invocation/target_resolution.rb +88 -0
  928. data/lib/hecks/runtime/reaction_invocation.rb +66 -183
  929. data/lib/hecks/runtime/reaction_outcome.rb +7 -3
  930. data/lib/hecks/runtime/read_model_interpreter/head_ordering.rb +49 -0
  931. data/lib/hecks/runtime/read_model_interpreter/reductions.rb +123 -0
  932. data/lib/hecks/runtime/read_model_interpreter/shaping.rb +100 -0
  933. data/lib/hecks/runtime/read_model_interpreter.rb +71 -214
  934. data/lib/hecks/runtime/reference_hop.rb +11 -11
  935. data/lib/hecks/runtime/refusal_wording.rb +11 -8
  936. data/lib/hecks/runtime/registry/chapter_verification.rb +84 -0
  937. data/lib/hecks/runtime/registry/declarations.rb +17 -0
  938. data/lib/hecks/runtime/registry/durability_warnings.rb +71 -0
  939. data/lib/hecks/runtime/registry/hecksagon_rules.rb +124 -0
  940. data/lib/hecks/runtime/registry/merging.rb +49 -0
  941. data/lib/hecks/runtime/registry/providers.rb +139 -0
  942. data/lib/hecks/runtime/registry/query_verification.rb +144 -0
  943. data/lib/hecks/runtime/registry/read_repository.rb +61 -0
  944. data/lib/hecks/runtime/registry/runtime_state.rb +27 -0
  945. data/lib/hecks/runtime/registry/saga_persistence.rb +12 -8
  946. data/lib/hecks/runtime/registry/verification.rb +45 -389
  947. data/lib/hecks/runtime/registry/world_defaults.rb +3 -3
  948. data/lib/hecks/runtime/registry.rb +58 -296
  949. data/lib/hecks/runtime/remote_dispatcher.rb +47 -31
  950. data/lib/hecks/runtime/saga_interpreter/compensation.rb +90 -0
  951. data/lib/hecks/runtime/saga_interpreter/correlation.rb +19 -10
  952. data/lib/hecks/runtime/saga_interpreter/delivery.rb +147 -0
  953. data/lib/hecks/runtime/saga_interpreter/lifecycle.rb +63 -0
  954. data/lib/hecks/runtime/saga_interpreter/transitions.rb +112 -0
  955. data/lib/hecks/runtime/saga_interpreter.rb +41 -320
  956. data/lib/hecks/runtime/tenant_check.rb +14 -9
  957. data/lib/hecks/runtime/value/admission.rb +28 -19
  958. data/lib/hecks/runtime/value/coercion.rb +55 -404
  959. data/lib/hecks/runtime/value/entity_list_coercion.rb +27 -29
  960. data/lib/hecks/runtime/value/field_checks.rb +57 -100
  961. data/lib/hecks/runtime/value/flags.rb +65 -0
  962. data/lib/hecks/runtime/value/identifiers.rb +64 -0
  963. data/lib/hecks/runtime/value/references.rb +195 -0
  964. data/lib/hecks/runtime/value/shape_checks.rb +61 -0
  965. data/lib/hecks/runtime/value/validation.rb +125 -0
  966. data/lib/hecks/runtime/value.rb +18 -6
  967. data/lib/hecks/rust_build/conformance/surface.rb +75 -0
  968. data/lib/hecks/rust_build/conformance.rb +27 -59
  969. data/lib/hecks/rust_build/conformance_fuzz.rb +26 -15
  970. data/lib/hecks/rust_build/coverage.rb +32 -104
  971. data/lib/hecks/rust_build/coverage_parts/expected.rb +68 -0
  972. data/lib/hecks/rust_build/coverage_parts/printer.rb +75 -0
  973. data/lib/hecks/rust_build/host.rb +23 -13
  974. data/lib/hecks/rust_build/kernel_input.rb +19 -12
  975. data/lib/hecks/rust_build/native_build.rb +19 -15
  976. data/lib/hecks/rust_build/project_rust/domain_loader.rb +75 -0
  977. data/lib/hecks/rust_build/project_rust/root_mod/notes.rb +66 -0
  978. data/lib/hecks/rust_build/project_rust/root_mod.rb +22 -55
  979. data/lib/hecks/rust_build/project_rust/target_ir.rb +74 -0
  980. data/lib/hecks/rust_build/project_rust.rb +21 -89
  981. data/lib/hecks/rust_build/wasm.rb +11 -4
  982. data/lib/hecks/rust_build/wasm_browser.rb +7 -7
  983. data/lib/hecks/rust_build/write_if_changed.rb +3 -6
  984. data/lib/hecks/rust_build.rb +7 -3
  985. data/lib/hecks/storehouse/audit.rb +106 -0
  986. data/lib/hecks/storehouse/callers.rb +73 -0
  987. data/lib/hecks/storehouse/dispatching.rb +149 -0
  988. data/lib/hecks/storehouse/introspection.rb +145 -0
  989. data/lib/hecks/storehouse/querying.rb +78 -0
  990. data/lib/hecks/storehouse.rb +43 -451
  991. data/lib/hecks/tickets/bluebook/tickets.bluebook +29 -0
  992. data/lib/hecks/tools/argument_gate_matrix/dispatching.rb +69 -0
  993. data/lib/hecks/tools/argument_gate_matrix/rows.rb +121 -0
  994. data/lib/hecks/tools/argument_gate_matrix/violations.rb +108 -0
  995. data/lib/hecks/tools/argument_gate_matrix.rb +43 -212
  996. data/lib/hecks/tools/ci_gate_decision.rb +21 -14
  997. data/lib/hecks/tools/ci_gates.rb +40 -26
  998. data/lib/hecks/tools/comment_style/baseline.rb +41 -0
  999. data/lib/hecks/tools/comment_style/caps_words.rb +104 -0
  1000. data/lib/hecks/tools/comment_style/cli.rb +121 -0
  1001. data/lib/hecks/tools/comment_style/dictionary.rb +60 -0
  1002. data/lib/hecks/tools/comment_style/fixing.rb +119 -0
  1003. data/lib/hecks/tools/comment_style/long_blocks.rb +51 -0
  1004. data/lib/hecks/tools/comment_style/masking.rb +94 -0
  1005. data/lib/hecks/tools/comment_style/patterns.rb +61 -0
  1006. data/lib/hecks/tools/comment_style/run.rb +161 -0
  1007. data/lib/hecks/tools/comment_style/signature.rb +59 -0
  1008. data/lib/hecks/tools/comment_style/source_file.rb +105 -0
  1009. data/lib/hecks/tools/comment_style/structure_checks.rb +113 -0
  1010. data/lib/hecks/tools/comment_style/text_checks.rb +67 -0
  1011. data/lib/hecks/tools/comment_style/vocabulary.rb +55 -0
  1012. data/lib/hecks/tools/comment_style/walker.rb +136 -0
  1013. data/lib/hecks/tools/comment_style.rb +17 -1000
  1014. data/lib/hecks/tools/corpus_report.rb +26 -16
  1015. data/lib/hecks/tools/deploy_recipe/targets.rb +61 -0
  1016. data/lib/hecks/tools/deploy_recipe/worlds.rb +114 -0
  1017. data/lib/hecks/tools/deploy_recipe.rb +60 -174
  1018. data/lib/hecks/tools/deploy_recipe_lint/checks.rb +78 -0
  1019. data/lib/hecks/tools/deploy_recipe_lint/fixtures.rb +88 -0
  1020. data/lib/hecks/tools/deploy_recipe_lint/makefile.rb +84 -0
  1021. data/lib/hecks/tools/deploy_recipe_lint.rb +29 -192
  1022. data/lib/hecks/tools/drop_implicit_append_fields.rb +20 -12
  1023. data/lib/hecks/tools/era_projection_backfill.rb +50 -33
  1024. data/lib/hecks/tools/era_reattest/reporting.rb +55 -0
  1025. data/lib/hecks/tools/era_reattest.rb +32 -43
  1026. data/lib/hecks/tools/evolve_run/arguments.rb +49 -0
  1027. data/lib/hecks/tools/evolve_run/words.rb +60 -0
  1028. data/lib/hecks/tools/evolve_run.rb +43 -90
  1029. data/lib/hecks/tools/fuzz_sweep/findings.rb +106 -0
  1030. data/lib/hecks/tools/fuzz_sweep/options.rb +69 -0
  1031. data/lib/hecks/tools/fuzz_sweep/pool.rb +74 -0
  1032. data/lib/hecks/tools/fuzz_sweep/reporting.rb +88 -0
  1033. data/lib/hecks/tools/fuzz_sweep.rb +75 -273
  1034. data/lib/hecks/tools/gate.rb +31 -16
  1035. data/lib/hecks/tools/heki_compaction.rb +63 -42
  1036. data/lib/hecks/tools/hoist_local_givens/rewriting.rb +119 -0
  1037. data/lib/hecks/tools/hoist_local_givens.rb +89 -157
  1038. data/lib/hecks/tools/journal_compaction.rb +40 -31
  1039. data/lib/hecks/tools/lanes/files.rb +82 -0
  1040. data/lib/hecks/tools/lanes/live.rb +41 -0
  1041. data/lib/hecks/tools/lanes/rows.rb +45 -0
  1042. data/lib/hecks/tools/lanes/ruleset.rb +44 -0
  1043. data/lib/hecks/tools/lanes/watch_workflow.rb +82 -0
  1044. data/lib/hecks/tools/lanes/workflow.rb +79 -0
  1045. data/lib/hecks/tools/lanes.rb +71 -0
  1046. data/lib/hecks/tools/oidc_manifests.rb +10 -9
  1047. data/lib/hecks/tools/query_ir_run.rb +7 -3
  1048. data/lib/hecks/tools/regeneration_run.rb +40 -26
  1049. data/lib/hecks/tools/rust_comment_style/caps_words.rb +98 -0
  1050. data/lib/hecks/tools/rust_comment_style/fixing.rb +102 -0
  1051. data/lib/hecks/tools/rust_comment_style/masking.rb +85 -0
  1052. data/lib/hecks/tools/rust_comment_style/run.rb +94 -0
  1053. data/lib/hecks/tools/rust_comment_style/source_file.rb +71 -0
  1054. data/lib/hecks/tools/rust_comment_style/structure.rb +79 -0
  1055. data/lib/hecks/tools/rust_comment_style/text_checks.rb +63 -0
  1056. data/lib/hecks/tools/rust_comment_style/tokenizer.rb +102 -0
  1057. data/lib/hecks/tools/rust_comment_style.rb +38 -535
  1058. data/lib/hecks/tools/sequence_script.rb +20 -14
  1059. data/lib/hecks/tools/site_routes/cli.rb +70 -0
  1060. data/lib/hecks/tools/site_routes/root_files.rb +59 -0
  1061. data/lib/hecks/tools/site_routes/templates.rb +77 -0
  1062. data/lib/hecks/tools/site_routes.rb +84 -160
  1063. data/lib/hecks/tools/tail_merge.rb +45 -37
  1064. data/lib/hecks/tools/tenant_provisioning.rb +45 -31
  1065. data/lib/hecks/tools/tools_doc.rb +42 -19
  1066. data/lib/hecks/tools/translation_audit/domain_loading.rb +71 -0
  1067. data/lib/hecks/tools/translation_audit/edges.rb +71 -0
  1068. data/lib/hecks/tools/translation_audit/verdicts.rb +107 -0
  1069. data/lib/hecks/tools/translation_audit.rb +30 -161
  1070. data/lib/hecks/tools/translation_scaffold/reporting.rb +40 -0
  1071. data/lib/hecks/tools/translation_scaffold.rb +77 -60
  1072. data/lib/hecks/tools.rb +1 -0
  1073. data/lib/hecks/vendoring.rb +37 -11
  1074. data/lib/hecks/version.rb +1 -1
  1075. data/lib/hecks/vocabulary.rb +16 -1
  1076. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +7 -9
  1077. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +8 -6
  1078. data/rust/.cargo/mutants.toml +8 -0
  1079. data/rust/host/Cargo.toml +5 -0
  1080. data/rust/host/HECKS_RELEASE +1 -1
  1081. data/rust/host/src/auth.rs +32 -0
  1082. data/rust/host/src/web.rs +2 -3
  1083. data/rust/src/kernel/dispatch.rs +4 -0
  1084. data/rust/src/kernel/dispatch_guard_tests.rs +506 -0
  1085. data/rust/src/kernel/orchestrate.rs +4 -0
  1086. data/rust/src/kernel/orchestrate_guard_tests.rs +708 -0
  1087. metadata +591 -3
  1088. /data/lib/hecks/bluebook/behaviour/{hexagon.rb → hecksagon.rb} +0 -0
@@ -6,6 +6,13 @@ require "optparse"
6
6
  require "pathname"
7
7
  require "ripper"
8
8
  require_relative "../tools"
9
+ require_relative "comment_style/vocabulary"
10
+ require_relative "comment_style/patterns"
11
+ require_relative "comment_style/dictionary"
12
+ require_relative "comment_style/baseline"
13
+ require_relative "comment_style/source_file"
14
+ require_relative "comment_style/run"
15
+ require_relative "comment_style/cli"
9
16
 
10
17
  module Hecks
11
18
  module Tools
@@ -42,158 +49,23 @@ module Hecks
42
49
 
43
50
  FIXABLE = %w[all_caps long_bold long_line].freeze
44
51
 
45
- # Initialisms and keywords that are correctly written in capitals.
46
- ACRONYMS = %w[
47
- ARGV ARN AZ ECR ES FSM GVL IAM IGW IPC IRB LLM MB MVCC NAT OOM RDS SG SIGKILL SIGTERM STDIO TOML
48
- UI VCS VM VPC WASI
49
- ABC ACID ADR API ASC ASCII AST AWS BFS BYPASSRLS CAS CDN CEO CI CLI CPU CRUD CSRF CSS CSV CTE DB DBA DDD DDL
50
- DELETE DESC DFS DML DNS DSL ENV ERB ERD FIFO FIXME FK FQN GC GET GIN HMAC HTML HTTP HTTPS ID INSERT IO IR ISO
51
- JS JSON JSONB JSONL JWKS JWT LIFO LRU MCP MDN MRI MVP NAMEDATALEN NIL NULL OIDC OK ORM OS
52
- PATCH PG PID PK PM POSIX POST PR PRAGMA PRD PUT QA QC RAM RBAC README REPL REST RLS RNG RPC RUBYOPT
53
- SAM SAVEPOINT SDK SELECT SHA SME SQL SSH SSL SSM SW STDERR STDIN STDOUT TCP TLA TLS TOCTOU TODO TTL UID UL
54
- URI URL UTC UTF UUID VO WASM XML YAML
55
- ACL ALB BC GNU OAC WAF
56
- CLOUDFRONT ECS HSM KMS
57
- ].to_set.freeze
58
-
59
- # Names whose correct spelling is neither all-caps nor all-lowercase.
60
- PROPER_NOUNS = {
61
- "CLOUDFLARE" => "Cloudflare", "OAUTH" => "OAuth", "RSPEC" => "RSpec", "JAVASCRIPT" => "JavaScript",
62
- "GITHUB" => "GitHub", "GOOGLE" => "Google", "GRAPHVIZ" => "Graphviz", "HECKS" => "Hecks",
63
- "POSTGRES" => "Postgres", "POSTGRESQL" => "PostgreSQL", "PUMA" => "Puma", "RAILS" => "Rails",
64
- "RUBY" => "Ruby", "RUST" => "Rust", "SQLITE" => "SQLite"
65
- }.freeze
66
-
67
- # Multi-word SQL is protected as a phrase, because the individual keywords
68
- # (`FROM`, `WHERE`, `TABLE`) are also ordinary English emphasis targets.
69
- SQL_PHRASES = Regexp.union(
70
- /\bINSERT INTO\b/, /\bSELECT\b.*?\bFROM\b/, /\bORDER BY\b/, /\bGROUP BY\b/,
71
- /\bON CONFLICT\b(?: DO (?:NOTHING|UPDATE))?/, /\bDO NOTHING\b/, /\bFOR (?:UPDATE|SHARE)\b/,
72
- /\bSKIP LOCKED\b/, /\b(?:PRIMARY|FOREIGN) KEY\b/, /\bIS(?: NOT)? NULL\b/, /\bNOT NULL\b/,
73
- /\bCREATE (?:UNIQUE )?(?:TABLE|INDEX|POLICY|ROLE|SCHEMA|TRIGGER|FUNCTION)\b/,
74
- /\bALTER (?:TABLE|ROLE|SCHEMA)\b/, /\bDROP (?:TABLE|COLUMN|INDEX|SCHEMA|POLICY)\b/,
75
- /\bADD (?:COLUMN|CONSTRAINT)\b/, /\bDELETE FROM\b/, /\bUNION ALL\b/, /\bSET LOCAL\b/,
76
- /\bBEGIN (?:IMMEDIATE|EXCLUSIVE|DEFERRED)\b/, /\bROW LEVEL SECURITY\b/,
77
- /\bCREATE \.\.\. PARTITION OF\b/, /\bPARTITION OF\b/, /\bIF (?:NOT )?EXISTS\b/,
78
- /\b(?:LEFT|INNER|OUTER|CROSS) JOIN\b/, /\bLIMIT \d+\b/, /\bOWNER TO\b/, /\b(?:on|in|onto) PATH\b/
79
- ).freeze
80
-
81
- # SQL keywords that are rarely English emphasis. Kept in capitals on a line
82
- # that reads as SQL, and always kept in a file that talks to a database.
83
- SQL_STRONG = %w[
84
- ALTER ATTACH BEGIN CASCADE COMMIT CONCURRENTLY DETACH DISTINCT FORCE GRANT LIMIT MATERIALIZED
85
- NULLS OFFSET PARTITION RETURNING REVOKE ROLLBACK SERIALIZABLE VACUUM
86
- ].to_set.freeze
87
-
88
- # SQL keywords that are also everyday emphasis targets (`FROM`, `FIRST`, `SET`).
89
- SQL_WEAK = %w[
90
- COLUMN CONSTRAINT CREATE DEFAULT DROP EXCLUSIVE EXISTS FIRST FROM INDEX JOIN LAST LOCK LOGIN
91
- NOBYPASSRLS NOCREATEDB NOCREATEROLE NOLOGIN NOSUPERUSER OWNER POLICY
92
- REFERENCES ROLE SCHEMA SET SHARE TABLE TRANSACTION TRIGGER UNION UNIQUE UPDATE VALUES VIEW WHERE
93
- ].to_set.freeze
94
-
95
- SQL_NEUTRAL = %w[ALL AND AS BY FOR IN IS NOT OF ON OR TO].to_set.freeze
96
-
97
- SQL_PATH = %r{sql|postgres|/d1|_era|/era|lineage|outbox|schema|persistence}
98
-
99
- PROTECTED_SPANS = Regexp.union(
100
- /`[^`]*`/, /"[^"]*"/, /'[^'\s]*'/, %r{\b[a-z]+://\S+}, SQL_PHRASES,
101
- /\b(?:NOTE|OPTIMIZE|HACK|REVIEW):/
102
- ).freeze
103
-
104
- CAPS_WORD = /
105
- (?<![\w:.\#@$'])
106
- (?:A|[A-Z]{2,}(?:'[A-Z]{1,2})?)
107
- (?![\w(]|'(?!s\b|\s|$)|-[a-z](?<=POST-.)|::|\.\w|\#\d|\s*=(?![=~>])|\s*\u2190)
108
- /x
109
-
110
- HISTORY = [
111
- /\bused to\b/i, /\bwas originally\b/i, /\boriginally (?:was|were|used|lived|named|called)\b/i,
112
- /\bpreviously\b/i, /\bformerly\b/i, /\bhistorically\b/i, /\bstale as of\b/i,
113
- /\b(?:was|were|been|got) renamed\b/i, /\brenamed from\b/i, /\bhas since\b/i,
114
- /\bbefore (?:this|the) (?:change|fix|refactor|split|rename|pr|commit|patch)\b/i, /\bBefore this, /,
115
- /\ban? earlier (?:version|revision|draft|pass)\b/i, /\bwhen this was (?:written|added)\b/i,
116
- /\b(?:in|since|by|after|before|until) (?:PR )?\#\d{2,}/i, /\bPR \#?\d{2,}\b/,
117
- /\bno longer\b/i, /\bany more\b/i, /\banymore\b/i
118
- ].freeze
119
-
120
- DIRECTIVE = /\A#\s*(?:rubocop:|frozen_string_literal:|encoding:|coding:|shareable_constant|typed:|!)/
121
-
122
- SELF_EVIDENT = %w[
123
- to_s to_str to_sym to_h to_a to_proc inspect hash eql? == === <=> method_missing
124
- respond_to_missing? pretty_print
125
- ].to_set.freeze
126
-
127
- # Stands in for a protected span so it still reads as a word, not a gap.
128
- FILLER = "\u00A7"
129
-
130
- # What may sit between the words of one all-caps heading run.
131
- GAP = %r{\A(?:[\s,/&'\-\d]|#{FILLER}|[A-Z]{2,})*\z}
132
-
133
- # A bold phrase opening a comment line, after an optional bullet.
134
- BOLD_HEADING = /\A(#+\s*(?:[-*]\s+)?)\*\*([^*]+)\*\*/
135
-
136
- # What ends a heading run: an em dash, or sentence punctuation.
137
- HEADING_END = /\A(?:\s+—|[.:!?](?:\s|\z))/
138
-
139
- CODE_NOISE = %i[on_comment on_sp on_nl on_ignored_nl].freeze
140
-
141
52
  Violation = Struct.new(:path, :line, :category, :message)
142
53
  MethodDef = Struct.new(:name, :line, :params, :visibility, :raises, :block_param, :type_fqn)
143
54
  TypeDef = Struct.new(:fqn, :line, :namespace_only)
144
55
  Comment = Struct.new(:line, :col, :text, :full_line)
145
56
  Block = Struct.new(:line, :lines, :key)
146
57
 
147
- # Decides whether a capitalised token is an English word being shouted;
148
- # anything else keeps its capitals and is reported for a person to look at.
149
- module Dictionary
150
- WORDS = "/usr/share/dict/words"
151
-
152
- # Technical vocabulary the system word list lacks.
153
- EXTRA = %w[
154
- adversarial allowlist anymore app apps arg args arity async backend backtrace baseline
155
- boolean bootable bootstrapping bluebook byte callee canonicalized changelog checklist
156
- checkpoint codebase codemod config database dataset dedupe deduped enqueue evaluator
157
- filename fixpoint formatting frontend glob grep handover hardcoded hecksagon heredoc held
158
- hoc hostname idempotency idempotent inline keyword landmine lifecycle lookup mapping
159
- memoised memoized memoizes metadata metaprogrammed monkeypatch monorepo multi mutex
160
- namespace noop numeric op ops parameterized params pathname payload plugin postcondition
161
- pre reachability reentrant refactor regex regexes rekey rekeyed repo restartable retrofit
162
- reusable roadmap rollup runtime shortcut standalone stringified struct subprocess
163
- subprocesses subroutine substring superuser symlink sync timestamp tmp tmpl touchpoint
164
- trickiest triggerable tuple undecoded unsanitized untargeted upsert username webhook
165
- widget workaround worklist worklists
166
- ].freeze
167
-
168
- SUFFIXES = [
169
- ["", ""], ["s", ""], ["es", ""], ["ed", ""], ["d", ""], ["ing", ""], ["ing", "e"], ["ies", "y"],
170
- ["ied", "y"], ["ly", ""], ["er", ""], ["est", ""], ["ier", "y"], ["ised", "ized"], ["ise", "ize"],
171
- ["isation", "ization"], ["our", "or"], ["lling", "ling"], ["lled", "led"]
172
- ].freeze
173
-
174
- module_function
175
-
176
- # Loads and memoizes the system word list, once per process.
177
- def words
178
- return @words if defined?(@words)
179
-
180
- @words = File.exist?(WORDS) ? Set.new(File.foreach(WORDS, chomp: true).map(&:downcase)).merge(EXTRA) : nil
181
- end
182
-
183
- # Checks a capitalized token against the word list, stripped and stemmed.
184
- # Always true when this machine has no word list to consult.
185
- def english?(word)
186
- return true unless words
187
-
188
- bare = word.downcase.sub(/n't\z/, "").sub(/'.*\z/, "")
189
- SUFFIXES.any? do |suffix, replacement|
190
- next false unless bare.end_with?(suffix)
58
+ # Bare class/module names on Hecks's documented public surface (ADR 0075 ticket 01):
59
+ # an operator or a client calls these directly. The DSL itself (`aggregate`,
60
+ # `command`, `query`, ...) is invoked through the `Hecks.bluebook` block, not by
61
+ # naming a builder class, so its builder classes stay off this list.
62
+ #
63
+ # A method's structural checks (tags, `undocumented_method`) apply only when its
64
+ # innermost enclosing type's own name is here; everything else is internal and
65
+ # needs no doc unless it carries a real why.
66
+ PUBLIC_SURFACE = Set["Storehouse"].freeze
191
67
 
192
- stem = bare.delete_suffix(suffix) + replacement
193
- words.include?(stem) || words.include?(stem.sub(/(.)\1\z/, '\1'))
194
- end
195
- end
196
- end
68
+ extend Cli
197
69
 
198
70
  # Class and module names defined under `lib/`, keyed by their shouted form.
199
71
  def self.class_names
@@ -209,864 +81,9 @@ module Hecks
209
81
  Ripper.lex(source).reject { |token| CODE_NOISE.include?(token[1]) }.map { |token| token[2] }
210
82
  end
211
83
 
212
- # Bare class/module names on Hecks's documented public surface (ADR 0075 ticket 01):
213
- # an operator or a client calls these directly. The DSL itself (`aggregate`,
214
- # `command`, `query`, ...) is invoked through the `Hecks.bluebook` block, not by
215
- # naming a builder class, so its builder classes stay off this list.
216
- #
217
- # A method's structural checks (tags, `undocumented_method`) apply only when its
218
- # innermost enclosing type's own name is here; everything else is internal and
219
- # needs no doc unless it carries a real why.
220
- PUBLIC_SURFACE = Set["Storehouse"].freeze
221
-
222
84
  def self.public_surface
223
85
  PUBLIC_SURFACE
224
86
  end
225
-
226
- # The checked-in list of comment blocks already over `MAX_BLOCK` lines.
227
- #
228
- # Keyed by first-line text, not line number, so edits above a block don't break its key.
229
- module Baseline
230
- FILE = ".standardize_comments_baseline.json"
231
-
232
- # The checkout this file lives in: the baseline's home when no `root:` is given.
233
- ROOT = File.expand_path("../../..", __dir__)
234
-
235
- PATH = File.join(ROOT, FILE)
236
-
237
- module_function
238
-
239
- # The baseline's location inside a checkout.
240
- #
241
- # @param root [String] the checkout's root directory
242
- # @return [String] the baseline file's path
243
- def path_in(root) = File.join(root, FILE)
244
-
245
- # Reads the baseline from disk.
246
- def load(path = PATH)
247
- File.exist?(path) ? JSON.parse(File.read(path, encoding: Encoding::UTF_8)) : {}
248
- end
249
-
250
- # Writes a baseline in a stable order, so a rewrite shows only real changes in a diff.
251
- def dump(blocks, path = PATH)
252
- sorted = blocks.sort.to_h { |file, keys| [file, keys.sort.to_h] }
253
- File.write(path, "#{JSON.pretty_generate(sorted)}\n")
254
- end
255
- end
256
-
257
- # One parsed source file and the violations found in it.
258
- class SourceFile
259
- attr_reader :path, :lines, :comments
260
-
261
- def initialize(path, source = nil)
262
- @path = path
263
- @source = source || File.read(path)
264
- @lines = @source.lines
265
- @comments = lex_comments
266
- @by_line = @comments.to_h { |c| [c.line, c] }
267
- @open_spans = open_spans
268
- @constants = code_words
269
- end
270
-
271
- # Finds every place this file departs from the style guide.
272
- def violations(only: nil, baseline: {})
273
- return [] if generated?
274
-
275
- found = text_violations + (test_file? ? [] : structure_violations) + long_block_violations(baseline)
276
- found = found.select { |v| only.include?(v.category) } if only
277
- found.sort_by { |v| [v.line, v.category] }
278
- end
279
-
280
- # Finds every run of consecutive full-line comments longer than `MAX_BLOCK`.
281
- def long_blocks
282
- return [] if generated?
283
-
284
- seen = Hash.new(0)
285
- runs = @comments.select(&:full_line).slice_when { |above, below| below.line != above.line + 1 }
286
- runs.select { |run| run.size > MAX_BLOCK }.map { |run| Block.new(run.first.line, run.size, block_key(run, seen)) }
287
- end
288
-
289
- # Rewrites the mechanical categories; raises rather than write a change that alters code.
290
- def fixed(only: nil)
291
- return @source if generated?
292
-
293
- wanted = (only || FIXABLE) & FIXABLE
294
- output = @lines.map.with_index(1) { |line, number| fix_line(line, number, wanted) }.join
295
- return @source if output == @source
296
-
297
- unchanged = CommentStyle.code_tokens(@source) == CommentStyle.code_tokens(output)
298
- raise "#{path}: fix would change code, refusing" unless unchanged
299
-
300
- output
301
- end
302
-
303
- # Parses this file's structure once and caches it.
304
- def types
305
- test_file? || generated? ? [] : structure.last
306
- end
307
-
308
- # A generated file's comments belong to its generator, and its
309
- # `GENERATED ... DO NOT EDIT` banner is a convention tools recognise.
310
- def generated?
311
- @lines.first(8).join.match?(/\bgenerated\b.*?\bdo not edit\b/im)
312
- end
313
-
314
- # Helpers defined inside a spec are test scaffolding, not API, so the
315
- # doc-tag categories skip them. The prose categories still apply.
316
- def test_file?
317
- path.end_with?("_spec.rb")
318
- end
319
-
320
- # A `class`/`module` is documented only if a comment block sits directly above it.
321
- def documented?(type)
322
- !doc_above(type.line).empty?
323
- end
324
-
325
- private
326
-
327
- # The text of a block's first line, without its `#`, is its key. A second block opened by
328
- # the same text in this file gets a `#2` suffix, and so on.
329
- def block_key(run, seen)
330
- text = run.first.text.sub(/\A#+\s*/, "").strip[0, MAX_ANCHOR]
331
- text = "(empty)" if text.empty?
332
- seen[text] += 1
333
- seen[text] == 1 ? text : "#{text} ##{seen[text]}"
334
- end
335
-
336
- # A block fails when it has no baseline entry, or has grown past the length recorded there.
337
- def long_block_violations(baseline)
338
- held = baseline.fetch(path.delete_prefix("./"), {})
339
- long_blocks.filter_map do |block|
340
- allowed = held[block.key]
341
- next if allowed && block.lines <= allowed
342
-
343
- detail = allowed ? "baselined at #{allowed}" : "over #{MAX_BLOCK}"
344
- Violation.new(path, block.line, "long_block", "#{block.lines} lines, #{detail}: #{block.key}")
345
- end
346
- end
347
-
348
- def lex_comments
349
- Ripper.lex(@source).filter_map do |(line, col), type, text, _|
350
- next unless type == :on_comment
351
-
352
- prefix = @lines[line - 1].byteslice(0, col)
353
- Comment.new(line, col, text.chomp, prefix.strip.empty?)
354
- end
355
- end
356
-
357
- def text_violations
358
- @comments.flat_map do |comment|
359
- next [] if comment.text.match?(DIRECTIVE)
360
-
361
- caps_violations(comment) + constant_violations(comment) + unknown_violations(comment) +
362
- bold_violations(comment) + history_violations(comment) + length_violations(comment)
363
- end
364
- end
365
-
366
- # All-caps names in this file's code: constants, heredoc delimiters, and
367
- # strings that are nothing but one such name (`ENV["PATH"]`).
368
- def code_words
369
- Ripper.lex(@source).each_with_object(Set.new) do |(_, type, text, _), words|
370
- case type
371
- when :on_const, :on_tstring_content then words << text if text.match?(/\A[A-Z][A-Z0-9_]+\z/)
372
- when :on_heredoc_beg, :on_heredoc_end then words.merge(text.scan(/[A-Z][A-Z0-9_]+/))
373
- end
374
- end
375
- end
376
-
377
- def unknown_violations(comment)
378
- words = mask(comment).scan(CAPS_WORD).uniq - ["A"]
379
- words.reject { |word| keep_capitals?(word) }.select { |word| unknown?(word) }.map do |word|
380
- Violation.new(path, comment.line, "unknown_capitals", word)
381
- end
382
- end
383
-
384
- def bold_violations(comment)
385
- heading = comment.text[BOLD_HEADING, 2]
386
- return [] unless heading && heading.split.size > MAX_HEADING_WORDS
387
-
388
- [Violation.new(path, comment.line, "long_bold", "#{heading.split.size} words")]
389
- end
390
-
391
- def caps_violations(comment)
392
- caps_words(comment).map do |_, word, replacement|
393
- Violation.new(path, comment.line, "all_caps", "#{word} -> #{replacement}")
394
- end
395
- end
396
-
397
- # A capitalised word that is also a constant defined in this file could be
398
- # either prose or a reference, so it is reported rather than rewritten.
399
- def constant_violations(comment)
400
- words = mask(comment).scan(CAPS_WORD).uniq
401
- words.select { |word| @constants.include?(word) && !ACRONYMS.include?(word) }.map do |word|
402
- Violation.new(path, comment.line, "bare_constant", word)
403
- end
404
- end
405
-
406
- def history_violations(comment)
407
- masked = comment.text.gsub(/`[^`]*`/) { |span| " " * span.length }
408
- pattern = HISTORY.find { |candidate| masked.match?(candidate) }
409
- return [] unless pattern
410
-
411
- [Violation.new(path, comment.line, "design_history", %("#{masked[pattern]}"))]
412
- end
413
-
414
- def length_violations(comment)
415
- length = @lines[comment.line - 1].chomp.length
416
- return [] if length <= MAX_LINE
417
-
418
- [Violation.new(path, comment.line, "long_line", "#{length} characters")]
419
- end
420
-
421
- def caps_words(comment)
422
- masked = mask(comment)
423
- matches = []
424
- masked.scan(CAPS_WORD) { matches << Regexp.last_match }
425
- sql = sql_keywords(comment, matches.map { |match| match[0] })
426
- found = matches.filter_map do |match|
427
- word = match[0]
428
- next if word != "A" && (keep_capitals?(word) || sql.include?(word) || unknown?(word))
429
-
430
- [match.begin(0), word, recase(word, sentence_start?(comment, masked, match.begin(0)))]
431
- end
432
- (drop_stray_articles(found) + possessives(masked)).sort_by(&:first)
433
- end
434
-
435
- # The comment with every span that must keep its capitals blanked out. A
436
- # backtick span can wrap across lines, so whether this line opens inside
437
- # one comes from the lines above it.
438
- def mask(comment)
439
- text = comment.text
440
- head = ""
441
- if @open_spans[comment.line]
442
- lead = text[/\A#+\s*/].length
443
- close = text.index("`", lead)
444
- cut = close ? close + 1 : text.length
445
- head = text[0, lead] + (FILLER * (cut - lead))
446
- text = text[cut..]
447
- end
448
- body = text.gsub(PROTECTED_SPANS) { |span| quoted_heading?(span) ? span : FILLER * span.length }
449
- open = body.rindex("`")
450
- body = body[0, open] + (FILLER * (body.length - open)) if open
451
- head + body
452
- end
453
-
454
- # Which lines begin inside a backtick span opened earlier in the paragraph.
455
- def open_spans
456
- inside = false
457
- @comments.each_with_object({}) do |comment, open|
458
- above = @by_line[comment.line - 1]
459
- continues = comment.full_line && above&.full_line && !above.text.match?(/\A#+\s*\z/)
460
- inside = false unless continues
461
- open[comment.line] = inside
462
- inside ^= comment.text.count("`").odd?
463
- end
464
- end
465
-
466
- # Capitalised SQL keywords to leave alone on this line. A keyword such as
467
- # `FROM` or `SET` is only SQL when the rest of the line reads as SQL too.
468
- def sql_keywords(comment, words)
469
- bare = words.map { |word| word.sub(/'[A-Z]{1,2}\z/, "") } - ["A"]
470
- strong = bare.select { |word| SQL_STRONG.include?(word) }
471
- weak = bare.select { |word| SQL_WEAK.include?(word) }
472
- database_file = path.match?(SQL_PATH)
473
- return bare.to_set if reads_as_sql?(bare, strong, weak, database_file)
474
- return (strong + weak).to_set if comment.text.match?(SQL_PHRASES)
475
-
476
- database_file ? strong.to_set : Set.new
477
- end
478
-
479
- def reads_as_sql?(bare, strong, weak, database_file)
480
- only_sql = bare.all? do |word|
481
- [SQL_STRONG, SQL_WEAK, SQL_NEUTRAL, ACRONYMS].any? { |set| set.include?(word) }
482
- end
483
- return false unless only_sql
484
-
485
- database_file ? (strong.any? || weak.any?) : (strong.any? && (strong + weak).size >= 2)
486
- end
487
-
488
- # `Finding`'s: the capital follows a code span, so `CAPS_WORD` never sees it.
489
- def possessives(masked)
490
- found = []
491
- masked.scan(/(?<=#{FILLER})'S\b/) { found << [Regexp.last_match.begin(0) + 1, "S", "s"] }
492
- found
493
- end
494
-
495
- # An "A" is only emphasis when a word beside it is being fixed too.
496
- def drop_stray_articles(found)
497
- found.each_with_index.reject do |(offset, word, _), index|
498
- before = index.positive? ? found[index - 1] : nil
499
- after = found[index + 1]
500
- beside = (before && before[0] + before[1].length + 1 == offset && before[1] != "A") ||
501
- (after && after[0] == offset + 2 && after[1] != "A")
502
- word == "A" && !beside
503
- end.map(&:first)
504
- end
505
-
506
- # A quoted run of capitals is another comment's heading, not a literal.
507
- def quoted_heading?(span)
508
- return false unless span.start_with?('"') && !span.match?(SQL_PHRASES)
509
-
510
- capitals = span.scan(/\b[A-Z]{2,}\b/).size
511
- capitals >= 3 || (capitals == 2 && !span.match?(/[a-z]/))
512
- end
513
-
514
- def recase(word, sentence_start)
515
- bare, suffix = word.split("'", 2)
516
- proper = proper_name(bare)
517
- return [proper, suffix&.downcase].compact.join("'") if proper
518
-
519
- sentence_start ? word.capitalize : word.downcase
520
- end
521
-
522
- # A shouted product or class name (`POSTGRESERA`) gets its real spelling
523
- # back. An English word that is also a class name (`VALUE`) stays prose.
524
- def proper_name(bare)
525
- PROPER_NOUNS[bare] || (CommentStyle.class_names[bare] unless Dictionary.english?(bare))
526
- end
527
-
528
- # Neither English nor a known name, so lowercasing it would be a guess.
529
- def unknown?(word)
530
- bare = word.split("'", 2).first
531
- !Dictionary.english?(word) && !proper_name(bare)
532
- end
533
-
534
- def keep_capitals?(word)
535
- bare = word.sub(/'[A-Z]{1,2}\z/, "")
536
- return false if PROPER_NOUNS.key?(bare)
537
-
538
- ACRONYMS.include?(bare) || @constants.include?(bare) || bare.match?(/\A[IVX]+\z/)
539
- end
540
-
541
- def sentence_start?(comment, masked, offset)
542
- before = masked[0, offset].sub(/\A#+\s*/, "")
543
- return before.match?(/[.!?]["')\]]*\s+\z/) if before.match?(/[[:alnum:]#{FILLER}]/)
544
- return true if before.match?(/\A\s*(?:[-*•]|─+|\d+[.)])\s+\z/)
545
- return false if masked[0, offset].match?(/\A#\s{3,}/)
546
-
547
- previous = comment.full_line ? @by_line[comment.line - 1] : nil
548
- return true unless previous&.full_line
549
-
550
- body = previous.text.sub(/\A#+\s*/, "").strip
551
- body.empty? || body.match?(/[.!?]["')\]]*\z/)
552
- end
553
-
554
- def fix_line(line, number, wanted)
555
- comment = @by_line[number]
556
- return line if comment.nil? || comment.text.match?(DIRECTIVE)
557
-
558
- text = wanted.include?("all_caps") ? fix_caps(comment) : comment.text
559
- text = unbold(text) if wanted.include?("long_bold")
560
- code = line.byteslice(0, comment.col)
561
- return wrap(code, text) if wanted.include?("long_line") && comment.full_line
562
-
563
- "#{code}#{text}#{"\n" if line.end_with?("\n")}"
564
- end
565
-
566
- def unbold(text)
567
- heading = text[BOLD_HEADING, 2]
568
- return text unless heading && heading.split.size > MAX_HEADING_WORDS
569
-
570
- text.sub(BOLD_HEADING) { "#{Regexp.last_match(1)}#{heading}" }
571
- end
572
-
573
- def fix_caps(comment)
574
- text = comment.text.dup
575
- found = caps_words(comment)
576
- found.reverse_each { |offset, word, replacement| text[offset, word.length] = replacement }
577
- first, last = heading_run(comment, found)
578
- return text unless first
579
-
580
- text.insert(last, "**").insert(first, "**")
581
- end
582
-
583
- # An all-caps run that opens a paragraph and stops at a dash or a full stop
584
- # is that paragraph's heading, so it becomes bold rather than plain text.
585
- def heading_run(comment, found)
586
- masked = mask(comment)
587
- return unless found.size >= 2 && paragraph_start?(comment, masked, found.first[0])
588
-
589
- run = found.slice_when { |(a, word, _), (b, _, _)| !masked[(a + word.length)...b].match?(GAP) }.first
590
- finish = run.last[0] + run.last[1].length
591
- finish += 2 if masked[finish, 2] == "'s"
592
- return unless run.size >= 2 && masked[finish..].match?(HEADING_END)
593
- return if masked[run.first[0]...finish].split.size > MAX_HEADING_WORDS || documents_method?(comment)
594
-
595
- [run.first[0], finish]
596
- end
597
-
598
- # The first line above a `def` has to be a sentence about the method, so a
599
- # heading there stays plain text.
600
- def documents_method?(comment)
601
- line = comment.line
602
- line += 1 while @by_line[line + 1]&.full_line
603
- @lines[line].to_s.match?(/\A\s*(?:\w+\s+)?def\s/)
604
- end
605
-
606
- def paragraph_start?(comment, masked, offset)
607
- return false if masked[0, offset].sub(/\A#+\s*/, "").match?(/[[:alnum:]#{FILLER}]/)
608
-
609
- above = comment.full_line ? @by_line[comment.line - 1] : nil
610
- !above&.full_line || above.text.match?(/\A#+\s*\z/)
611
- end
612
-
613
- # Splits one over-long prose comment line in two. Lines with no safe
614
- # break point (a long URL, an unbroken code span) are left for a person.
615
- def wrap(indent, text)
616
- whole = "#{indent}#{text}\n"
617
- return whole if whole.chomp.length <= MAX_LINE
618
-
619
- lead = text[/\A#+\s*/]
620
- lead += " " if text.match?(/\A#+\s*@\w/)
621
- cut = text[0, MAX_LINE - indent.length + 1].rindex(" ")
622
- return whole if cut.nil? || cut <= lead.length || text[0, cut].count("`").odd?
623
-
624
- "#{indent}#{text[0, cut].rstrip}\n#{wrap(indent, "#{lead}#{text[cut..].strip}")}"
625
- end
626
-
627
- def structure_violations
628
- methods, types = structure
629
- methods.flat_map { |m| method_violations(m) } + types.flat_map { |t| long_doc_violations(t) }
630
- end
631
-
632
- def structure
633
- @structure ||= begin
634
- walker = Walker.new
635
- sexp = Ripper.sexp(@source)
636
- walker.walk(sexp, Walker::Scope.new([], :public)) if sexp
637
- [walker.methods, walker.types]
638
- end
639
- end
640
-
641
- def doc_above(line)
642
- block = []
643
- cursor = line - 1
644
- while (comment = @by_line[cursor]) && comment.full_line
645
- block.unshift(comment.text)
646
- cursor -= 1
647
- end
648
- block
649
- end
650
-
651
- def method_violations(method)
652
- return [] unless method.visibility == :public
653
- return [] unless CommentStyle.public_surface.include?(method.type_fqn.to_s.split("::").last)
654
-
655
- doc = doc_above(method.line).join("\n")
656
- if doc.empty?
657
- return [] if SELF_EVIDENT.include?(method.name)
658
-
659
- return [Violation.new(path, method.line, "undocumented_method", "##{method.name}")]
660
- end
661
- return [] if doc.match?(/@api private|@private|:nodoc:|\(see (?:`[^`]+`|#\w)[^)]*\)\W*\z/m)
662
-
663
- tag_violations(method, doc)
664
- end
665
-
666
- def tag_violations(method, doc)
667
- param_violations(method, doc) + summary_violations(method, doc) + outcome_violations(method, doc)
668
- end
669
-
670
- def param_violations(method, doc)
671
- found = method.params.reject { |name| doc.match?(/@(?:param|option)\s+(?:\[[^\]\n]*\]\s*)?#{Regexp.escape(name)}\b/) }
672
- .map { |name| violation(method, "missing_param", "lacks @param #{name}") }
673
- block = method.block_param
674
- if block && !doc.match?(/@yield|@param\s+(?:\[[^\]\n]*\]\s*)?#{Regexp.escape(block)}\b/)
675
- found << violation(method, "missing_param", "lacks @yield for &#{block}")
676
- end
677
- found
678
- end
679
-
680
- def summary_violations(method, doc)
681
- found = []
682
- found << violation(method, "missing_summary", "") if tags_only?(doc) && method.name != "initialize"
683
- if heading_lead?(doc)
684
- found << violation(method, "missing_summary",
685
- "opens with a bold heading: add a verb-first lead sentence above it")
686
- end
687
- found
688
- end
689
-
690
- def outcome_violations(method, doc)
691
- found = []
692
- returns = method.name == "initialize" || method.name.end_with?("=") || doc.include?("@return")
693
- found << violation(method, "missing_return", "") unless returns
694
- found << violation(method, "missing_raise", "") if method.raises && !doc.include?("@raise")
695
- found
696
- end
697
-
698
- def tags_only?(doc)
699
- lead_line(doc).start_with?("@")
700
- end
701
-
702
- # A bold heading is a paragraph title, not a sentence about the method.
703
- def heading_lead?(doc)
704
- lead_line(doc).start_with?("**")
705
- end
706
-
707
- def lead_line(doc)
708
- doc.lines.map { |line| line.sub(/\A\s*#+\s?/, "").strip }.reject(&:empty?).first.to_s
709
- end
710
-
711
- def violation(method, category, detail)
712
- Violation.new(path, method.line, category, "##{method.name} #{detail}".strip)
713
- end
714
-
715
- def long_doc_violations(type)
716
- doc = doc_above(type.line)
717
- return [] if doc.length <= LONG_CLASS_DOC || doc.any? { |l| l.match?(/\A#\s*##+ /) }
718
-
719
- [Violation.new(path, type.line, "unstructured_class_doc", "#{type.fqn} (#{doc.length} lines)")]
720
- end
721
- end
722
-
723
- # Walks a Ripper s-expression collecting method and type definitions
724
- # along with the visibility each method ends up with.
725
- class Walker
726
- Scope = Struct.new(:nesting, :visibility)
727
-
728
- attr_reader :methods, :types
729
-
730
- def initialize
731
- @methods = []
732
- @types = []
733
- @private_names = Set.new
734
- end
735
-
736
- # Visits one Ripper node, recording any method or type it defines.
737
- def walk(node, scope)
738
- return unless node.is_a?(Array)
739
-
740
- case node[0]
741
- when :class then open_type(node[1], node[3], scope)
742
- when :module then open_type(node[1], node[2], scope)
743
- when :sclass then walk_body(node[2], Scope.new(scope.nesting, :public))
744
- when :def then record(node[1], node[2], node[3], scope.visibility, scope.nesting)
745
- when :defs then record(node[3], node[4], node[5], :public, scope.nesting)
746
- when :vcall, :var_ref then switch_visibility(node, scope)
747
- when :command, :method_add_arg then visibility_call?(node, scope) || walk_children(node, scope)
748
- else walk_children(node, scope)
749
- end
750
- end
751
-
752
- private
753
-
754
- def walk_children(node, scope)
755
- node.each { |child| walk(child, scope) if child.is_a?(Array) }
756
- end
757
-
758
- # `private :name` can follow the definition, so it is applied once the
759
- # whole body has been seen.
760
- def walk_body(body, scope)
761
- first = @methods.length
762
- outer = @private_names
763
- @private_names = Set.new
764
- walk_children(body, scope) if body.is_a?(Array)
765
- @methods[first..].each { |m| m.visibility = :private if @private_names.include?(m.name) }
766
- @private_names = outer
767
- end
768
-
769
- def open_type(const, body, scope)
770
- nesting = scope.nesting + [const_name(const)]
771
- statements = body.is_a?(Array) && body[0] == :bodystmt ? Array(body[1]) : []
772
- namespace = statements.all? { |s| !s.is_a?(Array) || %i[class module void_stmt].include?(s[0]) }
773
- @types << TypeDef.new(nesting.join("::"), line_of(const), namespace)
774
- walk_body(body, Scope.new(nesting, :public))
775
- end
776
-
777
- def const_name(node)
778
- node.flatten.grep(String).join("::")
779
- end
780
-
781
- def line_of(node)
782
- position = node.flatten.each_cons(2).find { |a, b| a.is_a?(Integer) && b.is_a?(Integer) }
783
- position ? position[0] : 0
784
- end
785
-
786
- def switch_visibility(node, scope)
787
- case node[1].is_a?(Array) ? node[1][1] : nil
788
- when "private", "protected" then scope.visibility = :private
789
- when "public", "module_function" then scope.visibility = :public
790
- end
791
- end
792
-
793
- def visibility_call?(node, scope)
794
- head = node[0] == :command ? node[1] : node.dig(1, 1)
795
- return false unless head.is_a?(Array) && head[0] == :@ident
796
- return false unless %w[private protected private_class_method].include?(head[1])
797
-
798
- arguments = node[0] == :command ? node[2] : node.dig(2, 1)
799
- Array(arguments.is_a?(Array) ? arguments[1] : nil).each do |argument|
800
- next unless argument.is_a?(Array)
801
-
802
- case argument[0]
803
- when :def then record(argument[1], argument[2], argument[3], :private, scope.nesting)
804
- when :defs then record(argument[3], argument[4], argument[5], :private, scope.nesting)
805
- when :symbol_literal, :dyna_symbol
806
- @private_names << argument.flatten.grep(String).last
807
- else walk(argument, scope)
808
- end
809
- end
810
- true
811
- end
812
-
813
- def record(name_node, params, body, visibility, nesting)
814
- names, block = parameter_names(params)
815
- @methods << MethodDef.new(name_node[1], name_node[2][0], names, visibility, raises?(body), block,
816
- nesting.join("::"))
817
- end
818
-
819
- def parameter_names(params)
820
- params = params[1] if params.is_a?(Array) && params[0] == :paren
821
- return [[], nil] unless params.is_a?(Array) && params[0] == :params
822
-
823
- _, required, optional, rest, post, keywords, keyrest, block = params
824
- names = idents(required) + Array(optional).map { |pair| pair[0][1] } + splat_name(rest) +
825
- idents(post) + Array(keywords).map { |pair| pair[0][1].chomp(":") } + splat_name(keyrest)
826
- [names, splat_name(block).first]
827
- end
828
-
829
- def idents(list)
830
- Array(list).select { |item| item.is_a?(Array) && item[0] == :@ident }.map { |item| item[1] }
831
- end
832
-
833
- # `rest`/`keyrest`/`block` are each either nil, an unnamed splat (`*`, `**`, `&`), or a
834
- # one-element `[:@ident, name, pos]` node wrapping the name.
835
- def splat_name(node)
836
- node.is_a?(Array) && node[1].is_a?(Array) ? [node[1][1]] : []
837
- end
838
-
839
- def raises?(node)
840
- return false unless node.is_a?(Array)
841
- return false if %i[def defs].include?(node[0])
842
-
843
- call = %i[command fcall vcall].include?(node[0]) && node[1].is_a?(Array) && node[1][1] == "raise"
844
- call || node.any? { |child| raises?(child) }
845
- end
846
- end
847
-
848
- # Lints a set of paths and renders the result.
849
- class Run
850
- # @param paths [Array<String>] files or directories
851
- # @param only [Array<String>, nil] categories to keep
852
- # @param baseline [Hash{String => Hash{String => Integer}}] the long blocks to tolerate
853
- def initialize(paths, only: nil, baseline: Baseline.load)
854
- @files = paths.flat_map { |p| File.directory?(p) ? ruby_files(p) : [p] }.sort.uniq
855
- @only = only
856
- @baseline = baseline
857
- end
858
-
859
- # Every block over `MAX_BLOCK` lines in these files, in the baseline's own shape.
860
- #
861
- # @return [Hash{String => Hash{String => Integer}}] file path to block key to length
862
- def long_block_baseline
863
- @files.each_with_object({}) do |path, held|
864
- blocks = SourceFile.new(path).long_blocks
865
- held[path.delete_prefix("./")] = blocks.to_h { |block| [block.key, block.lines] } if blocks.any?
866
- end
867
- end
868
-
869
- # Files whose code differs from `ref`, comments and whitespace aside.
870
- #
871
- # A file `ref` does not hold (new since `ref`, or outside the repository) counts as
872
- # changed: what cannot be compared is not vouched for.
873
- #
874
- # @param ref [String] revision to compare the working tree against
875
- # @return [Array<String>] paths whose non-comment token stream changed
876
- # @raise [ArgumentError] when `ref` names no commit
877
- def code_changed_since(ref)
878
- top = vcs_output("rev-parse", "--show-toplevel", chdir: Dir.pwd)&.strip
879
- raise ArgumentError, "not inside a checkout" if top.nil? || top.empty?
880
- unless vcs_output("rev-parse", "--verify", "--quiet", "#{ref}^{commit}", chdir: top)
881
- raise ArgumentError, "unknown ref #{ref.inspect}"
882
- end
883
-
884
- @files.select do |path|
885
- before = committed_source(ref, path, top)
886
- before.nil? || CommentStyle.code_tokens(before) != CommentStyle.code_tokens(File.read(path))
887
- end
888
- end
889
-
890
- # Every violation across the whole file set, computed once.
891
- #
892
- # @return [Array<Violation>]
893
- def violations
894
- @violations ||= begin
895
- sources = @files.map { |path| SourceFile.new(path) }
896
- found = sources.flat_map { |source| source.violations(only: @only, baseline: @baseline) } +
897
- undocumented_types(sources)
898
- @only ? found.select { |v| @only.include?(v.category) } : found
899
- end
900
- end
901
-
902
- # Rewrites every file's fixable categories in place.
903
- #
904
- # @return [Integer] number of files rewritten
905
- def fix!
906
- wants_caps = @only.nil? || @only.include?("all_caps")
907
- abort "fixing all_caps needs the word list at #{Dictionary::WORDS}" if wants_caps && !Dictionary.words
908
-
909
- @files.count do |path|
910
- original = File.read(path)
911
- output = SourceFile.new(path, original).fixed(only: @only)
912
- next false if output == original
913
-
914
- File.write(path, output)
915
- true
916
- end
917
- end
918
-
919
- # Renders the summary tables `--report` prints.
920
- #
921
- # @param top [Integer] rows to show in the per-file table
922
- # @return [String]
923
- def report(top: 20)
924
- by_file = violations.group_by(&:path)
925
- [
926
- "#{violations.size} violations in #{by_file.size} of #{@files.size} files\n",
927
- "By category:", *category_rows,
928
- "\nBy directory (top #{top}):", *directory_rows.first(top),
929
- "\nWorst #{top} files:", *file_rows(by_file, top)
930
- ].join("\n")
931
- end
932
-
933
- private
934
-
935
- # `path`'s text at `ref`, or nil when the repository at `top` holds none there.
936
- def committed_source(ref, path, top)
937
- relative = Pathname.new(File.expand_path(path)).relative_path_from(Pathname.new(top)).to_s
938
- return nil if relative == ".." || relative.start_with?("../")
939
-
940
- vcs_output("show", "#{ref}:#{relative}", chdir: top)&.force_encoding(Encoding::UTF_8)
941
- end
942
-
943
- # What `git <args>` printed, or nil when it failed.
944
- def vcs_output(*args, chdir:)
945
- out = IO.popen(["git", *args], chdir: chdir, err: File::NULL, &:read)
946
- $CHILD_STATUS.success? ? out : nil
947
- end
948
-
949
- # `.rb` files, plus extensionless scripts with a Ruby shebang. The first line is read as
950
- # bytes, so an extensionless binary (a compiled test fixture's output) is passed over
951
- # rather than refused for not being UTF-8.
952
- def ruby_files(dir)
953
- scripts = Dir[File.join(dir, "**", "*")].select do |path|
954
- File.file?(path) && File.extname(path).empty? && File.open(path, "rb", &:gets).to_s.match?(/\A#!.*ruby/n)
955
- end
956
- Dir[File.join(dir, "**", "*.rb")] + scripts
957
- end
958
-
959
- def category_rows
960
- counts = violations.group_by(&:category).transform_values(&:size)
961
- CATEGORIES.map do |name, description|
962
- suffix = FIXABLE.include?(name) ? " [fixable]" : ""
963
- " #{counts.fetch(name, 0).to_s.rjust(6)} #{name.ljust(24)} #{description}#{suffix}"
964
- end
965
- end
966
-
967
- def directory_rows
968
- violations.group_by { |v| v.path.split("/")[0, 3].join("/") }
969
- .sort_by { |_, list| -list.size }
970
- .map { |dir, list| " #{list.size.to_s.rjust(6)} #{dir}" }
971
- end
972
-
973
- def file_rows(by_file, top)
974
- by_file.sort_by { |_, list| -list.size }.first(top).map do |file, list|
975
- category, worst = list.group_by(&:category).max_by { |_, group| group.size }
976
- " #{list.size.to_s.rjust(6)} #{file} (mostly #{category}: #{worst.size})"
977
- end
978
- end
979
-
980
- # A type reopened across files counts as documented if any opening is.
981
- def undocumented_types(sources)
982
- openings = sources.flat_map { |s| s.types.reject(&:namespace_only).map { |t| [s, t] } }
983
- openings.group_by { |_, type| type.fqn }.filter_map do |fqn, group|
984
- next unless CommentStyle.public_surface.include?(fqn.to_s.split("::").last)
985
- next if group.any? { |source, type| source.documented?(type) }
986
-
987
- source, type = group.first
988
- Violation.new(source.path, type.line, "undocumented_class", fqn)
989
- end
990
- end
991
- end
992
-
993
- # Builds the CLI's option parser, filling `options` as flags are seen.
994
- def self.option_parser(options)
995
- OptionParser.new do |opts|
996
- opts.banner = "Usage: hecks check_comments [--report|--check|--fix] [--only a,b] PATH..."
997
- opts.on("--report", "summary tables (default)") { options[:mode] = :report }
998
- opts.on("--check", "list every violation, exit 1 if any") { options[:mode] = :check }
999
- opts.on("--fix", "rewrite fixable categories: #{FIXABLE.join(', ')}") { options[:mode] = :fix }
1000
- opts.on("--write-baseline", "record every block over #{MAX_BLOCK} lines as tolerated") do
1001
- options[:mode] = :write_baseline
1002
- end
1003
- opts.on("--only LIST", Array, "limit to: #{CATEGORIES.keys.join(', ')}") { |list| options[:only] = list }
1004
- opts.on("--code-unchanged REF", "exit 1 if any file's code (not comments) differs from REF") do |ref|
1005
- options[:mode] = :code_unchanged
1006
- options[:ref] = ref
1007
- end
1008
- opts.on("--json", "machine-readable output") { options[:json] = true }
1009
- opts.on("--top N", Integer, "rows in the worst-files table") { |n| options[:top] = n }
1010
- end
1011
- end
1012
-
1013
- # Prints `--report`, `--check`, or `--json` output for a finished run.
1014
- def self.render(run, options)
1015
- found = run.violations
1016
- if options[:json]
1017
- puts JSON.pretty_generate(found.map(&:to_h))
1018
- elsif options[:mode] == :check
1019
- found.each { |v| puts "#{v.path}:#{v.line}: [#{v.category}] #{v.message}" }
1020
- else
1021
- puts run.report(top: options[:top])
1022
- end
1023
- options[:mode] == :check && found.any? ? 1 : 0
1024
- end
1025
-
1026
- # The CLI entry point: parses `argv` and runs the requested mode.
1027
- #
1028
- # Paths after a `--` are never read as flags, and a path that does not exist is refused.
1029
- def self.main(argv, root: Baseline::ROOT)
1030
- options = { mode: :report, top: 20 }
1031
- parser = option_parser(options)
1032
- paths = parser.parse(argv)
1033
- unknown = Array(options[:only]) - CATEGORIES.keys
1034
- abort "unknown categories: #{unknown.join(', ')}" unless unknown.empty?
1035
- abort parser.help if paths.empty?
1036
- missing = paths.reject { |path| File.exist?(path) }
1037
- abort "no such path: #{missing.join(', ')}" unless missing.empty?
1038
-
1039
- baseline_path = Baseline.path_in(root)
1040
- run = Run.new(paths, only: options[:only], baseline: Baseline.load(baseline_path))
1041
- case options[:mode]
1042
- when :fix then puts("rewrote #{run.fix!} files") || 0
1043
- when :write_baseline then write_baseline(run, baseline_path)
1044
- when :code_unchanged then report_code_changes(changed_since(run, options[:ref]))
1045
- else render(run, options)
1046
- end
1047
- end
1048
-
1049
- # Rewrites the baseline file from the blocks over `MAX_BLOCK` lines that `run` finds.
1050
- def self.write_baseline(run, path = Baseline::PATH)
1051
- held = run.long_block_baseline
1052
- Baseline.dump(held, path)
1053
- puts "recorded #{held.values.sum(&:size)} blocks in #{held.size} files at #{path}"
1054
- 0
1055
- end
1056
-
1057
- # The files `run` finds changed since `ref`, refusing a ref that names no commit.
1058
- def self.changed_since(run, ref)
1059
- run.code_changed_since(ref)
1060
- rescue ArgumentError => e
1061
- abort e.message
1062
- end
1063
-
1064
- # Prints `--code-unchanged`'s verdict.
1065
- def self.report_code_changes(changed)
1066
- changed.each { |path| puts "#{path}: code changed, not just comments" }
1067
- puts "code unchanged" if changed.empty?
1068
- changed.empty? ? 0 : 1
1069
- end
1070
87
  end
1071
88
  end
1072
89
  end