hecks 1.4.0 → 1.5.1

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 (275) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/claude_code.rb +65 -0
  3. data/lib/hecks/adapters/driven/folder.rb +73 -0
  4. data/lib/hecks/adapters/driven/google_authentication.rb +25 -4
  5. data/lib/hecks/adapters/driven/governance_authorization.rb +57 -6
  6. data/lib/hecks/adapters/driven/heki/journal.rb +8 -0
  7. data/lib/hecks/adapters/driven/heki/saga_store.rb +53 -7
  8. data/lib/hecks/adapters/driven/heki/snapshot.rb +1 -1
  9. data/lib/hecks/adapters/driven/heki.rb +93 -2
  10. data/lib/hecks/adapters/driven/identity_registry.rb +10 -0
  11. data/lib/hecks/adapters/driven/in_memory_ordering.rb +22 -0
  12. data/lib/hecks/adapters/driven/in_process_key_vault.adapter +3 -0
  13. data/lib/hecks/adapters/driven/in_process_key_vault.rb +53 -0
  14. data/lib/hecks/adapters/driven/lambda/client.rb +35 -7
  15. data/lib/hecks/adapters/driven/lambda.rb +43 -2
  16. data/lib/hecks/adapters/driven/local_storage.rb +67 -1
  17. data/lib/hecks/adapters/driven/memory.rb +13 -13
  18. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +20 -0
  19. data/lib/hecks/adapters/driven/prism.rb +46 -7
  20. data/lib/hecks/adapters/driven/secure_random_identity.rb +3 -0
  21. data/lib/hecks/adapters/driven/system_clock.rb +3 -0
  22. data/lib/hecks/adapters/driven/tenant_provisioner.adapter +3 -0
  23. data/lib/hecks/adapters/driven/tenant_provisioner.rb +66 -0
  24. data/lib/hecks/adapters/driven.rb +2 -0
  25. data/lib/hecks/adapters/driving/github_webhook.rb +14 -1
  26. data/lib/hecks/behaviors/dsl.rb +58 -0
  27. data/lib/hecks/behaviors/expectations.rb +168 -7
  28. data/lib/hecks/behaviors/ir.rb +11 -0
  29. data/lib/hecks/behaviors/rspec.rb +8 -0
  30. data/lib/hecks/behaviors/runner.rb +19 -0
  31. data/lib/hecks/behaviors.rb +8 -0
  32. data/lib/hecks/bluebook/aggregate.rb +30 -0
  33. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +7 -0
  34. data/lib/hecks/bluebook/assembly/build.rb +25 -4
  35. data/lib/hecks/bluebook/assembly/contract.rb +66 -14
  36. data/lib/hecks/bluebook/assembly/contracts.rb +24 -19
  37. data/lib/hecks/bluebook/assembly/marks.rb +143 -14
  38. data/lib/hecks/bluebook/assembly/specializer.rb +19 -0
  39. data/lib/hecks/bluebook/assembly.rb +25 -7
  40. data/lib/hecks/bluebook/attribute.rb +17 -3
  41. data/lib/hecks/bluebook/behaviour/aggregate.rb +32 -0
  42. data/lib/hecks/bluebook/behaviour/attribute.rb +13 -0
  43. data/lib/hecks/bluebook/behaviour/chapter.rb +55 -2
  44. data/lib/hecks/bluebook/behaviour/command.rb +33 -3
  45. data/lib/hecks/bluebook/behaviour/domain_port.rb +20 -0
  46. data/lib/hecks/bluebook/behaviour/entity.rb +12 -0
  47. data/lib/hecks/bluebook/behaviour/hexagon.rb +26 -0
  48. data/lib/hecks/bluebook/behaviour/lifecycle.rb +23 -2
  49. data/lib/hecks/bluebook/behaviour/policy.rb +34 -9
  50. data/lib/hecks/bluebook/behaviour/process_manager.rb +32 -1
  51. data/lib/hecks/bluebook/behaviour/query.rb +5 -0
  52. data/lib/hecks/bluebook/behaviour/read_model.rb +21 -0
  53. data/lib/hecks/bluebook/behaviour/traits.rb +36 -0
  54. data/lib/hecks/bluebook/behaviour/value_object.rb +17 -5
  55. data/lib/hecks/bluebook/chapter.rb +23 -0
  56. data/lib/hecks/bluebook/command.rb +53 -8
  57. data/lib/hecks/bluebook/domain_port.rb +25 -0
  58. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +196 -37
  59. data/lib/hecks/bluebook/dsl/attribute_collector.rb +79 -42
  60. data/lib/hecks/bluebook/dsl/binding_proxy.rb +59 -0
  61. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +385 -53
  62. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +135 -21
  63. data/lib/hecks/bluebook/dsl/command_builder.rb +189 -27
  64. data/lib/hecks/bluebook/dsl/entity_builder.rb +139 -9
  65. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +41 -0
  66. data/lib/hecks/bluebook/dsl/identity_declaration.rb +23 -6
  67. data/lib/hecks/bluebook/dsl/policy_builder.rb +60 -19
  68. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +79 -15
  69. data/lib/hecks/bluebook/dsl/query_builder.rb +33 -4
  70. data/lib/hecks/bluebook/dsl/read_model_builder.rb +102 -27
  71. data/lib/hecks/bluebook/dsl/rule_reference.rb +60 -8
  72. data/lib/hecks/bluebook/dsl/translation_builder.rb +146 -40
  73. data/lib/hecks/bluebook/dsl/value_object_builder.rb +55 -7
  74. data/lib/hecks/bluebook/entity.rb +29 -0
  75. data/lib/hecks/bluebook/expression/ast_json.rb +115 -23
  76. data/lib/hecks/bluebook/expression/ast_reader.rb +29 -0
  77. data/lib/hecks/bluebook/expression/canonical_form.rb +48 -9
  78. data/lib/hecks/bluebook/expression/evaluator.rb +207 -29
  79. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +36 -0
  80. data/lib/hecks/bluebook/expression/resolver.rb +320 -77
  81. data/lib/hecks/bluebook/hexagon.rb +34 -0
  82. data/lib/hecks/bluebook/lifecycle.rb +11 -0
  83. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -0
  84. data/lib/hecks/bluebook/meta_validator/judge.rb +30 -26
  85. data/lib/hecks/bluebook/meta_validator/plan.rb +43 -8
  86. data/lib/hecks/bluebook/meta_validator/port_judge.rb +1 -0
  87. data/lib/hecks/bluebook/meta_validator/readings.rb +157 -7
  88. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -4
  89. data/lib/hecks/bluebook/meta_validator/shapes.rb +141 -0
  90. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +149 -23
  91. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +5 -4
  92. data/lib/hecks/bluebook/meta_validator/world_judge.rb +1 -0
  93. data/lib/hecks/bluebook/meta_validator.rb +180 -84
  94. data/lib/hecks/bluebook/model_check.rb +268 -24
  95. data/lib/hecks/bluebook/pattern_subset.rb +23 -1
  96. data/lib/hecks/bluebook/process_manager.rb +13 -0
  97. data/lib/hecks/bluebook/project_discovery.rb +5 -0
  98. data/lib/hecks/bluebook/project_loader.rb +40 -0
  99. data/lib/hecks/bluebook/project_register.rb +44 -0
  100. data/lib/hecks/bluebook/query.rb +27 -0
  101. data/lib/hecks/bluebook/read_model.rb +21 -1
  102. data/lib/hecks/bluebook/reference.rb +21 -8
  103. data/lib/hecks/bluebook/smoke_test.rb +29 -6
  104. data/lib/hecks/bluebook/synthesizer.rb +34 -0
  105. data/lib/hecks/bluebook/translation.rb +30 -1
  106. data/lib/hecks/bluebook/value_object.rb +23 -5
  107. data/lib/hecks/bluebook.rb +3 -4
  108. data/lib/hecks/codemod.rb +107 -20
  109. data/lib/hecks/construct.rb +15 -1
  110. data/lib/hecks/corpus.rb +146 -25
  111. data/lib/hecks/deploy/bluebook/deploy.bluebook +105 -0
  112. data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
  113. data/lib/hecks/deploy/oidc.json +5 -0
  114. data/lib/hecks/doc/reference.rb +185 -16
  115. data/lib/hecks/embryonaut_bluebook.rb +32 -9
  116. data/lib/hecks/facade/handle.rb +76 -3
  117. data/lib/hecks/facade/surface/aggregate_door.rb +8 -0
  118. data/lib/hecks/forms/field_shape.rb +3 -0
  119. data/lib/hecks/forms/page.rb +14 -0
  120. data/lib/hecks/forms/port_argument.rb +12 -0
  121. data/lib/hecks/forms/query_form_renderer.rb +63 -0
  122. data/lib/hecks/forms/record_renderer.rb +58 -0
  123. data/lib/hecks/forms/record_table.rb +27 -0
  124. data/lib/hecks/forms/reference_options.rb +24 -0
  125. data/lib/hecks/forms/value_object_shape.rb +10 -0
  126. data/lib/hecks/fqn.rb +58 -0
  127. data/lib/hecks/framework/bluebook/compliance.bluebook +221 -0
  128. data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
  129. data/lib/hecks/framework/oidc.json +15 -0
  130. data/lib/hecks/framework.rb +43 -20
  131. data/lib/hecks/freezer.rb +17 -1
  132. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +159 -24
  133. data/lib/hecks/fuzzing/combination_miner.rb +59 -0
  134. data/lib/hecks/fuzzing/concurrent_dispatch.rb +109 -8
  135. data/lib/hecks/fuzzing/coverage_campaign.rb +56 -13
  136. data/lib/hecks/fuzzing/differential.rb +34 -0
  137. data/lib/hecks/fuzzing/domain_generator.rb +188 -11
  138. data/lib/hecks/fuzzing/era_boundary.rb +45 -15
  139. data/lib/hecks/fuzzing/form_census.rb +86 -0
  140. data/lib/hecks/fuzzing/generated_domain_check.rb +76 -0
  141. data/lib/hecks/fuzzing/invalid_value_generator.rb +39 -0
  142. data/lib/hecks/fuzzing/isolated_boot.rb +79 -22
  143. data/lib/hecks/fuzzing/nondeterministic.rb +13 -1
  144. data/lib/hecks/fuzzing/persistence_parity.rb +95 -3
  145. data/lib/hecks/fuzzing/properties/corrections.rb +25 -0
  146. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +158 -14
  147. data/lib/hecks/fuzzing/properties/guards.rb +44 -0
  148. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +48 -0
  149. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +18 -0
  150. data/lib/hecks/fuzzing/properties/outbox.rb +49 -11
  151. data/lib/hecks/fuzzing/properties/querying.rb +68 -14
  152. data/lib/hecks/fuzzing/properties.rb +24 -15
  153. data/lib/hecks/fuzzing/qa_settings.rb +12 -0
  154. data/lib/hecks/fuzzing/replay.rb +137 -29
  155. data/lib/hecks/fuzzing/rotation_priority.rb +41 -21
  156. data/lib/hecks/fuzzing/rust_gap_manifest.rb +46 -20
  157. data/lib/hecks/fuzzing/self_consistency.rb +189 -40
  158. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +12 -6
  159. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +1 -1
  160. data/lib/hecks/fuzzing/sequence_generator.rb +47 -0
  161. data/lib/hecks/fuzzing/shrinker.rb +114 -2
  162. data/lib/hecks/fuzzing/structural_skips.rb +18 -4
  163. data/lib/hecks/fuzzing/sweep_depth.rb +8 -0
  164. data/lib/hecks/fuzzing/target_capabilities.rb +61 -14
  165. data/lib/hecks/fuzzing/value_generator.rb +98 -10
  166. data/lib/hecks/grammar/evolve.rb +178 -2
  167. data/lib/hecks/grammar.rb +46 -0
  168. data/lib/hecks/ir.rb +38 -7
  169. data/lib/hecks/language/hecksagon/hecksagon.bluebook +11 -0
  170. data/lib/hecks/literal.rb +32 -0
  171. data/lib/hecks/naming.rb +88 -7
  172. data/lib/hecks/ports/access_control.rb +5 -10
  173. data/lib/hecks/ports/authorization.rb +3 -6
  174. data/lib/hecks/ports/identity_assignment.rb +1 -2
  175. data/lib/hecks/ports/identity_resolution.rb +1 -2
  176. data/lib/hecks/ports/key_vault.port +6 -0
  177. data/lib/hecks/ports/key_vault.rb +85 -0
  178. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +6 -0
  179. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +38 -2
  180. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -0
  181. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +40 -0
  182. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +92 -1
  183. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
  184. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +8 -0
  185. data/lib/hecks/ports/query/in_memory.rb +39 -6
  186. data/lib/hecks/ports/query/ordering.rb +15 -0
  187. data/lib/hecks/ports.rb +1 -0
  188. data/lib/hecks/projections/bootstrap_table.rb +43 -8
  189. data/lib/hecks/projections/deploy/fargate.rb +666 -0
  190. data/lib/hecks/projections/deploy/lambda.rb +2423 -0
  191. data/lib/hecks/projections/deploy/shared.rb +624 -0
  192. data/lib/hecks/projections/diagrams.rb +243 -7
  193. data/lib/hecks/projections/glossary/html.rb +88 -0
  194. data/lib/hecks/projections/glossary/markdown.rb +47 -0
  195. data/lib/hecks/projections/glossary/mermaid.rb +48 -0
  196. data/lib/hecks/projections/glossary/sentences.rb +105 -0
  197. data/lib/hecks/projections/glossary.rb +161 -19
  198. data/lib/hecks/projections/model/deviations.rb +44 -0
  199. data/lib/hecks/projections/model.rb +51 -1
  200. data/lib/hecks/projections/oidc.rb +40 -3
  201. data/lib/hecks/projections/parser_table.rb +49 -8
  202. data/lib/hecks/projections/reference.rb +12 -0
  203. data/lib/hecks/projections/rust_vocabulary.rb +219 -16
  204. data/lib/hecks/projections/shape.rb +8 -0
  205. data/lib/hecks/projections/statements.rb +63 -16
  206. data/lib/hecks/projections/vocabulary.rb +17 -0
  207. data/lib/hecks/projections.rb +3 -0
  208. data/lib/hecks/projector/cli_projector.rb +218 -10
  209. data/lib/hecks/projector/docs_projector.rb +145 -19
  210. data/lib/hecks/projector/exporter.rb +65 -11
  211. data/lib/hecks/projector/ir_projector.rb +6 -0
  212. data/lib/hecks/projector/narrate_projector.rb +136 -15
  213. data/lib/hecks/projector/target.rb +47 -10
  214. data/lib/hecks/projector.rb +101 -19
  215. data/lib/hecks/query_ir.rb +47 -0
  216. data/lib/hecks/query_specification/common/null_policy.rb +5 -3
  217. data/lib/hecks/rendering.rb +6 -0
  218. data/lib/hecks/router/namespace_installer.rb +13 -0
  219. data/lib/hecks/router.rb +55 -0
  220. data/lib/hecks/runtime/aggregate_lock.rb +9 -0
  221. data/lib/hecks/runtime/boot_gates.rb +18 -0
  222. data/lib/hecks/runtime/caller.rb +32 -0
  223. data/lib/hecks/runtime/capability_graph.rb +11 -0
  224. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +23 -21
  225. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +14 -15
  226. data/lib/hecks/runtime/command_interpreter.rb +42 -17
  227. data/lib/hecks/runtime/command_rules/admissibility.rb +165 -14
  228. data/lib/hecks/runtime/command_rules/arithmetic.rb +17 -5
  229. data/lib/hecks/runtime/command_rules/references.rb +118 -28
  230. data/lib/hecks/runtime/dependency_planning.rb +45 -0
  231. data/lib/hecks/runtime/dispatcher.rb +28 -50
  232. data/lib/hecks/runtime/entity_element.rb +161 -8
  233. data/lib/hecks/runtime/entity_interpreter.rb +44 -9
  234. data/lib/hecks/runtime/errors.rb +18 -4
  235. data/lib/hecks/runtime/event.rb +10 -5
  236. data/lib/hecks/runtime/identity.rb +71 -3
  237. data/lib/hecks/runtime/instance.rb +67 -7
  238. data/lib/hecks/runtime/interpreting.rb +13 -5
  239. data/lib/hecks/runtime/invocation.rb +118 -36
  240. data/lib/hecks/runtime/loader.rb +94 -8
  241. data/lib/hecks/runtime/outbox.rb +145 -7
  242. data/lib/hecks/runtime/policy_interpreter.rb +22 -9
  243. data/lib/hecks/runtime/port_operation_interpreter.rb +20 -0
  244. data/lib/hecks/runtime/query_interpreter.rb +40 -12
  245. data/lib/hecks/runtime/reaction_invocation.rb +53 -8
  246. data/lib/hecks/runtime/read_model_interpreter.rb +23 -7
  247. data/lib/hecks/runtime/rebuild_sweep.rb +28 -0
  248. data/lib/hecks/runtime/reference_hop.rb +42 -0
  249. data/lib/hecks/runtime/refusal_wording.rb +50 -0
  250. data/lib/hecks/runtime/registry/saga_persistence.rb +11 -0
  251. data/lib/hecks/runtime/registry/verification.rb +119 -4
  252. data/lib/hecks/runtime/registry.rb +157 -4
  253. data/lib/hecks/runtime/remote_dispatcher.rb +92 -6
  254. data/lib/hecks/runtime/routing.rb +27 -2
  255. data/lib/hecks/runtime/saga_interpreter/correlation.rb +10 -12
  256. data/lib/hecks/runtime/saga_interpreter.rb +27 -13
  257. data/lib/hecks/runtime/tenant_check.rb +26 -6
  258. data/lib/hecks/runtime/tenant_scope.rb +18 -0
  259. data/lib/hecks/runtime/value/coercion.rb +255 -33
  260. data/lib/hecks/runtime/value/entity_list_coercion.rb +102 -30
  261. data/lib/hecks/runtime/value.rb +50 -0
  262. data/lib/hecks/runtime.rb +32 -0
  263. data/lib/hecks/storehouse.rb +305 -9
  264. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
  265. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
  266. data/lib/hecks/version.rb +1 -1
  267. data/lib/hecks.rb +79 -1
  268. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -0
  269. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +12 -2
  270. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +29 -5
  271. metadata +14 -5
  272. data/lib/hecks/codemod/legacy_dispatch_args.rb +0 -299
  273. data/lib/hecks/codemod/legacy_dispatch_recorder.rb +0 -186
  274. data/lib/hecks/deprecation.rb +0 -95
  275. data/lib/hecks/framework/bluebook/compliance.bluebook +0 -1
@@ -8,6 +8,13 @@ module Hecks
8
8
  # everything ambiguous left unresolved for a human.
9
9
  module Differ
10
10
  # Diff two bluebook IRs into the edge's declarations.
11
+ #
12
+ # @param held_bluebook [Bluebook::Chapter] the era being translated from
13
+ # @param current_bluebook [Bluebook::Chapter] the era being translated to
14
+ # @return [Hash{Symbol => Object}] `:aggregates` (Array<Scaffold::ScaffoldedAggregate>,
15
+ # one per matched aggregate carrying a rename or a rule), `:retired` (Array<String>,
16
+ # vanished aggregate names with no successor), `:unclaimed` (Array<String>, vanished
17
+ # aggregate names left ambiguous)
11
18
  def diff(held_bluebook, current_bluebook)
12
19
  held_shapes = projections(held_bluebook)
13
20
  current_shapes = projections(current_bluebook)
@@ -27,17 +34,31 @@ module Hecks
27
34
  { aggregates: aggregates, retired: matched[:retired], unclaimed: matched[:unclaimed] }
28
35
  end
29
36
 
37
+ # Projects a bluebook's storage shape and indexes its aggregates by name.
38
+ #
39
+ # @param bluebook [Bluebook::Chapter] the bluebook to project
40
+ # @return [Hash{String => Hash}] the bluebook's storage-shape aggregates (see
41
+ # `Runtime::StorageShape.project`), keyed by aggregate name
30
42
  def projections(bluebook)
31
43
  Runtime::StorageShape.project(bluebook)["aggregates"].to_h { |shape| [shape["name"], shape] }
32
44
  end
33
45
 
34
46
  # A vanished aggregate whose full shape reappears under exactly one
35
- # new name was renamed. `retired` is only confident when nothing
47
+ # new name counts as a rename. `retired` is only confident when nothing
36
48
  # remains it could plausibly have become — a vanished aggregate
37
49
  # beside an unmatched new one might be a rename-plus-reshape, and
38
50
  # writing `retired` there would be a guess that strands data.
39
51
  # Anything ambiguous stays unclaimed, and the coverage gate names
40
52
  # it until a human decides.
53
+ #
54
+ # @param held_shapes [Hash{String => Hash}] the held era's aggregate shapes, keyed by
55
+ # name (see `projections`)
56
+ # @param current_shapes [Hash{String => Hash}] the current era's aggregate shapes,
57
+ # keyed by name
58
+ # @return [Hash{Symbol => Object}] `:pairs` (Hash{String => String, nil}, each current
59
+ # name to its matched held name, or nil when unmatched), `:retired` (Array<String>,
60
+ # vanished names with no successor), `:unclaimed` (Array<String>, vanished names
61
+ # left ambiguous)
41
62
  def match_aggregates(held_shapes, current_shapes)
42
63
  pairs = {}
43
64
  current_shapes.each_key { |name| pairs[name] = held_shapes.key?(name) ? name : nil }
@@ -67,6 +88,13 @@ module Hecks
67
88
  # new type name: a retype. Anything else: unresolved, carrying its
68
89
  # type-compatible candidates (an empty list is the arrow toward
69
90
  # compute, or drop).
91
+ #
92
+ # @param held_shape [Hash{String => Object}] the held era's aggregate shape (see
93
+ # `projections`)
94
+ # @param current_shape [Hash{String => Object}] the current era's aggregate shape
95
+ # @return [Array<Hash{Symbol => Object}>] one rule Hash per changed path — `:kind`
96
+ # plus `:from`/`:to` for `:rename`/`:move`/`:retype`, or `:from`/`:candidates` for
97
+ # `:unresolved` (see `Renderer#render_rule`); `[]` when nothing changed
70
98
  def attribute_rules(held_shape, current_shape)
71
99
  rules = []
72
100
  rules << identity_hint(held_shape, current_shape)
@@ -93,6 +121,13 @@ module Hecks
93
121
 
94
122
  # retype pass: same attribute name, same member structure, the
95
123
  # type's own name changed
124
+ #
125
+ # @param held_attrs [Hash{String => Hash}] the held era's attributes, keyed by name
126
+ # @param current_attrs [Hash{String => Hash}] the current era's attributes, keyed by
127
+ # name
128
+ # @return [Array<Hash{Symbol => String}>] one `{kind: :retype, from:, to:}` rule per
129
+ # attribute whose members are unchanged but whose type name changed; `[]` when none
130
+ # match
96
131
  def retype_rules(held_attrs, current_attrs)
97
132
  (held_attrs.keys & current_attrs.keys).filter_map do |name|
98
133
  held = held_attrs[name]
@@ -105,6 +140,16 @@ module Hecks
105
140
  end
106
141
  end
107
142
 
143
+ # Resolves each vanished path into a rename, move or unresolved rule, appended to
144
+ # `rules` in place.
145
+ #
146
+ # @param rules [Array<Hash{Symbol => Object}>] the rule list to append to; also read
147
+ # for `:rename`/`:move` targets an earlier iteration already claimed
148
+ # @param vanished [Hash{String => Object}] vanished paths to type signature (see
149
+ # `vanished_paths`)
150
+ # @param appeared [Hash{String => Object}] appeared paths to type signature (see
151
+ # `appeared_paths`)
152
+ # @return [void]
108
153
  def resolve_vanished_rules!(rules, vanished, appeared)
109
154
  vanished.each do |path, signature|
110
155
  matches = appeared.select { |_, candidate| candidate == signature }.keys
@@ -125,6 +170,14 @@ module Hecks
125
170
  # and members that vanished (or changed type) inside a kept
126
171
  # attribute — mirroring exactly what EraGuard will demand coverage
127
172
  # for.
173
+ #
174
+ # @param held_attrs [Hash{String => Hash}] the held era's attributes, keyed by name
175
+ # @param current_attrs [Hash{String => Hash}] the current era's attributes, keyed by
176
+ # name
177
+ # @param retyped [Array<String>] type names already claimed by a `:retype` rule,
178
+ # excluded here
179
+ # @return [Hash{String => Object}] each vanished or changed path, dotted for a member,
180
+ # valued by that path's type signature
128
181
  def vanished_paths(held_attrs, current_attrs, retyped)
129
182
  paths = {}
130
183
  held_attrs.each do |name, held|
@@ -146,6 +199,17 @@ module Hecks
146
199
  paths
147
200
  end
148
201
 
202
+ # Current paths that need explaining: whole attributes that appeared, and members
203
+ # that appeared (or changed type) inside a kept attribute — the mirror of
204
+ # `vanished_paths`.
205
+ #
206
+ # @param held_attrs [Hash{String => Hash}] the held era's attributes, keyed by name
207
+ # @param current_attrs [Hash{String => Hash}] the current era's attributes, keyed by
208
+ # name
209
+ # @param retyped [Array<String>] type names already claimed by a `:retype` rule,
210
+ # excluded here
211
+ # @return [Hash{String => Object}] each appeared or changed path, dotted for a
212
+ # member, valued by that path's type signature
149
213
  def appeared_paths(held_attrs, current_attrs, retyped)
150
214
  paths = {}
151
215
  current_attrs.each do |name, current|
@@ -173,24 +237,51 @@ module Hecks
173
237
  # `check_identity_unchanged!` is the real gate this only hints
174
238
  # toward, the same "tool proactively guides you" pattern the
175
239
  # generic unresolved message already gives unfed fields.
240
+ #
241
+ # @param held_shape [Hash{String => Object}] the held era's aggregate shape
242
+ # @param current_shape [Hash{String => Object}] the current era's aggregate shape
243
+ # @return [Hash{Symbol => Object}, nil] `{kind: :unresolved, from: :identity,
244
+ # candidates: []}` when the identity paths differ; nil when they match
176
245
  def identity_hint(held_shape, current_shape)
177
246
  return if held_shape["identity"] == current_shape["identity"]
178
247
 
179
248
  { kind: :unresolved, from: :identity, candidates: [] }
180
249
  end
181
250
 
251
+ # Finds appeared paths type-compatible with a vanished path's own signature.
252
+ #
253
+ # @param appeared [Hash{String => Object}] appeared paths to type signature (see
254
+ # `appeared_paths`)
255
+ # @param signature [Object] the vanished path's own type signature to match against
256
+ # @return [Array<String>] appeared paths whose scalar signature (see `scalar_of`)
257
+ # matches `signature`'s
182
258
  def compatible_candidates(appeared, signature)
183
259
  appeared.select { |_, candidate| scalar_of(candidate) == scalar_of(signature) }.keys
184
260
  end
185
261
 
262
+ # Reduces a type signature to the shape `compatible_candidates` compares by.
263
+ #
264
+ # @param signature [Object, Hash] a path's type signature
265
+ # @return [Object] `signature["members"]` for a container Hash, else `signature`
266
+ # unchanged
186
267
  def scalar_of(signature) = signature.is_a?(Hash) ? signature["members"] : signature
187
268
 
269
+ # Reads a container attribute's own members, keyed by name.
270
+ #
271
+ # @param attribute [Hash{String => Object}] one path's type signature
272
+ # @return [Hash{String => Object}] each member's name to its type; `{}` when
273
+ # `attribute` is not a container
188
274
  def members_of(attribute)
189
275
  return {} unless container?(attribute)
190
276
 
191
277
  attribute["type"]["members"].to_h { |member| [member["name"], member["type"]] }
192
278
  end
193
279
 
280
+ # Reports whether a type signature is a container (value object or entity), not a
281
+ # scalar.
282
+ #
283
+ # @param attribute [Hash{String => Object}] one path's type signature
284
+ # @return [Boolean] true if `attribute["type"]` is a Hash, false for a scalar
194
285
  def container?(attribute) = attribute["type"].is_a?(Hash)
195
286
  end
196
287
  end
@@ -5,6 +5,10 @@ module Hecks
5
5
  # ambiguities as parse-refusing `unresolved` constructs, never
6
6
  # comments, so an unresolved file can only boot into a refusal.
7
7
  module Renderer
8
+ # Renders a scaffolded edge as loadable `.bluebook` translation source.
9
+ #
10
+ # @param edge [Scaffold::Edge] the edge to render
11
+ # @return [String] the edge's `.bluebook` text, ending in a newline
8
12
  def render(edge)
9
13
  lines = ["Hecks.data_translation #{edge.domain.inspect}, from: #{edge.from.inspect}, to: #{edge.to.inspect} do"]
10
14
  edge.aggregates.each do |aggregate|
@@ -19,6 +23,12 @@ module Hecks
19
23
  "#{lines.join("\n")}\n"
20
24
  end
21
25
 
26
+ # Renders one scaffolded rule as a line of `.bluebook` source.
27
+ #
28
+ # @param rule [Hash{Symbol => Object}] a rule Hash as `Differ#attribute_rules` builds
29
+ # it: `:kind` plus `:from`/`:to`, or `:from`/`:candidates` for `:unresolved`
30
+ # @return [String, nil] the rendered line; nil if `rule[:kind]` is none of `:rename`,
31
+ # `:move`, `:retype` or `:unresolved`
22
32
  def render_rule(rule)
23
33
  case rule[:kind]
24
34
  when :rename then "rename :#{rule[:from]}, to: :#{rule[:to]}"
@@ -30,6 +40,11 @@ module Hecks
30
40
  end
31
41
  end
32
42
 
43
+ # Renders a path as bluebook source: a Symbol literal when bare, a String literal
44
+ # when dotted.
45
+ #
46
+ # @param path [String, Symbol] a bare or dotted path
47
+ # @return [String] `":name"` for a bare path, `path.inspect` for a dotted one
33
48
  def render_path(path) = path.to_s.include?(".") ? path.to_s.inspect : ":#{path}"
34
49
  end
35
50
  end
@@ -6,10 +6,18 @@ module Hecks
6
6
  # Put the rendered edge on disk, regenerating in place when a file
7
7
  # for the same shape pair already exists.
8
8
  module Writer
9
+ # Renders an edge and writes it under `directory/translations`, regenerating in
10
+ # place when a file for the same shape pair already exists.
11
+ #
9
12
  # The edge file, regenerated in place when one for the same shape
10
13
  # pair already exists (matched textually — an unresolved file
11
14
  # cannot be loaded to ask, that being the whole point of
12
15
  # unresolved).
16
+ #
17
+ # @param directory [String] the domain's root directory
18
+ # @param edge [Scaffold::Edge] the edge to render and write
19
+ # @return [String] the path written, either the matched existing file or a new
20
+ # `<ordinal>-<label>.bluebook`
13
21
  def write!(directory, edge)
14
22
  translations_dir = File.join(directory, "translations")
15
23
  FileUtils.mkdir_p(translations_dir)
@@ -18,6 +18,19 @@ module Hecks
18
18
 
19
19
  module_function
20
20
 
21
+ # Filters, orders and pages `records` against a declared query specification.
22
+ #
23
+ # @param records [Array<Runtime::Instance, Hash>] the candidate records to filter —
24
+ # an Instance, a Value, or a plain row hash per record (see `FieldPath.dig`)
25
+ # @param declared [QuerySpecification::Common::Options,
26
+ # Bluebook::Behaviour::ReadModel::FilteredOptions] the specification providing
27
+ # `wheres`, `order_by`, `offset` and `limit`
28
+ # @param args [Hash{Symbol => Object}] bound values for any Symbol placeholder in a
29
+ # where clause, `offset`, or `limit`
30
+ # @param registry [Runtime::Registry, nil] the booted registry, passed through to a
31
+ # registry-aware comparison; nil when none is available
32
+ # @return [Array<Runtime::Instance, Hash>] the matching records, ordered and paged;
33
+ # the same element type as `records`
21
34
  def execute(records, declared, args = {}, registry: nil)
22
35
  matched = records.select do |record|
23
36
  declared.wheres.all? do |clause|
@@ -47,18 +60,38 @@ module Hecks
47
60
  end
48
61
 
49
62
  # The comparator table itself lives in
50
- # QuerySpecification::Common::Comparison — this module and
51
- # Runtime::QueryInterpreter used to carry a copy each, and the two
52
- # drifted (see that file's own comment for what it cost). What
53
- # stays here is how a value is reached for this path: a registry
54
- # arrives as an argument rather than as instance state, and the
55
- # field is dug through FieldPath before it arrives.
63
+ # QuerySpecification::Common::Comparison, not duplicated per
64
+ # caller — this module and Runtime::QueryInterpreter drifted
65
+ # when each carried its own copy (see that file's own comment
66
+ # for what it cost). What stays here is how a value is reached
67
+ # for this path: a registry arrives as an argument rather than
68
+ # as instance state, and the field is dug through FieldPath
69
+ # before it arrives.
70
+ # @param clause [QuerySpecification::Common::WhereClause] the declared comparison
71
+ # @param held [Object, nil] the record's own value for `clause.field`
72
+ # @param args [Hash{Symbol => Object}] bound values for a Symbol-named `clause.value`
73
+ # @param registry [Runtime::Registry, nil] used only by `none_in_state`; see
74
+ # `Comparison.holds?`
75
+ # @return [Boolean] whether the comparison holds
76
+ # @raise [Runtime::WiringError] if `clause.op` names no comparator in
77
+ # `Comparison`'s table, or `none_in_state`'s target aggregate has no wired repository
56
78
  def holds?(clause, held, args, registry: nil)
57
79
  Comparison.holds?(clause.op, held, comparable(resolve(clause.value, args)), registry: registry)
58
80
  end
59
81
 
82
+ # Resolves a where-clause value, looking up a Symbol placeholder in `args`.
83
+ #
84
+ # @param value [Object] a literal value, or a Symbol naming a key in `args`
85
+ # @param args [Hash{Symbol => Object}] bound argument values
86
+ # @return [Object] `args[value]` when `value` is a Symbol, else `value` unchanged
60
87
  def resolve(value, args) = value.is_a?(Symbol) ? args[value] : value
61
88
 
89
+ # Normalises a value into the shape `Comparison` compares against.
90
+ #
91
+ # @param value [Runtime::Value, Hash, Object, nil] a held or wanted value; a
92
+ # `Runtime::Value` is read through its `to_h`
93
+ # @return [Object, nil] the sole numeric member, or the sole member, of a Hash-shaped
94
+ # value; otherwise `value` unchanged
62
95
  def comparable(value) = Comparison.comparable(value)
63
96
  end
64
97
  end
@@ -24,6 +24,21 @@ module Hecks
24
24
  module Ordering
25
25
  module_function
26
26
 
27
+ # Orders an Array of rows by the declared order, with identity as the total-order
28
+ # tiebreaker.
29
+ #
30
+ # @param rows [Array<Object>] the rows to order; any shape `identity` and the block
31
+ # can read (a `Runtime::Instance`, a plain Hash row, …)
32
+ # @param order_by [QuerySpecification::Common::OrderBy, nil] the declared order;
33
+ # nil orders by identity alone
34
+ # @param null_semantics [QuerySpecification::Common::NullSemantics, nil] where a
35
+ # nil-valued row sorts, passed to `NullPolicy.order`; nil uses its native default
36
+ # @param identity [Proc] yields a row, returns its comparable identity value, used
37
+ # as the stable tiebreaker
38
+ # @yieldparam row [Object] one row being ordered
39
+ # @yieldreturn [Object, nil] the row's comparable value for `order_by`'s field
40
+ # @return [Array<Object>] `rows`, ordered by `order_by` then identity (identity
41
+ # alone when `order_by` is nil)
27
42
  def apply(rows, order_by, null_semantics = nil, identity:, &value_of)
28
43
  # Stable, because sort_by is not : two rows whose identity ties would
29
44
  # otherwise swap arbitrarily, and a tier meant to remove store-dependence
data/lib/hecks/ports.rb CHANGED
@@ -20,6 +20,7 @@ require_relative "ports/query"
20
20
  require_relative "ports/projection"
21
21
  require_relative "ports/extraction"
22
22
  require_relative "ports/identity_generation"
23
+ require_relative "ports/key_vault"
23
24
  require_relative "ports/authorization"
24
25
  require_relative "ports/identity_resolution"
25
26
  require_relative "ports/authentication"
@@ -9,14 +9,16 @@ module Hecks
9
9
  #
10
10
  # While `MetaValidator.bootstrapping?` the grammar table does not exist
11
11
  # yet, so `WordGate#method_missing` and `RuleReference#lookup` cannot
12
- # read `calls:`/`resolves_via:`/`disambiguator:` off it. They used to
13
- # read two hand-kept Hashes instead, each "kept in sync by hand" with
14
- # those columns. Kept in sync by hand meant a subset: 48 of the 87 live
15
- # `calls:` rows, chosen word by word by grepping which ones the core
16
- # chapters happened to use during bootstrap. A partition, not a filter:
17
- # every live row now lands in the table, and the ones no bootstrap
18
- # chapter calls cost nothing — a builder with its own `def` never
19
- # reaches `method_missing` at all.
12
+ # read `calls:`/`resolves_via:`/`disambiguator:` off it — this table is
13
+ # what they read instead.
14
+ #
15
+ # A partition, not a filter: every live row lands in the table, not only
16
+ # the ones a bootstrap chapter happens to call. Picking a subset by
17
+ # grepping which words the core chapters use during bootstrap would risk
18
+ # quietly dropping any word a chapter starts using later. Including
19
+ # every row instead costs nothing: a builder with its own `def` never
20
+ # reaches `method_missing` at all, so a row no bootstrap chapter calls
21
+ # is simply never read.
20
22
  #
21
23
  # The table cannot be built at boot for the same reason it exists: it
22
24
  # is read before the grammar it comes from has been assembled. So it is
@@ -43,11 +45,24 @@ module Hecks
43
45
 
44
46
  module_function
45
47
 
48
+ # Projects the bootstrap fallback table — the only real work this projector
49
+ # does, `bluebook` unused because the table is the whole language's grammar,
50
+ # not any one chapter's.
51
+ #
52
+ # @param bluebook [Bluebook::Chapter] ignored; present to satisfy the
53
+ # `Projector::Target` calling convention
54
+ # @param options [Hash{Symbol => Object}] ignored; present to satisfy the
55
+ # `Projector::Target` calling convention
56
+ # @return [String] the rendered `lib/hecks/bluebook/dsl/bootstrap_table.rb` source
46
57
  def call(bluebook:, options: {}) = render(bluebook)
47
58
 
48
59
  # Retired rows are out of the language; admitted and deprecated rows
49
60
  # still dispatch — the same `status != "retired"` reading
50
61
  # `GenericDispatch.shape_for` gives the live table.
62
+ #
63
+ # @return [Array<Hash{Symbol => String}>] every non-retired keyword row, each with
64
+ # at least `:context`, `:word`, `:status`, `:calls`, `:resolves_via` and
65
+ # `:disambiguator`, values stringified
51
66
  def live_keywords
52
67
  Bluebook::MetaValidator::SyntaxBoot.call[:keywords].reject { |row| row[:status] == "retired" }
53
68
  end
@@ -57,6 +72,13 @@ module Hecks
57
72
  # An overloaded word has one row per argument shape, and they all
58
73
  # name the same method. Two that don't would make the table keep
59
74
  # whichever came last, so that is refused instead.
75
+ #
76
+ # @param rows [Array<Hash{Symbol => String}>] the keyword rows to build from,
77
+ # defaulting to every live one
78
+ # @return [Hash{Array(String, String) => Symbol}] each `calls:`-declaring row's
79
+ # `[context, word]` mapped to the method name it dispatches to
80
+ # @raise [Projections::BootstrapTable::Conflict] if two rows sharing a
81
+ # `[context, word]` name different methods
60
82
  def calls(rows = live_keywords)
61
83
  rows.reject { |row| row[:calls].to_s.empty? }
62
84
  .group_by { |row| [row[:context], row[:word]] }
@@ -71,6 +93,12 @@ module Hecks
71
93
  # `[word, context] => { resolves_via:, disambiguator: }` —
72
94
  # RuleReference's own key order, blank columns omitted, the same
73
95
  # shape its live `lookup` answers.
96
+ #
97
+ # @param rows [Array<Hash{Symbol => String}>] the keyword rows to build from,
98
+ # defaulting to every live one
99
+ # @return [Hash{Array(String, String) => Hash{Symbol => String}}] each
100
+ # `resolves_via:`-declaring row's `[word, context]` mapped to its rule Hash,
101
+ # with any blank `resolves_via`/`disambiguator` column omitted
74
102
  def resolves(rows = live_keywords)
75
103
  rows.reject { |row| row[:resolves_via].to_s.empty? }.to_h do |row|
76
104
  rule = { resolves_via: row[:resolves_via], disambiguator: row[:disambiguator] }
@@ -78,6 +106,13 @@ module Hecks
78
106
  end
79
107
  end
80
108
 
109
+ # Renders the bootstrap table as committable Ruby source — `CALLS` and
110
+ # `RESOLVES` frozen Hash literals under the generated-file header.
111
+ #
112
+ # @param _bluebook [Bluebook::Chapter] ignored; the table draws from the whole
113
+ # language's grammar, not this argument
114
+ # @return [String] the full `lib/hecks/bluebook/dsl/bootstrap_table.rb` source,
115
+ # ready to write to disk
81
116
  def render(_bluebook)
82
117
  rows = live_keywords
83
118
  calls_lines = calls(rows).map { |key, target| " #{key.inspect} => #{target.inspect}" }