hecks 0.3.0 → 1.0.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 (202) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/d1.rb +38 -14
  3. data/lib/hecks/adapters/driven/governance_authorization.rb +41 -2
  4. data/lib/hecks/adapters/driven/heki/journal.rb +7 -2
  5. data/lib/hecks/adapters/driven/heki/saga_store.rb +0 -0
  6. data/lib/hecks/adapters/driven/heki/snapshot.rb +31 -4
  7. data/lib/hecks/adapters/driven/heki.rb +40 -9
  8. data/lib/hecks/adapters/driven/lambda.rb +16 -2
  9. data/lib/hecks/adapters/driven/postgres/codec.rb +8 -0
  10. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +45 -6
  11. data/lib/hecks/adapters/driven/postgres.rb +76 -29
  12. data/lib/hecks/adapters/driven/postgres_era.adapter +11 -0
  13. data/lib/hecks/adapters/driven/sql_query_builder.rb +1 -1
  14. data/lib/hecks/adapters/driven/sqlite/codec.rb +17 -0
  15. data/lib/hecks/adapters/driven/sqlite/projection.rb +76 -9
  16. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +17 -5
  17. data/lib/hecks/adapters/driven/sqlite.rb +30 -9
  18. data/lib/hecks/adapters/driven.rb +19 -1
  19. data/lib/hecks/behaviors/dsl.rb +29 -0
  20. data/lib/hecks/behaviors/expectations.rb +62 -2
  21. data/lib/hecks/bluebook/assembly/contracts.rb +36 -7
  22. data/lib/hecks/bluebook/assembly/marks.rb +4 -3
  23. data/lib/hecks/bluebook/assembly.rb +14 -1
  24. data/lib/hecks/bluebook/behaviour/lifecycle.rb +18 -1
  25. data/lib/hecks/bluebook/behaviour/process_manager.rb +14 -1
  26. data/lib/hecks/bluebook/behaviour/value_object.rb +13 -7
  27. data/lib/hecks/bluebook/chapter.rb +21 -11
  28. data/lib/hecks/bluebook/command.rb +1 -1
  29. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +117 -5
  30. data/lib/hecks/bluebook/dsl/attribute_collector.rb +21 -0
  31. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +71 -2
  32. data/lib/hecks/bluebook/dsl/command_builder.rb +144 -3
  33. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +49 -7
  34. data/lib/hecks/bluebook/dsl/entity_builder.rb +129 -4
  35. data/lib/hecks/bluebook/dsl/policy_builder.rb +18 -3
  36. data/lib/hecks/bluebook/dsl/port_builder.rb +12 -3
  37. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +109 -10
  38. data/lib/hecks/bluebook/dsl/rule_reference.rb +1 -0
  39. data/lib/hecks/bluebook/dsl/word_gate.rb +9 -2
  40. data/lib/hecks/bluebook/dsl/world_builder.rb +44 -4
  41. data/lib/hecks/bluebook/expression/ast_json.rb +149 -0
  42. data/lib/hecks/bluebook/expression/canonical_form.rb +71 -3
  43. data/lib/hecks/bluebook/expression/evaluator.rb +50 -7
  44. data/lib/hecks/bluebook/expression/projection.json +48 -0
  45. data/lib/hecks/bluebook/expression/resolver.rb +161 -10
  46. data/lib/hecks/bluebook/expression.rb +1 -0
  47. data/lib/hecks/bluebook/hexagon.rb +1 -1
  48. data/lib/hecks/bluebook/meta_validator/judge.rb +78 -16
  49. data/lib/hecks/bluebook/meta_validator/port_judge.rb +4 -0
  50. data/lib/hecks/bluebook/meta_validator/readings.rb +14 -4
  51. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +42 -3
  52. data/lib/hecks/bluebook/meta_validator/shapes.rb +30 -10
  53. data/lib/hecks/bluebook/meta_validator.rb +103 -13
  54. data/lib/hecks/bluebook/model_check.rb +147 -6
  55. data/lib/hecks/bluebook/pattern_subset.rb +66 -2
  56. data/lib/hecks/bluebook/process_manager.rb +53 -11
  57. data/lib/hecks/bluebook/project_register.rb +25 -0
  58. data/lib/hecks/bluebook/value_object.rb +19 -2
  59. data/lib/hecks/doc/reference.rb +22 -1
  60. data/lib/hecks/facade/cli_door.rb +6 -3
  61. data/lib/hecks/facade/json_door.rb +16 -4
  62. data/lib/hecks/forms/app.rb +47 -6
  63. data/lib/hecks/forms/command_form_renderer.rb +1 -1
  64. data/lib/hecks/forms/field_renderer.rb +11 -4
  65. data/lib/hecks/forms/html.rb +31 -0
  66. data/lib/hecks/forms/params.rb +30 -1
  67. data/lib/hecks/forms/port_argument.rb +46 -0
  68. data/lib/hecks/forms/record_renderer.rb +6 -2
  69. data/lib/hecks/forms/record_table.rb +6 -1
  70. data/lib/hecks/framework/bluebook/console_settings.bluebook +19 -19
  71. data/lib/hecks/framework/bluebook/governance.bluebook +26 -11
  72. data/lib/hecks/framework/bluebook/identity.bluebook +2 -2
  73. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +527 -0
  74. data/lib/hecks/fuzzing/invalid_value_generator.rb +3 -2
  75. data/lib/hecks/fuzzing/isolated_boot.rb +212 -18
  76. data/lib/hecks/fuzzing/properties.rb +52 -6
  77. data/lib/hecks/fuzzing/replay.rb +51 -18
  78. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +28 -2
  79. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +71 -6
  80. data/lib/hecks/fuzzing/sequence_generator.rb +5 -4
  81. data/lib/hecks/fuzzing/value_generator.rb +25 -2
  82. data/lib/hecks/grammar/evolve.rb +33 -0
  83. data/lib/hecks/grammar/expression.bluebook +8 -5
  84. data/lib/hecks/grammar/expression_operators.json +497 -1
  85. data/lib/hecks/language/bluebook/aggregate.bluebook +12 -12
  86. data/lib/hecks/language/bluebook/bluebook.bluebook +3 -3
  87. data/lib/hecks/language/bluebook/command.bluebook +42 -8
  88. data/lib/hecks/language/bluebook/entity.bluebook +86 -10
  89. data/lib/hecks/language/bluebook/policy.bluebook +21 -4
  90. data/lib/hecks/language/bluebook/process_manager.bluebook +135 -18
  91. data/lib/hecks/language/bluebook/projection.bluebook +6 -6
  92. data/lib/hecks/language/bluebook/query.bluebook +4 -4
  93. data/lib/hecks/language/bluebook/shape.bluebook +6 -6
  94. data/lib/hecks/language/bluebook/syntax.bluebook +12 -11
  95. data/lib/hecks/language/bluebook/vocabulary.bluebook +22 -7
  96. data/lib/hecks/language/hecksagon/domain_port.bluebook +25 -1
  97. data/lib/hecks/language/oidc.json +20 -0
  98. data/lib/hecks/language/port.bluebook +30 -2
  99. data/lib/hecks/naming.rb +54 -1
  100. data/lib/hecks/ports/access_control.port +7 -2
  101. data/lib/hecks/ports/access_control.rb +1 -1
  102. data/lib/hecks/ports/agent.port +6 -2
  103. data/lib/hecks/ports/agent.rb +1 -1
  104. data/lib/hecks/ports/authentication.port +4 -2
  105. data/lib/hecks/ports/authentication.rb +1 -1
  106. data/lib/hecks/ports/authorization.port +5 -2
  107. data/lib/hecks/ports/authorization.rb +14 -11
  108. data/lib/hecks/ports/clock.port +3 -2
  109. data/lib/hecks/ports/clock.rb +1 -1
  110. data/lib/hecks/ports/extraction.port +3 -2
  111. data/lib/hecks/ports/extraction.rb +1 -1
  112. data/lib/hecks/ports/identity_assignment.port +3 -2
  113. data/lib/hecks/ports/identity_assignment.rb +1 -1
  114. data/lib/hecks/ports/identity_generation.port +3 -2
  115. data/lib/hecks/ports/identity_generation.rb +1 -1
  116. data/lib/hecks/ports/identity_resolution.port +3 -2
  117. data/lib/hecks/ports/identity_resolution.rb +1 -1
  118. data/lib/hecks/ports/persistence/append_only.rb +40 -4
  119. data/lib/hecks/ports/persistence/execution.rb +6 -1
  120. data/lib/hecks/ports/persistence/plugin.rb +54 -0
  121. data/lib/hecks/{runtime → ports/persistence/plugins/era}/era_check.rb +41 -8
  122. data/lib/hecks/{runtime → ports/persistence/plugins/era}/era_guard.rb +24 -56
  123. data/lib/hecks/ports/persistence/{lineage.rb → plugins/era/lineage.rb} +31 -4
  124. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage/era_store.rb +3 -3
  125. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage/head_compiler.rb +59 -10
  126. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage/mint_transaction.rb +2 -2
  127. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage/provisioning.rb +29 -1
  128. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage/tail_merge.rb +11 -4
  129. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage/transform_installer.rb +20 -0
  130. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage.rb +1 -1
  131. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage_manager/coverage_check.rb +5 -5
  132. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage_manager/era_resolver.rb +5 -2
  133. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage_manager/merge_coordinator.rb +2 -2
  134. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage_manager/minter.rb +4 -4
  135. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage_manager.rb +2 -2
  136. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era.rb +116 -29
  137. data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/audit/approval_digest.rb +1 -1
  138. data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/audit/layer_one.rb +14 -5
  139. data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/audit/layer_two.rb +31 -6
  140. data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/audit/unfed_report.rb +5 -1
  141. data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/reattest.rb +3 -3
  142. data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/scaffold/differ.rb +1 -1
  143. data/lib/hecks/ports/persistence/plugins/era.rb +48 -0
  144. data/lib/hecks/ports/persistence.rb +1 -1
  145. data/lib/hecks/ports/projection.rb +20 -0
  146. data/lib/hecks/projections/diagrams.rb +230 -1
  147. data/lib/hecks/projections/shape.rb +15 -2
  148. data/lib/hecks/projector/cli_projector.rb +10 -1
  149. data/lib/hecks/projector/exporter.rb +19 -2
  150. data/lib/hecks/query_ir.rb +19 -0
  151. data/lib/hecks/query_specification/common/null_policy.rb +13 -1
  152. data/lib/hecks/query_specification/field_path.rb +20 -2
  153. data/lib/hecks/query_specification/hop_path.rb +7 -5
  154. data/lib/hecks/runtime/aggregate_lock.rb +45 -0
  155. data/lib/hecks/runtime/boot_gates.rb +41 -0
  156. data/lib/hecks/runtime/caller.rb +19 -3
  157. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +13 -2
  158. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +12 -0
  159. data/lib/hecks/runtime/command_interpreter.rb +165 -34
  160. data/lib/hecks/runtime/command_rules/admissibility.rb +101 -16
  161. data/lib/hecks/runtime/command_rules/arithmetic.rb +40 -4
  162. data/lib/hecks/runtime/command_rules/authorization.rb +2 -1
  163. data/lib/hecks/runtime/command_rules/references.rb +27 -19
  164. data/lib/hecks/runtime/dependency_planning.rb +66 -5
  165. data/lib/hecks/runtime/dispatcher.rb +19 -4
  166. data/lib/hecks/runtime/entity_interpreter.rb +92 -14
  167. data/lib/hecks/runtime/errors.rb +22 -0
  168. data/lib/hecks/runtime/identity.rb +30 -2
  169. data/lib/hecks/runtime/instance.rb +59 -4
  170. data/lib/hecks/runtime/interpreting.rb +21 -0
  171. data/lib/hecks/runtime/loader.rb +59 -18
  172. data/lib/hecks/runtime/query_interpreter.rb +36 -4
  173. data/lib/hecks/runtime/reaction_invocation.rb +9 -1
  174. data/lib/hecks/runtime/read_model_interpreter.rb +89 -5
  175. data/lib/hecks/runtime/refusal_wording.rb +2 -0
  176. data/lib/hecks/runtime/registry/saga_persistence.rb +75 -3
  177. data/lib/hecks/runtime/registry/verification.rb +88 -0
  178. data/lib/hecks/runtime/registry.rb +69 -8
  179. data/lib/hecks/runtime/saga_interpreter.rb +215 -13
  180. data/lib/hecks/runtime/saga_pending_dispatch.rb +45 -0
  181. data/lib/hecks/runtime/value/admission.rb +19 -1
  182. data/lib/hecks/runtime/value/coercion.rb +75 -10
  183. data/lib/hecks/runtime.rb +17 -5
  184. data/lib/hecks/storehouse.rb +632 -0
  185. data/lib/hecks/version.rb +1 -1
  186. data/lib/hecks/vocabulary.rb +6 -1
  187. data/lib/hecks.rb +28 -3
  188. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +90 -0
  189. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +128 -0
  190. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +160 -0
  191. metadata +49 -37
  192. /data/lib/hecks/{runtime → ports/persistence/plugins/era}/era_guard/shape_diff.rb +0 -0
  193. /data/lib/hecks/{runtime → ports/persistence/plugins/era}/era_tamper.rb +0 -0
  194. /data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage/field_cache.rb +0 -0
  195. /data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage/resumable_backfill.rb +0 -0
  196. /data/lib/hecks/{runtime → ports/persistence/plugins/era}/storage_shape.rb +0 -0
  197. /data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/audit.rb +0 -0
  198. /data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/rule_compiler.rb +0 -0
  199. /data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/scaffold/renderer.rb +0 -0
  200. /data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/scaffold/writer.rb +0 -0
  201. /data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/scaffold.rb +0 -0
  202. /data/lib/hecks/{translation.rb → ports/persistence/plugins/era/translation.rb} +0 -0
@@ -105,7 +105,7 @@ Hecks.bluebook "ConsoleSettings" do
105
105
  given("a style names its own aggregate") { !agg.value.to_s.empty? }
106
106
  given("a style names its own state") { !state.value.to_s.empty? }
107
107
 
108
- emits "StateStyleDeclared"
108
+ emits StateStyleDeclared
109
109
  end
110
110
 
111
111
  command "SetTone" do
@@ -116,7 +116,7 @@ Hecks.bluebook "ConsoleSettings" do
116
116
  attribute :tone, one_of("good", "warn", "danger", "muted", "accent")
117
117
 
118
118
  sets :tone
119
- emits "ToneSet"
119
+ emits ToneSet
120
120
  end
121
121
 
122
122
  command "SetAttention" do
@@ -127,7 +127,7 @@ Hecks.bluebook "ConsoleSettings" do
127
127
  attribute :attention, Flag
128
128
 
129
129
  sets :attention
130
- emits "AttentionSet"
130
+ emits AttentionSet
131
131
  end
132
132
 
133
133
  command "SetExtra" do
@@ -138,7 +138,7 @@ Hecks.bluebook "ConsoleSettings" do
138
138
  attribute :extra_json, StateStyleExtra
139
139
 
140
140
  sets :extra_json
141
- emits "StateStyleExtraSet"
141
+ emits StateStyleExtraSet
142
142
  end
143
143
  end
144
144
 
@@ -261,7 +261,7 @@ Hecks.bluebook "ConsoleSettings" do
261
261
 
262
262
  given("a collection names its own aggregate") { !agg.value.to_s.empty? }
263
263
 
264
- emits "CollectionDeclared"
264
+ emits CollectionDeclared
265
265
  end
266
266
 
267
267
  command "SetLabel" do
@@ -272,7 +272,7 @@ Hecks.bluebook "ConsoleSettings" do
272
272
  attribute :label, SettingsText
273
273
 
274
274
  sets :label
275
- emits "LabelSet"
275
+ emits LabelSet
276
276
  end
277
277
 
278
278
  command "SetKey" do
@@ -283,7 +283,7 @@ Hecks.bluebook "ConsoleSettings" do
283
283
  attribute :key, SettingsText
284
284
 
285
285
  sets :key
286
- emits "KeySet"
286
+ emits KeySet
287
287
  end
288
288
 
289
289
  command "SetNavOrder" do
@@ -294,7 +294,7 @@ Hecks.bluebook "ConsoleSettings" do
294
294
  attribute :nav_order, SettingsNumber
295
295
 
296
296
  sets :nav_order
297
- emits "NavOrderSet"
297
+ emits NavOrderSet
298
298
  end
299
299
 
300
300
  command "SetPrimaryField" do
@@ -305,7 +305,7 @@ Hecks.bluebook "ConsoleSettings" do
305
305
  attribute :primary_field, SettingsText
306
306
 
307
307
  sets :primary_field
308
- emits "PrimaryFieldSet"
308
+ emits PrimaryFieldSet
309
309
  end
310
310
 
311
311
  command "SetListQuery" do
@@ -316,7 +316,7 @@ Hecks.bluebook "ConsoleSettings" do
316
316
  attribute :list_query, SettingsText
317
317
 
318
318
  sets :list_query
319
- emits "ListQuerySet"
319
+ emits ListQuerySet
320
320
  end
321
321
 
322
322
  command "SetIdentity" do
@@ -332,7 +332,7 @@ Hecks.bluebook "ConsoleSettings" do
332
332
  sets :identity_source
333
333
  sets :identity_prefix
334
334
  sets :identity_extra_json
335
- emits "IdentitySet"
335
+ emits IdentitySet
336
336
  end
337
337
 
338
338
  command "SetExtra" do
@@ -343,7 +343,7 @@ Hecks.bluebook "ConsoleSettings" do
343
343
  attribute :extra_json, CollectionExtra
344
344
 
345
345
  sets :extra_json
346
- emits "CollectionExtraSet"
346
+ emits CollectionExtraSet
347
347
  end
348
348
 
349
349
  command "ReplaceColumns" do
@@ -353,7 +353,7 @@ Hecks.bluebook "ConsoleSettings" do
353
353
  reference_to Collection
354
354
 
355
355
  sets :columns
356
- emits "ColumnsReplaced"
356
+ emits ColumnsReplaced
357
357
  end
358
358
 
359
359
  command "ReplaceDetailFields" do
@@ -363,7 +363,7 @@ Hecks.bluebook "ConsoleSettings" do
363
363
  reference_to Collection
364
364
 
365
365
  sets :detail_fields
366
- emits "DetailFieldsReplaced"
366
+ emits DetailFieldsReplaced
367
367
  end
368
368
 
369
369
  command "ReplaceDetailFieldColumns" do
@@ -373,7 +373,7 @@ Hecks.bluebook "ConsoleSettings" do
373
373
  reference_to Collection
374
374
 
375
375
  sets :detail_field_columns
376
- emits "DetailFieldColumnsReplaced"
376
+ emits DetailFieldColumnsReplaced
377
377
  end
378
378
 
379
379
  command "ReplacePreconditions" do
@@ -383,7 +383,7 @@ Hecks.bluebook "ConsoleSettings" do
383
383
  reference_to Collection
384
384
 
385
385
  sets :preconditions
386
- emits "PreconditionsReplaced"
386
+ emits PreconditionsReplaced
387
387
  end
388
388
 
389
389
  command "ReplaceFieldFormats" do
@@ -393,7 +393,7 @@ Hecks.bluebook "ConsoleSettings" do
393
393
  reference_to Collection
394
394
 
395
395
  sets :field_formats
396
- emits "FieldFormatsReplaced"
396
+ emits FieldFormatsReplaced
397
397
  end
398
398
  end
399
399
 
@@ -450,7 +450,7 @@ Hecks.bluebook "ConsoleSettings" do
450
450
 
451
451
  given("only the one true singleton key is ever declared") { key.value == "overview" }
452
452
 
453
- emits "OverviewDeclared"
453
+ emits OverviewDeclared
454
454
  end
455
455
 
456
456
  command "ReplaceStats" do
@@ -460,7 +460,7 @@ Hecks.bluebook "ConsoleSettings" do
460
460
  reference_to Overview
461
461
 
462
462
  sets :stats
463
- emits "StatsReplaced"
463
+ emits StatsReplaced
464
464
  end
465
465
  end
466
466
 
@@ -48,7 +48,7 @@ Hecks.bluebook "Governance" do
48
48
  attribute :scope, Scope
49
49
  attribute :starts_at, Timestamp
50
50
 
51
- emits "RoleAssigned"
51
+ emits RoleAssigned
52
52
  end
53
53
 
54
54
  command "Revoke" do
@@ -59,7 +59,7 @@ Hecks.bluebook "Governance" do
59
59
  attribute :ends_at, Timestamp
60
60
 
61
61
  sets :ends_at
62
- emits "RoleRevoked"
62
+ emits RoleRevoked
63
63
  end
64
64
 
65
65
  query "AssignmentsForActor" do
@@ -73,15 +73,26 @@ Hecks.bluebook "Governance" do
73
73
  # may role X ACT AS role Y — the fact `act_as` (the plan's §2) needs before
74
74
  # a caller is allowed to dispatch under a role it does not itself hold.
75
75
  # Same event-log shape as RoleAssignment : a natural key, `ends_at` for
76
- # revocation, no delete.
76
+ # revocation, no delete — and, like RoleAssignment, THE IDENTITY IS THE
77
+ # GRANT, not the (from_role, to_role) pair. `starts_at` used to be
78
+ # missing from here — the one field RoleAssignment's own identity adds
79
+ # beyond its own (actor, role) pair specifically so a second, later
80
+ # assignment is a SECOND real record rather than a collision. Without
81
+ # it, `identified_by :from_role, :to_role` alone made a revoked pair's
82
+ # own identity permanently occupied: `Grant` is a creating command, the
83
+ # same (from_role, to_role) pair is the SAME id forever, and a repeat
84
+ # `Grant` after a `Revoke` was refused as `AlreadyExists` no matter how
85
+ # long ago the prior grant ended — an absorbing "revoked" state this
86
+ # very comment already claimed didn't exist.
77
87
  aggregate "RoleTransition" do
78
- description "Role X is authorized to act as role Y — until revoked."
88
+ description "Role X is authorized to act as role Y, from one point in time — until revoked, and grantable again afterward, exactly like RoleAssignment."
79
89
 
80
90
  attribute :from_role, RoleName
81
91
  attribute :to_role, RoleName
92
+ attribute :starts_at, Timestamp
82
93
  attribute :ends_at, Timestamp, optional: true
83
94
 
84
- identified_by :from_role, :to_role
95
+ identified_by :from_role, :to_role, :starts_at
85
96
 
86
97
  value_object "RoleName" do
87
98
  attribute :value, String
@@ -99,8 +110,9 @@ Hecks.bluebook "Governance" do
99
110
 
100
111
  attribute :from_role, RoleName
101
112
  attribute :to_role, RoleName
113
+ attribute :starts_at, Timestamp
102
114
 
103
- emits "RoleTransitionGranted"
115
+ emits RoleTransitionGranted
104
116
  end
105
117
 
106
118
  command "Revoke" do
@@ -111,17 +123,20 @@ Hecks.bluebook "Governance" do
111
123
  attribute :ends_at, Timestamp
112
124
 
113
125
  sets :ends_at
114
- emits "RoleTransitionRevoked"
126
+ emits RoleTransitionRevoked
115
127
  end
116
128
 
117
- # THE RECORD FOR THIS PAIR, granted or revoked — same deferral
129
+ # EVERY GRANT OF THIS PAIR, current or historical — same deferral
118
130
  # `AssignmentsForActor` already makes for RoleAssignment: the where
119
131
  # clause has no way to ask "and ends_at is absent", so the query
120
- # answers with the row and the CALLER reads `ends_at` to tell a
132
+ # answers with every row and the CALLER reads `ends_at` to tell a
121
133
  # live grant from a revoked one, exactly as it already must for
122
- # every assignment.
134
+ # every assignment. Can answer MORE than one row now that `starts_at`
135
+ # is part of the identity — a pair granted again after a revocation is
136
+ # a SECOND row, not an overwrite of the first, the same way a
137
+ # re-assigned actor/role is for RoleAssignment.
123
138
  query "Allowed" do
124
- description "The role transition granted for this exact pair, if any — revoked or not."
139
+ description "Every role transition granted for this exact pair, current or historical."
125
140
  attribute :from_role, RoleName
126
141
  attribute :to_role, RoleName
127
142
  where(from_role: :from_role, to_role: :to_role)
@@ -30,7 +30,7 @@ Hecks.bluebook "Identity" do
30
30
  goal "Recognize an identity"
31
31
 
32
32
  attribute :identity_id, IdentityId
33
- emits "IdentityRegistered"
33
+ emits IdentityRegistered
34
34
  end
35
35
  end
36
36
 
@@ -71,7 +71,7 @@ Hecks.bluebook "Identity" do
71
71
  attribute :issuer, Issuer
72
72
  attribute :subject, Subject
73
73
 
74
- emits "ExternalIdentifierLinked"
74
+ emits ExternalIdentifierLinked
75
75
  end
76
76
 
77
77
  # THE OIDC-SHAPED LOOKUP — a token hands over `(issuer, subject)`,