hecks 1.3.0 → 1.5.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 +72 -7
- data/lib/hecks/adapters/driven/d1.rb +187 -23
- data/lib/hecks/adapters/driven/folder.rb +83 -10
- data/lib/hecks/adapters/driven/google_authentication.rb +33 -12
- data/lib/hecks/adapters/driven/governance_authorization.rb +87 -18
- data/lib/hecks/adapters/driven/heki/journal.rb +13 -4
- data/lib/hecks/adapters/driven/heki/saga_store.rb +56 -10
- data/lib/hecks/adapters/driven/heki/snapshot.rb +1 -1
- data/lib/hecks/adapters/driven/heki.rb +106 -9
- data/lib/hecks/adapters/driven/identity_registry.rb +12 -2
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +25 -3
- data/lib/hecks/adapters/driven/in_process_key_vault.adapter +3 -0
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +53 -0
- data/lib/hecks/adapters/driven/lambda/client.rb +67 -14
- data/lib/hecks/adapters/driven/lambda.rb +82 -35
- data/lib/hecks/adapters/driven/local_storage.rb +83 -10
- data/lib/hecks/adapters/driven/memory.rb +205 -9
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +21 -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 +50 -11
- data/lib/hecks/adapters/driven/secure_random_identity.rb +3 -0
- 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/system_clock.rb +3 -0
- data/lib/hecks/adapters/driven/tenant_provisioner.adapter +3 -0
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +66 -0
- data/lib/hecks/adapters/driven.rb +6 -4
- data/lib/hecks/adapters/driving/github_webhook.rb +31 -18
- data/lib/hecks/behaviors/dsl.rb +60 -2
- data/lib/hecks/behaviors/expectations.rb +190 -29
- data/lib/hecks/behaviors/ir.rb +12 -1
- data/lib/hecks/behaviors/rspec.rb +9 -1
- data/lib/hecks/behaviors/runner.rb +21 -2
- data/lib/hecks/behaviors.rb +9 -1
- data/lib/hecks/bluebook/aggregate.rb +43 -13
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +17 -10
- data/lib/hecks/bluebook/assembly/build.rb +26 -5
- data/lib/hecks/bluebook/assembly/contract.rb +98 -23
- data/lib/hecks/bluebook/assembly/contracts.rb +59 -52
- data/lib/hecks/bluebook/assembly/marks.rb +159 -30
- data/lib/hecks/bluebook/assembly/specializer.rb +38 -21
- data/lib/hecks/bluebook/assembly.rb +32 -14
- data/lib/hecks/bluebook/attribute.rb +26 -12
- data/lib/hecks/bluebook/behaviour/aggregate.rb +43 -11
- data/lib/hecks/bluebook/behaviour/attribute.rb +18 -5
- data/lib/hecks/bluebook/behaviour/chapter.rb +76 -5
- data/lib/hecks/bluebook/behaviour/command.rb +55 -25
- data/lib/hecks/bluebook/behaviour/domain_port.rb +27 -7
- data/lib/hecks/bluebook/behaviour/entity.rb +20 -8
- data/lib/hecks/bluebook/behaviour/hexagon.rb +30 -4
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +27 -6
- data/lib/hecks/bluebook/behaviour/policy.rb +42 -17
- data/lib/hecks/bluebook/behaviour/process_manager.rb +39 -8
- data/lib/hecks/bluebook/behaviour/query.rb +6 -1
- data/lib/hecks/bluebook/behaviour/read_model.rb +29 -8
- data/lib/hecks/bluebook/behaviour/traits.rb +48 -12
- data/lib/hecks/bluebook/behaviour/value_object.rb +21 -9
- data/lib/hecks/bluebook/capabilities.rb +27 -0
- data/lib/hecks/bluebook/chapter.rb +51 -9
- data/lib/hecks/bluebook/command.rb +62 -17
- data/lib/hecks/bluebook/domain_port.rb +34 -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 +282 -123
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +112 -75
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +81 -2
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +486 -117
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +179 -47
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +284 -122
- 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 +191 -61
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +130 -30
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +38 -21
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
- data/lib/hecks/bluebook/dsl/policy_builder.rb +86 -36
- 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 +111 -47
- data/lib/hecks/bluebook/dsl/query_builder.rb +37 -8
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +127 -52
- data/lib/hecks/bluebook/dsl/rule_reference.rb +97 -43
- data/lib/hecks/bluebook/dsl/translation_builder.rb +150 -44
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +68 -20
- 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 +40 -11
- data/lib/hecks/bluebook/expression/ast_json.rb +128 -36
- data/lib/hecks/bluebook/expression/ast_reader.rb +32 -3
- data/lib/hecks/bluebook/expression/canonical_form.rb +55 -16
- data/lib/hecks/bluebook/expression/evaluator.rb +221 -43
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +54 -18
- data/lib/hecks/bluebook/expression/resolver.rb +369 -128
- data/lib/hecks/bluebook/hexagon.rb +35 -1
- data/lib/hecks/bluebook/lifecycle.rb +12 -1
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +2 -1
- data/lib/hecks/bluebook/meta_validator/judge.rb +126 -108
- data/lib/hecks/bluebook/meta_validator/plan.rb +81 -46
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +3 -2
- data/lib/hecks/bluebook/meta_validator/readings.rb +200 -50
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +68 -41
- data/lib/hecks/bluebook/meta_validator/shapes.rb +166 -21
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +286 -46
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +11 -10
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -5
- data/lib/hecks/bluebook/meta_validator.rb +235 -139
- data/lib/hecks/bluebook/model_check.rb +434 -104
- data/lib/hecks/bluebook/pattern_subset.rb +32 -10
- data/lib/hecks/bluebook/policy.rb +15 -13
- data/lib/hecks/bluebook/process_manager.rb +27 -14
- data/lib/hecks/bluebook/project_discovery.rb +5 -0
- data/lib/hecks/bluebook/project_loader.rb +40 -0
- data/lib/hecks/bluebook/project_register.rb +50 -6
- data/lib/hecks/bluebook/query.rb +31 -4
- data/lib/hecks/bluebook/read_model.rb +35 -15
- data/lib/hecks/bluebook/reference.rb +26 -13
- data/lib/hecks/bluebook/smoke_test.rb +46 -23
- data/lib/hecks/bluebook/synthesizer.rb +46 -12
- data/lib/hecks/bluebook/translation.rb +34 -5
- data/lib/hecks/bluebook/value_object.rb +29 -11
- data/lib/hecks/bluebook.rb +5 -6
- data/lib/hecks/codemod.rb +138 -50
- data/lib/hecks/construct.rb +21 -7
- data/lib/hecks/corpus.rb +438 -0
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
- data/lib/hecks/doc/reference.rb +200 -31
- data/lib/hecks/embryonaut_bluebook.rb +38 -15
- 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 +155 -35
- data/lib/hecks/facade/json_door.rb +106 -25
- data/lib/hecks/facade/surface/aggregate_door.rb +50 -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 +183 -19
- data/lib/hecks/forms/html.rb +51 -7
- data/lib/hecks/forms/index_renderer.rb +14 -2
- data/lib/hecks/forms/page.rb +14 -0
- data/lib/hecks/forms/params.rb +120 -23
- data/lib/hecks/forms/port_argument.rb +14 -2
- data/lib/hecks/forms/query_form_renderer.rb +65 -2
- data/lib/hecks/forms/record_renderer.rb +60 -2
- data/lib/hecks/forms/record_table.rb +28 -1
- data/lib/hecks/forms/reference_options.rb +24 -0
- data/lib/hecks/forms/value_object_shape.rb +13 -3
- data/lib/hecks/forms.rb +24 -4
- data/lib/hecks/fqn.rb +59 -1
- data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
- data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
- data/lib/hecks/framework/oidc.json +15 -0
- data/lib/hecks/framework.rb +79 -25
- data/lib/hecks/freezer.rb +27 -11
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +241 -106
- data/lib/hecks/fuzzing/combination_miner.rb +178 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +241 -45
- data/lib/hecks/fuzzing/coverage_campaign.rb +161 -0
- data/lib/hecks/fuzzing/differential.rb +192 -0
- data/lib/hecks/fuzzing/domain_generator.rb +871 -0
- data/lib/hecks/fuzzing/era_boundary.rb +68 -19
- data/lib/hecks/fuzzing/form_census.rb +121 -20
- data/lib/hecks/fuzzing/generated_domain_check.rb +171 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +45 -6
- data/lib/hecks/fuzzing/isolated_boot.rb +137 -80
- data/lib/hecks/fuzzing/nondeterministic.rb +79 -0
- data/lib/hecks/fuzzing/persistence_parity.rb +111 -21
- data/lib/hecks/fuzzing/properties/corrections.rb +34 -9
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +300 -51
- data/lib/hecks/fuzzing/properties/guards.rb +86 -42
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +80 -32
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +40 -35
- data/lib/hecks/fuzzing/properties/outbox.rb +70 -32
- data/lib/hecks/fuzzing/properties/querying.rb +82 -28
- data/lib/hecks/fuzzing/properties.rb +84 -51
- data/lib/hecks/fuzzing/qa_settings.rb +164 -0
- data/lib/hecks/fuzzing/replay.rb +241 -133
- data/lib/hecks/fuzzing/rotation_priority.rb +48 -28
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +139 -0
- data/lib/hecks/fuzzing/self_consistency.rb +315 -137
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +46 -40
- 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 +59 -32
- data/lib/hecks/fuzzing/sequence_generator.rb +158 -23
- data/lib/hecks/fuzzing/shrinker.rb +309 -0
- data/lib/hecks/fuzzing/structural_skips.rb +37 -130
- data/lib/hecks/fuzzing/sweep_depth.rb +12 -4
- data/lib/hecks/fuzzing/target_capabilities.rb +107 -29
- data/lib/hecks/fuzzing/value_generator.rb +110 -22
- data/lib/hecks/fuzzing.rb +5 -0
- data/lib/hecks/grammar/evolve.rb +188 -12
- data/lib/hecks/grammar.rb +53 -7
- data/lib/hecks/ir.rb +51 -20
- 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/hecksagon/hecksagon.bluebook +11 -0
- data/lib/hecks/language/oidc.json +5 -0
- data/lib/hecks/literal.rb +41 -9
- data/lib/hecks/naming.rb +112 -31
- data/lib/hecks/ports/access_control.rb +53 -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 +50 -11
- data/lib/hecks/ports/clock.rb +42 -23
- data/lib/hecks/ports/extraction.rb +16 -0
- data/lib/hecks/ports/identity_assignment.rb +23 -2
- data/lib/hecks/ports/identity_generation.rb +17 -3
- data/lib/hecks/ports/identity_resolution.rb +17 -1
- data/lib/hecks/ports/key_vault.port +6 -0
- data/lib/hecks/ports/key_vault.rb +58 -0
- 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 +34 -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 +95 -6
- 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 +48 -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 +58 -19
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +96 -5
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +9 -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 +41 -8
- data/lib/hecks/ports/query/ordering.rb +21 -6
- data/lib/hecks/ports/query.rb +35 -0
- data/lib/hecks/ports.rb +1 -0
- data/lib/hecks/projections/bootstrap_table.rb +147 -0
- data/lib/hecks/projections/diagrams.rb +315 -79
- data/lib/hecks/projections/glossary/html.rb +91 -3
- data/lib/hecks/projections/glossary/markdown.rb +50 -3
- data/lib/hecks/projections/glossary/mermaid.rb +49 -1
- data/lib/hecks/projections/glossary/sections.rb +1 -1
- data/lib/hecks/projections/glossary/sentences.rb +110 -5
- data/lib/hecks/projections/glossary.rb +173 -31
- data/lib/hecks/projections/ir.rb +1 -1
- data/lib/hecks/projections/model/deviations.rb +62 -17
- data/lib/hecks/projections/model.rb +75 -21
- data/lib/hecks/projections/oidc.rb +45 -8
- data/lib/hecks/projections/parser_table.rb +53 -12
- data/lib/hecks/projections/reference.rb +15 -3
- data/lib/hecks/projections/rust_vocabulary.rb +646 -0
- data/lib/hecks/projections/shape.rb +10 -2
- data/lib/hecks/projections/statements.rb +69 -22
- data/lib/hecks/projections/vocabulary.rb +26 -9
- data/lib/hecks/projections.rb +5 -3
- data/lib/hecks/projector/cli_projector.rb +245 -37
- data/lib/hecks/projector/docs_projector.rb +154 -28
- data/lib/hecks/projector/exporter.rb +104 -29
- data/lib/hecks/projector/ir_projector.rb +7 -1
- data/lib/hecks/projector/narrate_projector.rb +144 -23
- data/lib/hecks/projector/target.rb +42 -18
- data/lib/hecks/projector.rb +86 -18
- data/lib/hecks/query_ir.rb +94 -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 +61 -15
- 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 +9 -3
- data/lib/hecks/router/namespace_installer.rb +16 -3
- data/lib/hecks/router.rb +56 -1
- data/lib/hecks/runtime/aggregate_lock.rb +20 -11
- data/lib/hecks/runtime/boot_gates.rb +21 -3
- data/lib/hecks/runtime/caller.rb +40 -8
- data/lib/hecks/runtime/capability_graph.rb +13 -2
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +40 -41
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +45 -67
- data/lib/hecks/runtime/command_interpreter.rb +195 -113
- data/lib/hecks/runtime/command_rules/admissibility.rb +231 -80
- data/lib/hecks/runtime/command_rules/arithmetic.rb +154 -96
- 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 +145 -55
- data/lib/hecks/runtime/command_rules.rb +3 -0
- data/lib/hecks/runtime/dependency_planning.rb +56 -11
- data/lib/hecks/runtime/dispatcher.rb +263 -92
- data/lib/hecks/runtime/entity_element.rb +304 -67
- data/lib/hecks/runtime/entity_interpreter.rb +149 -88
- data/lib/hecks/runtime/errors.rb +37 -23
- data/lib/hecks/runtime/event.rb +15 -10
- data/lib/hecks/runtime/identity.rb +91 -23
- data/lib/hecks/runtime/instance.rb +103 -18
- data/lib/hecks/runtime/interpreting.rb +24 -16
- data/lib/hecks/runtime/invocation.rb +358 -0
- data/lib/hecks/runtime/loader.rb +105 -19
- data/lib/hecks/runtime/outbox.rb +164 -26
- data/lib/hecks/runtime/policy_interpreter.rb +73 -60
- data/lib/hecks/runtime/port_operation_interpreter.rb +42 -19
- data/lib/hecks/runtime/query_interpreter.rb +93 -74
- data/lib/hecks/runtime/reaction_invocation.rb +73 -28
- data/lib/hecks/runtime/read_model_interpreter.rb +60 -44
- data/lib/hecks/runtime/rebuild_sweep.rb +32 -4
- data/lib/hecks/runtime/reference_hop.rb +48 -6
- data/lib/hecks/runtime/refusal_wording.rb +142 -115
- data/lib/hecks/runtime/registry/saga_persistence.rb +32 -21
- data/lib/hecks/runtime/registry/verification.rb +153 -28
- data/lib/hecks/runtime/registry.rb +202 -32
- data/lib/hecks/runtime/remote_dispatcher.rb +125 -24
- data/lib/hecks/runtime/routing.rb +36 -154
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +25 -27
- data/lib/hecks/runtime/saga_interpreter.rb +90 -76
- data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
- data/lib/hecks/runtime/tenant_check.rb +33 -13
- data/lib/hecks/runtime/tenant_scope.rb +23 -5
- data/lib/hecks/runtime/value/admission.rb +75 -30
- data/lib/hecks/runtime/value/coercion.rb +555 -142
- data/lib/hecks/runtime/value/entity_list_coercion.rb +132 -60
- data/lib/hecks/runtime/value.rb +71 -21
- data/lib/hecks/runtime.rb +39 -7
- data/lib/hecks/storehouse.rb +368 -72
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
- data/lib/hecks/version.rb +3 -3
- data/lib/hecks/vocabulary.rb +205 -4
- data/lib/hecks.rb +91 -11
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +19 -11
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +24 -12
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +38 -14
- metadata +28 -2
|
@@ -16,12 +16,17 @@ module Hecks
|
|
|
16
16
|
|
|
17
17
|
attr_reader :root, :bluebooks, :hecksagons, :ports, :adapters, :worlds, :event_log,
|
|
18
18
|
:reaction_log, :saga_log, :saga_instances, :translations, :saga_mutex,
|
|
19
|
-
:saga_dispatch_log, :policy_dispatch_log
|
|
19
|
+
:saga_dispatch_log, :policy_dispatch_log, :bluebook_sources,
|
|
20
|
+
:pending_privacy_markings
|
|
20
21
|
|
|
22
|
+
# @param root [String, nil] the booting project's root directory, the base
|
|
23
|
+
# a shared ports/adapters root and a `.world`'s own relative paths resolve
|
|
24
|
+
# against; nil for a registry with no such root
|
|
21
25
|
def initialize(root: nil)
|
|
22
26
|
@root = root
|
|
23
27
|
@bluebooks = {}
|
|
24
|
-
@
|
|
28
|
+
@bluebook_sources = {}
|
|
29
|
+
@hecksagons = {}
|
|
25
30
|
@ports = {}
|
|
26
31
|
@adapters = {}
|
|
27
32
|
@worlds = {}
|
|
@@ -29,8 +34,16 @@ module Hecks
|
|
|
29
34
|
@event_log = []
|
|
30
35
|
@reaction_log = []
|
|
31
36
|
@saga_log = []
|
|
32
|
-
#
|
|
33
|
-
#
|
|
37
|
+
# A DECLARATIVE FACT, NOT YET A DISPATCHED ONE — `AggregateDoor#
|
|
38
|
+
# mark_sensitive` (called from a `.hecksagon` file, the same way
|
|
39
|
+
# `port`/`persisted_by` already are) appends here at
|
|
40
|
+
# hecksagon-build time; `Runtime::Loader.boot`'s own post-dispatcher
|
|
41
|
+
# step turns each entry into a real `Privacy::Marking.Mark`,
|
|
42
|
+
# idempotently, the same "declared here, taken effect once boot
|
|
43
|
+
# actually has a dispatcher" shape `redrive_outbox!` already has.
|
|
44
|
+
@pending_privacy_markings = []
|
|
45
|
+
# **Additive, Ruby-only** — never merged into saga_log/reaction_log.
|
|
46
|
+
# rust/src/kernel/orchestrate.rs ports those two arrays' exact
|
|
34
47
|
# shape byte-for-byte (spec/rust_conformance_spec.rb's own
|
|
35
48
|
# equality check) — a landmine found by reading that spec before
|
|
36
49
|
# touching anything, not by hitting it. These carry the raw
|
|
@@ -43,7 +56,7 @@ module Hecks
|
|
|
43
56
|
@saga_dispatch_log = []
|
|
44
57
|
@policy_dispatch_log = []
|
|
45
58
|
@saga_instances = Hash.new { |h, k| h[k] = {} }
|
|
46
|
-
#
|
|
59
|
+
# Guards `saga_instances`' own mutation+checkpoint sequence
|
|
47
60
|
# (`SagaInterpreter`'s 4 write points, §7) — the same shape of
|
|
48
61
|
# hazard this codebase's own prior audit already flagged for
|
|
49
62
|
# `Dispatcher#reenter`'s reaction-depth counter (M20: a
|
|
@@ -51,7 +64,7 @@ module Hecks
|
|
|
51
64
|
# `Thread.current`, dispatcher.rb), made meaningfully easier to
|
|
52
65
|
# hit here once a persistence write sits in the same critical
|
|
53
66
|
# section. Held across the in-memory
|
|
54
|
-
# mutation
|
|
67
|
+
# mutation and the checkpoint write together, never across a
|
|
55
68
|
# saga's own dispatch cascade — see `SagaInterpreter#advance_saga`'s
|
|
56
69
|
# own comment for why that distinction matters (non-reentrant
|
|
57
70
|
# Mutex, recursive re-entry is real).
|
|
@@ -59,18 +72,18 @@ module Hecks
|
|
|
59
72
|
@repositories = {}
|
|
60
73
|
@projection_repositories = {}
|
|
61
74
|
@bluebook_builders = {}
|
|
62
|
-
#
|
|
75
|
+
# **Eager, not lazy** — see `#resolved_eras`'s own comment for why. Built
|
|
63
76
|
# here rather than `@resolved_eras ||= {}` on first access so there is
|
|
64
77
|
# no window, post-boot, where two concurrently dispatching threads
|
|
65
78
|
# could race creating this Hash (Hecks/ThreadSharedIvarMutation; the
|
|
66
79
|
# same shape of hazard `Dispatcher#reenter`'s `@reaction_depth` was
|
|
67
|
-
# fixed for). Every
|
|
80
|
+
# fixed for). Every write into it still only ever happens at boot,
|
|
68
81
|
# single-threaded (`EraResolver.check!`, a `:pre_verify` boot gate) —
|
|
69
82
|
# this only removes the race on standing up the container itself for
|
|
70
83
|
# a boot with no era-plugin domain at all, whose first touch would
|
|
71
84
|
# otherwise be a live dispatch's own `RepositoryFactory.build` read.
|
|
72
85
|
@resolved_eras = {}
|
|
73
|
-
#
|
|
86
|
+
# The sibling fact `EraResolver.check!` records for an old checkout:
|
|
74
87
|
# domain name -> the newest held ordinal that superseded the era this
|
|
75
88
|
# boot resolved to; absent for every domain booting the current era.
|
|
76
89
|
# `RepositoryFactory.build` hands it to the adapter as
|
|
@@ -79,7 +92,7 @@ module Hecks
|
|
|
79
92
|
# half that holds even for a connection row-level security cannot
|
|
80
93
|
# bite (BUG#24). Eager for exactly the reason `@resolved_eras` is.
|
|
81
94
|
@superseded_eras = {}
|
|
82
|
-
#
|
|
95
|
+
# **Eager, not lazy** — see `#capability_graph`'s own comment for why.
|
|
83
96
|
# `CapabilityGraph.new` only stores the registry reference; there is
|
|
84
97
|
# no reason to defer it, and doing so removes the exact same
|
|
85
98
|
# first-access race `#resolved_eras` above does, while preserving the
|
|
@@ -87,10 +100,10 @@ module Hecks
|
|
|
87
100
|
# spec.rb` already requires.
|
|
88
101
|
@capability_graph = CapabilityGraph.new(self)
|
|
89
102
|
# `@saga_persistence` itself is eager (see `#saga_persistence`'s own
|
|
90
|
-
# comment) — only the
|
|
91
|
-
# expensive and lazy, guarded by this dedicated mutex.
|
|
103
|
+
# comment) — only the per-domain resolution inside it is genuinely
|
|
104
|
+
# expensive and lazy, guarded by this dedicated mutex. Not the same
|
|
92
105
|
# mutex as `@saga_mutex`: `checkpoint` (saga_interpreter.rb) calls
|
|
93
|
-
# `saga_persistence(domain)` from
|
|
106
|
+
# `saga_persistence(domain)` from inside an `@saga_mutex.synchronize`
|
|
94
107
|
# block, so reusing `@saga_mutex` here would deadlock the very first
|
|
95
108
|
# time a saga advanced (a `Mutex` is not reentrant — the exact
|
|
96
109
|
# warning `@saga_mutex`'s own comment already gives for a different
|
|
@@ -100,23 +113,31 @@ module Hecks
|
|
|
100
113
|
@outbox = Outbox::Relay.new(self)
|
|
101
114
|
end
|
|
102
115
|
|
|
103
|
-
#
|
|
116
|
+
# **The outbox relay** — one per registry, for its whole life (built
|
|
104
117
|
# here, never swapped, so no thread ever sees a different one).
|
|
105
118
|
# It can enqueue from the moment the registry exists; a Dispatcher
|
|
106
119
|
# attaches the interpreters that let it deliver. See
|
|
107
120
|
# `Runtime::Outbox`.
|
|
108
121
|
attr_reader :outbox
|
|
109
122
|
|
|
110
|
-
#
|
|
123
|
+
# The builder stays open for the life of this registry, keyed by chapter
|
|
111
124
|
# name — see the comment on `BluebookBuilder.build`. A chapter split across
|
|
112
125
|
# several files (`language/bluebook/*.bluebook`, all `Hecks.bluebook "Bluebook"`)
|
|
113
|
-
# needs its declarations to accumulate into
|
|
126
|
+
# needs its declarations to accumulate into one builder rather than each
|
|
114
127
|
# file minting its own and silently discarding the one before.
|
|
128
|
+
#
|
|
129
|
+
# @param name [String, Symbol] the chapter name the builder accumulates
|
|
130
|
+
# declarations for
|
|
131
|
+
# @yield the block that mints a fresh builder, called only the first time
|
|
132
|
+
# `name` is asked for
|
|
133
|
+
# @yieldreturn [Bluebook::DSL::BluebookBuilder] a fresh builder for `name`
|
|
134
|
+
# @return [Bluebook::DSL::BluebookBuilder] the builder already open for
|
|
135
|
+
# `name`, or the block's freshly minted one on the first call
|
|
115
136
|
def bluebook_builder(name)
|
|
116
137
|
@bluebook_builders[name.to_s] ||= yield
|
|
117
138
|
end
|
|
118
139
|
|
|
119
|
-
#
|
|
140
|
+
# Boot-time-only, single-threaded — every `add_*` below (through
|
|
120
141
|
# `add_translation`) is called exclusively from `Hecks.collect`
|
|
121
142
|
# (hecks.rb), which is what `Hecks.bluebook`/`.hecksagon`/`.port`/
|
|
122
143
|
# `.adapter`/`.world`/`.translation` run inside while a `.bluebook`/
|
|
@@ -130,23 +151,56 @@ module Hecks
|
|
|
130
151
|
# registry/saga_persistence.rb) there is no concurrent caller for
|
|
131
152
|
# `Hecks/ThreadSharedIvarMutation` to actually be warning about here.
|
|
132
153
|
# rubocop:disable Hecks/ThreadSharedIvarMutation
|
|
154
|
+
# Registers a loaded chapter, keyed by its own declared name.
|
|
155
|
+
#
|
|
156
|
+
# @param item [Bluebook::Chapter] the loaded, judged chapter
|
|
157
|
+
# @return [Bluebook::Chapter] `item`, unchanged
|
|
133
158
|
def add_bluebook(item) = @bluebooks[item.name] = item
|
|
134
159
|
|
|
135
|
-
#
|
|
160
|
+
# PROVENANCE, SIDE-CHANNEL — which real `.bluebook` file(s)
|
|
161
|
+
# contributed to a chapter name, never part of the exported IR (a
|
|
162
|
+
# boot-time loading fact, not a domain fact) and never Rust-mirrored
|
|
163
|
+
# (the same "additive, Ruby-only" shape `@translations` above already
|
|
164
|
+
# is). Legitimate accumulation (several files declaring the SAME
|
|
165
|
+
# chapter name on purpose — `lib/hecks/language/bluebook/*.bluebook`
|
|
166
|
+
# all open `Hecks.bluebook "Bluebook"`) pushes more than one path
|
|
167
|
+
# here too; that alone is not a problem. What this exists to let
|
|
168
|
+
# `refuse_cross_package_bluebook_merge!` (registry/verification.rb)
|
|
169
|
+
# catch is TWO UNRELATED PACKAGES accumulating into the same name by
|
|
170
|
+
# coincidence — a stale vendored fork's own copy of a real gem's
|
|
171
|
+
# chapter, still reachable on the load path, silently merging its
|
|
172
|
+
# aggregates into the real one via this exact accumulation mechanism.
|
|
173
|
+
def record_bluebook_source(name, path)
|
|
174
|
+
(@bluebook_sources[name.to_s] ||= []) << path
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Merged, not replaced — recovered, not new (see Runtime::Loader
|
|
136
178
|
# .boot's own comment for the provenance). A domain's hecksagon can
|
|
137
179
|
# now load in more than one block for the same domain (base file
|
|
138
180
|
# plus an `environments/<name>.hecksagon` overlay), and the second
|
|
139
|
-
# block should
|
|
181
|
+
# block should add to what the first declared, not silently
|
|
140
182
|
# discard it.
|
|
183
|
+
#
|
|
184
|
+
# @param item [Bluebook::Hecksagon] the declared wiring to register
|
|
185
|
+
# @return [void]
|
|
141
186
|
def add_hecksagon(item)
|
|
142
187
|
existing = @hecksagons[item.domain]
|
|
143
188
|
@hecksagons[item.domain] = existing ? merge_hecksagons(existing, item) : item
|
|
144
189
|
end
|
|
145
190
|
|
|
191
|
+
# Registers a loaded port, keyed by its own declared name.
|
|
192
|
+
#
|
|
193
|
+
# @param item [Bluebook::Port, Bluebook::DomainPort] the loaded port
|
|
194
|
+
# @return [Bluebook::Port, Bluebook::DomainPort] `item`, unchanged
|
|
146
195
|
def add_port(item) = @ports[item.name] = item
|
|
196
|
+
|
|
197
|
+
# Registers a loaded adapter, keyed by its own declared name.
|
|
198
|
+
#
|
|
199
|
+
# @param item [Bluebook::Adapter] the loaded, judged adapter
|
|
200
|
+
# @return [Bluebook::Adapter] `item`, unchanged
|
|
147
201
|
def add_adapter(item) = @adapters[item.name] = item
|
|
148
202
|
|
|
149
|
-
#
|
|
203
|
+
# Merged, not replaced — the same generalization for `World` that
|
|
150
204
|
# `add_hecksagon` above recovers for `Hecksagon`: an
|
|
151
205
|
# `environments/<name>.world` overlay (or a host-owned tenancy
|
|
152
206
|
# overlay world, same mechanism) can now add or override settings
|
|
@@ -156,16 +210,44 @@ module Hecks
|
|
|
156
210
|
# bare verb key and the `"verb:adapter"` qualified key point at the
|
|
157
211
|
# same resolved hash) — an overlay's key wins over the base's same
|
|
158
212
|
# key; a key only the base declares survives untouched.
|
|
213
|
+
#
|
|
214
|
+
# @param item [Bluebook::World] the declared world settings to register
|
|
215
|
+
# @return [void]
|
|
159
216
|
def add_world(item)
|
|
160
217
|
existing = @worlds[item.domain]
|
|
161
218
|
@worlds[item.domain] = existing ? merge_worlds(existing, item) : item
|
|
162
219
|
end
|
|
163
220
|
|
|
221
|
+
# Registers a loaded translation.
|
|
222
|
+
#
|
|
223
|
+
# @param item [Bluebook::Translation] the loaded, judged translation
|
|
224
|
+
# @return [Array<Bluebook::Translation>] every translation registered so far,
|
|
225
|
+
# `item` last
|
|
164
226
|
def add_translation(item) = @translations << item
|
|
227
|
+
|
|
228
|
+
# Declares one attribute of one domain's own aggregate sensitive — called from a
|
|
229
|
+
# terminal `has_<category>(readable_by:)` on a `Bluebook::DSL::AttributePath`
|
|
230
|
+
# (reached by chaining off a bare `Domain::Aggregate` inside a `.hecksagon` file
|
|
231
|
+
# being `Kernel.load`ed, or off an already-installed `AggregateDoor`), same timing
|
|
232
|
+
# (and same thread-safety argument, above) as `add_bluebook`/`add_port`. Recorded,
|
|
233
|
+
# not dispatched: `Runtime::Loader.boot`'s own `seed_privacy_markings!` turns each
|
|
234
|
+
# entry into a real `Privacy::Marking.Mark` once a dispatcher exists.
|
|
235
|
+
#
|
|
236
|
+
# @param domain [String] the marked attribute's own aggregate FQN, e.g.
|
|
237
|
+
# `"Lifeadelics::Registration"`
|
|
238
|
+
# @param attribute_path [String] the dotted path within that aggregate, e.g.
|
|
239
|
+
# `"attendee.medications"`
|
|
240
|
+
# @param category [String] the marking's own sensitivity category, e.g. `"phi"`
|
|
241
|
+
# @param readable_by [String] the Governance role a read must hold, unredacted
|
|
242
|
+
# @return [void]
|
|
243
|
+
def add_pending_privacy_marking(domain:, attribute_path:, category:, readable_by:)
|
|
244
|
+
@pending_privacy_markings << { domain: domain, attribute_path: attribute_path,
|
|
245
|
+
category: category, readable_by: readable_by }
|
|
246
|
+
end
|
|
165
247
|
# rubocop:enable Hecks/ThreadSharedIvarMutation
|
|
166
248
|
|
|
167
249
|
# {domain name => era ordinal} as resolved by the boot-time era
|
|
168
|
-
# gate. A lineage adapter writes into
|
|
250
|
+
# gate. A lineage adapter writes into its own era's partition —
|
|
169
251
|
# which, for an old checkout booting a held-but-superseded shape,
|
|
170
252
|
# is not the newest one. The Hash itself is stood up in `initialize`
|
|
171
253
|
# (see that comment) — this is a plain reader, not a memoizer;
|
|
@@ -176,18 +258,73 @@ module Hecks
|
|
|
176
258
|
# an old checkout only — see `initialize`'s own comment on it.
|
|
177
259
|
attr_reader :superseded_eras
|
|
178
260
|
|
|
261
|
+
# Finds a loaded chapter by name.
|
|
262
|
+
#
|
|
263
|
+
# @param name [String, Symbol] the chapter's declared name
|
|
264
|
+
# @return [Bluebook::Chapter, nil] the chapter, or nil if none is registered
|
|
265
|
+
# under `name`
|
|
179
266
|
def bluebook(name) = @bluebooks[name.to_s]
|
|
267
|
+
|
|
268
|
+
# Finds a domain's registered wiring by name.
|
|
269
|
+
#
|
|
270
|
+
# @param name [String, Symbol] the domain name
|
|
271
|
+
# @return [Bluebook::Hecksagon, nil] the domain's wiring, or nil if none is
|
|
272
|
+
# registered under `name`
|
|
180
273
|
def hecksagon(name) = @hecksagons[name.to_s]
|
|
274
|
+
|
|
275
|
+
# Finds a domain's registered world settings by name.
|
|
276
|
+
#
|
|
277
|
+
# @param name [String, Symbol] the domain name
|
|
278
|
+
# @return [Bluebook::World, nil] the domain's world, or nil if none is
|
|
279
|
+
# registered under `name`
|
|
181
280
|
def world(name) = @worlds[name.to_s]
|
|
182
281
|
|
|
282
|
+
# Every verb every loaded chapter declares, sorted.
|
|
283
|
+
#
|
|
284
|
+
# @return [Array<String>] every declared verb, across every loaded chapter
|
|
183
285
|
def verbs = @bluebooks.values.flat_map(&:verbs).sort
|
|
184
286
|
|
|
287
|
+
# The chapter that answers a role check for `domain` — the domain's
|
|
288
|
+
# own chapter, or any framework member its hecksagon attaches, that
|
|
289
|
+
# declares `provides "authorization"`. Nil when none does. Replaces
|
|
290
|
+
# every check for the literal name "Governance": Governance is
|
|
291
|
+
# recognised by what it declares, and a chapter that declares the
|
|
292
|
+
# same thing is recognised the same way.
|
|
293
|
+
#
|
|
294
|
+
# @param domain [String, Symbol] the domain whose role checks are being resolved
|
|
295
|
+
# @return [Bluebook::Chapter, nil] the chapter that answers `domain`'s role
|
|
296
|
+
# checks, or nil if none does
|
|
297
|
+
def authorization_provider_for(domain)
|
|
298
|
+
names = [domain.to_s, *Array(hecksagon(domain)&.framework_members)]
|
|
299
|
+
names.filter_map { |name| bluebook(name) }
|
|
300
|
+
.find { |chapter| chapter.provides?(Bluebook::Capabilities::AUTHORIZATION) }
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# Every loaded chapter declaring `provides "authorization"`.
|
|
304
|
+
#
|
|
305
|
+
# @return [Array<Bluebook::Chapter>] every loaded chapter that provides
|
|
306
|
+
# authorization
|
|
307
|
+
def authorization_providers
|
|
308
|
+
@bluebooks.values.select { |chapter| chapter.provides?(Bluebook::Capabilities::AUTHORIZATION) }
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# Resolves and memoizes `aggregate`'s authoritative repository.
|
|
312
|
+
#
|
|
313
|
+
# @param domain [String, Symbol] name of the domain `aggregate` belongs to
|
|
314
|
+
# @param aggregate [Bluebook::Aggregate] the aggregate to resolve a repository for
|
|
315
|
+
# @return [Persistence::AppendOnly] repository over the aggregate's authoritative
|
|
316
|
+
# adapter, or over a `Memory` adapter when the domain declares no hecksagon
|
|
317
|
+
# @raise [Runtime::WiringError] if the aggregate has no authoritative bind, more than
|
|
318
|
+
# one, or a bind with a role this port does not support; or if the bound adapter is
|
|
319
|
+
# unknown, answers a different verb, is given a setting it does not declare, has no
|
|
320
|
+
# Ruby implementation, or lacks a method its port's `answers` list or the
|
|
321
|
+
# append-only contract (`append`, `project`, `entries`) requires
|
|
185
322
|
def repository(domain, aggregate)
|
|
186
323
|
@repositories[[domain.to_s, aggregate.hecks_name]] ||= Ports::Persistence.repository(self, domain, aggregate)
|
|
187
324
|
end
|
|
188
325
|
|
|
189
|
-
#
|
|
190
|
-
#
|
|
326
|
+
# Everything a dispatch wrote, cleared; nothing a boot declared,
|
|
327
|
+
# touched. Bluebooks, hecksagons, ports, adapters, worlds and the
|
|
191
328
|
# resolved eras are what loading the files produced and stay as
|
|
192
329
|
# they are; the logs, the saga instances and the repositories are
|
|
193
330
|
# what running commands against them produced, and go back to
|
|
@@ -199,19 +336,21 @@ module Hecks
|
|
|
199
336
|
# that store again, the way `Loader.boot_files` does after
|
|
200
337
|
# `verify!`.
|
|
201
338
|
#
|
|
202
|
-
# What this is for: a test runner that
|
|
203
|
-
# test to get isolation (`Behaviors::Expectations.run_one`) — ~2s a
|
|
339
|
+
# What this is for: a test runner that would otherwise boot a runtime
|
|
340
|
+
# per test to get isolation (`Behaviors::Expectations.run_one`) — ~2s a
|
|
204
341
|
# boot, 76 chess behaviours = two and a half minutes of booting the
|
|
205
342
|
# same two files — can now boot once and reset between tests.
|
|
206
343
|
#
|
|
207
|
-
#
|
|
208
|
-
#
|
|
344
|
+
# Single-threaded caller, the same reason the `add_*` cluster above
|
|
345
|
+
# is exempt — `Behaviors::Expectations.run_one` is this method's only
|
|
209
346
|
# caller (verified by grep before writing this), and it runs one
|
|
210
347
|
# test at a time: `Runner#run` maps over tests sequentially, and
|
|
211
348
|
# `Behaviors.rspec`'s generated examples run under RSpec's own
|
|
212
349
|
# single-threaded example loop. No production dispatch path calls
|
|
213
350
|
# this at all — a live Puma worker pool never resets a registry out
|
|
214
351
|
# from under itself mid-flight.
|
|
352
|
+
#
|
|
353
|
+
# @return [Runtime::Registry] self
|
|
215
354
|
# rubocop:disable-next Hecks/ThreadSharedIvarMutation
|
|
216
355
|
def reset_runtime_state!
|
|
217
356
|
@event_log.clear
|
|
@@ -229,10 +368,21 @@ module Hecks
|
|
|
229
368
|
|
|
230
369
|
# Built eagerly in `initialize` (see that comment) — this is a plain
|
|
231
370
|
# reader, not a memoizer; `spec/runtime/capability_graph_spec.rb`
|
|
232
|
-
# asserts the
|
|
371
|
+
# asserts the same instance comes back every call, which this still
|
|
233
372
|
# gives, just without a lazy `||=` race on standing it up.
|
|
234
373
|
attr_reader :capability_graph
|
|
235
374
|
|
|
375
|
+
# Resolves and memoizes the repository to read `aggregate` from — a caught-up
|
|
376
|
+
# projection when one is bound and current, otherwise the authoritative repository.
|
|
377
|
+
#
|
|
378
|
+
# @param domain [String, Symbol] name of the domain `aggregate` belongs to
|
|
379
|
+
# @param aggregate [Bluebook::Aggregate] the aggregate to resolve a read
|
|
380
|
+
# repository for
|
|
381
|
+
# @return [Persistence::AppendOnly] the projection repository when one is bound
|
|
382
|
+
# and caught up with the authoritative store; the authoritative repository
|
|
383
|
+
# otherwise
|
|
384
|
+
# @raise [Runtime::WiringError] if the authoritative or projection bind cannot
|
|
385
|
+
# be resolved
|
|
236
386
|
def read_repository(domain, aggregate)
|
|
237
387
|
key = [domain.to_s, aggregate.hecks_name]
|
|
238
388
|
binding = Ports::Projection.binds_for(self, domain, aggregate).first
|
|
@@ -247,6 +397,15 @@ module Hecks
|
|
|
247
397
|
projection_current?(projection, authoritative) ? projection : authoritative
|
|
248
398
|
end
|
|
249
399
|
|
|
400
|
+
# Reports whether `projection`'s own journal entries and rows agree with
|
|
401
|
+
# `authoritative`'s, entry-for-entry.
|
|
402
|
+
#
|
|
403
|
+
# @param projection [Persistence::AppendOnly] the projection repository to check
|
|
404
|
+
# @param authoritative [Persistence::AppendOnly] the authoritative repository to
|
|
405
|
+
# check `projection` against
|
|
406
|
+
# @return [Boolean] true when `projection` holds the same entries and rows as
|
|
407
|
+
# `authoritative`, in the same order; false on any mismatch, or if comparing
|
|
408
|
+
# them raises
|
|
250
409
|
def projection_current?(projection, authoritative)
|
|
251
410
|
projected_entries = projection.entries
|
|
252
411
|
source_entries = authoritative.entries
|
|
@@ -262,15 +421,21 @@ module Hecks
|
|
|
262
421
|
false
|
|
263
422
|
end
|
|
264
423
|
|
|
265
|
-
#
|
|
424
|
+
# Recovered — see `add_hecksagon`'s own comment for provenance.
|
|
266
425
|
# Concatenates every list-shaped fact; `binds` in particular is
|
|
267
|
-
# additive because an overlay
|
|
426
|
+
# additive because an overlay rebinding an aggregate (a new
|
|
268
427
|
# `persisted_by` for the same aggregate/verb) is meant to shadow
|
|
269
428
|
# the base's own bind at resolution time, not erase it outright —
|
|
270
429
|
# `Ports::Persistence::BindingPolicy.resolve`'s own "exactly one
|
|
271
430
|
# authoritative bind" check is what actually catches a genuine
|
|
272
431
|
# double-bind; this merge only concatenates, it does not itself
|
|
273
432
|
# decide which of two binds for the same aggregate wins.
|
|
433
|
+
#
|
|
434
|
+
# @param base [Bluebook::Hecksagon] the domain's already-registered wiring
|
|
435
|
+
# @param overlay [Bluebook::Hecksagon] the newly loaded block's own wiring to
|
|
436
|
+
# fold in
|
|
437
|
+
# @return [Bluebook::Hecksagon] a new wiring with every list-shaped fact
|
|
438
|
+
# concatenated, `base` then `overlay`
|
|
274
439
|
def merge_hecksagons(base, overlay)
|
|
275
440
|
Bluebook::Hecksagon.new(
|
|
276
441
|
domain: base.domain,
|
|
@@ -281,13 +446,18 @@ module Hecks
|
|
|
281
446
|
)
|
|
282
447
|
end
|
|
283
448
|
|
|
284
|
-
#
|
|
449
|
+
# Recovered and generalized — see `add_world`'s own comment. `realm`/
|
|
285
450
|
# `latest` are scalars, so the overlay's value wins when present,
|
|
286
451
|
# else the base's survives; `settings` is a shallow merge keyed by
|
|
287
452
|
# verb (and `"verb:adapter"`) — an overlay entry for a key the base
|
|
288
|
-
# also declares
|
|
453
|
+
# also declares replaces that key's whole resolved hash (the same
|
|
289
454
|
# all-or-nothing shape `WorldBuilder#method_missing` already builds
|
|
290
455
|
# each entry as), it does not deep-merge field by field within it.
|
|
456
|
+
#
|
|
457
|
+
# @param base [Bluebook::World] the domain's already-registered world
|
|
458
|
+
# @param overlay [Bluebook::World] the newly loaded block's own world to fold in
|
|
459
|
+
# @return [Bluebook::World] a new world with `overlay`'s scalars winning when
|
|
460
|
+
# present, and `settings` shallow-merged, `overlay`'s keys winning
|
|
291
461
|
def merge_worlds(base, overlay)
|
|
292
462
|
Bluebook::World.new(
|
|
293
463
|
domain: base.domain,
|