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
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Runtime
|
|
3
|
-
#
|
|
3
|
+
# The one shared constant between `SagaInterpreter` (the writer) and
|
|
4
4
|
# `Registry::SagaPersistence#rehydrate_sagas!` (the reader) for a
|
|
5
5
|
# scoped, minimal answer to the saga-durability review's item 8 (a
|
|
6
6
|
# durable outbox): a marker that survives exactly the window a crash
|
|
7
7
|
# in `advance_saga`/`unwind` can otherwise hide.
|
|
8
8
|
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
9
|
+
# **The problem this closes** — `checkpoint` persists a saga's new state
|
|
10
|
+
# before the leg that justifies it (`handler.dispatches`) runs, and
|
|
11
11
|
# deliberately so: the mutex it holds is not reentrant, and a
|
|
12
12
|
# dispatch can re-enter this same interpreter. If the process dies
|
|
13
13
|
# in that window, the store says the transition happened and there
|
|
@@ -15,33 +15,33 @@ module Hecks
|
|
|
15
15
|
# domain never got asked), not a defect (nothing raised), just
|
|
16
16
|
# silence indistinguishable from a leg that finished cleanly.
|
|
17
17
|
#
|
|
18
|
-
#
|
|
18
|
+
# **The fix** — `checkpoint` now writes this key into the same already-
|
|
19
19
|
# durable `memory` blob (no new column, no adapter/schema change:
|
|
20
20
|
# `memory` is already an opaque, adapter-agnostic JSON blob every
|
|
21
21
|
# `save_saga` implementation round-trips verbatim) whenever it
|
|
22
|
-
# checkpoints a state a dispatch cascade hasn't run for
|
|
22
|
+
# checkpoints a state a dispatch cascade hasn't run for yet, and
|
|
23
23
|
# clears it (a second checkpoint, `pending: nil`) once that cascade
|
|
24
24
|
# — success, refusal-compensated, defect-compensated, or ceiling-
|
|
25
25
|
# compensated — has actually run. A crash between those two writes
|
|
26
26
|
# leaves the marker standing; `rehydrate_sagas!` strips it back out
|
|
27
|
-
# of the
|
|
27
|
+
# of the live instance's own `:memory` (so no dispatch/`given`/
|
|
28
28
|
# fuzzer/doc consumer of a saga's memory ever sees this key — it
|
|
29
29
|
# exists only in the persisted blob) and surfaces it loudly instead.
|
|
30
30
|
#
|
|
31
|
-
#
|
|
31
|
+
# **What this deliberately does not do** — auto-redrive the pending leg.
|
|
32
32
|
# Redelivering a dispatch whose outcome is genuinely unknown is only
|
|
33
33
|
# safe with idempotent delivery (the downstream command recognizing
|
|
34
34
|
# and no-op'ing a duplicate), which hecks's command/event pipeline
|
|
35
35
|
# has no mechanism for today. Blindly re-dispatching without that is
|
|
36
|
-
# how a stalled transfer becomes a
|
|
36
|
+
# how a stalled transfer becomes a double-credited one — a strictly
|
|
37
37
|
# worse defect than the stall it would replace. So this is real,
|
|
38
|
-
# durable, crash-surviving
|
|
38
|
+
# durable, crash-surviving visibility into exactly what a stalled
|
|
39
39
|
# saga was doing when the process died — the missing half of "no
|
|
40
40
|
# reconciliation pass exists". The full pending → claimed →
|
|
41
41
|
# delivered outbox now exists too (`Runtime::Outbox`, ADR 0053) and
|
|
42
|
-
# is
|
|
43
|
-
#
|
|
44
|
-
# claim it); this marker names the saga's own
|
|
42
|
+
# is complementary, not a replacement: the outbox row names the
|
|
43
|
+
# event owed to this saga (and redrives it if the saga never got to
|
|
44
|
+
# claim it); this marker names the saga's own leg mid-flight after
|
|
45
45
|
# the event was delivered. A crash can leave either standing.
|
|
46
46
|
SAGA_PENDING_DISPATCH_KEY = :__hecks_saga_pending_dispatch__
|
|
47
47
|
end
|
|
@@ -2,26 +2,26 @@ require_relative "../ports/persistence/binding_policy"
|
|
|
2
2
|
|
|
3
3
|
module Hecks
|
|
4
4
|
module Runtime
|
|
5
|
-
# The capability idiom for
|
|
5
|
+
# The capability idiom for multi-tenant hosting — mirrors EraCheck's
|
|
6
6
|
# own `lineage_capable?`, one level over. An adapter answers
|
|
7
|
-
# `tenant_capable?` with true when its
|
|
7
|
+
# `tenant_capable?` with true when its own instances genuinely
|
|
8
8
|
# isolate one boot's data from another's, given each tenant is its
|
|
9
9
|
# own separate `Runtime.boot` call (its own Registry, its own
|
|
10
10
|
# Dispatcher, its own adapter instances) rather than one shared
|
|
11
11
|
# process switching connections mid-dispatch.
|
|
12
12
|
#
|
|
13
|
-
#
|
|
13
|
+
# That last part is the finding this module encodes. The project
|
|
14
14
|
# register (Bluebook::ProjectRegister) already resolves an address's
|
|
15
|
-
#
|
|
15
|
+
# realm to a dispatcher at registration time — Router#resolve looks
|
|
16
16
|
# the FQN up in one flat table keyed by realm::domain::aggregate.verb,
|
|
17
|
-
# and each entry already carries its
|
|
18
|
-
# boot. So "which tenant" is decided
|
|
17
|
+
# and each entry already carries its own dispatcher from its own
|
|
18
|
+
# boot. So "which tenant" is decided once, at boot/registration time
|
|
19
19
|
# (which of possibly many boots of the same directory a request's
|
|
20
20
|
# realm resolves to), never per-dispatch inside a shared registry.
|
|
21
21
|
# No ambient thread-local "current tenant" is needed, and no
|
|
22
22
|
# connection cache is needed beyond what booting-once-per-tenant
|
|
23
23
|
# already gives for free — each tenant's own PostgresEra instance
|
|
24
|
-
#
|
|
24
|
+
# is its own connection, held for the life of that boot.
|
|
25
25
|
#
|
|
26
26
|
# So `tenant_capable?` asks a narrower question than it might sound:
|
|
27
27
|
# not "can this adapter switch tenants," but "does booting this
|
|
@@ -41,7 +41,7 @@ module Hecks
|
|
|
41
41
|
module TenantCheck
|
|
42
42
|
module_function
|
|
43
43
|
|
|
44
|
-
# A domain is safe to boot for
|
|
44
|
+
# A domain is safe to boot for more than one tenant only if every
|
|
45
45
|
# aggregate's resolved persistence adapter is tenant_capable? — one
|
|
46
46
|
# ungoverned adapter sharing state across two tenant boots is a
|
|
47
47
|
# real data leak, not a theoretical one, so this is checked before
|
|
@@ -67,7 +67,7 @@ module Hecks
|
|
|
67
67
|
"or keep #{domain} single-tenant."
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
-
# The capability idiom itself — an adapter
|
|
70
|
+
# The capability idiom itself — an adapter class that answers
|
|
71
71
|
# tenant_capable? with true keeps two boots' data apart by
|
|
72
72
|
# construction (Memory) or by an explicit per-boot isolation
|
|
73
73
|
# setting (PostgresEra's schema:). Same defensive shape
|
|
@@ -7,9 +7,9 @@ module Hecks
|
|
|
7
7
|
module Runtime
|
|
8
8
|
# `authorize policy, tenant: :field` declared a tenant boundary that
|
|
9
9
|
# nothing enforced — the policy name and the field were stored and read
|
|
10
|
-
# by nothing at dispatch time. This is the half that
|
|
10
|
+
# by nothing at dispatch time. This is the half that can be enforced
|
|
11
11
|
# without a caller-identity/session system: the boundary itself, made
|
|
12
|
-
# mandatory. Whether
|
|
12
|
+
# mandatory. Whether this caller actually holds `policy` for this
|
|
13
13
|
# tenant needs real identity infrastructure this runtime does not have
|
|
14
14
|
# — that stays a named, open gap, not something this quietly pretends
|
|
15
15
|
# to answer.
|
|
@@ -22,7 +22,7 @@ module Hecks
|
|
|
22
22
|
# free, with no per-engine code and no way for one engine to forget it.
|
|
23
23
|
# `Scoped` is handed only to those engines as their `declared`/
|
|
24
24
|
# `specification` argument — never returned to a caller that might call
|
|
25
|
-
# an IR-level method (`filtered_head_name`, `to_h`, …) whose
|
|
25
|
+
# an IR-level method (`filtered_head_name`, `to_h`, …) whose own
|
|
26
26
|
# internal `wheres` call would resolve against the original object, not
|
|
27
27
|
# this override, since `SimpleDelegator` only intercepts calls made
|
|
28
28
|
# directly on the wrapper.
|
|
@@ -35,8 +35,8 @@ module Hecks
|
|
|
35
35
|
|
|
36
36
|
tenant = tenant.to_sym
|
|
37
37
|
unless args.key?(tenant)
|
|
38
|
-
raise Unauthorized, RefusalWording.
|
|
39
|
-
|
|
38
|
+
raise Unauthorized, RefusalWording.render_site("Unauthorized", "tenant_required",
|
|
39
|
+
query: declared.name, field: tenant)
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
Scoped.new(declared, QuerySpecification::Common::WhereClause.new(field: tenant, op: "eq", value: tenant))
|
|
@@ -8,6 +8,15 @@ module Hecks
|
|
|
8
8
|
# refusals that make them rules rather than decoration. Extended into
|
|
9
9
|
# Value beside Coercion; `self` is the Value class.
|
|
10
10
|
module Admission
|
|
11
|
+
# Refuses fields that match no declared member of a closed-set (`one_of`) value object.
|
|
12
|
+
#
|
|
13
|
+
# @param value_object [Bluebook::ValueObject] the type being built; one with no
|
|
14
|
+
# members is not a closed set and is never refused
|
|
15
|
+
# @param fields [Hash{Symbol => Object}] the offered fields, keyed by attribute name
|
|
16
|
+
# @return [nil] when the type has no members, or some member matches on every
|
|
17
|
+
# field it names
|
|
18
|
+
# @raise [Runtime::InvariantViolation] if no member matches; the message lists the
|
|
19
|
+
# admitted values of the first attribute and the one offered
|
|
11
20
|
def admit_member(value_object, fields)
|
|
12
21
|
return if value_object.members.empty?
|
|
13
22
|
|
|
@@ -17,42 +26,54 @@ module Hecks
|
|
|
17
26
|
return if value_object.members.any? { |member| member_matches?(member, fields) }
|
|
18
27
|
|
|
19
28
|
raise InvariantViolation,
|
|
20
|
-
RefusalWording.
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
RefusalWording.render_site("InvariantViolation", "closed_set_member",
|
|
30
|
+
type: value_object.hecks_name,
|
|
31
|
+
admitted: admitted, offered: offered.inspect)
|
|
23
32
|
end
|
|
24
33
|
|
|
25
|
-
#
|
|
34
|
+
# Every declared field, not only the discriminant — a multi-column
|
|
26
35
|
# `member` row (`StatementFrequency`'s `cadence`/`retention_months`/
|
|
27
36
|
# `paper_fee_cents`, statements.bluebook) names a whole tuple, and a
|
|
28
37
|
# caller offering the right `cadence` with the wrong
|
|
29
|
-
# `retention_months` is not a genuine member just because the
|
|
30
|
-
# column happened to match.
|
|
31
|
-
#
|
|
38
|
+
# `retention_months` is not a genuine member just because the first
|
|
39
|
+
# column happened to match. Comparing only the discriminant
|
|
40
|
+
# (`admitted.include?(offered.to_s)`) stops at that first column —
|
|
32
41
|
# confirmed live: `Value.build(StatementFrequency, cadence:
|
|
33
|
-
# "monthly", retention_months: 999, paper_fee_cents: 999)`
|
|
34
|
-
# admitted outright, no member of the closed set declares that row.
|
|
42
|
+
# "monthly", retention_months: 999, paper_fee_cents: 999)` would be
|
|
43
|
+
# admitted outright, though no member of the closed set declares that row.
|
|
35
44
|
# A single-field set (`AccountKind`, `LedgerDirection`, ...) has
|
|
36
|
-
# exactly one key here, so this reduces to
|
|
37
|
-
#
|
|
38
|
-
#
|
|
45
|
+
# exactly one key here, so this reduces to a discriminant-only
|
|
46
|
+
# comparison for every set that has one column — same refusal,
|
|
47
|
+
# same wording.
|
|
39
48
|
private def member_matches?(member, fields)
|
|
40
49
|
member.all? { |field, value| fields[field].to_s == value.to_s }
|
|
41
50
|
end
|
|
42
51
|
|
|
43
|
-
#
|
|
52
|
+
# Checks a value against the closed set its attribute names with `admits:`.
|
|
53
|
+
#
|
|
54
|
+
# The same refusal, for a set named somewhere else.
|
|
44
55
|
#
|
|
45
|
-
# `admit_member` above refuses a non-member when the value object
|
|
46
|
-
#
|
|
47
|
-
# make, because it
|
|
56
|
+
# `admit_member` above refuses a non-member when the value object being
|
|
57
|
+
# built is itself the closed set — which is the only shape `one_of` can
|
|
58
|
+
# make, because it synthesises the set from the values written inline. An
|
|
48
59
|
# `admits:` attribute is the other direction: the value is an ordinary
|
|
49
60
|
# String or a plain text holder, and the set it must belong to was
|
|
50
61
|
# declared once, elsewhere, and is named rather than restated.
|
|
51
62
|
#
|
|
52
|
-
# Without this the word was a
|
|
63
|
+
# Without this the word was a declaration and nothing more — read by
|
|
53
64
|
# projections, read by nobody at the door. A rule
|
|
54
65
|
# that cannot be the one to refuse is decoration, and this language has
|
|
55
66
|
# paid for that mistake before.
|
|
67
|
+
#
|
|
68
|
+
# @param owner [Bluebook::Aggregate, Bluebook::Entity, Bluebook::ValueObject] the
|
|
69
|
+
# construct that declares `attribute`; the walk to the chapter starts here
|
|
70
|
+
# @param attribute [Bluebook::Attribute, nil] the attribute being filled; nil, or one
|
|
71
|
+
# with no `admits:`, means nothing is checked
|
|
72
|
+
# @param value [Object, nil] the coerced value: a bare scalar, or a `Runtime::Value`
|
|
73
|
+
# holding one field; nil is never checked
|
|
74
|
+
# @return [Object, nil] `value`, unchanged
|
|
75
|
+
# @raise [Runtime::InvariantViolation] if the value is not a member of the named set,
|
|
76
|
+
# or `admits:` names a set no aggregate in the chapter declares
|
|
56
77
|
def admit_declared_set(owner, attribute, value)
|
|
57
78
|
return value if attribute.nil? || attribute.admits.nil? || value.nil?
|
|
58
79
|
|
|
@@ -61,21 +82,32 @@ module Hecks
|
|
|
61
82
|
return value if admitted.include?(offered.to_s)
|
|
62
83
|
|
|
63
84
|
raise InvariantViolation,
|
|
64
|
-
RefusalWording.
|
|
65
|
-
|
|
66
|
-
|
|
85
|
+
RefusalWording.render_site("InvariantViolation", "admits_declared_set",
|
|
86
|
+
name: attribute.name, admits: attribute.admits,
|
|
87
|
+
admitted: admitted, offered: offered.inspect)
|
|
67
88
|
end
|
|
68
89
|
|
|
69
|
-
#
|
|
70
|
-
#
|
|
90
|
+
# Lists the values the closed set named by an attribute's `admits:` allows.
|
|
91
|
+
#
|
|
92
|
+
# `Vocabulary::MutationOp` — the aggregate that holds the set, then the
|
|
93
|
+
# set. Qualified because a closed set is a value object inside an
|
|
71
94
|
# aggregate, which is exactly why `admits` could not be spelled as a
|
|
72
95
|
# reference: `reference_to` reaches aggregate heads and nothing below one.
|
|
73
96
|
#
|
|
74
|
-
#
|
|
97
|
+
# Resolved late, like `Reference#resolve` and for the same reason — the
|
|
75
98
|
# set may be declared further down the file than the attribute that names
|
|
76
|
-
# it. And
|
|
99
|
+
# it. And refused when it resolves to nothing, also like `Reference`: a
|
|
77
100
|
# link checked against nothing is worse than no link, because it reads
|
|
78
101
|
# like a rule.
|
|
102
|
+
#
|
|
103
|
+
# @param owner [Bluebook::Aggregate, Bluebook::Entity, Bluebook::ValueObject] the
|
|
104
|
+
# construct that declares `attribute`
|
|
105
|
+
# @param attribute [Bluebook::Attribute] an attribute whose `admits` is
|
|
106
|
+
# `"Aggregate::SetName"`
|
|
107
|
+
# @return [Array<String>] each member's value for the set's first attribute, as a String,
|
|
108
|
+
# in declaration order
|
|
109
|
+
# @raise [Runtime::InvariantViolation] if `admits` is not qualified, no chapter is
|
|
110
|
+
# reachable from `owner`, or the chapter declares no such aggregate or value object
|
|
79
111
|
def admitted_members(owner, attribute)
|
|
80
112
|
aggregate_name, set_name = attribute.admits.to_s.split("::", 2)
|
|
81
113
|
chapter = chapter_of(owner)
|
|
@@ -83,26 +115,39 @@ module Hecks
|
|
|
83
115
|
|
|
84
116
|
unless set
|
|
85
117
|
raise InvariantViolation,
|
|
86
|
-
RefusalWording.
|
|
87
|
-
|
|
118
|
+
RefusalWording.render_site("InvariantViolation", "undeclared_set",
|
|
119
|
+
name: attribute.name, admits: attribute.admits)
|
|
88
120
|
end
|
|
89
121
|
|
|
90
122
|
discriminant = set.attributes.first.name
|
|
91
123
|
set.members.map { |member| member.to_h[discriminant].to_s }
|
|
92
124
|
end
|
|
93
125
|
|
|
94
|
-
#
|
|
126
|
+
# Walks `hecks_owner` links upward until it reaches the chapter.
|
|
127
|
+
#
|
|
128
|
+
# Up to the chapter, from wherever the attribute was declared. A value
|
|
95
129
|
# object's owner is its aggregate and an aggregate's owner is its chapter,
|
|
96
130
|
# so the walk stops at the first construct that can answer for an
|
|
97
|
-
# aggregate
|
|
131
|
+
# aggregate by name — which is the chapter, and only the chapter.
|
|
132
|
+
#
|
|
133
|
+
# @param construct [Bluebook::ValueObject, Bluebook::Entity, Bluebook::Aggregate,
|
|
134
|
+
# Bluebook::Chapter, nil] the construct to start from; the chapter answers itself
|
|
135
|
+
# @return [Bluebook::Chapter, nil] the first construct on the way up that responds to
|
|
136
|
+
# `aggregate`; nil when the chain ends without one
|
|
98
137
|
def chapter_of(construct)
|
|
99
138
|
node = construct
|
|
100
139
|
node = node.hecks_owner while node && !node.respond_to?(:aggregate) && node.respond_to?(:hecks_owner)
|
|
101
140
|
node.respond_to?(:aggregate) ? node : nil
|
|
102
141
|
end
|
|
103
142
|
|
|
104
|
-
#
|
|
143
|
+
# Unwraps a one-field value object to the scalar it holds, so membership compares scalars.
|
|
144
|
+
#
|
|
145
|
+
# An admitted value is a scalar however it arrived — as a bare string on a
|
|
105
146
|
# plain field, or wrapped in the one-field holder its type names.
|
|
147
|
+
#
|
|
148
|
+
# @param value [Object] a bare scalar, or a `Runtime::Value`
|
|
149
|
+
# @return [Object] the sole field's value for a one-field `Runtime::Value`; otherwise
|
|
150
|
+
# `value` itself, a multi-field `Runtime::Value` included
|
|
106
151
|
def admitted_scalar(value)
|
|
107
152
|
return value unless value.is_a?(self)
|
|
108
153
|
|
|
@@ -110,7 +155,7 @@ module Hecks
|
|
|
110
155
|
fields.size == 1 ? fields.values.first : value
|
|
111
156
|
end
|
|
112
157
|
|
|
113
|
-
# A
|
|
158
|
+
# A field of a value object may name a set too.
|
|
114
159
|
#
|
|
115
160
|
# Beside check_patterns and for the same reason: `Query::Filter.op` is a
|
|
116
161
|
# plain String field that admits `Vocabulary::QueryComparator`, and the
|