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
|
@@ -14,20 +14,20 @@ require_relative "../ports/persistence/binding_policy"
|
|
|
14
14
|
# runner.rb the same way the file-count/sweep concern is split from a
|
|
15
15
|
# single test's own execution.
|
|
16
16
|
#
|
|
17
|
-
#
|
|
17
|
+
# Two deliberate deviations from a prior port of this same idea (read
|
|
18
18
|
# before writing this, not reinvented):
|
|
19
19
|
#
|
|
20
20
|
# 1. A `setup` refusal and a refusal from the command under test are
|
|
21
|
-
# caught in
|
|
21
|
+
# caught in separate rescue scopes. The prior port wrapped the whole
|
|
22
22
|
# test body — setups included — in one `rescue *REFUSAL_CLASSES`, so a
|
|
23
23
|
# broken setup could spuriously satisfy `expect refused: "..."` if its
|
|
24
24
|
# own refusal happened to match the expected substring. Here, any
|
|
25
|
-
# domain refusal during `setup` is unconditionally an
|
|
25
|
+
# domain refusal during `setup` is unconditionally an error — the
|
|
26
26
|
# example never got to the situation it claims to test.
|
|
27
27
|
#
|
|
28
|
-
# 2. `emits:` is read off a `registry.event_log`
|
|
28
|
+
# 2. `emits:` is read off a `registry.event_log` diff around the tested
|
|
29
29
|
# dispatch, not off `Result#events` alone. `Result#events` only holds
|
|
30
|
-
# the events the
|
|
30
|
+
# the events the outermost dispatch announced; a policy's own cascade
|
|
31
31
|
# reenters through the same `Dispatcher#dispatch` (`PolicyInterpreter
|
|
32
32
|
# #deliver` → `door.reenter` → `dispatch`), and every dispatch's
|
|
33
33
|
# events — outer and reentrant alike — land in the one shared
|
|
@@ -66,18 +66,18 @@ module Hecks
|
|
|
66
66
|
error_result(test, "#{e.class}: #{e.message}")
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
-
#
|
|
69
|
+
# One boot per suite, not per test. The isolation a test needs is a
|
|
70
70
|
# runtime with nothing in it — and a boot of the same files gives
|
|
71
71
|
# exactly that back for the price of `Registry#reset_runtime_state!`
|
|
72
72
|
# instead of ~2s of loading, verifying and era-checking the same
|
|
73
73
|
# bluebook again (chess: 76 behaviours, 155s of which was booting
|
|
74
|
-
# `chess.bluebook` 76 times). Keyed by the suite's own `loads`
|
|
74
|
+
# `chess.bluebook` 76 times). Keyed by the suite's own `loads` and
|
|
75
75
|
# their mtimes, so an edited bluebook boots fresh on the next test
|
|
76
76
|
# rather than running against a stale one — the property a watch
|
|
77
77
|
# loop or a long rspec session actually relies on. `runtime:` lets
|
|
78
78
|
# a caller that already holds a booted runtime (a spec, a REPL)
|
|
79
79
|
# hand it in; it is reset the same way.
|
|
80
|
-
#
|
|
80
|
+
# Not frozen — a real cache, mutated below (`RUNTIMES[key] ||=
|
|
81
81
|
# boot_and_guard(files)`) and by #reset!. False positive for
|
|
82
82
|
# Style/MutableConstant.
|
|
83
83
|
# rubocop:disable-next Style/MutableConstant
|
|
@@ -94,12 +94,12 @@ module Hecks
|
|
|
94
94
|
end
|
|
95
95
|
end
|
|
96
96
|
|
|
97
|
-
# `reset_runtime_state!` only drops repository
|
|
97
|
+
# `reset_runtime_state!` only drops repository objects between
|
|
98
98
|
# tests (registry.rb) — sufficient isolation for `Memory`, whose
|
|
99
99
|
# `@records` is a plain per-instance ivar, so a fresh object really
|
|
100
100
|
# is a fresh store. Against anything else (Sqlite, Postgres) the
|
|
101
101
|
# rows themselves stay put: tests leak into each other, and a
|
|
102
|
-
# suite booted against a domain's
|
|
102
|
+
# suite booted against a domain's real hecksagon writes to a real
|
|
103
103
|
# database. Refusing that wiring here, at boot, is the same shape
|
|
104
104
|
# of guard `BindingPolicy` already applies to a missing bind — the
|
|
105
105
|
# project's identity is refusing bad wiring up front, not
|
|
@@ -159,13 +159,13 @@ module Hecks
|
|
|
159
159
|
check_ok(test) || check_fields(test, settled_state(runtime, verb, result)) || pass_result(test)
|
|
160
160
|
end
|
|
161
161
|
|
|
162
|
-
# A field expectation reads the aggregate
|
|
162
|
+
# A field expectation reads the aggregate as it stands once the
|
|
163
163
|
# dispatch and its whole cascade have run — the same "cascades are
|
|
164
164
|
# always on" reading `emits:` already commits to. `Result#state` is
|
|
165
|
-
# the wrong source for that: it snapshots the instance the
|
|
165
|
+
# the wrong source for that: it snapshots the instance the outer
|
|
166
166
|
# dispatch saved, and a policy's own reentrant dispatch (a ply
|
|
167
167
|
# advancing off a Moved event, a move count bumping) hydrates and
|
|
168
|
-
# saves a
|
|
168
|
+
# saves a fresh record afterward — so a field the cascade wrote
|
|
169
169
|
# read back stale (found live: `expect move_count: 1` got 0 while
|
|
170
170
|
# `emits:` saw MoveCountBumped in the same test). The repository
|
|
171
171
|
# holds the settled record; read it back by the id the dispatch
|
|
@@ -185,7 +185,7 @@ module Hecks
|
|
|
185
185
|
# A behaviors test writes a dispatch the way the guide's own chess
|
|
186
186
|
# examples do — receiver identity and command facts side by side
|
|
187
187
|
# (`label: "g", id: "wn", to: { file: 2, rank: 2 }`) — and since
|
|
188
|
-
# #335 the dispatcher's own `to:` keyword is the
|
|
188
|
+
# #335 the dispatcher's own `to:` keyword is the routing envelope,
|
|
189
189
|
# so forwarding those kwargs loose collides the moment a domain
|
|
190
190
|
# declares a command fact named `to` (chess does: every Move's own
|
|
191
191
|
# destination). Found live: every such test failed with "to: does
|
|
@@ -202,7 +202,7 @@ module Hecks
|
|
|
202
202
|
# expects a command's own declared attributes at the top level,
|
|
203
203
|
# not a port operation's already-wrapped `to:`/`with:` shape.
|
|
204
204
|
#
|
|
205
|
-
#
|
|
205
|
+
# This used to rely on `resolve_target` raising `UnknownVerb` for
|
|
206
206
|
# any port-operation verb — true only so long as nothing else ever
|
|
207
207
|
# asked it to resolve one. Now that a `policy` can legitimately
|
|
208
208
|
# `trigger` a port operation (`ReactionInvocation#resolve_target`'s
|
|
@@ -210,7 +210,7 @@ module Hecks
|
|
|
210
210
|
# for a port operation directly instead of leaning on a refusal
|
|
211
211
|
# that no longer happens.
|
|
212
212
|
def dispatch_command(runtime, verb, args)
|
|
213
|
-
return runtime.
|
|
213
|
+
return runtime.dispatch_flat(verb, args) if port_operation?(runtime, verb)
|
|
214
214
|
|
|
215
215
|
invocation = begin
|
|
216
216
|
Runtime::ReactionInvocation.build(registry: runtime.registry, verb: verb,
|
|
@@ -218,7 +218,7 @@ module Hecks
|
|
|
218
218
|
rescue Runtime::UnknownVerb
|
|
219
219
|
nil
|
|
220
220
|
end
|
|
221
|
-
return runtime.
|
|
221
|
+
return runtime.dispatch_flat(verb, args) unless invocation
|
|
222
222
|
|
|
223
223
|
if invocation.key?(:to)
|
|
224
224
|
runtime.dispatch(verb, to: invocation[:to], with: invocation[:with])
|
|
@@ -227,8 +227,8 @@ module Hecks
|
|
|
227
227
|
end
|
|
228
228
|
end
|
|
229
229
|
|
|
230
|
-
#
|
|
231
|
-
# `ReactionInvocation#resolve_target`
|
|
230
|
+
# The same "Head.Rest" shape `Dispatcher#dispatch` and
|
|
231
|
+
# `ReactionInvocation#resolve_target` both already check — a bare
|
|
232
232
|
# domain/aggregate lookup plus a port-name lookup, no command
|
|
233
233
|
# resolution needed since all this asks is whether one exists.
|
|
234
234
|
def port_operation?(runtime, verb)
|
|
@@ -331,7 +331,7 @@ module Hecks
|
|
|
331
331
|
# `expect` values both ways — bare (`expect kind: "bishop"`) and
|
|
332
332
|
# wrapped (`expect kind: { value: "bishop" }`). A live record's
|
|
333
333
|
# field always comes back as a `Hecks::Runtime::Value`;
|
|
334
|
-
# normalizing
|
|
334
|
+
# normalizing both sides to the same bare-scalar-or-plain-hash
|
|
335
335
|
# shape is the one comparison that accepts either spelling.
|
|
336
336
|
def normalize(value)
|
|
337
337
|
return Hecks::Runtime::Value.materialize_unwrapped(value) if value.is_a?(Hecks::Runtime::Value)
|
|
@@ -344,7 +344,7 @@ module Hecks
|
|
|
344
344
|
# can name more than one bluebook, so resolution searches every
|
|
345
345
|
# aggregate across every bluebook the suite booted for the one that
|
|
346
346
|
# actually declares the command. `on:` (when given, only ever on
|
|
347
|
-
# the
|
|
347
|
+
# the tested command — `setup` never receives it, see the DSL
|
|
348
348
|
# contract) narrows the search to one aggregate by name instead of
|
|
349
349
|
# searching all of them.
|
|
350
350
|
def qualify(command, on_aggregate, bluebooks, kind:)
|
|
@@ -354,7 +354,7 @@ module Hecks
|
|
|
354
354
|
disambiguate_qualified_name(candidates, command, kind, bluebooks)
|
|
355
355
|
end
|
|
356
356
|
|
|
357
|
-
#
|
|
357
|
+
# **The search** — every (bluebook, aggregate) pair that declares a
|
|
358
358
|
# command/query named `command`, narrowed to `on_aggregate` by name
|
|
359
359
|
# when given.
|
|
360
360
|
def qualify_candidates(command, on_aggregate, bluebooks, kind)
|
|
@@ -368,7 +368,7 @@ module Hecks
|
|
|
368
368
|
pairs.select { |_, agg| agg.public_send(members).any? { |m| m.hecks_name == command.to_s } }
|
|
369
369
|
end
|
|
370
370
|
|
|
371
|
-
#
|
|
371
|
+
# **The report** — zero candidates and more-than-one candidates both
|
|
372
372
|
# refuse (with a different message); exactly one resolves to its
|
|
373
373
|
# dotted FQN.
|
|
374
374
|
def disambiguate_qualified_name(candidates, command, kind, bluebooks)
|
data/lib/hecks/behaviors/ir.rb
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
#
|
|
3
3
|
# The `.behaviors` authoring surface's own IR — plain Structs, holding
|
|
4
4
|
# exactly what `Hecks.behaviors "Name" do ... end` collected. Deliberately
|
|
5
|
-
#
|
|
5
|
+
# not part of `Hecks::IR`/`emits_ir` — a behaviors suite is never
|
|
6
6
|
# collected into a domain Registry (see `Hecks.behaviors`), never
|
|
7
7
|
# dispatched through MetaValidator, and carries none of the self-hosted
|
|
8
8
|
# round-trip machinery a real bluebook construct does. It is a test
|
|
@@ -4,7 +4,7 @@ require_relative "../behaviors"
|
|
|
4
4
|
# `bundle exec rspec` uses to run `.behaviors` files as ordinary examples,
|
|
5
5
|
# one `it` per test, named by the test's own description string. Same
|
|
6
6
|
# shape `spec/guides_spec.rb` uses for doctested guides: the file is
|
|
7
|
-
#
|
|
7
|
+
# parsed at collection time (cheap — `Behaviors.parse`, no test actually
|
|
8
8
|
# run yet, just enough to know the `it` names), and each test's own
|
|
9
9
|
# `Expectations.run_one` runs lazily inside its own `it`, exactly when
|
|
10
10
|
# rspec actually executes it.
|
|
@@ -35,12 +35,12 @@ module Hecks
|
|
|
35
35
|
attr_accessor :last_suite
|
|
36
36
|
|
|
37
37
|
# `Kernel.load`s one `.behaviors` file and returns its suite, with
|
|
38
|
-
#
|
|
38
|
+
# no test actually executed yet — the cheap half, split out so a
|
|
39
39
|
# caller that only needs to know what tests exist (the rspec shim,
|
|
40
40
|
# naming its `it`s at collection time) doesn't pay for running them
|
|
41
41
|
# until it actually wants to. `Behaviors.loading_path` is bound only
|
|
42
42
|
# for the duration of the load, and `last_suite` is reset to nil
|
|
43
|
-
#
|
|
43
|
+
# before it — a file that loads without ever calling
|
|
44
44
|
# `Hecks.behaviors` is unambiguously a parse error, never a stale
|
|
45
45
|
# suite from whatever loaded before it in a sweep (a real bug in a
|
|
46
46
|
# prior port of this idea: compared only against nil, so after the
|
data/lib/hecks/behaviors.rb
CHANGED
|
@@ -8,7 +8,7 @@ require_relative "behaviors/runner"
|
|
|
8
8
|
# lib/hecks/fuzzing.rb, the shape this file mirrors) is opt-in too.
|
|
9
9
|
module Hecks
|
|
10
10
|
class << self
|
|
11
|
-
# `Hecks.behaviors "Name" do ... end` — deliberately
|
|
11
|
+
# `Hecks.behaviors "Name" do ... end` — deliberately not routed
|
|
12
12
|
# through `collect` the way `bluebook`/`hecksagon`/`world` are: a
|
|
13
13
|
# behaviors suite is a test artifact a runner reads on demand, never
|
|
14
14
|
# a thing a live domain boot needs, so it has no business landing in
|
|
@@ -3,15 +3,15 @@ require_relative "expression/ast_json"
|
|
|
3
3
|
|
|
4
4
|
module Hecks
|
|
5
5
|
module Bluebook
|
|
6
|
-
# A
|
|
6
|
+
# A field read through a `reference_to`, held locally (S12, ADR 0025
|
|
7
7
|
# — "Consistency across aggregate boundaries") — `projects
|
|
8
8
|
# :customer_status, from: :"customer.status"` declares that this
|
|
9
|
-
# aggregate's own `:customer_status` is a
|
|
9
|
+
# aggregate's own `:customer_status` is a copy of the target's own
|
|
10
10
|
# `:status`, kept fresh by a rebuild sweep rather than read live at
|
|
11
|
-
# rule-evaluation time. `reference` names the
|
|
11
|
+
# rule-evaluation time. `reference` names the local reference
|
|
12
12
|
# attribute to walk through (`:customer`, minted by this
|
|
13
13
|
# aggregate's own `reference_to Customer`); `remote_field` names
|
|
14
|
-
# the
|
|
14
|
+
# the scalar on the target aggregate to copy.
|
|
15
15
|
ProjectedField = Struct.new(:name, :reference, :remote_field, keyword_init: true)
|
|
16
16
|
|
|
17
17
|
# A construct like every other: the aggregate carries its own identity
|
|
@@ -19,14 +19,14 @@ module Hecks
|
|
|
19
19
|
# everything declared on it. The chain is model objects end to end —
|
|
20
20
|
# reference resolution and hecks_fqn both walk it.
|
|
21
21
|
#
|
|
22
|
-
#
|
|
22
|
+
# The holding half, and nothing else. Every line below restates what
|
|
23
23
|
# `language/bluebook/aggregate.bluebook` already declares — the field
|
|
24
24
|
# list, said again as readers, again as constructor keywords, and
|
|
25
25
|
# again as an emission. That triplication is what a generator removes;
|
|
26
|
-
# `Behaviour::Aggregate` carries everything that is
|
|
26
|
+
# `Behaviour::Aggregate` carries everything that is not derivable from
|
|
27
27
|
# the declaration, so regenerating this file can never be lossy.
|
|
28
28
|
#
|
|
29
|
-
#
|
|
29
|
+
# Prototype: hand-written in the shape a generator would emit, to
|
|
30
30
|
# prove the seam before the generator exists. `bin/project_model`
|
|
31
31
|
# would own this file; behaviour/aggregate.rb stays hand-written.
|
|
32
32
|
class Aggregate
|
|
@@ -42,16 +42,16 @@ module Hecks
|
|
|
42
42
|
value_objects: many(:value_objects),
|
|
43
43
|
commands: many(:commands),
|
|
44
44
|
invariants: -> { invariants.map { |rule| Expression::AstJson.rule_row(rule) } },
|
|
45
|
-
# A
|
|
46
|
-
# 0025) — the aggregate's
|
|
45
|
+
# A precondition shared across commands, declared once (S10, ADR
|
|
46
|
+
# 0025) — the aggregate's own named `given`s, the declaration a
|
|
47
47
|
# referencing command's own (already-resolved) `givens` entry
|
|
48
48
|
# came from. Both sides of "declared once, referenced many"
|
|
49
|
-
# are real IR, the same shape a value object's
|
|
49
|
+
# are real IR, the same shape a value object's type and an
|
|
50
50
|
# attribute's own reference to it both are.
|
|
51
51
|
preconditions: -> { preconditions.map { |rule| Expression::AstJson.rule_row(rule) } },
|
|
52
|
-
# S12, ADR 0025 — deliberately
|
|
52
|
+
# S12, ADR 0025 — deliberately not folded into `attributes`:
|
|
53
53
|
# `EraGuard::ShapeDiff` only ever walks `attributes` to decide
|
|
54
|
-
# whether a
|
|
54
|
+
# whether a new field leaves an existing record with something
|
|
55
55
|
# genuinely absent, and a projected field's own absence story
|
|
56
56
|
# is different — a record predating the `projects` declaration
|
|
57
57
|
# is expected to be missing it until the rebuild sweep runs,
|
|
@@ -64,7 +64,7 @@ module Hecks
|
|
|
64
64
|
lifecycle: one(:lifecycle),
|
|
65
65
|
entities: many(:entities),
|
|
66
66
|
queries: many(:queries),
|
|
67
|
-
#
|
|
67
|
+
# Additive — every domain that declares no port emits `ports: []`,
|
|
68
68
|
# the same "regenerate deliberately" wire-format change
|
|
69
69
|
# ir_golden_spec.rb's own header describes; no existing key
|
|
70
70
|
# moves. See docs/decisions (rust/project/ports.rb) for the
|
|
@@ -7,7 +7,7 @@ module Hecks
|
|
|
7
7
|
# is the part no field table can say: which construct holds which, and how a
|
|
8
8
|
# head's references become able to resolve.
|
|
9
9
|
#
|
|
10
|
-
# It
|
|
10
|
+
# It decides nothing. Whether a declaration is admissible was settled on the
|
|
11
11
|
# way in, by the language.
|
|
12
12
|
class AggregateAssembly
|
|
13
13
|
def initialize(row)
|
|
@@ -29,7 +29,7 @@ module Hecks
|
|
|
29
29
|
entities: entities,
|
|
30
30
|
queries: asks,
|
|
31
31
|
lifecycle: lifecycle_of(@row),
|
|
32
|
-
# A policy declared inside a head is
|
|
32
|
+
# A policy declared inside a head is hoisted onto the chapter by the
|
|
33
33
|
# builder, and `Aggregate#to_h` never carried it — so the language does
|
|
34
34
|
# not record which head a chapter-level policy was written in. The
|
|
35
35
|
# chapter holds them all, which is what `PolicyInterpreter` reads.
|
|
@@ -43,11 +43,11 @@ module Hecks
|
|
|
43
43
|
|
|
44
44
|
private
|
|
45
45
|
|
|
46
|
-
# What this head points at with an aggregate-level `reference_to`.
|
|
46
|
+
# What this head points at with an aggregate-level `reference_to`. Not the
|
|
47
47
|
# self-references its verbs carry — the first version read
|
|
48
48
|
# `commands.filter_map(&:references)` and gave Pizza two targets of "Pizza",
|
|
49
49
|
# its own verbs pointing at itself, where the builder recorded none. An
|
|
50
|
-
# aggregate-level `reference_to X` leaves a reference
|
|
50
|
+
# aggregate-level `reference_to X` leaves a reference attribute behind, so
|
|
51
51
|
# the attributes are where the answer is.
|
|
52
52
|
def reference_targets(fields)
|
|
53
53
|
fields.select(&:reference?).map { |field| field.type.target_name }
|
|
@@ -57,7 +57,7 @@ module Hecks
|
|
|
57
57
|
Build.call("ValueObject", row)
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
-
# S17, ADR 0026 —
|
|
60
|
+
# S17, ADR 0026 — recurses. "That is what `entity` is for, and
|
|
61
61
|
# `entity` is declared by the language and used zero times in it"
|
|
62
62
|
# — Dispatch, inside Handler, is the first use, so `row[:entities]`
|
|
63
63
|
# is built the same way `row` itself was reached: through this
|
|
@@ -75,7 +75,7 @@ module Hecks
|
|
|
75
75
|
|
|
76
76
|
# Every reference learns which head declares it, so it can reach the chapter
|
|
77
77
|
# and resolve. Deliberately across every list that can carry one — a
|
|
78
|
-
# reference the walk misses resolves to nil, and a nil target is
|
|
78
|
+
# reference the walk misses resolves to nil, and a nil target is skipped
|
|
79
79
|
# rather than refused, so the guarantee would go quiet instead of red.
|
|
80
80
|
def stamp_references(aggregate)
|
|
81
81
|
lists = [aggregate.attributes, *aggregate.commands.map(&:attributes), *aggregate.queries.map(&:attributes)]
|
|
@@ -84,7 +84,7 @@ module Hecks
|
|
|
84
84
|
lists.flatten.select(&:reference?).each { |field| field.type.declared_in = aggregate }
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
-
# S17, ADR 0026 — walks
|
|
87
|
+
# S17, ADR 0026 — walks nested entities too (Dispatch, inside
|
|
88
88
|
# Handler), not only an aggregate's own direct ones.
|
|
89
89
|
def entity_reference_lists(entities, lists)
|
|
90
90
|
entities.each do |piece|
|
|
@@ -95,7 +95,7 @@ module Hecks
|
|
|
95
95
|
end
|
|
96
96
|
end
|
|
97
97
|
|
|
98
|
-
#
|
|
98
|
+
# Three language fields, one IR object. `state_field`, `state_start` and
|
|
99
99
|
# `transitions` are separate declarations; the IR keeps one Lifecycle. The
|
|
100
100
|
# contract names them derived, and this is what derives them.
|
|
101
101
|
def lifecycle_of(row)
|
|
@@ -110,8 +110,8 @@ module Hecks
|
|
|
110
110
|
end
|
|
111
111
|
|
|
112
112
|
# A lifecycle holds [command, StateTransition] pairs and `to_h` expands one
|
|
113
|
-
# pair whose `from` is a list into several rows. Grouped back by command
|
|
114
|
-
#
|
|
113
|
+
# pair whose `from` is a list into several rows. Grouped back by command and
|
|
114
|
+
# target, because one declared pair has exactly one target and may have many
|
|
115
115
|
# froms — grouping by command alone would fuse two declarations that move the
|
|
116
116
|
# same verb to different states.
|
|
117
117
|
def transitions(rows)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
3
|
class Assembly
|
|
4
|
-
#
|
|
4
|
+
# One way to build a construct, for every construct.
|
|
5
5
|
#
|
|
6
6
|
# There used to be a method per category here — `value_object(row)`,
|
|
7
7
|
# `command(row)`, `policy(row)` — each one gathering the same keywords the
|
|
@@ -5,14 +5,14 @@ module Hecks
|
|
|
5
5
|
# struct format itself (this file) plus, reopened in contracts.rb, the
|
|
6
6
|
# `CONTRACTS` table of one `Contract` per construct category.
|
|
7
7
|
class Assembly
|
|
8
|
-
#
|
|
9
|
-
# field needs no assembling is
|
|
8
|
+
# What a construct needs that the language cannot say, and how a claim that a
|
|
9
|
+
# field needs no assembling is checked.
|
|
10
10
|
#
|
|
11
11
|
# `derived:` used to be a list of names, and a list of names is a promise with
|
|
12
12
|
# nobody holding it. The coverage gate only asked whether a field was accounted
|
|
13
13
|
# for — so writing `derived: %i[version]` would have satisfied it while dropping
|
|
14
14
|
# a chapter's version in silence, which is the exact shape of every defect this
|
|
15
|
-
# arc has found. Naming a field derived is a
|
|
15
|
+
# arc has found. Naming a field derived is a claim, and a claim needs a kind:
|
|
16
16
|
#
|
|
17
17
|
# :parent the containment tree supplies it — a `*_id`, or one of
|
|
18
18
|
# the named pointers below. Checked against the name.
|
|
@@ -25,16 +25,16 @@ module Hecks
|
|
|
25
25
|
# :elsewhere not a fact about this construct at all. Allow-listed one
|
|
26
26
|
# by one, because it is the kind with no other check.
|
|
27
27
|
#
|
|
28
|
-
# Every one of those can
|
|
28
|
+
# Every one of those can fail. That is the whole difference.
|
|
29
29
|
Contract = Struct.new(:holder, :make, :fields, :derived, :rows, :reads, keyword_init: true) do
|
|
30
|
-
# How a declaration key is read back off a
|
|
30
|
+
# How a declaration key is read back off a row. Absent means the default —
|
|
31
31
|
# `text(row[key])`, a single cell — which is most of them ; present names the
|
|
32
32
|
# shape, because a list needs a reader per element and a folded field is
|
|
33
33
|
# gathered rather than fetched. Same pattern as `rows`, in the other
|
|
34
34
|
# direction: declare the exceptions, default the rest.
|
|
35
35
|
def reader(key) = Hash(reads)[key.to_sym]
|
|
36
36
|
|
|
37
|
-
# How an appendable
|
|
37
|
+
# How an appendable list becomes rows the walk can offer. A list absent from
|
|
38
38
|
# here reads straight off the node ; one that is present names the shaper,
|
|
39
39
|
# because the IR keeps a shape the language does not — a transition whose
|
|
40
40
|
# `from` is a list is several rows, an append binds several fields at once,
|
|
@@ -45,12 +45,19 @@ module Hecks
|
|
|
45
45
|
|
|
46
46
|
def kind_of(field) = derived[field]
|
|
47
47
|
|
|
48
|
-
#
|
|
48
|
+
# The fields the walk supplies — every `derived: { field => :walk }` claim.
|
|
49
|
+
# The language declares them (`attribute :position, Position`) so the
|
|
50
|
+
# judge can order siblings, but no constructor takes one. This is the one
|
|
51
|
+
# place that fact is stated ; `Specializer` and `Model::Deviations` read it
|
|
52
|
+
# here rather than each keeping their own `%i[position]`.
|
|
53
|
+
def walked = derived.select { |_field, kind| kind == :walk }.keys
|
|
54
|
+
|
|
55
|
+
# Where a folded field actually lives, as [object, member].
|
|
49
56
|
#
|
|
50
57
|
# `[:folded, :lifecycle, :field]` says the language's `state_field` is the
|
|
51
58
|
# `field` of the IR's one Lifecycle. That is the same fact `Readings` used to
|
|
52
59
|
# state a second time as `node.lifecycle&.field` — so saying it once here
|
|
53
|
-
# drives
|
|
60
|
+
# drives both directions: the walk reads the member on the way in, and the
|
|
54
61
|
# reconstruction gathers the members back into the object on the way out.
|
|
55
62
|
#
|
|
56
63
|
# A nil member means the fold has no single member to name — `rows` is a
|
|
@@ -64,15 +71,15 @@ module Hecks
|
|
|
64
71
|
[kind[1], kind[2]]
|
|
65
72
|
end
|
|
66
73
|
|
|
67
|
-
#
|
|
74
|
+
# Computed means worked out, not merely answerable.
|
|
68
75
|
#
|
|
69
76
|
# Asking only whether the holder responds was too weak, and measurably so:
|
|
70
77
|
# `[:computed, :version]` passed, because `Bluebook` does answer to
|
|
71
78
|
# `version` — it just answers with what the constructor was handed. A field
|
|
72
|
-
# the constructor
|
|
79
|
+
# the constructor takes is stored, and calling it computed is how a chapter's
|
|
73
80
|
# version would have gone missing while the gate said yes.
|
|
74
81
|
#
|
|
75
|
-
# So a computed field is one the holder answers
|
|
82
|
+
# So a computed field is one the holder answers and the constructor does not
|
|
76
83
|
# accept. `query_name` qualifies (`Naming.snake(name)`) ; `version` cannot.
|
|
77
84
|
def computes?(method)
|
|
78
85
|
return false unless holder
|
|
@@ -92,17 +99,33 @@ module Hecks
|
|
|
92
99
|
end
|
|
93
100
|
end
|
|
94
101
|
|
|
95
|
-
# The fields that
|
|
96
|
-
#
|
|
97
|
-
#
|
|
98
|
-
|
|
102
|
+
# The fields that point at a parent without being spelled `*_id` — the one list,
|
|
103
|
+
# read by the assembly gate (a `:parent` claim), the model generator and
|
|
104
|
+
# QueryIR (a declared field the model composes instead of storing).
|
|
105
|
+
#
|
|
106
|
+
# aggregate, bluebook the bare parent link a creating command mints
|
|
107
|
+
# (ADR 0025) — exactly the `parent_key`s `Plan` reads
|
|
108
|
+
# off the language
|
|
109
|
+
# owner Entity's own text twin of that link, which is why
|
|
110
|
+
# Entity's contract claims `owner: :parent`
|
|
111
|
+
#
|
|
112
|
+
# It used to read `%i[owner shape handler]` here while the model's copy read
|
|
113
|
+
# `owner aggregate bluebook`: `shape` and `handler` were Member's and
|
|
114
|
+
# Dispatch's parent fields before S17 (ADR 0026) made both nested entities,
|
|
115
|
+
# and nothing noticed the two lists had stopped agreeing. spec/assembly_spec
|
|
116
|
+
# now derives this set from `Plan` and the contracts and fails on any drift.
|
|
117
|
+
PARENT_POINTERS = %i[aggregate bluebook owner].freeze
|
|
118
|
+
|
|
119
|
+
def self.parent_pointer?(field)
|
|
120
|
+
field.to_s.end_with?("_id") || PARENT_POINTERS.include?(field.to_sym)
|
|
121
|
+
end
|
|
99
122
|
|
|
100
123
|
# The only fields allowed to claim they describe something other than the
|
|
101
124
|
# construct they hang off, each with the reason spelled out.
|
|
102
125
|
#
|
|
103
126
|
# normalisations the canonical-form table belongs to the expression grammar.
|
|
104
127
|
# A chapter's rules are canonicalised on the way in, so the
|
|
105
|
-
# language models the table — but no chapter
|
|
128
|
+
# language models the table — but no chapter stores one, and
|
|
106
129
|
# `Bluebook#to_h` splices it in from Expression.
|
|
107
130
|
ELSEWHERE = {
|
|
108
131
|
Bluebook: %i[normalisations]
|