hecks 1.0.2 → 1.1.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 (239) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/claude_code.rb +3 -3
  3. data/lib/hecks/adapters/driven/d1.rb +48 -28
  4. data/lib/hecks/adapters/driven/folder.rb +10 -3
  5. data/lib/hecks/adapters/driven/google_authentication.rb +2 -4
  6. data/lib/hecks/adapters/driven/heki/journal.rb +1 -1
  7. data/lib/hecks/adapters/driven/heki.rb +3 -5
  8. data/lib/hecks/adapters/driven/in_memory_ordering.rb +4 -1
  9. data/lib/hecks/adapters/driven/lambda.rb +2 -2
  10. data/lib/hecks/adapters/driven/local_storage.adapter +3 -0
  11. data/lib/hecks/adapters/driven/local_storage.rb +120 -0
  12. data/lib/hecks/adapters/driven/memory.rb +48 -3
  13. data/lib/hecks/adapters/driven/postgres/codec.rb +3 -1
  14. data/lib/hecks/adapters/driven/postgres/outbox.rb +97 -0
  15. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +3 -2
  16. data/lib/hecks/adapters/driven/postgres.rb +25 -14
  17. data/lib/hecks/adapters/driven/prism.rb +11 -0
  18. data/lib/hecks/adapters/driven/sql_query_builder.rb +45 -20
  19. data/lib/hecks/adapters/driven/sqlite/codec.rb +3 -1
  20. data/lib/hecks/adapters/driven/sqlite/projection.rb +17 -1
  21. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +23 -1
  22. data/lib/hecks/adapters/driven/sqlite.rb +87 -8
  23. data/lib/hecks/adapters/driven.rb +4 -0
  24. data/lib/hecks/adapters.rb +4 -0
  25. data/lib/hecks/behaviors/dsl.rb +7 -0
  26. data/lib/hecks/behaviors/expectations.rb +32 -4
  27. data/lib/hecks/behaviors/rspec.rb +2 -0
  28. data/lib/hecks/behaviors/runner.rb +4 -0
  29. data/lib/hecks/bluebook/aggregate.rb +6 -5
  30. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +4 -4
  31. data/lib/hecks/bluebook/assembly/build.rb +6 -3
  32. data/lib/hecks/bluebook/assembly/contract.rb +5 -1
  33. data/lib/hecks/bluebook/assembly/contracts.rb +4 -0
  34. data/lib/hecks/bluebook/assembly/marks.rb +2 -1
  35. data/lib/hecks/bluebook/assembly.rb +1 -3
  36. data/lib/hecks/bluebook/attribute.rb +6 -0
  37. data/lib/hecks/bluebook/behaviour/policy.rb +18 -0
  38. data/lib/hecks/bluebook/behaviour/process_manager.rb +17 -1
  39. data/lib/hecks/bluebook/behaviour/traits.rb +1 -1
  40. data/lib/hecks/bluebook/command.rb +6 -5
  41. data/lib/hecks/bluebook/dsl/adapter_builder.rb +4 -1
  42. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +524 -0
  43. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +19 -472
  44. data/lib/hecks/bluebook/dsl/attribute_collector.rb +14 -6
  45. data/lib/hecks/bluebook/dsl/binding_proxy.rb +5 -0
  46. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +848 -0
  47. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +25 -784
  48. data/lib/hecks/bluebook/dsl/command_builder.rb +53 -13
  49. data/lib/hecks/bluebook/dsl/const_shim.rb +6 -0
  50. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +20 -7
  51. data/lib/hecks/bluebook/dsl/entity_builder.rb +27 -6
  52. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +12 -2
  53. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +8 -1
  54. data/lib/hecks/bluebook/dsl/identity_declaration.rb +24 -12
  55. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +40 -1
  56. data/lib/hecks/bluebook/dsl/policy_builder.rb +10 -1
  57. data/lib/hecks/bluebook/dsl/port_builder.rb +6 -1
  58. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +33 -12
  59. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +56 -12
  60. data/lib/hecks/bluebook/dsl/query_builder.rb +7 -1
  61. data/lib/hecks/bluebook/dsl/read_model_builder.rb +11 -3
  62. data/lib/hecks/bluebook/dsl/rule_reference.rb +3 -1
  63. data/lib/hecks/bluebook/dsl/translation_builder.rb +13 -2
  64. data/lib/hecks/bluebook/dsl/value_object_builder.rb +8 -1
  65. data/lib/hecks/bluebook/dsl/word_gate.rb +5 -0
  66. data/lib/hecks/bluebook/dsl/world_builder.rb +13 -3
  67. data/lib/hecks/bluebook/entity.rb +5 -3
  68. data/lib/hecks/bluebook/expression/ast_json.rb +88 -9
  69. data/lib/hecks/bluebook/expression/ast_reader.rb +107 -0
  70. data/lib/hecks/bluebook/expression/canonical_form.rb +7 -0
  71. data/lib/hecks/bluebook/expression/evaluator.rb +64 -11
  72. data/lib/hecks/bluebook/expression/projection.json +12 -0
  73. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +4 -3
  74. data/lib/hecks/bluebook/expression/resolver.rb +83 -11
  75. data/lib/hecks/bluebook/expression.rb +1 -0
  76. data/lib/hecks/bluebook/hexagon.rb +10 -0
  77. data/lib/hecks/bluebook/lifecycle.rb +9 -0
  78. data/lib/hecks/bluebook/meta_validator/judge.rb +10 -4
  79. data/lib/hecks/bluebook/meta_validator/plan.rb +4 -0
  80. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +1 -1
  81. data/lib/hecks/bluebook/meta_validator/shapes.rb +3 -3
  82. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +7 -4
  83. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +42 -3
  84. data/lib/hecks/bluebook/meta_validator/world_judge.rb +1 -1
  85. data/lib/hecks/bluebook/meta_validator.rb +5 -5
  86. data/lib/hecks/bluebook/model_check.rb +119 -82
  87. data/lib/hecks/bluebook/pattern_subset.rb +9 -0
  88. data/lib/hecks/bluebook/policy.rb +2 -1
  89. data/lib/hecks/bluebook/process_manager.rb +6 -0
  90. data/lib/hecks/bluebook/project_register.rb +45 -25
  91. data/lib/hecks/bluebook/query.rb +4 -0
  92. data/lib/hecks/bluebook/read_model.rb +2 -2
  93. data/lib/hecks/bluebook/smoke_test.rb +5 -0
  94. data/lib/hecks/bluebook/synthesizer.rb +2 -1
  95. data/lib/hecks/bluebook/translation.rb +3 -0
  96. data/lib/hecks/bluebook/value_object.rb +2 -2
  97. data/lib/hecks/codemod.rb +13 -0
  98. data/lib/hecks/doc/reference.rb +17 -11
  99. data/lib/hecks/facade/cli_runner.rb +2 -2
  100. data/lib/hecks/facade/command_request.rb +11 -11
  101. data/lib/hecks/facade/handle.rb +3 -3
  102. data/lib/hecks/facade/surface/aggregate_door.rb +52 -38
  103. data/lib/hecks/facade.rb +4 -0
  104. data/lib/hecks/forms/app.rb +10 -6
  105. data/lib/hecks/forms/command_form_renderer.rb +3 -3
  106. data/lib/hecks/forms/field_renderer.rb +14 -9
  107. data/lib/hecks/forms/field_shape.rb +7 -2
  108. data/lib/hecks/forms/html.rb +5 -2
  109. data/lib/hecks/forms/index_renderer.rb +1 -1
  110. data/lib/hecks/forms/page.rb +7 -3
  111. data/lib/hecks/forms/query_form_renderer.rb +3 -2
  112. data/lib/hecks/forms/record_renderer.rb +22 -16
  113. data/lib/hecks/forms.rb +4 -0
  114. data/lib/hecks/fqn.rb +21 -9
  115. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +30 -8
  116. data/lib/hecks/fuzzing/invalid_value_generator.rb +0 -1
  117. data/lib/hecks/fuzzing/isolated_boot.rb +14 -6
  118. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +264 -0
  119. data/lib/hecks/fuzzing/properties/guards.rb +251 -0
  120. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +351 -0
  121. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +99 -0
  122. data/lib/hecks/fuzzing/properties/querying.rb +206 -0
  123. data/lib/hecks/fuzzing/properties.rb +41 -1065
  124. data/lib/hecks/fuzzing/replay.rb +60 -16
  125. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +6 -2
  126. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +2 -1
  127. data/lib/hecks/fuzzing/sequence_generator.rb +0 -1
  128. data/lib/hecks/fuzzing/value_generator.rb +2 -2
  129. data/lib/hecks/grammar/evolve.rb +34 -10
  130. data/lib/hecks/grammar/expression_operators.json +124 -0
  131. data/lib/hecks/grammar.rb +15 -5
  132. data/lib/hecks/ir.rb +9 -0
  133. data/lib/hecks/language/bluebook/aggregate.bluebook +4 -2
  134. data/lib/hecks/language/bluebook/command.bluebook +2 -1
  135. data/lib/hecks/language/bluebook/entity.bluebook +2 -1
  136. data/lib/hecks/language/bluebook/projection.bluebook +2 -1
  137. data/lib/hecks/language/bluebook/query.bluebook +4 -2
  138. data/lib/hecks/language/bluebook/shape.bluebook +2 -1
  139. data/lib/hecks/language/bluebook/vocabulary.bluebook +2 -0
  140. data/lib/hecks/literal.rb +18 -8
  141. data/lib/hecks/naming.rb +6 -3
  142. data/lib/hecks/ports/access_control.rb +1 -1
  143. data/lib/hecks/ports/agent.rb +5 -3
  144. data/lib/hecks/ports/authentication.rb +3 -2
  145. data/lib/hecks/ports/authorization.rb +1 -1
  146. data/lib/hecks/ports/clock.rb +1 -1
  147. data/lib/hecks/ports/extraction.rb +5 -1
  148. data/lib/hecks/ports/identity_assignment.rb +1 -1
  149. data/lib/hecks/ports/identity_generation.rb +1 -1
  150. data/lib/hecks/ports/identity_resolution.rb +1 -1
  151. data/lib/hecks/ports/loading.rb +4 -1
  152. data/lib/hecks/ports/persistence/append_only.rb +40 -4
  153. data/lib/hecks/ports/persistence/binding_policy.rb +1 -1
  154. data/lib/hecks/ports/persistence/plugin.rb +3 -0
  155. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +3 -1
  156. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +5 -5
  157. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +14 -5
  158. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +4 -0
  159. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +1 -1
  160. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +80 -16
  161. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +23 -6
  162. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +43 -5
  163. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +25 -4
  164. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +49 -9
  165. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +2 -2
  166. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +10 -0
  167. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +49 -38
  168. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +23 -13
  169. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +19 -21
  170. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +27 -9
  171. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +9 -0
  172. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +25 -10
  173. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +2 -6
  174. data/lib/hecks/ports/persistence/plugins/era.rb +3 -1
  175. data/lib/hecks/ports/persistence/remote_runtime.rb +1 -1
  176. data/lib/hecks/ports/persistence.rb +7 -3
  177. data/lib/hecks/ports/projection.rb +13 -5
  178. data/lib/hecks/ports/query/in_memory.rb +8 -2
  179. data/lib/hecks/ports/query.rb +1 -3
  180. data/lib/hecks/ports.rb +2 -0
  181. data/lib/hecks/projections/diagrams.rb +11 -3
  182. data/lib/hecks/projections/ir.rb +2 -0
  183. data/lib/hecks/projections/model/deviations.rb +2 -1
  184. data/lib/hecks/projections/model.rb +15 -10
  185. data/lib/hecks/projections/vocabulary.rb +1 -3
  186. data/lib/hecks/projector/cli_projector.rb +49 -29
  187. data/lib/hecks/projector/docs_projector.rb +21 -12
  188. data/lib/hecks/projector/exporter.rb +10 -1
  189. data/lib/hecks/projector/narrate_projector.rb +83 -25
  190. data/lib/hecks/projector.rb +2 -2
  191. data/lib/hecks/query_ir.rb +60 -36
  192. data/lib/hecks/query_specification/common/authorization_spec.rb +1 -1
  193. data/lib/hecks/query_specification/common/comparators.rb +4 -0
  194. data/lib/hecks/query_specification/common/comparison.rb +8 -1
  195. data/lib/hecks/query_specification/common/dsl.rb +5 -0
  196. data/lib/hecks/query_specification/common/null_policy.rb +6 -2
  197. data/lib/hecks/query_specification/common/options.rb +17 -5
  198. data/lib/hecks/query_specification/field_path.rb +4 -4
  199. data/lib/hecks/query_specification/read_model/specification.rb +5 -2
  200. data/lib/hecks/router/namespace_installer.rb +22 -10
  201. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +57 -22
  202. data/lib/hecks/runtime/command_interpreter.rb +104 -65
  203. data/lib/hecks/runtime/command_rules/admissibility.rb +26 -8
  204. data/lib/hecks/runtime/command_rules/arithmetic.rb +35 -14
  205. data/lib/hecks/runtime/dependency_planning.rb +33 -21
  206. data/lib/hecks/runtime/dispatcher.rb +41 -7
  207. data/lib/hecks/runtime/entity_element.rb +25 -6
  208. data/lib/hecks/runtime/entity_interpreter.rb +28 -15
  209. data/lib/hecks/runtime/errors.rb +28 -2
  210. data/lib/hecks/runtime/instance.rb +9 -3
  211. data/lib/hecks/runtime/interpreting.rb +29 -2
  212. data/lib/hecks/runtime/loader.rb +21 -0
  213. data/lib/hecks/runtime/outbox.rb +365 -0
  214. data/lib/hecks/runtime/policy_interpreter.rb +32 -17
  215. data/lib/hecks/runtime/query_interpreter.rb +36 -16
  216. data/lib/hecks/runtime/reaction_invocation.rb +35 -22
  217. data/lib/hecks/runtime/read_model_interpreter.rb +26 -3
  218. data/lib/hecks/runtime/reference_hop.rb +1 -1
  219. data/lib/hecks/runtime/refusal_wording.rb +55 -49
  220. data/lib/hecks/runtime/registry/saga_persistence.rb +3 -1
  221. data/lib/hecks/runtime/registry/verification.rb +39 -2
  222. data/lib/hecks/runtime/registry.rb +21 -11
  223. data/lib/hecks/runtime/remote_dispatcher.rb +3 -1
  224. data/lib/hecks/runtime/routing.rb +52 -39
  225. data/lib/hecks/runtime/saga_interpreter/correlation.rb +7 -7
  226. data/lib/hecks/runtime/saga_interpreter.rb +119 -90
  227. data/lib/hecks/runtime/saga_pending_dispatch.rb +6 -3
  228. data/lib/hecks/runtime/tenant_scope.rb +7 -0
  229. data/lib/hecks/runtime/value/coercion.rb +207 -48
  230. data/lib/hecks/runtime/value.rb +1 -1
  231. data/lib/hecks/runtime.rb +7 -2
  232. data/lib/hecks/storehouse.rb +15 -8
  233. data/lib/hecks/version.rb +1 -1
  234. data/lib/hecks/vocabulary.rb +1 -0
  235. data/lib/hecks.rb +17 -13
  236. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +1 -1
  237. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +1 -1
  238. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +1 -1
  239. metadata +14 -2
@@ -1,4 +1,5 @@
1
1
  require_relative "behaviour/aggregate"
2
+ require_relative "expression/ast_json"
2
3
 
3
4
  module Hecks
4
5
  module Bluebook
@@ -40,14 +41,14 @@ module Hecks
40
41
  attributes: many(:attributes),
41
42
  value_objects: many(:value_objects),
42
43
  commands: many(:commands),
43
- invariants: -> { invariants.map { |rule| { description: rule.description, canonical: rule.canonical } } },
44
+ invariants: -> { invariants.map { |rule| Expression::AstJson.rule_row(rule) } },
44
45
  # A PRECONDITION SHARED ACROSS COMMANDS, DECLARED ONCE (S10, ADR
45
46
  # 0025) — the aggregate's OWN named `given`s, the declaration a
46
47
  # referencing command's own (already-resolved) `givens` entry
47
48
  # came from. Both sides of "declared once, referenced many"
48
49
  # are real IR, the same shape a value object's TYPE and an
49
50
  # attribute's own reference to it both are.
50
- preconditions: -> { preconditions.map { |rule| { description: rule.description, canonical: rule.canonical } } },
51
+ preconditions: -> { preconditions.map { |rule| Expression::AstJson.rule_row(rule) } },
51
52
  # S12, ADR 0025 — deliberately NOT folded into `attributes`:
52
53
  # `EraGuard::ShapeDiff` only ever walks `attributes` to decide
53
54
  # whether a NEW field leaves an existing record with something
@@ -55,10 +56,10 @@ module Hecks
55
56
  # is different — a record predating the `projects` declaration
56
57
  # is expected to be missing it until the rebuild sweep runs,
57
58
  # not a shape drift a translation needs to explain.
58
- projected_fields: -> {
59
- projected_fields.map { |field|
59
+ projected_fields: lambda {
60
+ projected_fields.map do |field|
60
61
  { name: field.name.to_s, reference: field.reference.to_s, remote_field: field.remote_field.to_s }
61
- }
62
+ end
62
63
  },
63
64
  lifecycle: one(:lifecycle),
64
65
  entities: many(:entities),
@@ -77,11 +77,11 @@ module Hecks
77
77
  # and resolve. Deliberately across every list that can carry one — a
78
78
  # reference the walk misses resolves to nil, and a nil target is SKIPPED
79
79
  # rather than refused, so the guarantee would go quiet instead of red.
80
- def stamp_references(ir)
81
- lists = [ir.attributes, *ir.commands.map(&:attributes), *ir.queries.map(&:attributes)]
82
- entity_reference_lists(ir.entities, lists)
80
+ def stamp_references(aggregate)
81
+ lists = [aggregate.attributes, *aggregate.commands.map(&:attributes), *aggregate.queries.map(&:attributes)]
82
+ entity_reference_lists(aggregate.entities, lists)
83
83
 
84
- lists.flatten.select(&:reference?).each { |field| field.type.declared_in = ir }
84
+ lists.flatten.select(&:reference?).each { |field| field.type.declared_in = aggregate }
85
85
  end
86
86
 
87
87
  # S17, ADR 0026 — walks NESTED entities too (Dispatch, inside
@@ -20,7 +20,7 @@ module Hecks
20
20
  contract = Assembly.contract(category)
21
21
  keywords = contract.fields.to_h { |keyword, (key, reader)| [keyword, read(reader, row[key])] }
22
22
 
23
- holder(contract).public_send(contract.make, **keywords.merge(extra))
23
+ holder(contract).public_send(contract.make, **keywords, **extra)
24
24
  end
25
25
 
26
26
  def holder(contract)
@@ -37,8 +37,11 @@ module Hecks
37
37
  when Array
38
38
  # [:each, reader] maps a list ; [:option, name] reads one named option,
39
39
  # which needs its name as well as its value.
40
- reader.first == :each ? Array(value).map { |held| Marks.public_send(reader.last, held) }
41
- : Marks.option(reader.last, value)
40
+ if reader.first == :each
41
+ Array(value).map { |held| Marks.public_send(reader.last, held) }
42
+ else
43
+ Marks.option(reader.last, value)
44
+ end
42
45
  else Marks.public_send(reader, value)
43
46
  end
44
47
  end
@@ -1,5 +1,9 @@
1
1
  module Hecks
2
2
  module Bluebook
3
+ # The rulebook translating between the language's own declarations and the
4
+ # IR objects `Build`/`Reconstruction` produce from them — the `Contract`
5
+ # struct format itself (this file) plus, reopened in contracts.rb, the
6
+ # `CONTRACTS` table of one `Contract` per construct category.
3
7
  class Assembly
4
8
  # WHAT A CONSTRUCT NEEDS THAT THE LANGUAGE CANNOT SAY, and how a claim that a
5
9
  # field needs no assembling is CHECKED.
@@ -78,7 +82,7 @@ module Hecks
78
82
  end
79
83
 
80
84
  def answers?(method)
81
- make == :declare ? holder.respond_to?(method) : holder.instance_methods.include?(method)
85
+ make == :declare ? holder.respond_to?(method) : holder.method_defined?(method)
82
86
  end
83
87
 
84
88
  def accepts?(keyword)
@@ -1,5 +1,9 @@
1
1
  module Hecks
2
2
  module Bluebook
3
+ # Reopened here for `CONTRACTS`/`.contract(category)` — the one table of
4
+ # every construct category's own `Contract` (Bluebook, Aggregate,
5
+ # Command, ValueObject, Query, ...); see `Contract`'s own header
6
+ # (contract.rb) for the struct format each entry below fills in.
3
7
  class Assembly
4
8
  # ONE TABLE, WHERE THERE WERE FIVE HAND-WRITTEN MIRRORS OF IT.
5
9
  #
@@ -129,7 +129,8 @@ module Hecks
129
129
  # `:delegate`/`:corrects` (CommandBuilder#delegates_to's and
130
130
  # #corrects_impl's own comments) ride the SAME multi-binding
131
131
  # shape `:append` does.
132
- return Mutation.new(target: target, op: op, source: appended(change[:fields])) if [:append, :delegate, :corrects].include?(op)
132
+ return Mutation.new(target: target, op: op, source: appended(change[:fields])) if [:append, :delegate,
133
+ :corrects].include?(op)
133
134
 
134
135
  Mutation.new(target: target, op: op, source: classified(change[:source]))
135
136
  end
@@ -38,15 +38,13 @@ module Hecks
38
38
  aggregates = Array(@declaration[:aggregates]).map { |row| AggregateAssembly.new(row).aggregate }
39
39
  models = Array(@declaration[:read_models]).map { |row| Build.call("ReadModel", row) }
40
40
 
41
- chapter = Build.call(
41
+ Build.call(
42
42
  "Bluebook", @declaration,
43
43
  aggregates: aggregates,
44
44
  read_models: models,
45
45
  policies: reactions(aggregates),
46
46
  process_managers: Array(@declaration[:process_managers]).map { |row| process_manager(row) }
47
47
  )
48
-
49
- chapter
50
48
  end
51
49
 
52
50
  private
@@ -5,6 +5,12 @@ require_relative "../naming"
5
5
 
6
6
  module Hecks
7
7
  module Bluebook
8
+ # One declared field on a construct — name, type (a primitive, a
9
+ # Reference, or another construct's name, always spelled via `#spell`),
10
+ # whether it's a list, its default/optional/pattern, and the closed set
11
+ # it `admits`. What `attribute :x, Type` (or `identified_by`,
12
+ # `reference_to`, etc.) actually mints, on an Aggregate, Entity, Command,
13
+ # ValueObject, Query or PortOperation alike.
8
14
  class Attribute
9
15
  include Hecks::IR
10
16
  include Behaviour::Attribute
@@ -24,6 +24,24 @@ module Hecks
24
24
  # event's own payload.
25
25
  def guarded? = !@where.to_s.empty?
26
26
 
27
+ # THE STRUCTURED FORM OF `where`, derived once — the same tree
28
+ # `AstJson.emit_predicate` spells for every rule row, memoized
29
+ # here because a policy is consulted once per event, not once per
30
+ # boot. Nil when there is no `where`, exactly as the wire carries
31
+ # it.
32
+ def where_ast
33
+ if defined?(@where_ast)
34
+ @where_ast
35
+ else
36
+ (@where_ast = guarded? ? Expression::AstJson.emit_predicate(@where) : nil)
37
+ end
38
+ end
39
+
40
+ # The rule-shaped reading of the guard, for `Evaluator.call_rule`
41
+ # — a policy's `where` has no description (nothing refuses with
42
+ # it; an unmet where is a silent skip).
43
+ def where_rule = @where_rule ||= Given.new(description: nil, canonical: @where, ast: where_ast)
44
+
27
45
  # THE FAN-OUT QUERY'S ROUTE, split the way the runtime runs it:
28
46
  # `[query_domain, aggregate_name, query_name]`. The query runs
29
47
  # against the triggering event's OWN domain unless `for_each`
@@ -7,7 +7,23 @@ module Hecks
7
7
  module ProcessManager
8
8
  def hecks_name = @name
9
9
 
10
- def handler_for(event) = @handlers.find { |h| h.event_type == event.to_s }
10
+ # THE LEG THAT ANSWERS selected by (event, current state), C10.3
11
+ # (docs/semantics/bluebook-semantics.md). Two legs may answer the
12
+ # same event from different states; which one runs is decided by
13
+ # the state the instance is in, never by declaration order. With
14
+ # no `state` the reading is declarative — "does any leg answer this
15
+ # event, and what is it" — for callers that ask about the
16
+ # procedure rather than an instance (`saga`, the outbox's consumer
17
+ # selection). Build refuses two legs on one (event, state) pair
18
+ # (`ProcessManagerBuilder#validate!`), so a stated lookup is
19
+ # unambiguous by construction.
20
+ def handler_for(event, state = nil)
21
+ @handlers.find { |h| h.event_type == event.to_s && (state.nil? || h.from_state == state.to_s) }
22
+ end
23
+
24
+ def handlers_for(event) = @handlers.select { |h| h.event_type == event.to_s }
25
+
26
+ def handles?(event) = @handlers.any? { |h| h.event_type == event.to_s }
11
27
 
12
28
  # WHETHER A STATE IS ONE THIS PROCEDURE DECLARES — asked of a value
13
29
  # a real run left a saga instance holding (its live or rehydrated
@@ -29,7 +29,7 @@ module Hecks
29
29
  # readers that need all of them ask for `identity_heads`.
30
30
  module Identified
31
31
  def derive_identity
32
- @identity_paths = Array(@identified_by).map { |path| path.to_s }.reject(&:empty?)
32
+ @identity_paths = Array(@identified_by).map(&:to_s).reject(&:empty?)
33
33
  @identity_heads = @identity_paths.map { |path| path.split(".").first.to_sym }.uniq
34
34
  @identified_by = @identity_heads.size == 1 ? @identity_heads.first : nil
35
35
  end
@@ -1,9 +1,10 @@
1
1
  require_relative "behaviour/command"
2
2
  require_relative "../vocabulary"
3
+ require_relative "expression/ast_json"
3
4
 
4
5
  module Hecks
5
6
  module Bluebook
6
- Given = Struct.new(:description, :canonical, :predicate, keyword_init: true)
7
+ Given = Struct.new(:description, :canonical, :predicate, :ast, keyword_init: true)
7
8
 
8
9
  Mutation = Struct.new(:target, :op, :source, keyword_init: true) do
9
10
  include Hecks::IR
@@ -24,8 +25,8 @@ module Hecks
24
25
  # real layering inversion (runtime depends on bluebook, not the
25
26
  # reverse). "" (not nil) for ops with no sign, matching every other
26
27
  # optional IR text field's own absent-is-empty-string convention.
27
- def self.sign_for(op)
28
- Hecks::Vocabulary.rows("MutationOp").find { |row| row["name"] == op.to_s }&.fetch("sign", "") || ""
28
+ def self.sign_for(oper)
29
+ Hecks::Vocabulary.rows("MutationOp").find { |row| row["name"] == oper.to_s }&.fetch("sign", "") || ""
29
30
  end
30
31
 
31
32
  emits_ir(target: :target, op: :op, sign: -> { Mutation.sign_for(op) })
@@ -74,8 +75,8 @@ module Hecks
74
75
  goal: :goal,
75
76
  references: :references,
76
77
  attributes: many(:attributes),
77
- givens: -> { givens.map { |rule| { description: rule.description, canonical: rule.canonical } } },
78
- ensures: -> { ensures.map { |rule| { description: rule.description, canonical: rule.canonical } } },
78
+ givens: -> { givens.map { |rule| Expression::AstJson.rule_row(rule) } },
79
+ ensures: -> { ensures.map { |rule| Expression::AstJson.rule_row(rule) } },
79
80
  mutations: many(:mutations),
80
81
  emits: :emits,
81
82
  # THE LIFECYCLE STATE THIS COMMAND IS ADMISSIBLE FROM (S10, ADR
@@ -2,8 +2,11 @@ require_relative "word_gate"
2
2
  module Hecks
3
3
  module Bluebook
4
4
  module DSL
5
+ # The `Hecks.adapter "Name" do port ...; field ...; secret ... end`
6
+ # receiver — collects the port it implements plus its own settings
7
+ # fields and secrets, then builds and judges an `Adapter` construct.
5
8
  class AdapterBuilder
6
- GRAMMAR_CONTEXT = "Adapter"
9
+ GRAMMAR_CONTEXT = "Adapter".freeze
7
10
 
8
11
  include WordGate
9
12