hecks 2.8.0 → 2.9.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.
- checksums.yaml +4 -4
- data/exe/hecks +4 -6
- data/lib/hecks/adapters/driven/claude_code.rb +4 -104
- data/lib/hecks/adapters/driven/d1.rb +44 -237
- data/lib/hecks/adapters/driven/folder.rb +17 -76
- data/lib/hecks/adapters/driven/google_authentication.rb +15 -49
- data/lib/hecks/adapters/driven/governance_authorization.rb +23 -70
- data/lib/hecks/adapters/driven/heki/journal.rb +8 -41
- data/lib/hecks/adapters/driven/heki/saga_store.rb +12 -54
- data/lib/hecks/adapters/driven/heki/snapshot.rb +4 -12
- data/lib/hecks/adapters/driven/heki.rb +13 -42
- data/lib/hecks/adapters/driven/identity_registry.rb +3 -14
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +9 -26
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +2 -14
- data/lib/hecks/adapters/driven/lambda/client.rb +10 -51
- data/lib/hecks/adapters/driven/lambda.rb +10 -84
- data/lib/hecks/adapters/driven/local_storage.rb +5 -40
- data/lib/hecks/adapters/driven/memory.rb +17 -98
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +5 -21
- data/lib/hecks/adapters/driven/postgres/codec.rb +6 -28
- data/lib/hecks/adapters/driven/postgres/outbox.rb +3 -12
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +4 -30
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
- data/lib/hecks/adapters/driven/postgres.rb +145 -134
- data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
- data/lib/hecks/adapters/driven/prism.rb +7 -30
- data/lib/hecks/adapters/driven/sql_query_builder.rb +21 -121
- data/lib/hecks/adapters/driven/sqlite/codec.rb +8 -40
- data/lib/hecks/adapters/driven/sqlite/projection.rb +26 -82
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +40 -84
- data/lib/hecks/adapters/driven/sqlite.rb +128 -65
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +5 -48
- data/lib/hecks/adapters/driven.rb +5 -36
- data/lib/hecks/adapters/driving/github_webhook.rb +8 -76
- data/lib/hecks/behaviors/dsl.rb +7 -25
- data/lib/hecks/behaviors/expectations.rb +31 -141
- data/lib/hecks/behaviors/ir.rb +7 -20
- data/lib/hecks/behaviors/rspec.rb +6 -17
- data/lib/hecks/behaviors/runner.rb +10 -34
- data/lib/hecks/behaviors.rb +4 -8
- data/lib/hecks/bluebook/aggregate.rb +9 -75
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +9 -38
- data/lib/hecks/bluebook/assembly/build.rb +4 -15
- data/lib/hecks/bluebook/assembly/contract.rb +15 -136
- data/lib/hecks/bluebook/assembly/contracts.rb +54 -174
- data/lib/hecks/bluebook/assembly/marks.rb +31 -223
- data/lib/hecks/bluebook/assembly/specializer.rb +6 -52
- data/lib/hecks/bluebook/assembly.rb +6 -45
- data/lib/hecks/bluebook/attribute.rb +12 -61
- data/lib/hecks/bluebook/behaviour/aggregate.rb +10 -39
- data/lib/hecks/bluebook/behaviour/attribute.rb +3 -14
- data/lib/hecks/bluebook/behaviour/chapter.rb +14 -42
- data/lib/hecks/bluebook/behaviour/command.rb +14 -66
- data/lib/hecks/bluebook/behaviour/domain_port.rb +4 -20
- data/lib/hecks/bluebook/behaviour/entity.rb +7 -27
- data/lib/hecks/bluebook/behaviour/hexagon.rb +23 -41
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +15 -40
- data/lib/hecks/bluebook/behaviour/policy.rb +23 -52
- data/lib/hecks/bluebook/behaviour/process_manager.rb +30 -57
- data/lib/hecks/bluebook/behaviour/read_model.rb +25 -31
- data/lib/hecks/bluebook/behaviour/traits.rb +27 -64
- data/lib/hecks/bluebook/behaviour/value_object.rb +5 -24
- data/lib/hecks/bluebook/capabilities.rb +17 -49
- data/lib/hecks/bluebook/chapter.rb +10 -48
- data/lib/hecks/bluebook/command.rb +23 -81
- data/lib/hecks/bluebook/domain_port.rb +14 -63
- data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +50 -229
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +50 -377
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +33 -204
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +16 -69
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +86 -708
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +56 -170
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +1 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +74 -546
- data/lib/hecks/bluebook/dsl/const_shim.rb +8 -41
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +12 -87
- data/lib/hecks/bluebook/dsl/entity_builder.rb +44 -330
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +20 -269
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +26 -194
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +13 -88
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +6 -20
- data/lib/hecks/bluebook/dsl/policy_builder.rb +16 -88
- data/lib/hecks/bluebook/dsl/port_builder.rb +2 -6
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +22 -96
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +31 -184
- data/lib/hecks/bluebook/dsl/query_builder.rb +11 -42
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +75 -201
- data/lib/hecks/bluebook/dsl/rule_reference.rb +22 -163
- data/lib/hecks/bluebook/dsl/translation_builder.rb +28 -114
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +10 -124
- data/lib/hecks/bluebook/dsl/word_gate.rb +16 -162
- data/lib/hecks/bluebook/dsl/world_builder.rb +19 -111
- data/lib/hecks/bluebook/entity.rb +13 -71
- data/lib/hecks/bluebook/expression/ast_json.rb +24 -190
- data/lib/hecks/bluebook/expression/ast_reader.rb +12 -54
- data/lib/hecks/bluebook/expression/canonical_form.rb +11 -88
- data/lib/hecks/bluebook/expression/evaluator.rb +47 -295
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +30 -147
- data/lib/hecks/bluebook/expression/resolver.rb +37 -660
- data/lib/hecks/bluebook/hexagon.rb +5 -28
- data/lib/hecks/bluebook/lifecycle.rb +5 -16
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -7
- data/lib/hecks/bluebook/meta_validator/judge.rb +111 -361
- data/lib/hecks/bluebook/meta_validator/plan.rb +33 -162
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -7
- data/lib/hecks/bluebook/meta_validator/readings.rb +56 -342
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -176
- data/lib/hecks/bluebook/meta_validator/shapes.rb +53 -267
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +43 -305
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
- data/lib/hecks/bluebook/meta_validator.rb +79 -349
- data/lib/hecks/bluebook/model_check/client_profile.rb +6 -21
- data/lib/hecks/bluebook/model_check.rb +100 -597
- data/lib/hecks/bluebook/pattern_subset.rb +11 -69
- data/lib/hecks/bluebook/process_manager.rb +13 -83
- data/lib/hecks/bluebook/project_loader.rb +3 -39
- data/lib/hecks/bluebook/project_register.rb +12 -60
- data/lib/hecks/bluebook/query.rb +9 -48
- data/lib/hecks/bluebook/read_model.rb +57 -81
- data/lib/hecks/bluebook/reference.rb +7 -41
- data/lib/hecks/bluebook/smoke_test.rb +8 -98
- data/lib/hecks/bluebook/synthesizer.rb +13 -62
- data/lib/hecks/bluebook/translation.rb +26 -62
- data/lib/hecks/bluebook/value_object.rb +11 -36
- data/lib/hecks/bluebook.rb +5 -22
- data/lib/hecks/cache_dir.rb +4 -23
- data/lib/hecks/cli/document.rb +3 -21
- data/lib/hecks/cli/ir.rb +3 -5
- data/lib/hecks/cli/mcp.rb +2 -3
- data/lib/hecks/cli/mcp_door.rb +19 -114
- data/lib/hecks/cli/model_check.rb +18 -85
- data/lib/hecks/cli/project_cli.rb +7 -40
- data/lib/hecks/cli/project_diagrams.rb +0 -7
- data/lib/hecks/cli/run.rb +12 -58
- data/lib/hecks/cli/smoke_test.rb +2 -16
- data/lib/hecks/cli/stores.rb +1 -2
- data/lib/hecks/cli.rb +18 -24
- data/lib/hecks/construct.rb +8 -34
- data/lib/hecks/deploy/bluebook/deploy.bluebook +33 -142
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +2 -9
- data/lib/hecks/embryonaut_bluebook/lock.rb +3 -35
- data/lib/hecks/embryonaut_bluebook/shape.rb +1 -17
- data/lib/hecks/embryonaut_bluebook/vendor.rb +8 -53
- data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +4 -19
- data/lib/hecks/embryonaut_bluebook.rb +4 -71
- data/lib/hecks/facade/cli_door.rb +24 -106
- data/lib/hecks/facade/cli_runner.rb +28 -172
- data/lib/hecks/facade/command_request.rb +13 -33
- data/lib/hecks/facade/handle.rb +34 -121
- data/lib/hecks/facade/json_door.rb +31 -146
- data/lib/hecks/facade/surface/aggregate_door.rb +39 -104
- data/lib/hecks/facade/surface/chapter.rb +10 -54
- data/lib/hecks/facade/surface.rb +8 -23
- data/lib/hecks/facade.rb +10 -25
- data/lib/hecks/forms/app.rb +15 -70
- data/lib/hecks/forms/command_form_renderer.rb +10 -29
- data/lib/hecks/forms/field_renderer.rb +17 -156
- data/lib/hecks/forms/field_shape.rb +30 -222
- data/lib/hecks/forms/html.rb +12 -47
- data/lib/hecks/forms/index_renderer.rb +1 -4
- data/lib/hecks/forms/page.rb +3 -9
- data/lib/hecks/forms/params.rb +14 -60
- data/lib/hecks/forms/port_argument.rb +2 -19
- data/lib/hecks/forms/query_form_renderer.rb +4 -79
- data/lib/hecks/forms/record_renderer.rb +8 -75
- data/lib/hecks/forms/record_table.rb +7 -50
- data/lib/hecks/forms/reference_options.rb +3 -30
- data/lib/hecks/forms/value_object_shape.rb +8 -35
- data/lib/hecks/forms.rb +5 -33
- data/lib/hecks/fqn.rb +5 -12
- data/lib/hecks/framework/bluebook/compliance.bluebook +16 -65
- data/lib/hecks/framework/bluebook/console_settings.bluebook +47 -134
- data/lib/hecks/framework/bluebook/framework.hecksagon +5 -14
- data/lib/hecks/framework/bluebook/governance.bluebook +13 -34
- data/lib/hecks/framework/bluebook/identity.bluebook +11 -26
- data/lib/hecks/framework/bluebook/privacy.bluebook +8 -33
- data/lib/hecks/framework.rb +11 -80
- data/lib/hecks/freezer.rb +5 -39
- data/lib/hecks/grammar/expression.bluebook +15 -40
- data/lib/hecks/grammar/translation.bluebook +3 -6
- data/lib/hecks/grammar.rb +16 -41
- data/lib/hecks/ir.rb +14 -83
- data/lib/hecks/language/adapter.bluebook +2 -7
- data/lib/hecks/language/bluebook/aggregate.bluebook +56 -221
- data/lib/hecks/language/bluebook/attaches/paging.bluebook +6 -15
- data/lib/hecks/language/bluebook/bluebook.bluebook +15 -83
- data/lib/hecks/language/bluebook/bluebook.hecksagon +3 -15
- data/lib/hecks/language/bluebook/command.bluebook +27 -144
- data/lib/hecks/language/bluebook/entity.bluebook +24 -97
- data/lib/hecks/language/bluebook/policy.bluebook +16 -67
- data/lib/hecks/language/bluebook/process_manager.bluebook +28 -151
- data/lib/hecks/language/bluebook/projection.bluebook +126 -60
- data/lib/hecks/language/bluebook/query.bluebook +12 -43
- data/lib/hecks/language/bluebook/shape.bluebook +23 -73
- data/lib/hecks/language/bluebook/syntax.bluebook +51 -262
- data/lib/hecks/language/bluebook/vocabulary.bluebook +82 -197
- data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
- data/lib/hecks/language/hecksagon/domain_port.bluebook +4 -19
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +6 -25
- data/lib/hecks/language/hecksagon/port_operation.bluebook +3 -14
- data/lib/hecks/language/oidc.json +35 -0
- data/lib/hecks/language/port.bluebook +4 -17
- data/lib/hecks/language/translation/translation.bluebook +3 -9
- data/lib/hecks/language/translation/translation_aggregate.bluebook +5 -24
- data/lib/hecks/language/world/wiring.bluebook +1 -3
- data/lib/hecks/language/world/world.bluebook +5 -7
- data/lib/hecks/literal.rb +8 -50
- data/lib/hecks/mcp_door_scope.rb +9 -90
- data/lib/hecks/mcp_stdio_guard.rb +4 -41
- data/lib/hecks/naming.rb +29 -137
- data/lib/hecks/ports/access_control.rb +7 -23
- data/lib/hecks/ports/agent/answers.rb +5 -23
- data/lib/hecks/ports/agent.rb +47 -172
- data/lib/hecks/ports/authentication.rb +12 -39
- data/lib/hecks/ports/authorization.rb +16 -43
- data/lib/hecks/ports/clock.rb +5 -45
- data/lib/hecks/ports/extraction.rb +7 -12
- data/lib/hecks/ports/identity_assignment.rb +8 -29
- data/lib/hecks/ports/identity_generation.rb +6 -29
- data/lib/hecks/ports/identity_resolution.rb +2 -10
- data/lib/hecks/ports/key_vault.rb +9 -45
- data/lib/hecks/ports/persistence/append_only.rb +61 -64
- data/lib/hecks/ports/persistence/binding_policy.rb +4 -14
- data/lib/hecks/ports/persistence/codec_boundary.rb +20 -80
- data/lib/hecks/ports/persistence/null_saga_store.rb +4 -20
- data/lib/hecks/ports/persistence/plugin.rb +13 -42
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +38 -141
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +16 -84
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +6 -25
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +3 -22
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +55 -181
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/events.rb +77 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +12 -122
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +28 -128
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +73 -474
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +31 -124
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +45 -219
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +13 -108
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +18 -75
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +9 -49
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +6 -123
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +23 -55
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +14 -72
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +7 -16
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +14 -115
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +127 -554
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +2 -17
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +3 -14
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +13 -58
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -17
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -26
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +9 -25
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -124
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +34 -123
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +5 -12
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +3 -9
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -14
- data/lib/hecks/ports/persistence/plugins/era.rb +4 -23
- data/lib/hecks/ports/persistence/postgres_dump/connection.rb +7 -23
- data/lib/hecks/ports/persistence/postgres_dump.rb +4 -35
- data/lib/hecks/ports/persistence/remote_runtime.rb +2 -24
- data/lib/hecks/ports/persistence/repository_factory.rb +8 -27
- data/lib/hecks/ports/persistence/state_codec.rb +23 -103
- data/lib/hecks/ports/persistence.rb +7 -14
- data/lib/hecks/ports/projection.rb +42 -84
- data/lib/hecks/ports/query/in_memory.rb +10 -58
- data/lib/hecks/ports/query/ordering.rb +11 -32
- data/lib/hecks/ports/query.rb +14 -37
- data/lib/hecks/ports.rb +2 -8
- data/lib/hecks/projections/bootstrap_table.rb +11 -43
- data/lib/hecks/projections/deploy/fargate/assembly.rb +2 -15
- data/lib/hecks/projections/deploy/fargate/cdn.rb +1 -48
- data/lib/hecks/projections/deploy/fargate/cdn_default.rb +2 -11
- data/lib/hecks/projections/deploy/fargate/check.rb +5 -14
- data/lib/hecks/projections/deploy/fargate/containers.rb +3 -78
- data/lib/hecks/projections/deploy/fargate/extras.rb +3 -44
- data/lib/hecks/projections/deploy/fargate/monitoring.rb +0 -44
- data/lib/hecks/projections/deploy/fargate/settings.rb +2 -54
- data/lib/hecks/projections/deploy/fargate/yaml.rb +13 -58
- data/lib/hecks/projections/deploy/fargate.rb +35 -202
- data/lib/hecks/projections/deploy/lambda.rb +150 -678
- data/lib/hecks/projections/deploy/preview/access_yaml.rb +1 -33
- data/lib/hecks/projections/deploy/preview/containers.rb +6 -111
- data/lib/hecks/projections/deploy/preview/routing_yaml.rb +4 -56
- data/lib/hecks/projections/deploy/preview/script.rb +2 -14
- data/lib/hecks/projections/deploy/preview/settings.rb +3 -33
- data/lib/hecks/projections/deploy/preview/task_yaml.rb +2 -38
- data/lib/hecks/projections/deploy/preview/template.rb +2 -58
- data/lib/hecks/projections/deploy/preview.rb +2 -67
- data/lib/hecks/projections/deploy/scripts/settings.rb +3 -25
- data/lib/hecks/projections/deploy/scripts.rb +4 -57
- data/lib/hecks/projections/deploy/shared.rb +47 -146
- data/lib/hecks/projections/deploy/smoke.rb +4 -61
- data/lib/hecks/projections/deploy/template_diff/comparer.rb +3 -25
- data/lib/hecks/projections/deploy/template_diff/loader.rb +2 -11
- data/lib/hecks/projections/deploy/template_diff.rb +5 -57
- data/lib/hecks/projections/diagrams.rb +20 -582
- data/lib/hecks/projections/glossary/html.rb +14 -130
- data/lib/hecks/projections/glossary/markdown.rb +8 -69
- data/lib/hecks/projections/glossary/mermaid.rb +9 -66
- data/lib/hecks/projections/glossary/sensitivity.rb +5 -34
- data/lib/hecks/projections/glossary/sentences.rb +14 -159
- data/lib/hecks/projections/glossary.rb +30 -222
- data/lib/hecks/projections/ir.rb +3 -10
- data/lib/hecks/projections/model/deviations.rb +15 -42
- data/lib/hecks/projections/model.rb +12 -51
- data/lib/hecks/projections/oidc.rb +12 -56
- data/lib/hecks/projections/parser_table.rb +9 -28
- data/lib/hecks/projections/reference.rb +2 -15
- data/lib/hecks/projections/rust_vocabulary.rb +12 -250
- data/lib/hecks/projections/shape.rb +3 -29
- data/lib/hecks/projections/statements.rb +7 -86
- data/lib/hecks/projections/vocabulary.rb +6 -22
- data/lib/hecks/projections.rb +2 -16
- data/lib/hecks/projector/cli_projector.rb +60 -375
- data/lib/hecks/projector/docs_projector.rb +10 -201
- data/lib/hecks/projector/exporter.rb +42 -134
- data/lib/hecks/projector/ir_projector.rb +2 -9
- data/lib/hecks/projector/narrate_projector.rb +15 -189
- data/lib/hecks/projector/target.rb +13 -76
- data/lib/hecks/projector.rb +30 -121
- data/lib/hecks/query_specification/common/comparators.rb +5 -26
- data/lib/hecks/query_specification/common/comparison.rb +35 -143
- data/lib/hecks/query_specification/common/dsl.rb +8 -24
- data/lib/hecks/query_specification/common/null_policy.rb +20 -70
- data/lib/hecks/query_specification/common/options.rb +2 -6
- data/lib/hecks/query_specification/common/where_clause.rb +2 -10
- data/lib/hecks/query_specification/field_path.rb +37 -77
- data/lib/hecks/query_specification/hop_path.rb +12 -76
- data/lib/hecks/release/gem_pin.rb +2 -26
- data/lib/hecks/release/runner/ci_publisher.rb +3 -6
- data/lib/hecks/release/runner/commands.rb +2 -4
- data/lib/hecks/release/runner/gem_publisher.rb +2 -3
- data/lib/hecks/release/runner/npm_publisher.rb +6 -20
- data/lib/hecks/release/runner/preflight.rb +4 -19
- data/lib/hecks/release/runner/tagger.rb +2 -9
- data/lib/hecks/release/runner.rb +3 -32
- data/lib/hecks/rendering.rb +4 -26
- data/lib/hecks/router/namespace_installer.rb +12 -36
- data/lib/hecks/router.rb +4 -8
- data/lib/hecks/runtime/aggregate_lock.rb +8 -33
- data/lib/hecks/runtime/boot_gates.rb +3 -18
- data/lib/hecks/runtime/caller.rb +19 -59
- data/lib/hecks/runtime/capability_graph.rb +5 -21
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +23 -160
- data/lib/hecks/runtime/command_interpreter.rb +124 -306
- data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
- data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
- data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
- data/lib/hecks/runtime/command_rules/emission.rb +8 -21
- data/lib/hecks/runtime/command_rules/references.rb +23 -202
- data/lib/hecks/runtime/dependency_planning.rb +30 -102
- data/lib/hecks/runtime/dispatcher.rb +82 -241
- data/lib/hecks/runtime/entity_element.rb +40 -462
- data/lib/hecks/runtime/entity_interpreter.rb +64 -219
- data/lib/hecks/runtime/errors.rb +32 -115
- data/lib/hecks/runtime/event.rb +3 -25
- data/lib/hecks/runtime/identity.rb +18 -154
- data/lib/hecks/runtime/instance.rb +18 -150
- data/lib/hecks/runtime/interpreting.rb +11 -70
- data/lib/hecks/runtime/invocation.rb +28 -164
- data/lib/hecks/runtime/loader.rb +48 -186
- data/lib/hecks/runtime/outbox.rb +38 -261
- data/lib/hecks/runtime/policy_interpreter.rb +38 -200
- data/lib/hecks/runtime/port_operation_interpreter.rb +26 -87
- data/lib/hecks/runtime/query_interpreter.rb +45 -187
- data/lib/hecks/runtime/reaction_invocation.rb +36 -138
- data/lib/hecks/runtime/read_model_interpreter.rb +117 -208
- data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
- data/lib/hecks/runtime/reference_hop.rb +8 -81
- data/lib/hecks/runtime/refusal_wording.rb +8 -89
- data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
- data/lib/hecks/runtime/registry/verification.rb +52 -225
- data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
- data/lib/hecks/runtime/registry.rb +99 -244
- data/lib/hecks/runtime/remote_dispatcher.rb +37 -152
- data/lib/hecks/runtime/routing.rb +4 -15
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
- data/lib/hecks/runtime/saga_interpreter.rb +79 -263
- data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
- data/lib/hecks/runtime/tenant_check.rb +6 -68
- data/lib/hecks/runtime/tenant_scope.rb +3 -28
- data/lib/hecks/runtime/value/admission.rb +16 -67
- data/lib/hecks/runtime/value/coercion.rb +155 -641
- data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
- data/lib/hecks/runtime/value.rb +12 -84
- data/lib/hecks/runtime.rb +17 -65
- data/lib/hecks/storehouse.rb +141 -568
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +8 -32
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
- data/lib/hecks/vendoring/git_environment.rb +2 -8
- data/lib/hecks/vendoring/git_source.rb +4 -10
- data/lib/hecks/vendoring.rb +7 -46
- data/lib/hecks/version.rb +3 -14
- data/lib/hecks.rb +26 -81
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
- metadata +3 -2
|
@@ -9,123 +9,61 @@ require_relative "../ports/persistence/remote_runtime"
|
|
|
9
9
|
|
|
10
10
|
module Hecks
|
|
11
11
|
module Runtime
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# own `.world` declares Lambda routing (see loader.rb's own
|
|
15
|
-
# `dispatcher_class_for`). Matches `Dispatcher`'s public shape
|
|
16
|
-
# closely enough that everything built on top of a dispatcher —
|
|
17
|
-
# `Handle`, `AggregateDoor`, `Facade::Surface` — needs no changes
|
|
18
|
-
# at all: `Handle#run`'s entire contract is
|
|
19
|
-
# `@dispatcher.dispatch("#{fqn}.#{command}", to: @id, with: args).instance.state`,
|
|
20
|
-
# and both classes answer that identically.
|
|
12
|
+
# Dispatcher for a domain whose `.world` declares Lambda routing.
|
|
13
|
+
# Same public shape as `Dispatcher`; writes go remote, reads stay local.
|
|
21
14
|
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
# (`Adapters::Lambda`, `persisted_by("Lambda")`, Phase 2's other
|
|
26
|
-
# half), the inherited query machinery (`QueryInterpreter`,
|
|
27
|
-
# `Ports::Query::InMemory.execute`) works completely unchanged, no
|
|
28
|
-
# query logic duplicated here. `dispatch` can't delegate the same
|
|
29
|
-
# way: command validation (givens, constraints) has to actually
|
|
30
|
-
# run in Rust for a Lambda-routed domain, not get pre-checked here
|
|
31
|
-
# against incomplete local state and then merely persisted.
|
|
15
|
+
# Reads delegate to a local `Dispatcher` because the registry's repositories are
|
|
16
|
+
# already Lambda-backed. Writes cannot: givens and constraints must run in Rust,
|
|
17
|
+
# not be pre-checked here against incomplete local state.
|
|
32
18
|
class RemoteDispatcher
|
|
33
19
|
Result = Struct.new(:verb, :instance, :events, keyword_init: true) do
|
|
34
|
-
#
|
|
20
|
+
# The settled record's identity.
|
|
35
21
|
#
|
|
36
|
-
# @return [String]
|
|
22
|
+
# @return [String]
|
|
37
23
|
def id = instance.id
|
|
38
24
|
|
|
39
|
-
#
|
|
25
|
+
# The settled record's attributes.
|
|
40
26
|
#
|
|
41
|
-
# @return [Hash{Symbol => Object}]
|
|
42
|
-
# in last
|
|
27
|
+
# @return [Hash{Symbol => Object}] `:id` merged in last
|
|
43
28
|
def state = instance.to_h
|
|
44
29
|
end
|
|
45
30
|
|
|
46
31
|
attr_reader :registry
|
|
47
32
|
|
|
48
|
-
# @param registry [Runtime::Registry] the booted registry this dispatcher fronts
|
|
49
|
-
# read-side calls (`query`/`reference_query`) delegate to a local `Dispatcher`
|
|
50
|
-
# built over the same registry
|
|
33
|
+
# @param registry [Runtime::Registry] the booted registry this dispatcher fronts
|
|
51
34
|
# @param region [String] the AWS region the routed Lambda function lives in
|
|
52
|
-
# @param function [String, nil] the
|
|
53
|
-
#
|
|
54
|
-
# resolves the function name from `ENV["DOMAIN_NAME"]` or `registry.root`
|
|
35
|
+
# @param function [String, nil] the `dispatched_by("Lambda")` function name; nil
|
|
36
|
+
# resolves it from `ENV["DOMAIN_NAME"]` or `registry.root`
|
|
55
37
|
def initialize(registry, region: "us-east-1", function: nil)
|
|
56
38
|
@registry = registry
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
# exactly (see its own comment on why: one merged Lambda per
|
|
60
|
-
# deploy, not one per attached chapter, and `root` is the one
|
|
61
|
-
# signal every bluebook in this registry shares regardless of
|
|
62
|
-
# which one attached it) — including that same adapter's own
|
|
63
|
-
# `ENV["DOMAIN_NAME"]`-first fix: `root` is always `/var/task`
|
|
64
|
-
# inside a deployed Lambda, giving "task" instead of the real
|
|
65
|
-
# domain name (a real, live AccessDeniedException on
|
|
66
|
-
# "hecks-task" caught this).
|
|
67
|
-
# `function:` — the `.world`'s own `dispatched_by("Lambda")`
|
|
68
|
-
# naming of which function this is, for a deployment whose stack
|
|
69
|
-
# name isn't `hecks-<domain>` (Client's own comment has the real
|
|
70
|
-
# case). Absent, the resolution above is unchanged.
|
|
39
|
+
# ENV["DOMAIN_NAME"] first: inside a deployed Lambda `root` is always
|
|
40
|
+
# `/var/task`, which would resolve the function name to "task".
|
|
71
41
|
@client = Adapters::Lambda::Client.new(domain: ENV["DOMAIN_NAME"] || File.basename(registry.root),
|
|
72
42
|
region: region, function: function)
|
|
73
|
-
# Read-side delegate only (see class comment) — never dispatched
|
|
74
|
-
# through; a real Dispatcher's own `query`/`reference_query`
|
|
75
|
-
# already resolve generically via `registry.repository(...)`,
|
|
76
|
-
# so building one here reuses that instead of duplicating it.
|
|
77
43
|
@local = Dispatcher.new(registry)
|
|
78
44
|
end
|
|
79
45
|
|
|
80
|
-
# Dispatches a command by verb,
|
|
81
|
-
#
|
|
82
|
-
#
|
|
83
|
-
# Same shape as `Dispatcher#dispatch_flat` — everything but
|
|
84
|
-
# `saga_correlation:` is forwarded through unread, `to:`/`with:`
|
|
85
|
-
# included, and lifted out downstream by whichever path actually
|
|
86
|
-
# dispatches (`@local.dispatch_flat` locally, the flat wire form
|
|
87
|
-
# remotely). Not the strict `to:`/`with:`-only door `Dispatcher#
|
|
88
|
-
# dispatch` is — see that class's own comment for why this file
|
|
89
|
-
# never had one.
|
|
46
|
+
# Dispatches a command by verb, locally or through the routed Lambda depending
|
|
47
|
+
# on the aggregate's bound adapter.
|
|
90
48
|
#
|
|
91
49
|
# @param verb [String] the fully qualified verb, `"Domain::Aggregate.Command"`
|
|
92
|
-
#
|
|
93
|
-
# @param
|
|
94
|
-
# emitted event when a saga leg causes this dispatch; nil otherwise
|
|
95
|
-
# @param args [Hash] the facts, plus optional `:to`/`:with` keys, read the same
|
|
96
|
-
# way `dispatch_flat` reads them
|
|
50
|
+
# @param saga_correlation [Hash, nil] correlation head => value stamped on emitted events
|
|
51
|
+
# @param args [Hash] the facts, plus optional `:to`/`:with` keys
|
|
97
52
|
# @return [RemoteDispatcher::Result] the verb, settled instance and emitted events
|
|
98
|
-
# @raise [Runtime::UnknownVerb] if the verb is not fully qualified
|
|
99
|
-
# domain or aggregate
|
|
100
|
-
# @raise [StandardError] any class in `Runtime::DOMAIN_REFUSALS` when a
|
|
101
|
-
# locally-routed aggregate refuses the call
|
|
102
|
-
# @raise [Runtime::StaleWrite] if concurrent local writers beat this one through
|
|
103
|
-
# every retry, for a locally-routed aggregate
|
|
104
|
-
# @raise [Runtime::WiringError] if the aggregate's adapter or repository cannot be
|
|
105
|
-
# resolved, or the remote call is accepted but reports no mutation for it
|
|
53
|
+
# @raise [Runtime::UnknownVerb] if the verb is not fully qualified or names an
|
|
54
|
+
# undeclared domain or aggregate
|
|
106
55
|
# @raise [Runtime::RemoteRefusal] if the routed Lambda refuses the call
|
|
56
|
+
# @raise [Runtime::WiringError] if the adapter cannot be resolved, or the remote call
|
|
57
|
+
# reports no mutation for the aggregate
|
|
107
58
|
def dispatch(verb, saga_correlation: nil, **args)
|
|
108
59
|
dispatch_flat(verb, args.merge(saga_correlation: saga_correlation))
|
|
109
60
|
end
|
|
110
61
|
|
|
111
|
-
#
|
|
112
|
-
# adapter is not remote-backed (`Ports::Persistence::RemoteRuntime`),
|
|
113
|
-
# otherwise dispatches through the routed Lambda. Same flat-facts wire
|
|
114
|
-
# form as `Dispatcher#dispatch_flat`.
|
|
62
|
+
# Same routing as `dispatch`, with the flat-facts wire form of `Dispatcher#dispatch_flat`.
|
|
115
63
|
#
|
|
116
|
-
# @param verb [String] the fully qualified verb
|
|
117
|
-
# @param args [Hash] the facts, plus optional Symbol keys `:to`, `:with`
|
|
118
|
-
# `:saga_correlation`, read as `dispatch`'s keywords of the same names; not mutated
|
|
64
|
+
# @param verb [String] the fully qualified verb
|
|
65
|
+
# @param args [Hash] the facts, plus optional Symbol keys `:to`, `:with`, `:saga_correlation`
|
|
119
66
|
# @return [RemoteDispatcher::Result] the verb, settled instance and emitted events
|
|
120
|
-
# @raise [Runtime::UnknownVerb] if the verb is not fully qualified, or names a
|
|
121
|
-
# domain or aggregate that is not declared
|
|
122
|
-
# @raise [StandardError] any class in `Runtime::DOMAIN_REFUSALS` when a
|
|
123
|
-
# locally-routed aggregate refuses the call
|
|
124
|
-
# @raise [Runtime::StaleWrite] if concurrent local writers beat this one through
|
|
125
|
-
# every retry, for a locally-routed aggregate
|
|
126
|
-
# @raise [Runtime::WiringError] if the aggregate's adapter or repository cannot be
|
|
127
|
-
# resolved, or the remote call is accepted but reports no mutation for it
|
|
128
|
-
# @raise [Runtime::RemoteRefusal] if the routed Lambda refuses the call
|
|
129
67
|
def dispatch_flat(verb, args = {})
|
|
130
68
|
args = args.dup
|
|
131
69
|
saga_correlation = args.delete(:saga_correlation)
|
|
@@ -136,21 +74,8 @@ module Hecks
|
|
|
136
74
|
raise(UnknownVerb,
|
|
137
75
|
RefusalWording.render_site("UnknownVerb", "no_aggregate", domain: domain, aggregate: aggregate_name))
|
|
138
76
|
|
|
139
|
-
# Not every aggregate in a
|
|
140
|
-
#
|
|
141
|
-
# `compute` rule (era_check.rb's own `check_compute_rules!`),
|
|
142
|
-
# which can only ever run against Postgres, permanently. Its
|
|
143
|
-
# own `.hecksagon` bind stays "Postgres" even when
|
|
144
|
-
# `dispatched_by("Lambda")` is on for everything else — checked
|
|
145
|
-
# here by real capability (`Ports::Persistence::RemoteRuntime`,
|
|
146
|
-
# §1), not by comparing the adapter's own name to the string
|
|
147
|
-
# "Lambda" — a bind resolves to whatever adapter class actually
|
|
148
|
-
# backs it, and only a class shaped like "the real interpreter
|
|
149
|
-
# lives behind a call boundary" forwards here; anything else
|
|
150
|
-
# (Postgres, Memory, any future local adapter) falls through to
|
|
151
|
-
# the real local Dispatcher instead of being forwarded to a
|
|
152
|
-
# Lambda that has no way to represent its lineage history at
|
|
153
|
-
# all.
|
|
77
|
+
# Not every aggregate in a routed domain is Lambda-bound (a `compute` rule can
|
|
78
|
+
# only run against Postgres). Test the adapter's capability, not its name.
|
|
154
79
|
adapter_name = Ports::Persistence::BindingPolicy.resolve(@registry, domain, aggregate).adapter
|
|
155
80
|
unless @registry.adapter_class(adapter_name) <= Ports::Persistence::RemoteRuntime
|
|
156
81
|
return @local.dispatch_flat(verb, args.merge(saga_correlation: saga_correlation))
|
|
@@ -161,15 +86,8 @@ module Hecks
|
|
|
161
86
|
refusal = response.fetch("refusals", []).find { |r| r["verb"] == verb }
|
|
162
87
|
raise RemoteRefusal, "#{verb} refused: #{refusal['error']}" if refusal
|
|
163
88
|
|
|
164
|
-
#
|
|
165
|
-
#
|
|
166
|
-
# Phase 1), so `.last` is exactly the step just dispatched.
|
|
167
|
-
# Matched by fully-qualified aggregate name, not just "the
|
|
168
|
-
# first mutation" — a command whose reaction also mutates a
|
|
169
|
-
# different aggregate (a policy, a saga leg) puts more than
|
|
170
|
-
# one mutation in the same step, and the direct effect of
|
|
171
|
-
# this verb is the one this dispatch's own caller expects
|
|
172
|
-
# `.instance` to be.
|
|
89
|
+
# `mutations` has one entry per replayed step, so `.last` is this step. Match by
|
|
90
|
+
# aggregate name: a reaction can mutate other aggregates in the same step.
|
|
173
91
|
fqn = "#{domain}::#{aggregate.hecks_name}"
|
|
174
92
|
mutation = response.fetch("mutations", []).last&.find { |m| m["aggregate"] == fqn } ||
|
|
175
93
|
raise(WiringError,
|
|
@@ -181,46 +99,17 @@ module Hecks
|
|
|
181
99
|
Result.new(verb: verb, instance: instance, events: step_events(response))
|
|
182
100
|
end
|
|
183
101
|
|
|
184
|
-
# Delegates to the local `Dispatcher
|
|
185
|
-
# class comment on why reads, unlike writes, need no remote-specific logic.
|
|
186
|
-
#
|
|
187
|
-
# @param verb [String, Symbol] the query's verb, in one of `Dispatcher#query`'s
|
|
188
|
-
# three shapes
|
|
189
|
-
# @param args [Hash{Symbol => Object}] the query's declared arguments
|
|
190
|
-
# @return [Array<Hash>] see `Dispatcher#query`'s own return
|
|
191
|
-
# @raise [Runtime::UnknownVerb] if the verb is not fully qualified, or names a
|
|
192
|
-
# domain, aggregate, entity, query or read model that is not declared
|
|
193
|
-
# @raise [Runtime::NotFound] if a read model's root reference names no record
|
|
194
|
-
# @raise [Runtime::TypeMismatch] if an argument cannot be coerced to its declared type
|
|
195
|
-
# @raise [KeyError] if a rooted read model is asked without its reference argument
|
|
196
|
-
# @raise [Runtime::WiringError] if the aggregate's repository cannot be resolved
|
|
102
|
+
# Delegates to the local `Dispatcher`; see `Dispatcher#query`.
|
|
197
103
|
def query(verb, **args) = @local.query(verb, **args)
|
|
198
104
|
|
|
199
|
-
# Delegates to the local `Dispatcher
|
|
200
|
-
# class comment on why reads, unlike writes, need no remote-specific logic.
|
|
201
|
-
#
|
|
202
|
-
# @param verb [String] the fully qualified query verb, `"Domain::Aggregate.Query"`
|
|
203
|
-
# or `"Domain::Aggregate.Entity.Query"`
|
|
204
|
-
# @param args [Hash{Symbol => Object}] the query's declared arguments
|
|
205
|
-
# @return [Array<Hash>] one Hash per matching record, its state with `:id` merged
|
|
206
|
-
# in last; for an entity query, one Hash per matching element
|
|
207
|
-
# @raise [Runtime::UnknownVerb] if the verb is not fully qualified, or names a
|
|
208
|
-
# domain, aggregate, entity or query that is not declared
|
|
209
|
-
# @raise [Runtime::TypeMismatch] if an argument cannot be coerced to its declared type
|
|
210
|
-
# @raise [Runtime::WiringError] if the aggregate's repository cannot be resolved
|
|
105
|
+
# Delegates to the local `Dispatcher`; see `Dispatcher#reference_query`.
|
|
211
106
|
def reference_query(verb, **args) = @local.reference_query(verb, **args)
|
|
212
107
|
|
|
213
|
-
# Fetches the
|
|
214
|
-
#
|
|
215
|
-
# so its own `events` array already is the complete log, the
|
|
216
|
-
# same thing `@registry.event_log` would answer for a local
|
|
217
|
-
# dispatch. Not cached: `AggregateDoor.events`/`Handle#events`
|
|
218
|
-
# are not called in this codebase's own hot paths today: if that
|
|
219
|
-
# changes, caching belongs here, not in every caller.
|
|
108
|
+
# Fetches the domain's whole event history from the routed Lambda on every call.
|
|
109
|
+
# Uncached: nothing calls it on a hot path.
|
|
220
110
|
#
|
|
221
|
-
# @return [Array<Runtime::Event>] every event
|
|
222
|
-
#
|
|
223
|
-
# timestamp-free by design)
|
|
111
|
+
# @return [Array<Runtime::Event>] every journaled event, oldest first, with
|
|
112
|
+
# `occurred_at` always nil
|
|
224
113
|
def events
|
|
225
114
|
@client.read.fetch("events", []).map { |e| build_event(e) }
|
|
226
115
|
end
|
|
@@ -231,12 +120,8 @@ module Hecks
|
|
|
231
120
|
response.fetch("events", []).map { |e| build_event(e) }
|
|
232
121
|
end
|
|
233
122
|
|
|
234
|
-
#
|
|
235
|
-
#
|
|
236
|
-
# (nothing in the kernel tracks wall-clock time; every
|
|
237
|
-
# replay is deterministic and timestamp-free by design). `nil`
|
|
238
|
-
# here, not a synthesized `Time.now` that would silently lie
|
|
239
|
-
# about when something actually happened.
|
|
123
|
+
# The kernel's event JSON carries no timestamp, so `occurred_at` stays nil
|
|
124
|
+
# rather than a synthesized `Time.now`.
|
|
240
125
|
def build_event(json)
|
|
241
126
|
Event.new(name: json["name"], aggregate: json["aggregate"], id: json["id"],
|
|
242
127
|
payload: JSON.parse(JSON.generate(json["payload"]), symbolize_names: true), occurred_at: nil)
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Runtime
|
|
3
|
-
# The invocation address
|
|
4
|
-
#
|
|
5
|
-
# the aggregate receiver followed by one identity for every entity hop.
|
|
6
|
-
#
|
|
7
|
-
# Reading a call's shape — what `to:` and `with:` mean, the BUG#7/#17/#18
|
|
8
|
-
# rules — lives in `Runtime::Invocation.from_call` now (invocation.rb).
|
|
9
|
-
# `envelope`/`payload` are kept as thin delegators for any caller still
|
|
10
|
-
# naming them; the dispatcher itself builds an Invocation instead.
|
|
3
|
+
# The invocation address (`to:`), kept apart from a command's domain payload.
|
|
4
|
+
# Thin delegators to `Runtime::Invocation`, which reads a call's shape.
|
|
11
5
|
module Routing
|
|
12
6
|
Envelope = Struct.new(:aggregate, :entities, keyword_init: true) do
|
|
13
7
|
# @param aggregate [String, #to_s] the receiving aggregate's identity
|
|
@@ -21,8 +15,7 @@ module Hecks
|
|
|
21
15
|
|
|
22
16
|
module_function
|
|
23
17
|
|
|
24
|
-
# Resolves a `to:` argument into a routing envelope
|
|
25
|
-
# `Invocation.route`; the dispatcher itself builds an `Invocation` instead.
|
|
18
|
+
# Resolves a `to:` argument into a routing envelope (see `Invocation.route`).
|
|
26
19
|
#
|
|
27
20
|
# @param to [String, Hash, nil] a bare aggregate identity, or a Hash with
|
|
28
21
|
# `aggregate:` and `entity:`/`entities:`; nil for no receiver
|
|
@@ -32,11 +25,7 @@ module Hecks
|
|
|
32
25
|
# match `entity_depth`
|
|
33
26
|
def envelope(to, entity_depth: 0) = Invocation.route(to, entity_depth: entity_depth)
|
|
34
27
|
|
|
35
|
-
# Resolves a command's offered facts into its flat args hash
|
|
36
|
-
# delegator to `Invocation.facts_for` for any caller still naming it.
|
|
37
|
-
#
|
|
38
|
-
# Loaded by invocation.rb (which requires this file), so `Invocation`
|
|
39
|
-
# is always defined by the time either method runs.
|
|
28
|
+
# Resolves a command's offered facts into its flat args hash (see `Invocation.facts_for`).
|
|
40
29
|
#
|
|
41
30
|
# @param command [Class] a `Bluebook::Command` subclass (or port-operation class)
|
|
42
31
|
# responding to `hecks_name` and `attributes`
|
|
@@ -3,80 +3,33 @@ require_relative "../value"
|
|
|
3
3
|
module Hecks
|
|
4
4
|
module Runtime
|
|
5
5
|
class SagaInterpreter
|
|
6
|
-
# How a saga decides which conversation an event belongs to
|
|
7
|
-
# tiers, each one a lesson.
|
|
6
|
+
# How a saga decides which conversation an event belongs to.
|
|
8
7
|
module Correlation
|
|
9
8
|
private
|
|
10
9
|
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# itself? its serialised text?). `:"end_to_end.value"` reads the one
|
|
16
|
-
# field with a single unambiguous rendering.
|
|
10
|
+
# Resolves the correlation key in three tiers: the payload path, the stamp from
|
|
11
|
+
# the dispatching saga, then the emitting record's own identity.
|
|
12
|
+
# A dotted path (`:"end_to_end.value"`) names one scalar; a whole value object
|
|
13
|
+
# has no single unambiguous rendering as a key.
|
|
17
14
|
def saga_correlation(process_manager, event)
|
|
18
15
|
path = process_manager.correlates_by.to_s.split(".")
|
|
19
|
-
# A
|
|
20
|
-
#
|
|
21
|
-
# `reference` is a TransferReference) — but a downstream event this
|
|
22
|
-
# same value was smuggled through as a passthrough argument
|
|
23
|
-
# (AccountDebited's `reference:`, resolved by `dispatch_args` to the
|
|
24
|
-
# bare correlation string) carries it as a scalar already, with
|
|
25
|
-
# nothing left to dig. `"xfer-1".respond_to?(:[])` is true — String
|
|
26
|
-
# has its own `[]` (substring indexing) — so checking for keyed
|
|
27
|
-
# lookup explicitly, rather than "responds to `[]` at all", is what
|
|
28
|
-
# stops the second segment from being read as a symbol index into a
|
|
29
|
-
# string that has already arrived.
|
|
16
|
+
# A downstream event may carry the bare scalar already. Strings respond to `[]`,
|
|
17
|
+
# so test for Hash/Value explicitly rather than `respond_to?(:[])`.
|
|
30
18
|
value = path.reduce(event.payload) do |held, segment|
|
|
31
19
|
held.is_a?(Hash) || held.is_a?(Value) ? held[segment.to_sym] : held
|
|
32
20
|
end
|
|
33
21
|
return value unless value.to_s.empty?
|
|
34
22
|
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
# own reference key (the two tiers above). command_interpreter/
|
|
39
|
-
# argument_gate.rb names the old payload-only lookup "the weakest
|
|
40
|
-
# part of the gate" : a correlation key arriving on a command only
|
|
41
|
-
# because `correlation_keys` widens the undeclared-argument
|
|
42
|
-
# allow-list domain-wide. This is the additive fix — a leg that
|
|
43
|
-
# carries nothing correlation-shaped at all still correlates,
|
|
44
|
-
# because the saga that dispatched it already knows the answer.
|
|
45
|
-
# Keyed by `correlation_head`
|
|
46
|
-
# rather than a bare scalar so an event stamped by one saga cannot be
|
|
47
|
-
# misread by an unrelated one correlating on a different field.
|
|
23
|
+
# The stamp `deliver_saga_dispatch` puts on its own event, for legs whose command
|
|
24
|
+
# carries no correlation field. Keyed by `correlation_head` so another saga
|
|
25
|
+
# correlating on a different field cannot misread it.
|
|
48
26
|
stamped = event.correlation && event.correlation[process_manager.correlation_head.to_s]
|
|
49
27
|
return stamped unless stamped.nil? || stamped.to_s.empty?
|
|
50
28
|
|
|
51
|
-
# A self-referencing leg
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
# (`to:`/`with:`, the facade's own `Handle#run` always uses it), so
|
|
56
|
-
# `own_key` (the aggregate's own reference-key convention, "wire",
|
|
57
|
-
# "transfer") never rides along in the payload unfiltered. Digging
|
|
58
|
-
# for it there would read an empty Hash for any self-referencing
|
|
59
|
-
# leg with no other declared attributes (`OnboardingCase.Clear`,
|
|
60
|
-
# `.Decline` — no `attribute` lines at all), silently stopping the
|
|
61
|
-
# saga from advancing, forever, for exactly the leg this tier
|
|
62
|
-
# exists to correlate.
|
|
63
|
-
#
|
|
64
|
-
# `event.id` says the identical thing this tier always meant —
|
|
65
|
-
# "the record that just emitted this event, by its own identity" —
|
|
66
|
-
# and unlike a payload dig it is populated by the record itself,
|
|
67
|
-
# not by which dispatch convention the caller happened to use.
|
|
68
|
-
#
|
|
69
|
-
# Gated, still — a manually-dispatched command on a wholly
|
|
70
|
-
# unrelated aggregate can share an event name this process_manager happens to
|
|
71
|
-
# handle (`Drawer.Take` also emits "Taken", the same name a
|
|
72
|
-
# saga-dispatched leg uses) with nothing this saga should read as
|
|
73
|
-
# its own conversation. What makes a leg genuinely
|
|
74
|
-
# self-referencing — the one fact worth trusting `event.id`
|
|
75
|
-
# for — is that `correlates_by`'s own head field is this event's
|
|
76
|
-
# own aggregate's declared identity, not merely a same-shaped
|
|
77
|
-
# name: `OnboardingCase.identity_heads` really does include
|
|
78
|
-
# `:reference`, `correlates_by :"reference.value"`'s own head ;
|
|
79
|
-
# `Drawer.identity_heads` is `[:number]`, nowhere close.
|
|
29
|
+
# A self-referencing leg correlates by `event.id`: routing keys never ride in the
|
|
30
|
+
# payload, so a payload dig finds nothing for legs with no declared attributes.
|
|
31
|
+
# Gated on the head being the aggregate's own identity, so an unrelated aggregate
|
|
32
|
+
# emitting a same-named event is not read as this saga's conversation.
|
|
80
33
|
self_identified?(process_manager, event) ? event.id : nil
|
|
81
34
|
end
|
|
82
35
|
|