hecks 2.7.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 +11 -0
- 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 +18 -99
- 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 +15 -37
- 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 +32 -142
- 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 +17 -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 +38 -26
- 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 +17 -65
- 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 -189
- 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 -106
- 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 +54 -268
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +47 -308
- 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 +10 -91
- data/lib/hecks/bluebook/model_check.rb +102 -600
- 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 +84 -0
- data/lib/hecks/cli/document.rb +86 -0
- data/lib/hecks/cli/ir.rb +45 -0
- data/lib/hecks/cli/mcp.rb +31 -0
- data/lib/hecks/cli/mcp_door.rb +366 -0
- data/lib/hecks/cli/model_check.rb +203 -0
- data/lib/hecks/cli/project_cli.rb +90 -0
- data/lib/hecks/cli/project_diagrams.rb +36 -0
- data/lib/hecks/cli/run.rb +173 -0
- data/lib/hecks/cli/smoke_test.rb +45 -0
- data/lib/hecks/cli/stores.rb +53 -0
- data/lib/hecks/cli.rb +160 -0
- 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 +98 -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 +95 -0
- 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 +62 -65
- 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 +7 -26
- 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 +22 -122
- 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 +151 -679
- 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 +9 -16
- 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 +46 -138
- 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 +93 -0
- data/lib/hecks/release/runner/commands.rb +66 -0
- data/lib/hecks/release/runner/console.rb +52 -0
- data/lib/hecks/release/runner/gem_publisher.rb +43 -0
- data/lib/hecks/release/runner/git.rb +48 -0
- data/lib/hecks/release/runner/npm_publisher.rb +75 -0
- data/lib/hecks/release/runner/preflight.rb +104 -0
- data/lib/hecks/release/runner/published.rb +55 -0
- data/lib/hecks/release/runner/tagger.rb +80 -0
- data/lib/hecks/release/runner.rb +215 -0
- 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 +125 -307
- 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 +65 -220
- 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 +139 -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 +103 -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 +146 -567
- 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/vocabulary.rb +5 -0
- data/lib/hecks.rb +32 -82
- 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 +30 -59
- data/lib/hecks/bench/cli.rb +0 -74
- data/lib/hecks/bench/environment.rb +0 -100
- data/lib/hecks/bench/postgres_probe.rb +0 -41
- data/lib/hecks/bench/report.rb +0 -116
- data/lib/hecks/bench/ruby_runner.rb +0 -151
- data/lib/hecks/bench/run.rb +0 -55
- data/lib/hecks/bench/rust_runner.rb +0 -165
- data/lib/hecks/bench/stats.rb +0 -75
- data/lib/hecks/bench/suite.rb +0 -172
- data/lib/hecks/bench/workload.rb +0 -137
- data/lib/hecks/bench.rb +0 -28
- data/lib/hecks/codemod.rb +0 -443
- data/lib/hecks/corpus.rb +0 -479
- data/lib/hecks/doc/reference.rb +0 -585
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -684
- data/lib/hecks/fuzzing/combination_miner.rb +0 -178
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +0 -469
- data/lib/hecks/fuzzing/coverage_campaign.rb +0 -161
- data/lib/hecks/fuzzing/differential.rb +0 -192
- data/lib/hecks/fuzzing/domain_generator.rb +0 -871
- data/lib/hecks/fuzzing/era_boundary.rb +0 -154
- data/lib/hecks/fuzzing/form_census.rb +0 -285
- data/lib/hecks/fuzzing/generated_domain_check.rb +0 -171
- data/lib/hecks/fuzzing/invalid_value_generator.rb +0 -125
- data/lib/hecks/fuzzing/isolated_boot.rb +0 -658
- data/lib/hecks/fuzzing/nondeterministic.rb +0 -79
- data/lib/hecks/fuzzing/persistence_parity.rb +0 -253
- data/lib/hecks/fuzzing/properties/corrections.rb +0 -125
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +0 -688
- data/lib/hecks/fuzzing/properties/guards.rb +0 -398
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +0 -399
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +0 -137
- data/lib/hecks/fuzzing/properties/outbox.rb +0 -180
- data/lib/hecks/fuzzing/properties/querying.rb +0 -260
- data/lib/hecks/fuzzing/properties.rb +0 -267
- data/lib/hecks/fuzzing/qa_settings.rb +0 -164
- data/lib/hecks/fuzzing/replay.rb +0 -930
- data/lib/hecks/fuzzing/rotation_priority.rb +0 -114
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +0 -139
- data/lib/hecks/fuzzing/self_consistency.rb +0 -825
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +0 -532
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +0 -171
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +0 -127
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +0 -116
- data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +0 -138
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +0 -344
- data/lib/hecks/fuzzing/sequence_generator.rb +0 -311
- data/lib/hecks/fuzzing/shrinker.rb +0 -309
- data/lib/hecks/fuzzing/structural_skips.rb +0 -53
- data/lib/hecks/fuzzing/sweep_depth.rb +0 -61
- data/lib/hecks/fuzzing/target_capabilities.rb +0 -255
- data/lib/hecks/fuzzing/value_generator.rb +0 -340
- data/lib/hecks/fuzzing.rb +0 -23
- data/lib/hecks/grammar/evolve.rb +0 -524
- data/lib/hecks/query_ir.rb +0 -482
|
@@ -11,69 +11,21 @@ module Hecks
|
|
|
11
11
|
# Whether a command may run at all: its declared givens, and the
|
|
12
12
|
# lifecycle transition it asks for.
|
|
13
13
|
module Admissibility
|
|
14
|
-
# Wraps `subject` so a guard
|
|
15
|
-
#
|
|
16
|
-
# #hydrate_with_defaults` deliberately leaves one absent rather
|
|
17
|
-
# than nil-filled — "an attribute with no default stays absent,
|
|
18
|
-
# exactly as stored," that file's own comment — which is right
|
|
19
|
-
# for storage fidelity but wrong for a `given`/`ensures` reading
|
|
20
|
-
# an optional field a record predates (measured, not assumed: a
|
|
21
|
-
# real Item.Promote crashed on `!promoted` against a record from
|
|
22
|
-
# before `promoted` existed). Defensive on purpose — a `subject`
|
|
23
|
-
# with no `.aggregate` (an entity's own view/settled wrapper,
|
|
24
|
-
# entity_interpreter.rb's own callers) just degrades to today's
|
|
25
|
-
# exact behavior, zero risk to a path this bug was never measured
|
|
26
|
-
# against.
|
|
27
|
-
#
|
|
28
|
-
# NIL is not the only outcome, though. The Item.Promote fix above
|
|
29
|
-
# covered an optional field — the honest case, where absence is
|
|
30
|
-
# exactly what optional means. It also, as a side effect, let a
|
|
31
|
-
# non-optional field predating a record read nil the same way,
|
|
32
|
-
# which is the `ne:`/array-`in:` bug class applied to rule
|
|
33
|
-
# evaluation: a predicate silently answers against a value nobody
|
|
34
|
-
# ever wrote (ADR 0025, "Added attributes and absence"). `[]`
|
|
35
|
-
# below narrows the nil-read back to what it was built for —
|
|
36
|
-
# optional stays nil, non-optional raises a named refusal
|
|
37
|
-
# identifying the field, so a rule that reads it fails loud
|
|
38
|
-
# instead of quietly wrong.
|
|
14
|
+
# Wraps `subject` so a guard reads a declared, optional attribute
|
|
15
|
+
# that predates the record as nil instead of raising.
|
|
39
16
|
class GuardState
|
|
40
|
-
# @param instance [Runtime::Instance] the record a `given`/`ensures`/
|
|
41
|
-
# `invariant` rule reads state from
|
|
42
17
|
def initialize(instance)
|
|
43
18
|
@instance = instance
|
|
44
19
|
@declared = instance.respond_to?(:aggregate) ? instance.aggregate.attributes.to_h { |a| [a.name, a] } : {}
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
# rather than folded into `attributes` (see that field's
|
|
48
|
-
# own comment): a projected field's absence means
|
|
49
|
-
# something different from an ordinary attribute's, so it
|
|
50
|
-
# needs its own refusal below, not `AttributeAbsent`'s.
|
|
51
|
-
# `projects` is aggregate-scoped only — an entity's own
|
|
52
|
-
# `instance.aggregate` answers the entity construct here
|
|
53
|
-
# (EntityInterpreter's own subject), which declares no
|
|
54
|
-
# `projected_fields` of its own, hence the extra guard
|
|
55
|
-
# `@declared` above does not need.
|
|
20
|
+
# Separate index: a projected field's absence raises its own
|
|
21
|
+
# refusal, not AttributeAbsent's (ADR 0025).
|
|
56
22
|
owner = instance.aggregate if instance.respond_to?(:aggregate)
|
|
57
23
|
@projected = owner.respond_to?(:projected_fields) ? owner.projected_fields.to_h { |f| [f.name, f] } : {}
|
|
58
24
|
end
|
|
59
25
|
|
|
60
|
-
# Reports whether `name` is a declared attribute, a projected field, or
|
|
61
|
-
# a key `instance`'s own state actually holds.
|
|
62
|
-
#
|
|
63
|
-
# @param name [String, Symbol] the field name to check
|
|
64
|
-
# @return [Boolean] true if `name` is readable one of those three ways
|
|
65
26
|
def key?(name) = @declared.key?(name.to_sym) || @projected.key?(name.to_sym) || @instance.key?(name)
|
|
66
27
|
|
|
67
|
-
#
|
|
68
|
-
# absence to read.
|
|
69
|
-
#
|
|
70
|
-
# @param name [String, Symbol] the field name to read
|
|
71
|
-
# @return [Object, nil] the stored value; nil for a declared, optional
|
|
72
|
-
# attribute the record predates
|
|
73
|
-
# @raise [Runtime::AttributeAbsent] if `name` is a declared, non-optional
|
|
74
|
-
# attribute the record's own state does not hold
|
|
75
|
-
# @raise [Runtime::ProjectionAbsent] if `name` is a `projects` field the
|
|
76
|
-
# record's own state does not hold
|
|
28
|
+
# Nil for an absent optional attribute; raises for anything else absent.
|
|
77
29
|
def [](name)
|
|
78
30
|
return @instance[name] if @instance.key?(name)
|
|
79
31
|
|
|
@@ -99,83 +51,23 @@ module Hecks
|
|
|
99
51
|
end
|
|
100
52
|
private_constant :GuardState
|
|
101
53
|
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
# declaring aggregate/entity when `subject` carries one (an
|
|
105
|
-
# entity's pre-mutation `view` does, same as an aggregate's
|
|
106
|
-
# `Instance`); a `subject` with no `.aggregate` just hydrates
|
|
107
|
-
# nothing from state, same as GuardState degrades above.
|
|
108
|
-
#
|
|
109
|
-
# Merge order matters, and it is not "args always win": an
|
|
110
|
-
# unaliased command-level reference dereferences under a
|
|
111
|
-
# different name than the argument holds (`account_id` the arg,
|
|
112
|
-
# `account` the hydrated key — no collision, order is moot). An
|
|
113
|
-
# aliased one (`reference_to Customer, as: :customer`) hydrates
|
|
114
|
-
# under the same name the argument itself holds — `customer` is
|
|
115
|
-
# both the raw id an arg puts there and the key `customer.status`
|
|
116
|
-
# expects to dig into. If `args` merged last, the raw id (a
|
|
117
|
-
# String) would win and `.status` on a String is where a fuzzer
|
|
118
|
-
# found this — TypeError, not a refusal. Command-level
|
|
119
|
-
# dereferencing is the one thing that is supposed to override
|
|
120
|
-
# its own source argument for exactly this reason; args still
|
|
121
|
-
# wins over stored owner state.
|
|
122
|
-
# `parent:` is an entity command's own parent aggregate record
|
|
123
|
-
# (EntityInterpreter's `ctx.instance` — "the parent aggregate
|
|
124
|
-
# record", its own doc comment) — the entity's containment, not a
|
|
125
|
-
# declared reference attribute, so it doesn't come from
|
|
126
|
-
# `dereference`'s attribute scan the way `owner`'s do. Hydrated
|
|
127
|
-
# the same shape regardless: the parent's own state, merged so
|
|
128
|
-
# the dereferenced hash wins over the raw reference it replaces
|
|
129
|
-
# (ADR 0025 drops the `_id` suffix that once kept the two
|
|
130
|
-
# apart by name, so `parent.state.merge(dereference(...))` is
|
|
131
|
-
# load-bearing, not redundant) — plus its own references
|
|
132
|
-
# dereferenced one level in, so `parent.customer.status` (a
|
|
133
|
-
# parent aggregate reaching its own customer) resolves the same
|
|
134
|
-
# way `account.customer.status` does for a command-level reference.
|
|
135
|
-
# nil for an aggregate command — CommandInterpreter never passes it.
|
|
136
|
-
# `correction:` — the `{as_name => payload}` bindings
|
|
137
|
-
# `enforce_correction_target` (above) already located, merged in
|
|
138
|
-
# last so an `as:` name wins the same way `old:` always wins in
|
|
139
|
-
# `enforce_ensures`, below — it is a fresh local binding a
|
|
140
|
-
# `corrects` command introduces, not a real argument/state field
|
|
141
|
-
# a caller could collide with by accident.
|
|
54
|
+
# Checks whether `command` may run against `subject`: its declared
|
|
55
|
+
# `given`s, then `declaring`'s lifecycle guard.
|
|
142
56
|
#
|
|
143
|
-
# @param subject [Runtime::Instance]
|
|
144
|
-
#
|
|
145
|
-
#
|
|
146
|
-
#
|
|
147
|
-
# @param
|
|
148
|
-
# readable by a given
|
|
149
|
-
# @param domain [String, Symbol] the domain a reference-typed argument is
|
|
150
|
-
# dereferenced in
|
|
151
|
-
# @param declaring [Bluebook::Aggregate, Bluebook::Entity, nil] the construct
|
|
152
|
-
# `command` is declared on; also checks `enforce_lifecycle_guard` when given
|
|
153
|
-
# @param parent [Runtime::Instance, nil] an entity command's own parent
|
|
154
|
-
# aggregate record, readable as `parent.*`; nil for an aggregate command
|
|
155
|
-
# @param correction [Hash{Symbol => Object}] the `{as_name => payload}`
|
|
156
|
-
# bindings a `corrects` command's located old event offers
|
|
57
|
+
# @param subject [Runtime::Instance] pre-mutation record a `given` reads
|
|
58
|
+
# @param declaring [Bluebook::Aggregate, Bluebook::Entity, nil] also runs
|
|
59
|
+
# the lifecycle guard when given
|
|
60
|
+
# @param parent [Runtime::Instance, nil] entity command's parent record
|
|
61
|
+
# @param correction [Hash{Symbol => Object}] `corrects` event bindings
|
|
157
62
|
# @return [void]
|
|
158
|
-
# @raise [Runtime::GivenNotMet]
|
|
159
|
-
# @raise [Runtime::LifecycleRefused]
|
|
160
|
-
#
|
|
161
|
-
# @raise [Bluebook::Expression::EvaluationError] if a given's own rule cannot
|
|
162
|
-
# be evaluated (an unresolvable field, a bad comparison)
|
|
63
|
+
# @raise [Runtime::GivenNotMet] a declared `given` does not hold
|
|
64
|
+
# @raise [Runtime::LifecycleRefused] `declaring`'s `from:` guard refuses
|
|
65
|
+
# the current lifecycle state
|
|
163
66
|
def enforce_givens(subject, command, args, domain:, declaring: nil, parent: nil, correction: {})
|
|
164
67
|
state = GuardState.new(subject)
|
|
165
|
-
# A rule
|
|
166
|
-
#
|
|
167
|
-
#
|
|
168
|
-
# :"customer.status"` field is just `subject`'s own
|
|
169
|
-
# field, a regular stored attribute, already present in
|
|
170
|
-
# `subject`/`state` with no hydration step needed. `dereference`
|
|
171
|
-
# is still called on `command`/`args`, below — that is a
|
|
172
|
-
# different case the ADR explicitly keeps in bounds ("its command
|
|
173
|
-
# arguments"): a reference-typed argument this dispatch was just
|
|
174
|
-
# handed (`Dispute`'s own `disputed_by`, say) has nothing stored
|
|
175
|
-
# to project yet, so resolving it here, once, synchronously with
|
|
176
|
-
# this command's own admission, is not the live-query-against-
|
|
177
|
-
# another-aggregate's-stored-state pattern the boundary rule
|
|
178
|
-
# forbids.
|
|
68
|
+
# A rule reads only within its own aggregate boundary (ADR 0025);
|
|
69
|
+
# `dereference` below resolves fresh reference arguments only,
|
|
70
|
+
# never a stored `projects` field, which `state` already has.
|
|
179
71
|
attrs = args.merge(dereference(domain, command, args))
|
|
180
72
|
attrs = attrs.merge(parent: parent.state) if parent
|
|
181
73
|
attrs = attrs.merge(correction) unless correction.empty?
|
|
@@ -191,90 +83,38 @@ module Hecks
|
|
|
191
83
|
enforce_lifecycle_guard(declaring, command, subject) if declaring
|
|
192
84
|
end
|
|
193
85
|
|
|
194
|
-
# `corrects`
|
|
195
|
-
#
|
|
196
|
-
#
|
|
197
|
-
#
|
|
198
|
-
# raised structurally here, the same way NotFound/AlreadyExists
|
|
199
|
-
# are, rather than through the expression evaluator. The build-
|
|
200
|
-
# time half — does anything in this aggregate ever emit the named
|
|
201
|
-
# event at all — is `AggregateBuilder#seal_correction_targets`;
|
|
202
|
-
# this is the dispatch-time half — has this record actually done
|
|
203
|
-
# so yet.
|
|
204
|
-
#
|
|
205
|
-
# Also locates the matched event now, not just its existence, and
|
|
206
|
-
# returns a `{as_name => payload}` bindings hash — one entry per
|
|
207
|
-
# `:corrects` mutation that named an `as:` — so `given`/`ensures`
|
|
208
|
-
# on a corrects-bearing command can reference the located old
|
|
209
|
-
# event by that name, the same shape `enforce_ensures`'s own
|
|
210
|
-
# `old:` binding already has (CommandBuilder#corrects_impl's own
|
|
211
|
-
# comment: `as:` was stored, from the start, specifically to be
|
|
212
|
-
# wired into the evaluator once a real runtime consumer existed —
|
|
213
|
-
# this is that consumer). `.reverse.find` — the most recent
|
|
214
|
-
# matching event, if this record has somehow emitted the same
|
|
215
|
-
# correction target more than once; the prior existence-only
|
|
216
|
-
# check never had to make this choice, so it's a genuinely new
|
|
217
|
-
# one, made deliberately: `as:` reads as "the instance being
|
|
218
|
-
# corrected," which is naturally the latest fact on record, not
|
|
219
|
-
# an arbitrary one.
|
|
220
|
-
# Reads the durable event history a `corrects` mutation is judged against.
|
|
221
|
-
#
|
|
222
|
-
# C9.2 (docs/semantics/bluebook-semantics.md) — a correction target
|
|
223
|
-
# is judged against the record's durable history: the events the
|
|
224
|
-
# aggregate's own store recorded (`AppendOnly#events`), which
|
|
225
|
-
# survive a restart the way the Rust kernel's persisted
|
|
226
|
-
# `emitted_<event>` flag does. The in-process log is the fallback
|
|
227
|
-
# only for an adapter that records no readable history.
|
|
86
|
+
# Reads the durable event history a `corrects` mutation is judged
|
|
87
|
+
# against (C9.2), scoped to the one record being corrected so a
|
|
88
|
+
# durable adapter need not load every aggregate's events to check
|
|
89
|
+
# one; falls back to the in-process log when needed.
|
|
228
90
|
#
|
|
229
91
|
# @param domain [String, Symbol] the domain `aggregate` belongs to
|
|
230
92
|
# @param aggregate [Bluebook::Aggregate] the aggregate whose repository is read
|
|
231
|
-
# @
|
|
232
|
-
#
|
|
233
|
-
#
|
|
234
|
-
|
|
235
|
-
|
|
93
|
+
# @param event_key [String] the `"domain::AggregateName"` key events are stored under
|
|
94
|
+
# @param id [String, Object] the record's identity being corrected
|
|
95
|
+
# @return [Array<Runtime::Event>] the record's recorded events, or the
|
|
96
|
+
# registry's in-process log as a fallback
|
|
97
|
+
def correction_history(domain, aggregate, event_key, id)
|
|
98
|
+
repository_events = @registry.repository(domain, aggregate).events_for(aggregate: event_key, id: id)
|
|
99
|
+
return repository_events if repository_events
|
|
100
|
+
|
|
101
|
+
# The in-process log holds every domain's events, so the fallback
|
|
102
|
+
# scopes it the same way a durable adapter's own query would.
|
|
103
|
+
@registry.event_log.select { |event| event.aggregate == event_key && event.id.to_s == id.to_s }
|
|
236
104
|
end
|
|
237
105
|
|
|
238
|
-
# Locates each `:corrects` mutation's
|
|
239
|
-
# binds every `as:`-named one for `given`/`ensures` to reference.
|
|
240
|
-
#
|
|
241
|
-
#
|
|
242
|
-
# expressible as an ordinary `given`: "has this exact record
|
|
243
|
-
# already emitted this exact event" is not a predicate over the
|
|
244
|
-
# record's own fields, it is a fact about the event log, so it is
|
|
245
|
-
# raised structurally here, the same way NotFound/AlreadyExists
|
|
246
|
-
# are, rather than through the expression evaluator. The build-
|
|
247
|
-
# time half — does anything in this aggregate ever emit the named
|
|
248
|
-
# event at all — is `AggregateBuilder#seal_correction_targets`;
|
|
249
|
-
# this is the dispatch-time half — has this record actually done
|
|
250
|
-
# so yet.
|
|
251
|
-
#
|
|
252
|
-
# Also locates the matched event now, not just its existence, and
|
|
253
|
-
# returns a `{as_name => payload}` bindings hash — one entry per
|
|
254
|
-
# `:corrects` mutation that named an `as:` — so `given`/`ensures`
|
|
255
|
-
# on a corrects-bearing command can reference the located old
|
|
256
|
-
# event by that name, the same shape `enforce_ensures`'s own
|
|
257
|
-
# `old:` binding already has (CommandBuilder#corrects_impl's own
|
|
258
|
-
# comment: `as:` was stored, from the start, specifically to be
|
|
259
|
-
# wired into the evaluator once a real runtime consumer existed —
|
|
260
|
-
# this is that consumer). `.reverse.find` — the most recent
|
|
261
|
-
# matching event, if this record has somehow emitted the same
|
|
262
|
-
# correction target more than once; the prior existence-only
|
|
263
|
-
# check never had to make this choice, so it's a genuinely new
|
|
264
|
-
# one, made deliberately: `as:` reads as "the instance being
|
|
265
|
-
# corrected," which is naturally the latest fact on record, not
|
|
266
|
-
# an arbitrary one.
|
|
106
|
+
# Locates each `:corrects` mutation's already-emitted target event
|
|
107
|
+
# and binds every `as:`-named one for `given`/`ensures` to reference.
|
|
108
|
+
# Not an ordinary `given`: this is a log fact, raised structurally,
|
|
109
|
+
# like NotFound/AlreadyExists.
|
|
267
110
|
#
|
|
268
111
|
# @param instance [Runtime::Instance] the record being corrected
|
|
269
112
|
# @param aggregate [Bluebook::Aggregate] the aggregate `instance` belongs to
|
|
270
|
-
# @param command [Class] the command
|
|
271
|
-
# whose `:corrects` mutations are located
|
|
113
|
+
# @param command [Class] the command whose `:corrects` mutations are located
|
|
272
114
|
# @param domain [String, Symbol] the domain `aggregate` belongs to
|
|
273
|
-
# @return [Hash{Symbol => Object}]
|
|
274
|
-
#
|
|
275
|
-
#
|
|
276
|
-
# @raise [Runtime::NothingToCorrect] if a `:corrects` mutation names an
|
|
277
|
-
# event `instance` has never emitted
|
|
115
|
+
# @return [Hash{Symbol => Object}] payload per mutation's `as:` name; empty
|
|
116
|
+
# for a mutation with no `as:`
|
|
117
|
+
# @raise [Runtime::NothingToCorrect] a named event `instance` never emitted
|
|
278
118
|
def enforce_correction_target(instance, aggregate, command, domain:)
|
|
279
119
|
bindings = {}
|
|
280
120
|
command.mutations.each do |mutation|
|
|
@@ -282,8 +122,11 @@ module Hecks
|
|
|
282
122
|
|
|
283
123
|
event_key = "#{domain}::#{aggregate.hecks_name}"
|
|
284
124
|
event_name = mutation.target.to_s
|
|
285
|
-
|
|
286
|
-
|
|
125
|
+
# Most recent match wins if this record emitted the same event
|
|
126
|
+
# more than once; aggregate and id are already scoped by
|
|
127
|
+
# correction_history, so only the event name is filtered here.
|
|
128
|
+
corrected = correction_history(domain, aggregate, event_key, instance.id).reverse.find do |event|
|
|
129
|
+
event.name == event_name
|
|
287
130
|
end
|
|
288
131
|
|
|
289
132
|
unless corrected
|
|
@@ -298,25 +141,16 @@ module Hecks
|
|
|
298
141
|
bindings
|
|
299
142
|
end
|
|
300
143
|
|
|
301
|
-
#
|
|
302
|
-
#
|
|
303
|
-
# dispatch step `given` already runs at (both are preconditions,
|
|
304
|
-
# evaluated before any mutation) rather than earning its own
|
|
305
|
-
# DISPATCH_ORDER entry. A guard, never a transition: it names no
|
|
306
|
-
# target state and `step_advance_lifecycle` never sees it — see
|
|
307
|
-
# `admissible_transition`, right below, for the transition this
|
|
308
|
-
# is deliberately not reusing (its own `StateTransition#target`
|
|
309
|
-
# is required, and a guard-only command has none to give it).
|
|
144
|
+
# Checks a command's own `from:` lifecycle guard — a precondition,
|
|
145
|
+
# not a transition; `admissible_transition` below handles moves.
|
|
310
146
|
#
|
|
311
|
-
# @param declaring [Bluebook::Aggregate, Bluebook::Entity]
|
|
312
|
-
#
|
|
313
|
-
# @param command [Class] the command class
|
|
314
|
-
#
|
|
315
|
-
# @param subject [Runtime::Instance] the pre-mutation record to read the
|
|
316
|
-
# current lifecycle state off
|
|
147
|
+
# @param declaring [Bluebook::Aggregate, Bluebook::Entity] construct whose
|
|
148
|
+
# lifecycle field is checked
|
|
149
|
+
# @param command [Class] the command class whose `from:` guard is checked
|
|
150
|
+
# @param subject [Runtime::Instance] pre-mutation record to read lifecycle off
|
|
317
151
|
# @return [void]
|
|
318
|
-
# @raise [Runtime::LifecycleRefused]
|
|
319
|
-
#
|
|
152
|
+
# @raise [Runtime::LifecycleRefused] `command` declares `from:` and current
|
|
153
|
+
# state isn't one of them
|
|
320
154
|
def enforce_lifecycle_guard(declaring, command, subject)
|
|
321
155
|
return unless command.from
|
|
322
156
|
|
|
@@ -324,15 +158,8 @@ module Hecks
|
|
|
324
158
|
current = Value.scalar(subject[lifecycle.field]).to_s
|
|
325
159
|
return if Array(command.from).include?(current)
|
|
326
160
|
|
|
327
|
-
# Routed through
|
|
328
|
-
#
|
|
329
|
-
# already raises LifecycleRefused through for the same
|
|
330
|
-
# refusal class, rather than hand-rolling its own wording
|
|
331
|
-
# inline ("...only runs from..." vs. the template's "...moves
|
|
332
|
-
# it only from..."), two shapes for one refusal kind, so
|
|
333
|
-
# anything string-matching a LifecycleRefused message (a
|
|
334
|
-
# property, a spec, a caller) would have to know both existed
|
|
335
|
-
# rather than one.
|
|
161
|
+
# Routed through the same "transition_blocked" template
|
|
162
|
+
# `#admissible_transition` uses, so both raise identical wording.
|
|
336
163
|
raise LifecycleRefused,
|
|
337
164
|
RefusalWording.render_site("LifecycleRefused", "transition_blocked",
|
|
338
165
|
command: command.hecks_name, field: lifecycle.field,
|
|
@@ -340,55 +167,21 @@ module Hecks
|
|
|
340
167
|
allowed: Array(command.from))
|
|
341
168
|
end
|
|
342
169
|
|
|
343
|
-
#
|
|
344
|
-
#
|
|
345
|
-
#
|
|
346
|
-
# the attrs at evaluation time only; the payload gate never sees it.
|
|
347
|
-
#
|
|
348
|
-
# `old` — and every dispatch argument — wins over a same-named state
|
|
349
|
-
# field in expression scope (Resolver#fetch checks attrs first). An
|
|
350
|
-
# ensures naming a field the command also takes as an argument (or,
|
|
351
|
-
# on an entity, a field that doubles as the addressing argument
|
|
352
|
-
# element_of reads) will read the argument, not the settled value.
|
|
353
|
-
# Not new to ensures — `given` lives under the same rule — but an
|
|
354
|
-
# ensures is more likely to collide, since it typically re-reads a
|
|
355
|
-
# field the command just took in to mutate it.
|
|
170
|
+
# Checks `command`'s declared `ensures` against `subject`, the settled
|
|
171
|
+
# post-mutation record; `old` carries the pre-mutation state.
|
|
172
|
+
# An argument sharing a settled field's name does not shadow it here.
|
|
356
173
|
#
|
|
357
|
-
# @param subject [Runtime::Instance] the settled, post-mutation record
|
|
358
|
-
#
|
|
359
|
-
# @param
|
|
360
|
-
#
|
|
361
|
-
# @param args [Hash{String, Symbol => Object}] the offered command arguments,
|
|
362
|
-
# readable by an ensures unless `subject` shares the same field name
|
|
363
|
-
# @param old [Hash{Symbol => Object}, nil] the pre-mutation state, readable
|
|
364
|
-
# as `old.*`; nil when `command` declares no ensures (dup skipped upstream)
|
|
365
|
-
# @param domain [String, Symbol] the domain a reference-typed argument is
|
|
366
|
-
# dereferenced in
|
|
367
|
-
# @param parent [Runtime::Instance, nil] an entity command's own parent
|
|
368
|
-
# aggregate record, readable as `parent.*`; nil for an aggregate command
|
|
369
|
-
# @param correction [Hash{Symbol => Object}] the `{as_name => payload}`
|
|
370
|
-
# bindings a `corrects` command's located old event offers
|
|
174
|
+
# @param subject [Runtime::Instance] the settled, post-mutation record
|
|
175
|
+
# @param old [Hash{Symbol => Object}, nil] pre-mutation state, readable as `old.*`
|
|
176
|
+
# @param parent [Runtime::Instance, nil] entity command's parent record, if any
|
|
177
|
+
# @param correction [Hash{Symbol => Object}] `corrects` event bindings, if any
|
|
371
178
|
# @return [void]
|
|
372
|
-
# @raise [Runtime::EnsuresNotMet]
|
|
373
|
-
# @raise [Bluebook::Expression::EvaluationError] if an ensures's own rule
|
|
374
|
-
# cannot be evaluated (an unresolvable field, a bad comparison)
|
|
179
|
+
# @raise [Runtime::EnsuresNotMet] a declared `ensures` does not hold
|
|
375
180
|
def enforce_ensures(subject, command, args, old:, domain:, parent: nil, correction: {})
|
|
376
181
|
state = GuardState.new(subject)
|
|
377
|
-
#
|
|
378
|
-
#
|
|
379
|
-
#
|
|
380
|
-
# part of `state`. `command`/`args` still dereferences — a
|
|
381
|
-
# fresh reference-typed argument stays in bounds.
|
|
382
|
-
# `old` still wins over everything, unchanged. `correction`
|
|
383
|
-
# (an `as:`-bound corrected event, if this command declares
|
|
384
|
-
# one) wins right alongside it — a settled-record ensures can
|
|
385
|
-
# reference the correction target exactly as freely as a
|
|
386
|
-
# pre-mutation given already can.
|
|
387
|
-
# C2.3 (docs/semantics/bluebook-semantics.md) — an ensures reads
|
|
388
|
-
# the settled state first: an argument that shares a field's
|
|
389
|
-
# name does not shadow the candidate here (it does in a given,
|
|
390
|
-
# C2.2), so `sets :note` + `ensures { note == ... }` judges what
|
|
391
|
-
# landed, and `old.<field>` remains the pre-state.
|
|
182
|
+
# Same aggregate-boundary rule as enforce_givens (ADR 0025) —
|
|
183
|
+
# `state` already carries projected fields; only a fresh
|
|
184
|
+
# reference-typed argument needs `dereference` here.
|
|
392
185
|
attrs = args.reject { |name, _| subject.key?(name) }.merge(dereference(domain, command, args))
|
|
393
186
|
attrs = attrs.merge(parent: parent.state) if parent
|
|
394
187
|
attrs = attrs.merge(correction) unless correction.empty?
|
|
@@ -400,37 +193,18 @@ module Hecks
|
|
|
400
193
|
end
|
|
401
194
|
end
|
|
402
195
|
|
|
403
|
-
#
|
|
404
|
-
#
|
|
405
|
-
# ensures` already checks at, and for the same reason: an
|
|
406
|
-
# invariant is a claim about the settled record, not the
|
|
407
|
-
# command that produced it, so it reads no `args`/`old` at all,
|
|
408
|
-
# only the record's own state. `subject` here is
|
|
409
|
-
# always the aggregate's own instance — `CommandInterpreter`
|
|
410
|
-
# passes its own `ctx.instance`, and `EntityInterpreter` passes
|
|
411
|
-
# the parent record (`ctx.instance`, not the element), since an
|
|
412
|
-
# entity mutation changes data inside the same aggregate
|
|
413
|
-
# boundary the invariant guards; there is no separate "entity
|
|
414
|
-
# invariant" to check the piece's own view against.
|
|
196
|
+
# Checks `aggregate`'s declared invariants against the settled
|
|
197
|
+
# `subject`, then each of its entities' own invariants.
|
|
415
198
|
#
|
|
416
|
-
# No `dereference` (
|
|
417
|
-
# `subject`'s own boundary, same rule
|
|
418
|
-
# `enforce_ensures` now hold to. No invariant in the corpus has
|
|
419
|
-
# ever read across a `reference_to` (verified by grep), so this
|
|
420
|
-
# is not a migration, just closing the same capability off here
|
|
421
|
-
# that was already unused.
|
|
199
|
+
# No `dereference` here (ADR 0025) — an invariant may only read
|
|
200
|
+
# `subject`'s own boundary, same rule enforce_givens/ensures hold to.
|
|
422
201
|
#
|
|
423
|
-
# @param subject [Runtime::Instance] the settled aggregate record
|
|
424
|
-
#
|
|
425
|
-
# @param
|
|
426
|
-
# `invariants` are checked
|
|
427
|
-
# @param domain [String, Symbol] the domain `aggregate` belongs to, passed
|
|
428
|
-
# through to `check_entity_invariants`
|
|
202
|
+
# @param subject [Runtime::Instance] the settled aggregate record checked
|
|
203
|
+
# @param aggregate [Bluebook::Aggregate] the aggregate whose invariants are checked
|
|
204
|
+
# @param domain [String, Symbol] domain `aggregate` belongs to
|
|
429
205
|
# @return [void]
|
|
430
|
-
# @raise [Runtime::InvariantViolation]
|
|
431
|
-
#
|
|
432
|
-
# @raise [Bluebook::Expression::EvaluationError] if an invariant's own rule
|
|
433
|
-
# cannot be evaluated (an unresolvable field, a bad comparison)
|
|
206
|
+
# @raise [Runtime::InvariantViolation] a declared invariant does not hold, on
|
|
207
|
+
# `aggregate` itself or any of its entities
|
|
434
208
|
def enforce_invariants(subject, aggregate, domain:)
|
|
435
209
|
state = GuardState.new(subject)
|
|
436
210
|
attrs = {}
|
|
@@ -443,38 +217,17 @@ module Hecks
|
|
|
443
217
|
check_entity_invariants(aggregate, subject, domain: domain)
|
|
444
218
|
end
|
|
445
219
|
|
|
446
|
-
#
|
|
447
|
-
#
|
|
448
|
-
# own invariants just above (same two checkpoints: after every
|
|
449
|
-
# mutation, before save), just a wider one: the aggregate's own
|
|
450
|
-
# consistency includes each of its pieces individually looking
|
|
451
|
-
# right, the same way `ValueObject#invariants` already checks
|
|
452
|
-
# each of its own instances one construct up. Recurses into
|
|
453
|
-
# nested pieces (S17, ADR 0026 — Dispatch inside Handler) the
|
|
454
|
-
# same way `check_entity_invariants`'s own caller recurses
|
|
455
|
-
# nowhere else needs to, since a piece's `entities` are already
|
|
456
|
-
# exactly as reachable as an aggregate's.
|
|
220
|
+
# Checks each of `owner_construct`'s entity types' own invariants
|
|
221
|
+
# against every instance it holds, recursing into nested entities.
|
|
457
222
|
#
|
|
458
|
-
#
|
|
459
|
-
#
|
|
460
|
-
# element by identity — this reads every element instead, but
|
|
461
|
-
# the "which field on the owner holds this piece's own
|
|
462
|
-
# instances" question is the identical one. A piece declaring
|
|
463
|
-
# invariants that nothing on its owner actually holds (no
|
|
464
|
-
# matching list attribute) is a static-analysis gap for a
|
|
465
|
-
# future gate, not a runtime concern here — `next` past it
|
|
466
|
-
# rather than raising mid-enforcement for an unrelated command.
|
|
223
|
+
# An entity with no matching list attribute on its owner is skipped,
|
|
224
|
+
# not raised — a static-analysis gap, not a runtime concern here.
|
|
467
225
|
#
|
|
468
|
-
# @param owner_construct [Bluebook::Aggregate, Bluebook::Entity]
|
|
469
|
-
#
|
|
470
|
-
# @param owner_instance [Runtime::Instance] the settled record holding
|
|
471
|
-
# `owner_construct`'s own entity lists
|
|
226
|
+
# @param owner_construct [Bluebook::Aggregate, Bluebook::Entity] whose entities are checked
|
|
227
|
+
# @param owner_instance [Runtime::Instance] settled record holding the entity lists
|
|
472
228
|
# @param domain [String, Symbol] the domain `owner_construct` belongs to
|
|
473
229
|
# @return [void]
|
|
474
|
-
# @raise [Runtime::InvariantViolation]
|
|
475
|
-
# not hold, on any element or its own nested entities
|
|
476
|
-
# @raise [Bluebook::Expression::EvaluationError] if an invariant's own rule
|
|
477
|
-
# cannot be evaluated (an unresolvable field, a bad comparison)
|
|
230
|
+
# @raise [Runtime::InvariantViolation] an entity invariant does not hold
|
|
478
231
|
def check_entity_invariants(owner_construct, owner_instance, domain:)
|
|
479
232
|
owner_construct.entities.each do |entity|
|
|
480
233
|
next if entity.invariants.empty?
|
|
@@ -485,7 +238,7 @@ module Hecks
|
|
|
485
238
|
Array(owner_instance[list_attr.name]).each do |element|
|
|
486
239
|
wrapped = Instance.new(aggregate: entity, id: nil, state: element)
|
|
487
240
|
element_state = GuardState.new(wrapped)
|
|
488
|
-
# No `dereference` (
|
|
241
|
+
# No `dereference` (ADR 0025) — same boundary rule as
|
|
489
242
|
# enforce_invariants above; `parent` (the owner's own
|
|
490
243
|
# state, projected fields included) stays readable.
|
|
491
244
|
attrs = { parent: owner_instance.state }
|
|
@@ -501,20 +254,16 @@ module Hecks
|
|
|
501
254
|
end
|
|
502
255
|
end
|
|
503
256
|
|
|
504
|
-
# Finds the lifecycle transition `command` admits from `subject`'s
|
|
505
|
-
# state, if `declaring` declares a lifecycle and
|
|
257
|
+
# Finds the lifecycle transition `command` admits from `subject`'s
|
|
258
|
+
# current state, if `declaring` declares a lifecycle and moves it.
|
|
506
259
|
#
|
|
507
|
-
# @param declaring [Bluebook::Aggregate, Bluebook::Entity] the construct
|
|
508
|
-
#
|
|
509
|
-
# @param
|
|
510
|
-
#
|
|
511
|
-
#
|
|
512
|
-
#
|
|
513
|
-
#
|
|
514
|
-
# when `declaring` has no lifecycle or `command` declares no transition
|
|
515
|
-
# @raise [Runtime::LifecycleRefused] if `command` declares one or more
|
|
516
|
-
# transitions, each constrained by its own `from:`, and none admits the
|
|
517
|
-
# record's current lifecycle state
|
|
260
|
+
# @param declaring [Bluebook::Aggregate, Bluebook::Entity] the construct with the lifecycle
|
|
261
|
+
# @param command [Class] the command class to find a declared transition for
|
|
262
|
+
# @param subject [Runtime::Instance] pre-mutation record read for current state
|
|
263
|
+
# @return [Bluebook::StateTransition, nil] admitted transition, or nil when
|
|
264
|
+
# `declaring` has no lifecycle or `command` declares none
|
|
265
|
+
# @raise [Runtime::LifecycleRefused] `command` declares transitions and none
|
|
266
|
+
# admits the record's current state
|
|
518
267
|
def admissible_transition(declaring, command, subject)
|
|
519
268
|
lifecycle = declaring.lifecycle
|
|
520
269
|
return nil unless lifecycle
|
|
@@ -522,28 +271,8 @@ module Hecks
|
|
|
522
271
|
candidates = lifecycle.transitions_for(command.hecks_name)
|
|
523
272
|
return nil if candidates.empty?
|
|
524
273
|
|
|
525
|
-
# `Value.scalar`
|
|
526
|
-
#
|
|
527
|
-
# lifecycle field (the norm, not the exception, per this
|
|
528
|
-
# corpus's own no-primitive-envy convention) holds a real
|
|
529
|
-
# `Runtime::Value` here, and a bare `.to_s` on that hit Ruby's
|
|
530
|
-
# default `Object#to_s` instead of unwrapping the inner
|
|
531
|
-
# scalar first -- `current` came back as a raw object-pointer
|
|
532
|
-
# string (`"#<Hecks::Runtime::Value:0x...>"`) that could
|
|
533
|
-
# never match any declared `from` state, so every transition
|
|
534
|
-
# on a VO-typed lifecycle field refused unconditionally, and
|
|
535
|
-
# when it refused the message leaked the pointer too.
|
|
536
|
-
# Confirmed live via `Plan::Task.Complete` (status defaults to
|
|
537
|
-
# `TaskStatus`, a single-field VO), not inferred. Reuses
|
|
538
|
-
# `Value.scalar` -- this file's own third candidate for "how
|
|
539
|
-
# to unwrap a Value/Hash-shaped field," already built and
|
|
540
|
-
# already documented for exactly this job ("rendering a value
|
|
541
|
-
# object into a column or a message, where there is no path
|
|
542
|
-
# to consult," `value/coercion.rb`'s own comment) -- rather
|
|
543
|
-
# than inventing a second unwrap helper beside `Resolver#
|
|
544
|
-
# unwrap_scalar`'s bare-comparison one. Duck-typed the same
|
|
545
|
-
# way : a bare, non-VO lifecycle field passes through
|
|
546
|
-
# unchanged (`Value.scalar` only opens a `Value` instance).
|
|
274
|
+
# `Value.scalar` unwraps a VO-typed lifecycle field before `.to_s`;
|
|
275
|
+
# without it, a wrapped field never matches any `from:` state.
|
|
547
276
|
current = Value.scalar(subject[lifecycle.field]).to_s
|
|
548
277
|
admitted = candidates.find { |t| !t.constrained? || Array(t.from).include?(current) }
|
|
549
278
|
return admitted if admitted
|