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
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Bluebook
|
|
3
|
+
# What a chapter may declare it provides, and what each capability
|
|
4
|
+
# must name. A chapter that `provides "authorization"` is trusted in
|
|
5
|
+
# place of a check for the literal name "Governance" — by the role
|
|
6
|
+
# check at dispatch, the ungoverned-role refusal at boot, the
|
|
7
|
+
# authorization adapter and the fuzzer — so the declaration has to be
|
|
8
|
+
# complete and point at real verbs. `BluebookBuilder::Validation
|
|
9
|
+
# #validate_provisions!` holds every `provides` to this table.
|
|
10
|
+
#
|
|
11
|
+
# key => :command | :query the kind of verb that key must name
|
|
12
|
+
module Capabilities
|
|
13
|
+
AUTHORIZATION = "authorization".freeze
|
|
14
|
+
|
|
15
|
+
CONTRACTS = {
|
|
16
|
+
AUTHORIZATION => {
|
|
17
|
+
# every assignment an actor holds, current or historical
|
|
18
|
+
assignments: :query,
|
|
19
|
+
# the command that grants an actor a role
|
|
20
|
+
grant: :command,
|
|
21
|
+
# every grant of one role acting as another
|
|
22
|
+
transitions: :query
|
|
23
|
+
}.freeze
|
|
24
|
+
}.freeze
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
require_relative "behaviour/chapter"
|
|
2
|
+
require_relative "capabilities"
|
|
2
3
|
require_relative "../ir"
|
|
3
4
|
|
|
4
5
|
module Hecks
|
|
5
6
|
module Bluebook
|
|
6
|
-
#
|
|
7
|
+
# The chapter, and no longer the namespace it lives in.
|
|
7
8
|
#
|
|
8
|
-
# `Hecks::Bluebook` was briefly a
|
|
9
|
+
# `Hecks::Bluebook` was briefly a class, because dropping the
|
|
9
10
|
# old `IR::` segment would otherwise have produced
|
|
10
11
|
# `Bluebook::Bluebook` — a class shadowing its own enclosing module.
|
|
11
12
|
# Naming the construct what the code already called it everywhere
|
|
12
13
|
# (a chapter; this file was already chapter.rb) removes the clash
|
|
13
14
|
# at the source, so the namespace goes back to being a module.
|
|
14
15
|
#
|
|
15
|
-
# Measured before changing: `Bluebook` was used
|
|
16
|
+
# Measured before changing: `Bluebook` was used as a class in five
|
|
16
17
|
# places and as a namespace in seventy-seven files.
|
|
17
18
|
class Chapter
|
|
18
19
|
include Construct
|
|
19
20
|
include Behaviour::Chapter
|
|
20
21
|
include Hecks::IR
|
|
21
22
|
|
|
22
|
-
#
|
|
23
|
-
# "v1", a business fact the
|
|
23
|
+
# **The schema's own version** — not a domain's `version:` (Banking's
|
|
24
|
+
# "v1", a business fact the author chose), but the shape `to_h`
|
|
24
25
|
# itself emits. A consumer reading exported IR with no Ruby DSL to
|
|
25
26
|
# cross-check against (a build-time generator, a stored snapshot)
|
|
26
|
-
# needs to know
|
|
27
|
+
# needs to know which shape it's holding before it can safely
|
|
27
28
|
# interpret any of the rest of it. Bump this when `to_h`'s own
|
|
28
29
|
# shape changes in a way a consumer would need to know about —
|
|
29
30
|
# not when a domain's own declarations change.
|
|
@@ -40,7 +41,7 @@ module Hecks
|
|
|
40
41
|
# `bluebook.formerly_known_as` off the live Ruby object) and the
|
|
41
42
|
# meta-validator's cache key is `SHA256(JSON(bluebook.to_h))` — so
|
|
42
43
|
# a fact this method didn't spell was a fact two chapters
|
|
43
|
-
# differing
|
|
44
|
+
# differing only by their old name could hash identically on,
|
|
44
45
|
# same as read-model filters before them. Spelled here for the
|
|
45
46
|
# same reason `version`/`vision` are: a plain field, present
|
|
46
47
|
# (possibly nil) rather than silently absent.
|
|
@@ -50,15 +51,32 @@ module Hecks
|
|
|
50
51
|
policies: many(:policies),
|
|
51
52
|
process_managers: many(:process_managers),
|
|
52
53
|
attaches_to: :attaches_to,
|
|
54
|
+
# A capability this chapter answers (`provides "authorization",
|
|
55
|
+
# grant: "RoleAssignment.Assign", ...`), one row per key, in the
|
|
56
|
+
# order written. Present (possibly empty) on every chapter, the
|
|
57
|
+
# same reading `attaches_to` beside it gives.
|
|
58
|
+
provides: -> { provides.map(&:to_h) },
|
|
53
59
|
canonical_form: -> { Expression::CanonicalForm.table }
|
|
54
60
|
)
|
|
55
61
|
|
|
62
|
+
# **One row of a declared capability** — `verb` is chapter-local
|
|
63
|
+
# ("RoleAssignment.Assign"); `Behaviour::Chapter#provided_verb`
|
|
64
|
+
# qualifies it with the chapter's own name.
|
|
65
|
+
Provision = Struct.new(:capability, :key, :verb, keyword_init: true) do
|
|
66
|
+
def self.from(row)
|
|
67
|
+
return row if row.is_a?(self)
|
|
68
|
+
|
|
69
|
+
fields = row.to_h.transform_keys(&:to_sym)
|
|
70
|
+
new(capability: fields[:capability].to_s, key: fields[:key].to_s, verb: fields[:verb].to_s)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
56
74
|
attr_reader :name, :version, :vision, :aggregates, :policies, :process_managers,
|
|
57
|
-
:classification, :read_models, :ports, :formerly_known_as, :attaches_to
|
|
75
|
+
:classification, :read_models, :ports, :formerly_known_as, :attaches_to, :provides
|
|
58
76
|
|
|
59
77
|
def initialize(name:, version: nil, vision: nil, aggregates: [], policies: [],
|
|
60
78
|
process_managers: [], classification: nil, read_models: [], formerly_known_as: nil,
|
|
61
|
-
attaches_to: [])
|
|
79
|
+
attaches_to: [], provides: [])
|
|
62
80
|
@policies = policies
|
|
63
81
|
@process_managers = process_managers
|
|
64
82
|
@name = name.to_s
|
|
@@ -71,6 +89,7 @@ module Hecks
|
|
|
71
89
|
@classification = classification&.to_s
|
|
72
90
|
@formerly_known_as = formerly_known_as&.to_s
|
|
73
91
|
@attaches_to = Array(attaches_to).map(&:to_s)
|
|
92
|
+
@provides = Array(provides).map { |row| Provision.from(row) }
|
|
74
93
|
settle
|
|
75
94
|
end
|
|
76
95
|
end
|
|
@@ -12,12 +12,12 @@ module Hecks
|
|
|
12
12
|
|
|
13
13
|
# `sign:` — item #5 of the whole-project table-unification survey.
|
|
14
14
|
# `increment`/`decrement`'s own +1/-1 used to be re-derived from the
|
|
15
|
-
# op
|
|
15
|
+
# op name by two independent Rust codegen scripts (rust/project/
|
|
16
16
|
# mutations.rb, rust/codegen/src/mutations.rs — a ternary on
|
|
17
17
|
# `op == "increment"` in each), even though the fact was already
|
|
18
18
|
# table-driven on the Ruby runtime side
|
|
19
|
-
# (Runtime::CommandRules::Arithmetic::MUTATION_OPS,
|
|
20
|
-
# Vocabulary::MutationOp
|
|
19
|
+
# (Runtime::CommandRules::Arithmetic::MUTATION_OPS, itself read off
|
|
20
|
+
# the same generated Vocabulary::MutationOp table).
|
|
21
21
|
# Reads `Vocabulary::MutationOp` directly (plain data, no framework
|
|
22
22
|
# dependency — safe during parsing, same reason `RuleReference`'s
|
|
23
23
|
# own bootstrap concerns don't apply here) rather than requiring
|
|
@@ -31,8 +31,8 @@ module Hecks
|
|
|
31
31
|
|
|
32
32
|
emits_ir(target: :target, op: :op, sign: -> { Mutation.sign_for(op) })
|
|
33
33
|
|
|
34
|
-
#
|
|
35
|
-
#
|
|
34
|
+
# The one genuinely branching emission in the model: an append (or a
|
|
35
|
+
# delegate — `CommandBuilder#delegates_to`'s own comment gives the
|
|
36
36
|
# full reasoning for reusing this exact wire shape rather than
|
|
37
37
|
# inventing a parallel one) binds several fields at once and carries
|
|
38
38
|
# `fields:`, everything else carries a single `source:`. Declared
|
|
@@ -46,9 +46,9 @@ module Hecks
|
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
# A command, as a
|
|
49
|
+
# A command, as a Ruby class.
|
|
50
50
|
#
|
|
51
|
-
#
|
|
51
|
+
# Not nested as a constant, and that is a finding rather than a shortcut. A
|
|
52
52
|
# command and a value object may legitimately share a name inside one
|
|
53
53
|
# aggregate — the language does it six times, and means it: the command
|
|
54
54
|
# `Argument` is the verb that appends to the `arguments` list whose element
|
|
@@ -56,10 +56,10 @@ module Hecks
|
|
|
56
56
|
# So `Bluebook::Command::Argument` cannot be both, and a single constant
|
|
57
57
|
# namespace cannot index a kind-ambiguous name. The same follows for
|
|
58
58
|
# `hecks_fqn` : `Bluebook::Command.Argument` names both, which is why the judge's
|
|
59
|
-
# ids only work per-category, each in its own repository.
|
|
60
|
-
# (
|
|
59
|
+
# ids only work per-category, each in its own repository. Identity is
|
|
60
|
+
# (kind, FQN), not fqn.
|
|
61
61
|
#
|
|
62
|
-
# It is a declaration holder anyway, because that is where the
|
|
62
|
+
# It is a declaration holder anyway, because that is where the edges live.
|
|
63
63
|
# `acts_on` answers with the owning construct itself — the Aggregate,
|
|
64
64
|
# or the entity holder for a piece's verb — rather than the name of one.
|
|
65
65
|
# The invocation door (`pizza.add_topping`) is the facade's business, a
|
|
@@ -79,8 +79,8 @@ module Hecks
|
|
|
79
79
|
ensures: -> { ensures.map { |rule| Expression::AstJson.rule_row(rule) } },
|
|
80
80
|
mutations: many(:mutations),
|
|
81
81
|
emits: :emits,
|
|
82
|
-
#
|
|
83
|
-
# 0025 — "lifecycle state becomes a command guard") — a
|
|
82
|
+
# The lifecycle state this command is admissible from (S10, ADR
|
|
83
|
+
# 0025 — "lifecycle state becomes a command guard") — a guard,
|
|
84
84
|
# not a transition: `command "Debit", from: "open"` replaces
|
|
85
85
|
# `given("account is open") { status == "open" }`, checked
|
|
86
86
|
# against the owning construct's own lifecycle field the same
|
|
@@ -2,8 +2,8 @@ require_relative "behaviour/domain_port"
|
|
|
2
2
|
|
|
3
3
|
module Hecks
|
|
4
4
|
module Bluebook
|
|
5
|
-
#
|
|
6
|
-
#
|
|
5
|
+
# The primary/driving half of hexagonal architecture (Cockburn) — called
|
|
6
|
+
# by an adapter living outside the bluebook entirely, never by the
|
|
7
7
|
# domain calling out. That is already `Hecks.port` (persistence,
|
|
8
8
|
# projection, extraction, loading) plus `Ports::*` : the secondary/
|
|
9
9
|
# driven half, unchanged by this.
|
|
@@ -21,7 +21,7 @@ module Hecks
|
|
|
21
21
|
|
|
22
22
|
attr_reader :hecks_name, :attributes, :emits, :direction, :answers, :refuses, :to
|
|
23
23
|
|
|
24
|
-
#
|
|
24
|
+
# Two directions through one door.
|
|
25
25
|
#
|
|
26
26
|
# `:inbound` is what this class has always been — `tells`, spelled
|
|
27
27
|
# `operation` before it had a twin: an external fact arriving, turned
|
|
@@ -29,7 +29,7 @@ module Hecks
|
|
|
29
29
|
# channel back to whoever called.
|
|
30
30
|
#
|
|
31
31
|
# `:outbound` is `asks` — the domain wanting something from outside
|
|
32
|
-
# and having to live with either answer. It names
|
|
32
|
+
# and having to live with either answer. It names both: `answers` for
|
|
33
33
|
# what the adapter came back with, `refuses` for what it said instead.
|
|
34
34
|
# Naming only the happy one would put the failure somewhere the model
|
|
35
35
|
# cannot see, which is the whole reason a boundary is worth modelling.
|
|
@@ -48,15 +48,15 @@ module Hecks
|
|
|
48
48
|
def inbound? = @direction == :inbound
|
|
49
49
|
|
|
50
50
|
# No root reference of its own — unlike a command, every attribute
|
|
51
|
-
#
|
|
51
|
+
# equally describes the payload, including whichever one identifies
|
|
52
52
|
# the record its emitted event belongs to. Kept only so
|
|
53
53
|
# CommandInterpreter::ArgumentGate's `reference_key` can ask for it
|
|
54
54
|
# without learning this isn't a command.
|
|
55
55
|
|
|
56
|
-
# `direction`/`answers`/`refuses` are deliberately
|
|
56
|
+
# `direction`/`answers`/`refuses` are deliberately outside `emits_ir`'s
|
|
57
57
|
# declared shape and added here only for an outbound operation — an
|
|
58
58
|
# ordinary inbound one (`tells`, still spelled `operation` everywhere
|
|
59
|
-
# in the existing corpus) keeps the
|
|
59
|
+
# in the existing corpus) keeps the exact prior IR shape, byte for
|
|
60
60
|
# byte. Pizzas' `PaymentGateway` port is inbound-only and is checked
|
|
61
61
|
# against `hecks-parse`'s own Rust output for byte-identity
|
|
62
62
|
# (parser_parity_spec.rb) — the Rust side has no notion of `asks` yet,
|
|
@@ -64,12 +64,12 @@ module Hecks
|
|
|
64
64
|
# domain that never asked for the feature. Only a chapter that
|
|
65
65
|
# actually declares `asks` (this extraction's own QualityControl
|
|
66
66
|
# ledger, not yet in any Rust-parity corpus) pays for it.
|
|
67
|
-
# `to` —
|
|
67
|
+
# `to` — same "deliberately outside emits_ir, merged in only when
|
|
68
68
|
# present" treatment as direction/answers/refuses just above, and
|
|
69
69
|
# for the identical reason: an operation still spelled the old way
|
|
70
70
|
# (`reference_to` inside the block, shadow-parsing only — see
|
|
71
71
|
# reference_to_impl's own comment) or one that simply hasn't
|
|
72
|
-
# migrated yet keeps the
|
|
72
|
+
# migrated yet keeps the exact prior IR shape, byte for byte,
|
|
73
73
|
# instead of an unconditional new key breaking parser_parity_spec
|
|
74
74
|
# for every domain that never touched this.
|
|
75
75
|
def to_h
|
|
@@ -10,22 +10,46 @@ module Hecks
|
|
|
10
10
|
|
|
11
11
|
include WordGate
|
|
12
12
|
|
|
13
|
+
# @param name [String] the adapter's name, as written after `Hecks.adapter`
|
|
13
14
|
def initialize(name)
|
|
14
15
|
@name = name
|
|
15
16
|
@fields = []
|
|
16
17
|
@secrets = []
|
|
17
18
|
end
|
|
18
19
|
|
|
20
|
+
# Names the port this adapter implements; a repeated call replaces the earlier name.
|
|
21
|
+
#
|
|
22
|
+
# @param value [String, Symbol] the port's name, such as `"CI"` or `"agent"`
|
|
23
|
+
# @return [String] the port name as stored
|
|
19
24
|
def port(value) = @port = value.to_s
|
|
20
25
|
|
|
26
|
+
# Declares one plain setting a `.world` file may supply for this adapter.
|
|
27
|
+
#
|
|
28
|
+
# @param name [Symbol, String] the setting's name, such as `:database`
|
|
29
|
+
# @return [Array<Symbol>] every field declared so far, this one last
|
|
21
30
|
def field(name) = @fields << name.to_sym
|
|
22
31
|
|
|
32
|
+
# Declares one setting whose value is a secret, such as an API token.
|
|
33
|
+
#
|
|
34
|
+
# @param name [Symbol, String] the secret's name, such as `:api_token`
|
|
35
|
+
# @return [Array<Symbol>] every secret declared so far, this one last
|
|
23
36
|
def secret(name) = @secrets << name.to_sym
|
|
24
37
|
|
|
38
|
+
# Assembles the collected port, fields and secrets, judged by the adapter language.
|
|
39
|
+
#
|
|
40
|
+
# @return [Bluebook::Adapter] the adapter, returned once the language accepts it
|
|
41
|
+
# @raise [Bluebook::DSL::Malformed] if the adapter language refuses the declaration
|
|
25
42
|
def build
|
|
26
43
|
MetaValidator.call_adapter(Adapter.new(name: @name, port: @port, fields: @fields, secrets: @secrets))
|
|
27
44
|
end
|
|
28
45
|
|
|
46
|
+
# Evaluates an `Hecks.adapter` block against a fresh builder and returns what it built.
|
|
47
|
+
#
|
|
48
|
+
# @param name [String] the adapter's name
|
|
49
|
+
# @yield the adapter body, evaluated with the builder as `self`; may be omitted
|
|
50
|
+
# @return [Bluebook::Adapter] the judged adapter
|
|
51
|
+
# @raise [Bluebook::DSL::Malformed] if the adapter language refuses the declaration, or
|
|
52
|
+
# the block uses a word the `Adapter` grammar does not admit
|
|
29
53
|
def self.build(name, &block)
|
|
30
54
|
builder = new(name)
|
|
31
55
|
builder.instance_eval(&block) if block
|
|
@@ -2,17 +2,17 @@ module Hecks
|
|
|
2
2
|
module Bluebook
|
|
3
3
|
module DSL
|
|
4
4
|
class AggregateBuilder
|
|
5
|
-
#
|
|
5
|
+
# **The "SEAL_*" pass** — everything `#build` runs once every
|
|
6
6
|
# declaration (attributes, entities, commands, queries, the
|
|
7
7
|
# lifecycle) is otherwise in place, checking that what a command,
|
|
8
|
-
# query, or default
|
|
8
|
+
# query, or default names actually exists elsewhere on the
|
|
9
9
|
# aggregate. Split out of aggregate_builder.rb (which keeps the
|
|
10
10
|
# DSL surface itself — attribute/command/query/policy declaration
|
|
11
11
|
# — and the smaller drain_pending!/identity bookkeeping) because
|
|
12
12
|
# this cluster is one cohesive concern: cross-field validation
|
|
13
13
|
# that can only run after every declaration is real, the same
|
|
14
14
|
# relationship BluebookBuilder's own `self.validate_*` cluster has
|
|
15
|
-
# to
|
|
15
|
+
# to its chapter (see that file's own header for the parallel).
|
|
16
16
|
# `include`d back into AggregateBuilder, same pattern as
|
|
17
17
|
# `Runtime::Registry`'s own `include Verification` /
|
|
18
18
|
# `include SagaPersistence` — same class, its private instance
|
|
@@ -23,10 +23,10 @@ module Hecks
|
|
|
23
23
|
# Every reference is told which Aggregate declares it, so it can
|
|
24
24
|
# find the chapter and resolve its target.
|
|
25
25
|
#
|
|
26
|
-
# Stamped
|
|
26
|
+
# Stamped here, at build, rather than at `reference_to`, because a command
|
|
27
27
|
# builder does not hold the aggregate and should not learn to. And
|
|
28
28
|
# deliberately across every list that can carry one — a reference the walk
|
|
29
|
-
# missed would resolve to nil, and `resolve_references`
|
|
29
|
+
# missed would resolve to nil, and `resolve_references` skips a nil target,
|
|
30
30
|
# so the guarantee would go quiet instead of going red. That is the exact
|
|
31
31
|
# shape of the bug that let an Account belong to an unregistered customer
|
|
32
32
|
# fourteen times over.
|
|
@@ -34,8 +34,8 @@ module Hecks
|
|
|
34
34
|
reference_bearing_attributes.each { |attribute| attribute.type.declared_in = aggregate }
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
#
|
|
38
|
-
#
|
|
37
|
+
# An owned piece's own `reference_to` is an edge this aggregate
|
|
38
|
+
# points across too (S9, ADR 0025 — "entity/aggregate shared
|
|
39
39
|
# vocabulary") — a ring closing through a contained piece (Board
|
|
40
40
|
# -> Board::Card -> Product -> Board) is the same "no boundary
|
|
41
41
|
# anyone can reason about alone" `validate_no_bidirectional_
|
|
@@ -43,7 +43,7 @@ module Hecks
|
|
|
43
43
|
# aggregate ring; it was invisible before this because only
|
|
44
44
|
# `AggregateBuilder#reference_to` ever fed `@reference_targets`,
|
|
45
45
|
# never `EntityBuilder#reference_to`. Command/query reference
|
|
46
|
-
#
|
|
46
|
+
# arguments are deliberately excluded — they are data flowing
|
|
47
47
|
# through a dispatch, not persisted state the graph a cycle
|
|
48
48
|
# means anything over.
|
|
49
49
|
def entity_reference_targets
|
|
@@ -61,12 +61,12 @@ module Hecks
|
|
|
61
61
|
lists.flatten.select(&:reference?)
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
-
# A mutation must name a field the aggregate actually
|
|
64
|
+
# A mutation must name a field the aggregate actually has.
|
|
65
65
|
#
|
|
66
|
-
#
|
|
66
|
+
# Not moved to the language, and deliberately so. The language says only
|
|
67
67
|
# `given("a mutation names a target") { !target.value.to_s.empty? }` —
|
|
68
68
|
# non-emptiness — because saying more means reaching a list that lives on
|
|
69
|
-
# a
|
|
69
|
+
# a different root : a command's changes hang off Command, the fields they
|
|
70
70
|
# name hang off Aggregate, and a given is a closed predicate over its own
|
|
71
71
|
# state. Aggregate.Seal is the right shape and cannot see commands ; the
|
|
72
72
|
# reference trick that rescued "attributes use value-object types" needs a
|
|
@@ -78,10 +78,10 @@ module Hecks
|
|
|
78
78
|
# So it lives here, at build, where every declaration is present. Found by
|
|
79
79
|
# writing `then_set :disputed_by` on CardPayment before the field existed :
|
|
80
80
|
# it wrote into nothing, refused nothing, and every check stayed green.
|
|
81
|
-
# A
|
|
81
|
+
# A default fills the shape it is declared on, or it fills nothing.
|
|
82
82
|
#
|
|
83
83
|
# `attribute :cover, one_of("covered", "open"), default: "open"` builds
|
|
84
|
-
# cleanly and then refuses
|
|
84
|
+
# cleanly and then refuses every create at dispatch — "cover is a Cover,
|
|
85
85
|
# pass its fields as an object" — because the value object wants its
|
|
86
86
|
# fields and got a bare string. The bluebook is wrong at the line where
|
|
87
87
|
# it is written and says so nowhere near it.
|
|
@@ -91,12 +91,12 @@ module Hecks
|
|
|
91
91
|
# consistency about nothing. `till.bluebook` has always had the right shape
|
|
92
92
|
# — `default: { cents: 0 }`.
|
|
93
93
|
#
|
|
94
|
-
# A
|
|
94
|
+
# A primitive takes a scalar and a value object takes its fields, so the
|
|
95
95
|
# test is simply which one the type names. Nothing here guesses at the
|
|
96
96
|
# keys: a default that is a Hash is left to `Value.for_attribute`, which
|
|
97
|
-
# is where a wrong
|
|
97
|
+
# is where a wrong field belongs.
|
|
98
98
|
def seal_defaults
|
|
99
|
-
# `closed_sets`
|
|
99
|
+
# `closed_sets` too, not only `@value_objects` — the exact gap
|
|
100
100
|
# this method's own comment names: an inline `one_of(...)`
|
|
101
101
|
# synthesises its value object through `closed_sets`
|
|
102
102
|
# (AttributeCollector#synthesise_closed_set), never installed
|
|
@@ -120,7 +120,7 @@ module Hecks
|
|
|
120
120
|
end
|
|
121
121
|
|
|
122
122
|
# A command's `from:` guard needs a lifecycle field to check
|
|
123
|
-
# against — declared at
|
|
123
|
+
# against — declared at build time (S10, ADR 0025), the same
|
|
124
124
|
# point every other "does this actually resolve" check in this
|
|
125
125
|
# file runs, rather than left to crash `enforce_lifecycle_
|
|
126
126
|
# guard` the first time such a command is ever dispatched.
|
|
@@ -137,12 +137,12 @@ module Hecks
|
|
|
137
137
|
end
|
|
138
138
|
end
|
|
139
139
|
|
|
140
|
-
# `projects`'s
|
|
141
|
-
# ADR 0025) — the
|
|
140
|
+
# `projects`'s own half of "does this actually resolve" (S12,
|
|
141
|
+
# ADR 0025) — the local half only: `reference` must name a real
|
|
142
142
|
# reference-typed attribute this aggregate declares, and
|
|
143
143
|
# `name` must not collide with an attribute already declared
|
|
144
144
|
# (a projected field is its own kind of field, never a second
|
|
145
|
-
# spelling of one that already exists). The
|
|
145
|
+
# spelling of one that already exists). The target aggregate's
|
|
146
146
|
# own field is checked separately, once every aggregate in the
|
|
147
147
|
# chapter is real — see BluebookBuilder#validate_projected_
|
|
148
148
|
# fields!'s own comment for why that half cannot happen here.
|
|
@@ -173,23 +173,23 @@ module Hecks
|
|
|
173
173
|
@commands.each do |command|
|
|
174
174
|
command.mutations.each do |mutation|
|
|
175
175
|
# `:delegate` — CommandBuilder#delegates_to's own comment —
|
|
176
|
-
# targets no field of
|
|
176
|
+
# targets no field of this aggregate at all; its `target`
|
|
177
177
|
# names an "Entity.Command" pair instead, checked when the
|
|
178
178
|
# command builds (`delegates_to`'s own `rpartition` guard)
|
|
179
179
|
# and again at dispatch time (`CommandInterpreter
|
|
180
180
|
# #step_delegate_to_entity`, which refuses a real one that
|
|
181
|
-
# names no such entity or command). Sealing
|
|
181
|
+
# names no such entity or command). Sealing this check
|
|
182
182
|
# against it would refuse every delegating command outright.
|
|
183
183
|
# `:corrects` — CommandBuilder#corrects_impl's own comment —
|
|
184
|
-
# targets an
|
|
184
|
+
# targets an event name, not a field either; checked instead
|
|
185
185
|
# by `seal_correction_targets`, below.
|
|
186
186
|
next if [:delegate, :corrects].include?(mutation.op)
|
|
187
187
|
|
|
188
188
|
# C5.3 (docs/semantics/bluebook-semantics.md) — the
|
|
189
|
-
# lifecycle field moves
|
|
189
|
+
# lifecycle field moves only by transition; a `sets` on it
|
|
190
190
|
# would be overwritten by any transition and bypass the
|
|
191
191
|
# state machine otherwise. Refused at build — except for
|
|
192
|
-
#
|
|
192
|
+
# frozen era text (`MetaValidator.shadow_parsing?`), which
|
|
193
193
|
# is history and must keep parsing as the language tightens.
|
|
194
194
|
if @lifecycle && mutation.target.to_sym == @lifecycle.field.to_sym && !MetaValidator.shadow_parsing?
|
|
195
195
|
raise Malformed,
|
|
@@ -212,26 +212,26 @@ module Hecks
|
|
|
212
212
|
# itself — a command cannot see its own siblings' `emits` while
|
|
213
213
|
# it is still being built). Two things are checked:
|
|
214
214
|
#
|
|
215
|
-
# 1. The named event must be something a
|
|
215
|
+
# 1. The named event must be something a sibling command here
|
|
216
216
|
# actually `emits` — naming an event nothing in this aggregate
|
|
217
217
|
# ever announces is a build-time authoring error. (Whether
|
|
218
|
-
#
|
|
218
|
+
# this record has actually emitted it yet is the dispatch-time
|
|
219
219
|
# half — CommandRules::Admissibility#enforce_correction_target.)
|
|
220
220
|
#
|
|
221
221
|
# 2. `reverses: true` derives the corrective `sets` from the
|
|
222
|
-
#
|
|
222
|
+
# original command's own mutations, rather than the author
|
|
223
223
|
# writing them — but only when every one of those mutations is
|
|
224
|
-
#
|
|
224
|
+
# structurally invertible with no runtime data: increment/
|
|
225
225
|
# decrement, same argument, opposite verb (`sign_for`'s own
|
|
226
226
|
# +1/-1 pair — CommandRules::Arithmetic applies `current +
|
|
227
|
-
# sign * amount`, so the
|
|
227
|
+
# sign * amount`, so the same source with the opposite sign
|
|
228
228
|
# undoes it exactly). Nothing else qualifies today: `set` has
|
|
229
|
-
# no such rule at all — inverting it needs the
|
|
229
|
+
# no such rule at all — inverting it needs the specific prior
|
|
230
230
|
# value at the moment the original fired, which is per-
|
|
231
231
|
# instance runtime data no build-time derivation can have;
|
|
232
232
|
# `multiply`/`clamp` are lossy by design (a clamped value's
|
|
233
233
|
# own pre-clamp magnitude is not recoverable from the mutation
|
|
234
|
-
# at all); `append`/`remove`
|
|
234
|
+
# at all); `append`/`remove` look symmetric but are not
|
|
235
235
|
# reliably so — `append`'s source is a per-field binding hash
|
|
236
236
|
# (`append: { name: :name, amount: :amount }`), `remove`'s is
|
|
237
237
|
# a single resolved value to match by equality
|
|
@@ -243,9 +243,9 @@ module Hecks
|
|
|
243
243
|
# — see docs/decisions/ for the ADR that draws this exact
|
|
244
244
|
# line.
|
|
245
245
|
# One closed cluster of `corrects`/`reverses: true` rules, run
|
|
246
|
-
# in sequence against
|
|
246
|
+
# in sequence against one command at a time (emission exists,
|
|
247
247
|
# reverses/own-sets conflict, invertibility, then the actual
|
|
248
|
-
# derivation) — each `raise` gates the next check for
|
|
248
|
+
# derivation) — each `raise` gates the next check for that
|
|
249
249
|
# command, and `inverse_op`/`emitted_by` are shared read-only
|
|
250
250
|
# lookups built once up front. Splitting the per-command body
|
|
251
251
|
# out would still need all of `command`/`event`/`sources`/
|
|
@@ -299,11 +299,11 @@ module Hecks
|
|
|
299
299
|
end
|
|
300
300
|
end
|
|
301
301
|
|
|
302
|
-
# A query must ask about a field the aggregate actually
|
|
302
|
+
# A query must ask about a field the aggregate actually has — the same
|
|
303
303
|
# seal `then_set` gets, closing the same silence: a where over a field
|
|
304
304
|
# nothing declares matches nothing and refuses nothing, forever, on
|
|
305
305
|
# every adapter. Three more silences close with it. A dotted path may
|
|
306
|
-
# reach through the value-object graph but must
|
|
306
|
+
# reach through the value-object graph but must land on a scalar
|
|
307
307
|
# member (QuerySpecification::FieldPath is the one walk every engine
|
|
308
308
|
# now shares) — landing on a value object hands SQL a JSON object
|
|
309
309
|
# where the reference interpreter unwraps a hash. An ordered
|
|
@@ -339,13 +339,13 @@ module Hecks
|
|
|
339
339
|
@entities.map { |entity| ["#{@name}::#{entity.hecks_name}", entity.attributes, entity.lifecycle, entity.queries] }
|
|
340
340
|
end
|
|
341
341
|
|
|
342
|
-
# `/`
|
|
343
|
-
#
|
|
342
|
+
# `/` crosses into another record, `.` walks fields inside this
|
|
343
|
+
# one (ADR 0025, "References") — the operator answers which
|
|
344
344
|
# kind of path this is now, not a name collision to arbitrate,
|
|
345
345
|
# so a hop is routed to its own method before any `.`-splitting
|
|
346
346
|
# runs at all; `seal_query_hop` below never sees a field this
|
|
347
347
|
# one would also have tried to resolve as a local dotted walk.
|
|
348
|
-
# A closed decision tree over where
|
|
348
|
+
# A closed decision tree over where one field can resolve —
|
|
349
349
|
# hop, local scalar, lifecycle field, value object (refused),
|
|
350
350
|
# or nothing (refused) — see the doc comment above (and the
|
|
351
351
|
# method-level comments on the hop/ordering split) for why each
|
|
@@ -378,18 +378,18 @@ module Hecks
|
|
|
378
378
|
"matches nothing and refuses nothing"
|
|
379
379
|
end
|
|
380
380
|
|
|
381
|
-
# ORDER BY refuses a hop
|
|
381
|
+
# ORDER BY refuses a hop outright, right here — unlike a WHERE
|
|
382
382
|
# hop (deferred below), this doesn't need the target's shape to
|
|
383
383
|
# answer: an ask is ordered by what its own answering rows
|
|
384
384
|
# hold, and a hop answers with a candidate set, not a sort key
|
|
385
385
|
# (see Runtime::ReferenceHop).
|
|
386
386
|
#
|
|
387
|
-
# A
|
|
387
|
+
# A where hop is only recognised here, and checked later. The
|
|
388
388
|
# head names one of this aggregate's own references, which is
|
|
389
389
|
# answerable now — a Reference knows its own target_name at
|
|
390
|
-
# declaration. What it points
|
|
390
|
+
# declaration. What it points at is not: stamp_references has
|
|
391
391
|
# already run by this point, but the chapter (Bluebook, and the
|
|
392
|
-
# owning aggregate's
|
|
392
|
+
# owning aggregate's own place in it) does not exist yet, so
|
|
393
393
|
# Reference#resolve would answer nil for every target in the
|
|
394
394
|
# file, including ones declared above this one. The tail, and
|
|
395
395
|
# whether the target even exists, are BluebookBuilder's
|
|
@@ -416,14 +416,14 @@ module Hecks
|
|
|
416
416
|
def seal_ordered_comparator(owner, query, fields, clause)
|
|
417
417
|
return unless ORDERED_COMPARATORS.include?(clause.op.to_s.to_sym)
|
|
418
418
|
|
|
419
|
-
# A
|
|
419
|
+
# A where clause hopping through a reference with an ordered
|
|
420
420
|
# comparator is legitimate ("client whose balance > 500") —
|
|
421
421
|
# unlike ORDER BY (refused outright in seal_query_field, see
|
|
422
422
|
# its own comment), a where-clause hop answers a real
|
|
423
423
|
# candidate set either way, ordered or not. Deferred for the
|
|
424
424
|
# same reason any other hop is: whether the tail is even
|
|
425
425
|
# numeric is BluebookBuilder#validate_query_hops!'s question
|
|
426
|
-
# to ask of the
|
|
426
|
+
# to ask of the target's shape, not this aggregate's own.
|
|
427
427
|
return if clause.field.to_s.include?("/") && QuerySpecification::HopPath.hop_head?(clause.field, fields)
|
|
428
428
|
|
|
429
429
|
name, *nested = clause.field.to_s.split(".")
|
|
@@ -473,17 +473,17 @@ module Hecks
|
|
|
473
473
|
query.attributes << leaf if leaf
|
|
474
474
|
end
|
|
475
475
|
|
|
476
|
-
# A
|
|
477
|
-
#
|
|
476
|
+
# A bare field naming a value object has to say which member it
|
|
477
|
+
# means, when more than one could answer. The dotted case above
|
|
478
478
|
# already refuses a path that lands on a value object rather than
|
|
479
479
|
# a scalar; a bare name was returning unconditionally, so
|
|
480
480
|
# `where(frequency: ...)` against a StatementFrequency
|
|
481
481
|
# (cadence, retention_months, paper_fee_cents) compiled — and the
|
|
482
482
|
# engines then disagreed about which member it meant, one taking
|
|
483
|
-
# the
|
|
483
|
+
# the first numeric and another declining to unwrap at all.
|
|
484
484
|
#
|
|
485
485
|
# Unambiguous is: exactly one member, whatever its type, or
|
|
486
|
-
# exactly one
|
|
486
|
+
# exactly one numeric member among several (Money's `cents`
|
|
487
487
|
# beside its `currency` — the reading every engine already
|
|
488
488
|
# shared, and what the corpus relies on). Anything else names
|
|
489
489
|
# its member with a dotted path, which already works.
|