hecks 1.3.0 → 1.4.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/lib/hecks/adapters/driven/claude_code.rb +7 -7
- data/lib/hecks/adapters/driven/d1.rb +187 -23
- data/lib/hecks/adapters/driven/folder.rb +10 -10
- data/lib/hecks/adapters/driven/google_authentication.rb +8 -8
- data/lib/hecks/adapters/driven/governance_authorization.rb +31 -13
- data/lib/hecks/adapters/driven/heki/journal.rb +5 -4
- data/lib/hecks/adapters/driven/heki/saga_store.rb +5 -5
- data/lib/hecks/adapters/driven/heki.rb +13 -7
- data/lib/hecks/adapters/driven/identity_registry.rb +2 -2
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +3 -3
- data/lib/hecks/adapters/driven/lambda/client.rb +34 -9
- data/lib/hecks/adapters/driven/lambda.rb +39 -33
- data/lib/hecks/adapters/driven/local_storage.rb +17 -10
- data/lib/hecks/adapters/driven/memory.rb +205 -9
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +1 -1
- data/lib/hecks/adapters/driven/postgres/codec.rb +27 -11
- data/lib/hecks/adapters/driven/postgres/outbox.rb +40 -2
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +23 -7
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +14 -14
- data/lib/hecks/adapters/driven/postgres.rb +175 -28
- data/lib/hecks/adapters/driven/prism.rb +4 -4
- data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
- data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
- data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
- data/lib/hecks/adapters/driven/sqlite.rb +181 -21
- data/lib/hecks/adapters/driven.rb +4 -4
- data/lib/hecks/adapters/driving/github_webhook.rb +18 -18
- data/lib/hecks/behaviors/dsl.rb +2 -2
- data/lib/hecks/behaviors/expectations.rb +23 -23
- data/lib/hecks/behaviors/ir.rb +1 -1
- data/lib/hecks/behaviors/rspec.rb +1 -1
- data/lib/hecks/behaviors/runner.rb +2 -2
- data/lib/hecks/behaviors.rb +1 -1
- data/lib/hecks/bluebook/aggregate.rb +13 -13
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +10 -10
- data/lib/hecks/bluebook/assembly/build.rb +1 -1
- data/lib/hecks/bluebook/assembly/contract.rb +39 -16
- data/lib/hecks/bluebook/assembly/contracts.rb +40 -38
- data/lib/hecks/bluebook/assembly/marks.rb +18 -18
- data/lib/hecks/bluebook/assembly/specializer.rb +19 -21
- data/lib/hecks/bluebook/assembly.rb +9 -9
- data/lib/hecks/bluebook/attribute.rb +9 -9
- data/lib/hecks/bluebook/behaviour/aggregate.rb +11 -11
- data/lib/hecks/bluebook/behaviour/attribute.rb +5 -5
- data/lib/hecks/bluebook/behaviour/chapter.rb +23 -5
- data/lib/hecks/bluebook/behaviour/command.rb +23 -23
- data/lib/hecks/bluebook/behaviour/domain_port.rb +7 -7
- data/lib/hecks/bluebook/behaviour/entity.rb +8 -8
- data/lib/hecks/bluebook/behaviour/hexagon.rb +4 -4
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +5 -5
- data/lib/hecks/bluebook/behaviour/policy.rb +12 -12
- data/lib/hecks/bluebook/behaviour/process_manager.rb +7 -7
- data/lib/hecks/bluebook/behaviour/query.rb +1 -1
- data/lib/hecks/bluebook/behaviour/read_model.rb +8 -8
- data/lib/hecks/bluebook/behaviour/traits.rb +12 -12
- data/lib/hecks/bluebook/behaviour/value_object.rb +6 -6
- data/lib/hecks/bluebook/capabilities.rb +27 -0
- data/lib/hecks/bluebook/chapter.rb +28 -9
- data/lib/hecks/bluebook/command.rb +12 -12
- data/lib/hecks/bluebook/domain_port.rb +9 -9
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +96 -96
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +41 -41
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +22 -2
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +111 -74
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +48 -30
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +103 -103
- data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
- data/lib/hecks/bluebook/dsl/entity_builder.rb +56 -56
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +89 -30
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +17 -17
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
- data/lib/hecks/bluebook/dsl/policy_builder.rb +30 -21
- data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +35 -35
- data/lib/hecks/bluebook/dsl/query_builder.rb +5 -5
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +34 -34
- data/lib/hecks/bluebook/dsl/rule_reference.rb +41 -39
- data/lib/hecks/bluebook/dsl/translation_builder.rb +9 -9
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +16 -16
- data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
- data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
- data/lib/hecks/bluebook/entity.rb +11 -11
- data/lib/hecks/bluebook/expression/ast_json.rb +20 -20
- data/lib/hecks/bluebook/expression/ast_reader.rb +3 -3
- data/lib/hecks/bluebook/expression/canonical_form.rb +9 -9
- data/lib/hecks/bluebook/expression/evaluator.rb +18 -18
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +18 -18
- data/lib/hecks/bluebook/expression/resolver.rb +60 -62
- data/lib/hecks/bluebook/hexagon.rb +1 -1
- data/lib/hecks/bluebook/lifecycle.rb +1 -1
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -1
- data/lib/hecks/bluebook/meta_validator/judge.rb +101 -87
- data/lib/hecks/bluebook/meta_validator/plan.rb +39 -39
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -2
- data/lib/hecks/bluebook/meta_validator/readings.rb +43 -43
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +44 -37
- data/lib/hecks/bluebook/meta_validator/shapes.rb +25 -21
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +145 -31
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +6 -6
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +5 -5
- data/lib/hecks/bluebook/meta_validator.rb +70 -70
- data/lib/hecks/bluebook/model_check.rb +170 -84
- data/lib/hecks/bluebook/pattern_subset.rb +9 -9
- data/lib/hecks/bluebook/policy.rb +15 -13
- data/lib/hecks/bluebook/process_manager.rb +14 -14
- data/lib/hecks/bluebook/project_register.rb +6 -6
- data/lib/hecks/bluebook/query.rb +4 -4
- data/lib/hecks/bluebook/read_model.rb +14 -14
- data/lib/hecks/bluebook/reference.rb +8 -8
- data/lib/hecks/bluebook/smoke_test.rb +19 -19
- data/lib/hecks/bluebook/synthesizer.rb +12 -12
- data/lib/hecks/bluebook/translation.rb +4 -4
- data/lib/hecks/bluebook/value_object.rb +6 -6
- data/lib/hecks/bluebook.rb +2 -2
- data/lib/hecks/codemod/legacy_dispatch_args.rb +299 -0
- data/lib/hecks/codemod/legacy_dispatch_recorder.rb +186 -0
- data/lib/hecks/codemod.rb +36 -35
- data/lib/hecks/construct.rb +6 -6
- data/lib/hecks/corpus.rb +317 -0
- data/lib/hecks/deprecation.rb +95 -0
- data/lib/hecks/doc/reference.rb +19 -19
- data/lib/hecks/embryonaut_bluebook.rb +11 -11
- data/lib/hecks/facade/cli_door.rb +69 -10
- data/lib/hecks/facade/cli_runner.rb +105 -24
- data/lib/hecks/facade/command_request.rb +23 -0
- data/lib/hecks/facade/handle.rb +79 -32
- data/lib/hecks/facade/json_door.rb +106 -25
- data/lib/hecks/facade/surface/aggregate_door.rb +42 -27
- data/lib/hecks/facade/surface/chapter.rb +26 -17
- data/lib/hecks/facade/surface.rb +16 -3
- data/lib/hecks/facade.rb +15 -4
- data/lib/hecks/forms/app.rb +46 -30
- data/lib/hecks/forms/command_form_renderer.rb +70 -9
- data/lib/hecks/forms/field_renderer.rb +142 -6
- data/lib/hecks/forms/field_shape.rb +180 -19
- data/lib/hecks/forms/html.rb +51 -7
- data/lib/hecks/forms/index_renderer.rb +14 -2
- data/lib/hecks/forms/params.rb +120 -23
- data/lib/hecks/forms/port_argument.rb +2 -2
- data/lib/hecks/forms/query_form_renderer.rb +2 -2
- data/lib/hecks/forms/record_renderer.rb +2 -2
- data/lib/hecks/forms/record_table.rb +1 -1
- data/lib/hecks/forms/value_object_shape.rb +3 -3
- data/lib/hecks/forms.rb +24 -4
- data/lib/hecks/fqn.rb +1 -1
- data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
- data/lib/hecks/framework.rb +48 -17
- data/lib/hecks/freezer.rb +11 -11
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +90 -90
- data/lib/hecks/fuzzing/combination_miner.rb +119 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +140 -45
- data/lib/hecks/fuzzing/coverage_campaign.rb +118 -0
- data/lib/hecks/fuzzing/differential.rb +158 -0
- data/lib/hecks/fuzzing/domain_generator.rb +694 -0
- data/lib/hecks/fuzzing/era_boundary.rb +37 -18
- data/lib/hecks/fuzzing/form_census.rb +35 -20
- data/lib/hecks/fuzzing/generated_domain_check.rb +95 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +6 -6
- data/lib/hecks/fuzzing/isolated_boot.rb +62 -62
- data/lib/hecks/fuzzing/nondeterministic.rb +67 -0
- data/lib/hecks/fuzzing/persistence_parity.rb +19 -21
- data/lib/hecks/fuzzing/properties/corrections.rb +9 -9
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +154 -49
- data/lib/hecks/fuzzing/properties/guards.rb +42 -42
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +32 -32
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +22 -35
- data/lib/hecks/fuzzing/properties/outbox.rb +24 -24
- data/lib/hecks/fuzzing/properties/querying.rb +17 -17
- data/lib/hecks/fuzzing/properties.rb +64 -40
- data/lib/hecks/fuzzing/qa_settings.rb +152 -0
- data/lib/hecks/fuzzing/replay.rb +114 -114
- data/lib/hecks/fuzzing/rotation_priority.rb +10 -10
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +113 -0
- data/lib/hecks/fuzzing/self_consistency.rb +141 -112
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +35 -35
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +18 -11
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +13 -12
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +21 -12
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +58 -31
- data/lib/hecks/fuzzing/sequence_generator.rb +111 -23
- data/lib/hecks/fuzzing/shrinker.rb +197 -0
- data/lib/hecks/fuzzing/structural_skips.rb +23 -130
- data/lib/hecks/fuzzing/sweep_depth.rb +4 -4
- data/lib/hecks/fuzzing/target_capabilities.rb +49 -18
- data/lib/hecks/fuzzing/value_generator.rb +14 -14
- data/lib/hecks/fuzzing.rb +5 -0
- data/lib/hecks/grammar/evolve.rb +10 -10
- data/lib/hecks/grammar.rb +7 -7
- data/lib/hecks/ir.rb +13 -13
- data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
- data/lib/hecks/language/bluebook/policy.bluebook +11 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +348 -13
- data/lib/hecks/language/oidc.json +5 -0
- data/lib/hecks/literal.rb +9 -9
- data/lib/hecks/naming.rb +25 -25
- data/lib/hecks/ports/access_control.rb +58 -2
- data/lib/hecks/ports/agent/answers.rb +83 -6
- data/lib/hecks/ports/agent.rb +119 -35
- data/lib/hecks/ports/authentication.rb +44 -4
- data/lib/hecks/ports/authorization.rb +53 -11
- data/lib/hecks/ports/clock.rb +42 -23
- data/lib/hecks/ports/extraction.rb +16 -0
- data/lib/hecks/ports/identity_assignment.rb +24 -2
- data/lib/hecks/ports/identity_generation.rb +17 -3
- data/lib/hecks/ports/identity_resolution.rb +18 -1
- data/lib/hecks/ports/loading.rb +4 -0
- data/lib/hecks/ports/persistence/append_only.rb +172 -8
- data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
- data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
- data/lib/hecks/ports/persistence/execution.rb +4 -0
- data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
- data/lib/hecks/ports/persistence/plugin.rb +42 -4
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +118 -66
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +28 -5
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +33 -9
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +57 -4
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +314 -90
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +42 -8
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -19
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +5 -5
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
- data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
- data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
- data/lib/hecks/ports/persistence/repository_factory.rb +23 -3
- data/lib/hecks/ports/persistence/state_codec.rb +319 -0
- data/lib/hecks/ports/persistence.rb +36 -1
- data/lib/hecks/ports/projection.rb +61 -7
- data/lib/hecks/ports/query/in_memory.rb +3 -3
- data/lib/hecks/ports/query/ordering.rb +6 -6
- data/lib/hecks/ports/query.rb +35 -0
- data/lib/hecks/projections/bootstrap_table.rb +112 -0
- data/lib/hecks/projections/diagrams.rb +75 -75
- data/lib/hecks/projections/glossary/html.rb +3 -3
- data/lib/hecks/projections/glossary/markdown.rb +3 -3
- data/lib/hecks/projections/glossary/mermaid.rb +1 -1
- data/lib/hecks/projections/glossary/sections.rb +1 -1
- data/lib/hecks/projections/glossary/sentences.rb +5 -5
- data/lib/hecks/projections/glossary.rb +15 -15
- data/lib/hecks/projections/ir.rb +1 -1
- data/lib/hecks/projections/model/deviations.rb +18 -17
- data/lib/hecks/projections/model.rb +25 -21
- data/lib/hecks/projections/oidc.rb +7 -7
- data/lib/hecks/projections/parser_table.rb +5 -5
- data/lib/hecks/projections/reference.rb +3 -3
- data/lib/hecks/projections/rust_vocabulary.rb +443 -0
- data/lib/hecks/projections/shape.rb +2 -2
- data/lib/hecks/projections/statements.rb +11 -11
- data/lib/hecks/projections/vocabulary.rb +9 -9
- data/lib/hecks/projections.rb +5 -3
- data/lib/hecks/projector/cli_projector.rb +29 -29
- data/lib/hecks/projector/docs_projector.rb +13 -13
- data/lib/hecks/projector/exporter.rb +42 -21
- data/lib/hecks/projector/ir_projector.rb +1 -1
- data/lib/hecks/projector/narrate_projector.rb +11 -11
- data/lib/hecks/projector/target.rb +13 -13
- data/lib/hecks/projector.rb +15 -15
- data/lib/hecks/query_ir.rb +47 -47
- data/lib/hecks/query_specification/common/comparators.rb +19 -3
- data/lib/hecks/query_specification/common/comparison.rb +109 -27
- data/lib/hecks/query_specification/common/dsl.rb +65 -9
- data/lib/hecks/query_specification/common/null_policy.rb +57 -13
- data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
- data/lib/hecks/query_specification/common/options.rb +25 -0
- data/lib/hecks/query_specification/field_path.rb +69 -15
- data/lib/hecks/query_specification/hop_path.rb +57 -20
- data/lib/hecks/query_specification/read_model/specification.rb +4 -0
- data/lib/hecks/rendering.rb +3 -3
- data/lib/hecks/router/namespace_installer.rb +3 -3
- data/lib/hecks/router.rb +1 -1
- data/lib/hecks/runtime/aggregate_lock.rb +11 -11
- data/lib/hecks/runtime/boot_gates.rb +3 -3
- data/lib/hecks/runtime/caller.rb +8 -8
- data/lib/hecks/runtime/capability_graph.rb +2 -2
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +22 -25
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +42 -63
- data/lib/hecks/runtime/command_interpreter.rb +159 -102
- data/lib/hecks/runtime/command_rules/admissibility.rb +69 -69
- data/lib/hecks/runtime/command_rules/arithmetic.rb +137 -91
- data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
- data/lib/hecks/runtime/command_rules/emission.rb +18 -1
- data/lib/hecks/runtime/command_rules/references.rb +35 -35
- data/lib/hecks/runtime/command_rules.rb +3 -0
- data/lib/hecks/runtime/dependency_planning.rb +11 -11
- data/lib/hecks/runtime/dispatcher.rb +283 -90
- data/lib/hecks/runtime/entity_element.rb +151 -67
- data/lib/hecks/runtime/entity_interpreter.rb +108 -82
- data/lib/hecks/runtime/errors.rb +19 -19
- data/lib/hecks/runtime/event.rb +6 -6
- data/lib/hecks/runtime/identity.rb +22 -22
- data/lib/hecks/runtime/instance.rb +39 -14
- data/lib/hecks/runtime/interpreting.rb +12 -12
- data/lib/hecks/runtime/invocation.rb +276 -0
- data/lib/hecks/runtime/loader.rb +14 -14
- data/lib/hecks/runtime/outbox.rb +23 -23
- data/lib/hecks/runtime/policy_interpreter.rb +54 -54
- data/lib/hecks/runtime/port_operation_interpreter.rb +22 -19
- data/lib/hecks/runtime/query_interpreter.rb +55 -64
- data/lib/hecks/runtime/reaction_invocation.rb +20 -20
- data/lib/hecks/runtime/read_model_interpreter.rb +40 -40
- data/lib/hecks/runtime/rebuild_sweep.rb +4 -4
- data/lib/hecks/runtime/reference_hop.rb +6 -6
- data/lib/hecks/runtime/refusal_wording.rb +92 -115
- data/lib/hecks/runtime/registry/saga_persistence.rb +21 -21
- data/lib/hecks/runtime/registry/verification.rb +36 -26
- data/lib/hecks/runtime/registry.rb +45 -28
- data/lib/hecks/runtime/remote_dispatcher.rb +38 -23
- data/lib/hecks/runtime/routing.rb +10 -153
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +17 -17
- data/lib/hecks/runtime/saga_interpreter.rb +64 -64
- data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
- data/lib/hecks/runtime/tenant_check.rb +9 -9
- data/lib/hecks/runtime/tenant_scope.rb +5 -5
- data/lib/hecks/runtime/value/admission.rb +75 -30
- data/lib/hecks/runtime/value/coercion.rb +318 -127
- data/lib/hecks/runtime/value/entity_list_coercion.rb +34 -34
- data/lib/hecks/runtime/value.rb +21 -21
- data/lib/hecks/runtime.rb +7 -7
- data/lib/hecks/storehouse.rb +64 -64
- data/lib/hecks/version.rb +3 -3
- data/lib/hecks/vocabulary.rb +205 -4
- data/lib/hecks.rb +13 -11
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +11 -11
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +14 -12
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +11 -11
- metadata +22 -2
|
@@ -11,7 +11,7 @@ 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
|
-
#
|
|
14
|
+
# Wraps `subject` so a guard can read a declared-but-storage-absent
|
|
15
15
|
# optional attribute as nil, not "cannot resolve." `Instance
|
|
16
16
|
# #hydrate_with_defaults` deliberately leaves one absent rather
|
|
17
17
|
# than nil-filled — "an attribute with no default stays absent,
|
|
@@ -25,10 +25,10 @@ module Hecks
|
|
|
25
25
|
# exact behavior, zero risk to a path this bug was never measured
|
|
26
26
|
# against.
|
|
27
27
|
#
|
|
28
|
-
# NIL
|
|
29
|
-
# covered an
|
|
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
30
|
# exactly what optional means. It also, as a side effect, let a
|
|
31
|
-
#
|
|
31
|
+
# non-optional field predating a record read nil the same way,
|
|
32
32
|
# which is the `ne:`/array-`in:` bug class applied to rule
|
|
33
33
|
# evaluation: a predicate silently answers against a value nobody
|
|
34
34
|
# ever wrote (ADR 0025, "Added attributes and absence"). `[]`
|
|
@@ -40,14 +40,14 @@ module Hecks
|
|
|
40
40
|
def initialize(instance)
|
|
41
41
|
@instance = instance
|
|
42
42
|
@declared = instance.respond_to?(:aggregate) ? instance.aggregate.attributes.to_h { |a| [a.name, a] } : {}
|
|
43
|
-
# S12, ADR 0025 — a
|
|
43
|
+
# S12, ADR 0025 — a separate index, the same reason
|
|
44
44
|
# `Aggregate#projected_fields` is a separate IR collection
|
|
45
45
|
# rather than folded into `attributes` (see that field's
|
|
46
46
|
# own comment): a projected field's absence means
|
|
47
47
|
# something different from an ordinary attribute's, so it
|
|
48
48
|
# needs its own refusal below, not `AttributeAbsent`'s.
|
|
49
|
-
# `projects` is
|
|
50
|
-
# `instance.aggregate` answers the
|
|
49
|
+
# `projects` is aggregate-scoped only — an entity's own
|
|
50
|
+
# `instance.aggregate` answers the entity construct here
|
|
51
51
|
# (EntityInterpreter's own subject), which declares no
|
|
52
52
|
# `projected_fields` of its own, hence the extra guard
|
|
53
53
|
# `@declared` above does not need.
|
|
@@ -67,17 +67,17 @@ module Hecks
|
|
|
67
67
|
return nil if attribute.nil? || attribute.optional?
|
|
68
68
|
|
|
69
69
|
raise AttributeAbsent,
|
|
70
|
-
RefusalWording.
|
|
71
|
-
|
|
70
|
+
RefusalWording.render_site("AttributeAbsent", "absent_read",
|
|
71
|
+
aggregate: @instance.aggregate.hecks_name, field: name)
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
private
|
|
75
75
|
|
|
76
76
|
def raise_projection_absent(projected)
|
|
77
77
|
raise ProjectionAbsent,
|
|
78
|
-
RefusalWording.
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
RefusalWording.render_site("ProjectionAbsent", "absent_read",
|
|
79
|
+
aggregate: @instance.aggregate.hecks_name, field: projected.name,
|
|
80
|
+
reference: projected.reference, remote_field: projected.remote_field)
|
|
81
81
|
end
|
|
82
82
|
end
|
|
83
83
|
private_constant :GuardState
|
|
@@ -89,54 +89,54 @@ module Hecks
|
|
|
89
89
|
# `Instance`); a `subject` with no `.aggregate` just hydrates
|
|
90
90
|
# nothing from state, same as GuardState degrades above.
|
|
91
91
|
#
|
|
92
|
-
#
|
|
92
|
+
# Merge order matters, and it is not "args always win": an
|
|
93
93
|
# unaliased command-level reference dereferences under a
|
|
94
94
|
# different name than the argument holds (`account_id` the arg,
|
|
95
95
|
# `account` the hydrated key — no collision, order is moot). An
|
|
96
|
-
#
|
|
97
|
-
# under the
|
|
96
|
+
# aliased one (`reference_to Customer, as: :customer`) hydrates
|
|
97
|
+
# under the same name the argument itself holds — `customer` is
|
|
98
98
|
# both the raw id an arg puts there and the key `customer.status`
|
|
99
99
|
# expects to dig into. If `args` merged last, the raw id (a
|
|
100
100
|
# String) would win and `.status` on a String is where a fuzzer
|
|
101
101
|
# found this — TypeError, not a refusal. Command-level
|
|
102
|
-
# dereferencing is the one thing that is
|
|
102
|
+
# dereferencing is the one thing that is supposed to override
|
|
103
103
|
# its own source argument for exactly this reason; args still
|
|
104
|
-
# wins over stored
|
|
105
|
-
# `parent:` is an entity command's
|
|
106
|
-
# (EntityInterpreter's `ctx.instance` — "the
|
|
104
|
+
# wins over stored owner state (unchanged from before this fix).
|
|
105
|
+
# `parent:` is an entity command's own parent aggregate record
|
|
106
|
+
# (EntityInterpreter's `ctx.instance` — "the parent aggregate
|
|
107
107
|
# record", its own doc comment) — the entity's containment, not a
|
|
108
108
|
# declared reference attribute, so it doesn't come from
|
|
109
109
|
# `dereference`'s attribute scan the way `owner`'s do. Hydrated
|
|
110
|
-
# the same shape regardless: the parent's own state,
|
|
110
|
+
# the same shape regardless: the parent's own state, merged so
|
|
111
111
|
# the dereferenced hash wins over the raw reference it replaces
|
|
112
112
|
# (ADR 0025 dropped the `_id` suffix that used to keep the two
|
|
113
113
|
# apart by name, so `parent.state.merge(dereference(...))` is
|
|
114
|
-
# now load-bearing, not redundant) — plus
|
|
114
|
+
# now load-bearing, not redundant) — plus its own references
|
|
115
115
|
# dereferenced one level in, so `parent.customer.status` (a
|
|
116
|
-
# parent aggregate reaching
|
|
116
|
+
# parent aggregate reaching its own customer) resolves the same
|
|
117
117
|
# way `account.customer.status` does for a command-level reference.
|
|
118
118
|
# nil for an aggregate command — CommandInterpreter never passes it.
|
|
119
119
|
# `correction:` — the `{as_name => payload}` bindings
|
|
120
120
|
# `enforce_correction_target` (above) already located, merged in
|
|
121
|
-
#
|
|
121
|
+
# last so an `as:` name wins the same way `old:` always wins in
|
|
122
122
|
# `enforce_ensures`, below — it is a fresh local binding a
|
|
123
123
|
# `corrects` command introduces, not a real argument/state field
|
|
124
124
|
# a caller could collide with by accident.
|
|
125
125
|
def enforce_givens(subject, command, args, domain:, declaring: nil, parent: nil, correction: {})
|
|
126
126
|
state = GuardState.new(subject)
|
|
127
|
-
# A
|
|
128
|
-
# ADR 0025) — `subject`'s own
|
|
127
|
+
# A rule may only read within its own aggregate boundary (S12,
|
|
128
|
+
# ADR 0025) — `subject`'s own stored references are no longer
|
|
129
129
|
# dereferenced here at all. What used to be a live query against
|
|
130
130
|
# another aggregate's own repository is now just `subject`'s own
|
|
131
131
|
# state: a `projects :customer_status, from: :"customer.status"`
|
|
132
|
-
# field is a
|
|
132
|
+
# field is a regular stored attribute, already present in
|
|
133
133
|
# `subject`/`state` with no hydration step needed. `dereference`
|
|
134
134
|
# is still called on `command`/`args`, below — that is a
|
|
135
|
-
#
|
|
136
|
-
# arguments"): a reference-typed
|
|
135
|
+
# different case the ADR explicitly keeps in bounds ("its command
|
|
136
|
+
# arguments"): a reference-typed argument this dispatch was just
|
|
137
137
|
# handed (`Dispute`'s own `disputed_by`, say) has nothing stored
|
|
138
138
|
# to project yet, so resolving it here, once, synchronously with
|
|
139
|
-
#
|
|
139
|
+
# this command's own admission, is not the live-query-against-
|
|
140
140
|
# another-aggregate's-stored-state pattern the boundary rule
|
|
141
141
|
# forbids.
|
|
142
142
|
attrs = args.merge(dereference(domain, command, args))
|
|
@@ -154,26 +154,26 @@ module Hecks
|
|
|
154
154
|
enforce_lifecycle_guard(declaring, command, subject) if declaring
|
|
155
155
|
end
|
|
156
156
|
|
|
157
|
-
# `corrects` — CommandBuilder#corrects_impl's own comment.
|
|
157
|
+
# `corrects` — CommandBuilder#corrects_impl's own comment. Not
|
|
158
158
|
# expressible as an ordinary `given`: "has this exact record
|
|
159
159
|
# already emitted this exact event" is not a predicate over the
|
|
160
|
-
# record's
|
|
160
|
+
# record's own fields, it is a fact about the event log, so it is
|
|
161
161
|
# raised structurally here, the same way NotFound/AlreadyExists
|
|
162
162
|
# are, rather than through the expression evaluator. The build-
|
|
163
|
-
# time half — does
|
|
163
|
+
# time half — does anything in this aggregate ever emit the named
|
|
164
164
|
# event at all — is `AggregateBuilder#seal_correction_targets`;
|
|
165
|
-
# this is the dispatch-time half — has
|
|
165
|
+
# this is the dispatch-time half — has this record actually done
|
|
166
166
|
# so yet.
|
|
167
167
|
#
|
|
168
|
-
#
|
|
168
|
+
# Also locates the matched event now, not just its existence, and
|
|
169
169
|
# returns a `{as_name => payload}` bindings hash — one entry per
|
|
170
170
|
# `:corrects` mutation that named an `as:` — so `given`/`ensures`
|
|
171
|
-
# on a corrects-bearing command can reference the located
|
|
171
|
+
# on a corrects-bearing command can reference the located old
|
|
172
172
|
# event by that name, the same shape `enforce_ensures`'s own
|
|
173
173
|
# `old:` binding already has (CommandBuilder#corrects_impl's own
|
|
174
174
|
# comment: `as:` was stored, from the start, specifically to be
|
|
175
175
|
# wired into the evaluator once a real runtime consumer existed —
|
|
176
|
-
# this is that consumer). `.reverse.find` — the
|
|
176
|
+
# this is that consumer). `.reverse.find` — the most recent
|
|
177
177
|
# matching event, if this record has somehow emitted the same
|
|
178
178
|
# correction target more than once; the prior existence-only
|
|
179
179
|
# check never had to make this choice, so it's a genuinely new
|
|
@@ -181,7 +181,7 @@ module Hecks
|
|
|
181
181
|
# corrected," which is naturally the latest fact on record, not
|
|
182
182
|
# an arbitrary one.
|
|
183
183
|
# C9.2 (docs/semantics/bluebook-semantics.md) — a correction target
|
|
184
|
-
# is judged against the record's
|
|
184
|
+
# is judged against the record's durable history: the events the
|
|
185
185
|
# aggregate's own store recorded (`AppendOnly#events`), which
|
|
186
186
|
# survive a restart the way the Rust kernel's persisted
|
|
187
187
|
# `emitted_<event>` flag does. The in-process log is the fallback
|
|
@@ -213,14 +213,14 @@ module Hecks
|
|
|
213
213
|
bindings
|
|
214
214
|
end
|
|
215
215
|
|
|
216
|
-
#
|
|
217
|
-
# "Debit", from: "open"` checked here, folded into the
|
|
216
|
+
# Lifecycle state as a command guard (S10, ADR 0025) — `command
|
|
217
|
+
# "Debit", from: "open"` checked here, folded into the same
|
|
218
218
|
# dispatch step `given` already runs at (both are preconditions,
|
|
219
219
|
# evaluated before any mutation) rather than earning its own
|
|
220
|
-
# DISPATCH_ORDER entry. A
|
|
220
|
+
# DISPATCH_ORDER entry. A guard, never a transition: it names no
|
|
221
221
|
# target state and `step_advance_lifecycle` never sees it — see
|
|
222
222
|
# `admissible_transition`, right below, for the transition this
|
|
223
|
-
# is deliberately
|
|
223
|
+
# is deliberately not reusing (its own `StateTransition#target`
|
|
224
224
|
# is required, and a guard-only command has none to give it).
|
|
225
225
|
def enforce_lifecycle_guard(declaring, command, subject)
|
|
226
226
|
return unless command.from
|
|
@@ -229,8 +229,8 @@ module Hecks
|
|
|
229
229
|
current = Value.scalar(subject[lifecycle.field]).to_s
|
|
230
230
|
return if Array(command.from).include?(current)
|
|
231
231
|
|
|
232
|
-
#
|
|
233
|
-
#
|
|
232
|
+
# Routed through RefusalWording's own "transition_blocked"
|
|
233
|
+
# template — the same one #admissible_transition, right below,
|
|
234
234
|
# already raises LifecycleRefused through for the same
|
|
235
235
|
# refusal class. This used to hand-roll its own wording
|
|
236
236
|
# inline ("...only runs from..." vs. the template's "...moves
|
|
@@ -239,22 +239,22 @@ module Hecks
|
|
|
239
239
|
# property, a spec, a caller) had to know both existed rather
|
|
240
240
|
# than one.
|
|
241
241
|
raise LifecycleRefused,
|
|
242
|
-
RefusalWording.
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
242
|
+
RefusalWording.render_site("LifecycleRefused", "transition_blocked",
|
|
243
|
+
command: command.hecks_name, field: lifecycle.field,
|
|
244
|
+
current: Rendering.describe(current),
|
|
245
|
+
allowed: Array(command.from))
|
|
246
246
|
end
|
|
247
247
|
|
|
248
|
-
# The far side of the contract: evaluated against the
|
|
248
|
+
# The far side of the contract: evaluated against the settled record
|
|
249
249
|
# — after mutations and the lifecycle move, before anything persists
|
|
250
250
|
# — with `old` carrying the state as the givens saw it. Injected into
|
|
251
251
|
# the attrs at evaluation time only; the payload gate never sees it.
|
|
252
252
|
#
|
|
253
|
-
# `old` — and every dispatch
|
|
253
|
+
# `old` — and every dispatch argument — wins over a same-named state
|
|
254
254
|
# field in expression scope (Resolver#fetch checks attrs first). An
|
|
255
255
|
# ensures naming a field the command also takes as an argument (or,
|
|
256
256
|
# on an entity, a field that doubles as the addressing argument
|
|
257
|
-
# element_of reads) will read the
|
|
257
|
+
# element_of reads) will read the argument, not the settled value.
|
|
258
258
|
# Not new to ensures — `given` lives under the same rule — but an
|
|
259
259
|
# ensures is more likely to collide, since it typically re-reads a
|
|
260
260
|
# field the command just took in to mutate it.
|
|
@@ -264,14 +264,14 @@ module Hecks
|
|
|
264
264
|
# above: `subject`'s own stored references are no longer
|
|
265
265
|
# dereferenced here; a `projects`-maintained field is already
|
|
266
266
|
# part of `state`. `command`/`args` still dereferences — a
|
|
267
|
-
# fresh reference-typed
|
|
267
|
+
# fresh reference-typed argument stays in bounds.
|
|
268
268
|
# `old` still wins over everything, unchanged. `correction`
|
|
269
269
|
# (an `as:`-bound corrected event, if this command declares
|
|
270
270
|
# one) wins right alongside it — a settled-record ensures can
|
|
271
271
|
# reference the correction target exactly as freely as a
|
|
272
272
|
# pre-mutation given already can.
|
|
273
273
|
# C2.3 (docs/semantics/bluebook-semantics.md) — an ensures reads
|
|
274
|
-
# the
|
|
274
|
+
# the settled state first: an argument that shares a field's
|
|
275
275
|
# name does not shadow the candidate here (it does in a given,
|
|
276
276
|
# C2.2), so `sets :note` + `ensures { note == ... }` judges what
|
|
277
277
|
# landed, and `old.<field>` remains the pre-state.
|
|
@@ -286,20 +286,20 @@ module Hecks
|
|
|
286
286
|
end
|
|
287
287
|
end
|
|
288
288
|
|
|
289
|
-
#
|
|
289
|
+
# The aggregate boundary, checked after every command, before
|
|
290
290
|
# save (S10, ADR 0025 — "Rules") — the same point `enforce_
|
|
291
291
|
# ensures` already checks at, and for the same reason: an
|
|
292
|
-
# invariant is a claim about the
|
|
292
|
+
# invariant is a claim about the settled record, not the
|
|
293
293
|
# command that produced it, so it reads no `args`/`old` at all,
|
|
294
294
|
# only the record's own state. `subject` here is
|
|
295
|
-
# always the
|
|
295
|
+
# always the aggregate's own instance — `CommandInterpreter`
|
|
296
296
|
# passes its own `ctx.instance`, and `EntityInterpreter` passes
|
|
297
|
-
# the
|
|
298
|
-
# entity mutation changes data inside the
|
|
297
|
+
# the parent record (`ctx.instance`, not the element), since an
|
|
298
|
+
# entity mutation changes data inside the same aggregate
|
|
299
299
|
# boundary the invariant guards; there is no separate "entity
|
|
300
300
|
# invariant" to check the piece's own view against.
|
|
301
301
|
#
|
|
302
|
-
#
|
|
302
|
+
# No `dereference` (S12, ADR 0025) — an invariant may only read
|
|
303
303
|
# `subject`'s own boundary, same rule `enforce_givens`/
|
|
304
304
|
# `enforce_ensures` now hold to. No invariant in the corpus has
|
|
305
305
|
# ever read across a `reference_to` (verified before this
|
|
@@ -317,20 +317,20 @@ module Hecks
|
|
|
317
317
|
check_entity_invariants(aggregate, subject, domain: domain)
|
|
318
318
|
end
|
|
319
319
|
|
|
320
|
-
# A
|
|
320
|
+
# A piece's own shape rule, checked against every instance the
|
|
321
321
|
# aggregate holds — not a separate boundary from the aggregate's
|
|
322
322
|
# own invariants just above (same two checkpoints: after every
|
|
323
|
-
# mutation, before save), just a
|
|
323
|
+
# mutation, before save), just a wider one: the aggregate's own
|
|
324
324
|
# consistency includes each of its pieces individually looking
|
|
325
325
|
# right, the same way `ValueObject#invariants` already checks
|
|
326
|
-
# each of
|
|
326
|
+
# each of its own instances one construct up. Recurses into
|
|
327
327
|
# nested pieces (S17, ADR 0026 — Dispatch inside Handler) the
|
|
328
328
|
# same way `check_entity_invariants`'s own caller recurses
|
|
329
329
|
# nowhere else needs to, since a piece's `entities` are already
|
|
330
330
|
# exactly as reachable as an aggregate's.
|
|
331
331
|
#
|
|
332
|
-
# `list_attr` reuses the
|
|
333
|
-
# element_of` already makes to locate a
|
|
332
|
+
# `list_attr` reuses the exact lookup `EntityInterpreter#
|
|
333
|
+
# element_of` already makes to locate a single addressed
|
|
334
334
|
# element by identity — this reads every element instead, but
|
|
335
335
|
# the "which field on the owner holds this piece's own
|
|
336
336
|
# instances" question is the identical one. A piece declaring
|
|
@@ -348,7 +348,7 @@ module Hecks
|
|
|
348
348
|
Array(owner_instance[list_attr.name]).each do |element|
|
|
349
349
|
wrapped = Instance.new(aggregate: entity, id: nil, state: element)
|
|
350
350
|
element_state = GuardState.new(wrapped)
|
|
351
|
-
#
|
|
351
|
+
# No `dereference` (S12, ADR 0025) — same boundary rule as
|
|
352
352
|
# enforce_invariants above; `parent` (the owner's own
|
|
353
353
|
# state, projected fields included) stays readable.
|
|
354
354
|
attrs = { parent: owner_instance.state }
|
|
@@ -379,7 +379,7 @@ module Hecks
|
|
|
379
379
|
# default `Object#to_s` instead of unwrapping the inner
|
|
380
380
|
# scalar first -- `current` came back as a raw object-pointer
|
|
381
381
|
# string (`"#<Hecks::Runtime::Value:0x...>"`) that could
|
|
382
|
-
# never match any declared `from` state, so
|
|
382
|
+
# never match any declared `from` state, so every transition
|
|
383
383
|
# on a VO-typed lifecycle field refused unconditionally, and
|
|
384
384
|
# when it refused the message leaked the pointer too.
|
|
385
385
|
# Confirmed live via `Plan::Task.Complete` (status defaults to
|
|
@@ -399,10 +399,10 @@ module Hecks
|
|
|
399
399
|
|
|
400
400
|
allowed = candidates.flat_map { |t| Array(t.from) }.uniq
|
|
401
401
|
raise LifecycleRefused,
|
|
402
|
-
RefusalWording.
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
402
|
+
RefusalWording.render_site("LifecycleRefused", "transition_blocked",
|
|
403
|
+
command: command.hecks_name, field: lifecycle.field,
|
|
404
|
+
current: Rendering.describe(current),
|
|
405
|
+
allowed: allowed)
|
|
406
406
|
end
|
|
407
407
|
end
|
|
408
408
|
end
|