hecks 0.2.0 → 0.3.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 +5 -5
- data/lib/hecks/adapters/driven/claude_code.adapter +3 -0
- data/lib/hecks/adapters/driven/claude_code.rb +127 -0
- data/lib/hecks/adapters/driven/d1.adapter +12 -0
- data/lib/hecks/adapters/driven/d1.rb +403 -0
- data/lib/hecks/adapters/driven/folder.adapter +3 -0
- data/lib/hecks/adapters/driven/folder.rb +199 -0
- data/lib/hecks/adapters/driven/google_authentication.adapter +3 -0
- data/lib/hecks/adapters/driven/google_authentication.rb +100 -0
- data/lib/hecks/adapters/driven/governance_authorization.adapter +3 -0
- data/lib/hecks/adapters/driven/governance_authorization.rb +62 -0
- data/lib/hecks/adapters/driven/heki/journal.rb +51 -0
- data/lib/hecks/adapters/driven/heki/saga_store.rb +0 -0
- data/lib/hecks/adapters/driven/heki/snapshot.rb +38 -0
- data/lib/hecks/adapters/driven/heki.adapter +4 -0
- data/lib/hecks/adapters/driven/heki.rb +148 -0
- data/lib/hecks/adapters/driven/identity_registry.adapter +3 -0
- data/lib/hecks/adapters/driven/identity_registry.rb +26 -0
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +51 -0
- data/lib/hecks/adapters/driven/lambda/client.rb +63 -0
- data/lib/hecks/adapters/driven/lambda.adapter +4 -0
- data/lib/hecks/adapters/driven/lambda.rb +131 -0
- data/lib/hecks/adapters/driven/memory.adapter +3 -0
- data/lib/hecks/adapters/driven/memory.rb +103 -0
- data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +3 -0
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +28 -0
- data/lib/hecks/adapters/driven/postgres/codec.rb +80 -0
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +168 -0
- data/lib/hecks/adapters/driven/postgres.adapter +5 -0
- data/lib/hecks/adapters/driven/postgres.rb +391 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/era_store.rb +171 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/field_cache.rb +190 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/head_compiler.rb +429 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/mint_transaction.rb +166 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/provisioning.rb +286 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/resumable_backfill.rb +168 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/tail_merge.rb +163 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/transform_installer.rb +114 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage.rb +137 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/coverage_check.rb +89 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/era_resolver.rb +82 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/merge_coordinator.rb +43 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/minter.rb +132 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager.rb +74 -0
- data/lib/hecks/adapters/driven/postgres_era.adapter +6 -0
- data/lib/hecks/adapters/driven/postgres_era.rb +671 -0
- data/lib/hecks/adapters/driven/prism.adapter +3 -0
- data/lib/hecks/adapters/driven/prism.rb +80 -0
- data/lib/hecks/adapters/driven/secure_random_identity.adapter +3 -0
- data/lib/hecks/adapters/driven/secure_random_identity.rb +14 -0
- data/lib/hecks/adapters/driven/sql_query_builder.rb +221 -0
- data/lib/hecks/adapters/driven/sqlite/codec.rb +63 -0
- data/lib/hecks/adapters/driven/sqlite/projection.rb +105 -0
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +180 -0
- data/lib/hecks/adapters/driven/sqlite.adapter +9 -0
- data/lib/hecks/adapters/driven/sqlite.rb +285 -0
- data/lib/hecks/adapters/driven/system_clock.adapter +3 -0
- data/lib/hecks/adapters/driven/system_clock.rb +14 -0
- data/lib/hecks/adapters/driven.rb +36 -0
- data/lib/hecks/adapters.rb +6 -0
- data/lib/hecks/behaviors/dsl.rb +96 -0
- data/lib/hecks/behaviors/expectations.rb +280 -0
- data/lib/hecks/behaviors/ir.rb +31 -0
- data/lib/hecks/behaviors/rspec.rb +42 -0
- data/lib/hecks/behaviors/runner.rb +96 -0
- data/lib/hecks/behaviors.rb +25 -0
- data/lib/hecks/bluebook/aggregate.rb +108 -0
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +134 -0
- data/lib/hecks/bluebook/assembly/build.rb +48 -0
- data/lib/hecks/bluebook/assembly/contract.rb +112 -0
- data/lib/hecks/bluebook/assembly/contracts.rb +409 -0
- data/lib/hecks/bluebook/assembly/marks.rb +227 -0
- data/lib/hecks/bluebook/assembly/specializer.rb +70 -0
- data/lib/hecks/bluebook/assembly.rb +78 -0
- data/lib/hecks/bluebook/attribute.rb +96 -0
- data/lib/hecks/bluebook/behaviour/aggregate.rb +83 -0
- data/lib/hecks/bluebook/behaviour/attribute.rb +27 -0
- data/lib/hecks/bluebook/behaviour/chapter.rb +72 -0
- data/lib/hecks/bluebook/behaviour/command.rb +116 -0
- data/lib/hecks/bluebook/behaviour/domain_port.rb +25 -0
- data/lib/hecks/bluebook/behaviour/entity.rb +59 -0
- data/lib/hecks/bluebook/behaviour/hexagon.rb +50 -0
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +51 -0
- data/lib/hecks/bluebook/behaviour/policy.rb +52 -0
- data/lib/hecks/bluebook/behaviour/process_manager.rb +38 -0
- data/lib/hecks/bluebook/behaviour/query.rb +10 -0
- data/lib/hecks/bluebook/behaviour/read_model.rb +29 -0
- data/lib/hecks/bluebook/behaviour/traits.rb +81 -0
- data/lib/hecks/bluebook/behaviour/value_object.rb +33 -0
- data/lib/hecks/bluebook/chapter.rb +68 -0
- data/lib/hecks/bluebook/command.rb +124 -0
- data/lib/hecks/bluebook/domain_port.rb +102 -0
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +34 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +906 -0
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +327 -0
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +71 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +1018 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +626 -0
- data/lib/hecks/bluebook/dsl/const_shim.rb +81 -0
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +79 -0
- data/lib/hecks/bluebook/dsl/entity_builder.rb +305 -0
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +366 -0
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +163 -0
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +191 -0
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +44 -0
- data/lib/hecks/bluebook/dsl/malformed.rb +7 -0
- data/lib/hecks/bluebook/dsl/policy_builder.rb +120 -0
- data/lib/hecks/bluebook/dsl/port_builder.rb +30 -0
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +142 -0
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +207 -0
- data/lib/hecks/bluebook/dsl/query_builder.rb +113 -0
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +233 -0
- data/lib/hecks/bluebook/dsl/rule_reference.rb +172 -0
- data/lib/hecks/bluebook/dsl/translation_builder.rb +243 -0
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +178 -0
- data/lib/hecks/bluebook/dsl/word_gate.rb +221 -0
- data/lib/hecks/bluebook/dsl/world_builder.rb +77 -0
- data/lib/hecks/bluebook/dsl.rb +45 -0
- data/lib/hecks/bluebook/entity.rb +103 -0
- data/lib/hecks/bluebook/expression/canonical_form.rb +55 -0
- data/lib/hecks/bluebook/expression/evaluator.rb +261 -0
- data/lib/hecks/bluebook/expression/projection.json +170 -0
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +233 -0
- data/lib/hecks/bluebook/expression/resolver.rb +630 -0
- data/lib/hecks/bluebook/expression.rb +13 -0
- data/lib/hecks/bluebook/hexagon.rb +60 -0
- data/lib/hecks/bluebook/lifecycle.rb +42 -0
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +54 -0
- data/lib/hecks/bluebook/meta_validator/judge.rb +559 -0
- data/lib/hecks/bluebook/meta_validator/plan.rb +332 -0
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +47 -0
- data/lib/hecks/bluebook/meta_validator/readings.rb +350 -0
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +312 -0
- data/lib/hecks/bluebook/meta_validator/shapes.rb +246 -0
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +255 -0
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +138 -0
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +78 -0
- data/lib/hecks/bluebook/meta_validator.rb +452 -0
- data/lib/hecks/bluebook/model_check.rb +318 -0
- data/lib/hecks/bluebook/pattern_subset.rb +120 -0
- data/lib/hecks/bluebook/policy.rb +41 -0
- data/lib/hecks/bluebook/process_manager.rb +86 -0
- data/lib/hecks/bluebook/project_discovery.rb +30 -0
- data/lib/hecks/bluebook/project_loader.rb +40 -0
- data/lib/hecks/bluebook/project_register.rb +82 -0
- data/lib/hecks/bluebook/query.rb +61 -0
- data/lib/hecks/bluebook/read_model.rb +109 -0
- data/lib/hecks/bluebook/reference.rb +74 -0
- data/lib/hecks/bluebook/smoke_test.rb +166 -0
- data/lib/hecks/bluebook/synthesizer.rb +95 -0
- data/lib/hecks/bluebook/translation.rb +92 -0
- data/lib/hecks/bluebook/value_object.rb +58 -0
- data/lib/hecks/bluebook.rb +74 -0
- data/lib/hecks/codemod.rb +342 -0
- data/lib/hecks/construct.rb +71 -0
- data/lib/hecks/deploy/bluebook/deploy.bluebook +219 -0
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +4 -0
- data/lib/hecks/deploy/oidc.json +18 -0
- data/lib/hecks/doc/reference.rb +389 -0
- data/lib/hecks/embryonaut_bluebook.rb +75 -0
- data/lib/hecks/facade/cli_door.rb +116 -0
- data/lib/hecks/facade/cli_runner.rb +190 -0
- data/lib/hecks/facade/command_request.rb +105 -0
- data/lib/hecks/facade/handle.rb +173 -0
- data/lib/hecks/facade/json_door.rb +154 -0
- data/lib/hecks/facade/surface/aggregate_door.rb +185 -0
- data/lib/hecks/facade/surface/chapter.rb +107 -0
- data/lib/hecks/facade/surface.rb +48 -0
- data/lib/hecks/facade.rb +44 -0
- data/lib/hecks/forms/app.rb +300 -0
- data/lib/hecks/forms/command_form_renderer.rb +113 -0
- data/lib/hecks/forms/examples/banking_console.bluebook +3 -0
- data/lib/hecks/forms/field_renderer.rb +170 -0
- data/lib/hecks/forms/field_shape.rb +232 -0
- data/lib/hecks/forms/html.rb +53 -0
- data/lib/hecks/forms/index_renderer.rb +35 -0
- data/lib/hecks/forms/page.rb +157 -0
- data/lib/hecks/forms/params.rb +132 -0
- data/lib/hecks/forms/query_form_renderer.rb +114 -0
- data/lib/hecks/forms/record_renderer.rb +115 -0
- data/lib/hecks/forms/record_table.rb +63 -0
- data/lib/hecks/forms/reference_options.rb +30 -0
- data/lib/hecks/forms/value_object_shape.rb +46 -0
- data/lib/hecks/forms.rb +54 -0
- data/lib/hecks/fqn.rb +94 -0
- data/lib/hecks/framework/bluebook/compliance.bluebook +1 -0
- data/lib/hecks/framework/bluebook/console_settings.bluebook +489 -0
- data/lib/hecks/framework/bluebook/framework.hecksagon +32 -0
- data/lib/hecks/framework/bluebook/governance.bluebook +130 -0
- data/lib/hecks/framework/bluebook/identity.bluebook +90 -0
- data/lib/hecks/framework/oidc.json +39 -0
- data/lib/hecks/framework.rb +90 -0
- data/lib/hecks/freezer.rb +67 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +86 -0
- data/lib/hecks/fuzzing/isolated_boot.rb +92 -0
- data/lib/hecks/fuzzing/properties.rb +1146 -0
- data/lib/hecks/fuzzing/replay.rb +635 -0
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +93 -0
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +54 -0
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +96 -0
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +185 -0
- data/lib/hecks/fuzzing/sequence_generator.rb +130 -0
- data/lib/hecks/fuzzing/value_generator.rb +177 -0
- data/lib/hecks/fuzzing.rb +12 -0
- data/lib/hecks/grammar/evolve.rb +291 -0
- data/lib/hecks/grammar/expression.bluebook +417 -0
- data/lib/hecks/grammar/expression_operators.json +1152 -0
- data/lib/hecks/grammar/grammar.hecksagon +20 -0
- data/lib/hecks/grammar/oidc.json +69 -0
- data/lib/hecks/grammar/translation.bluebook +203 -0
- data/lib/hecks/grammar.rb +178 -0
- data/lib/hecks/ir.rb +126 -0
- data/lib/hecks/language/adapter.bluebook +116 -0
- data/lib/hecks/language/bluebook/aggregate.bluebook +581 -0
- data/lib/hecks/language/bluebook/attaches/paging.bluebook +76 -0
- data/lib/hecks/language/bluebook/bluebook.bluebook +256 -0
- data/lib/hecks/language/bluebook/bluebook.hecksagon +24 -0
- data/lib/hecks/language/bluebook/command.bluebook +437 -0
- data/lib/hecks/language/bluebook/entity.bluebook +316 -0
- data/lib/hecks/language/bluebook/policy.bluebook +172 -0
- data/lib/hecks/language/bluebook/process_manager.bluebook +264 -0
- data/lib/hecks/language/bluebook/projection.bluebook +267 -0
- data/lib/hecks/language/bluebook/query.bluebook +245 -0
- data/lib/hecks/language/bluebook/shape.bluebook +292 -0
- data/lib/hecks/language/bluebook/syntax.bluebook +447 -0
- data/lib/hecks/language/bluebook/vocabulary.bluebook +364 -0
- data/lib/hecks/language/hecksagon/adapter_binding.bluebook +51 -0
- data/lib/hecks/language/hecksagon/domain_port.bluebook +76 -0
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +131 -0
- data/lib/hecks/language/hecksagon/port_operation.bluebook +102 -0
- data/lib/hecks/language/oidc.json +313 -0
- data/lib/hecks/language/port.bluebook +92 -0
- data/lib/hecks/language/translation/translation.bluebook +110 -0
- data/lib/hecks/language/translation/translation_aggregate.bluebook +267 -0
- data/lib/hecks/language/world/wiring.bluebook +62 -0
- data/lib/hecks/language/world/world.bluebook +84 -0
- data/lib/hecks/literal.rb +125 -0
- data/lib/hecks/naming.rb +121 -0
- data/lib/hecks/ports/access_control.port +4 -0
- data/lib/hecks/ports/access_control.rb +62 -0
- data/lib/hecks/ports/agent/answers.rb +104 -0
- data/lib/hecks/ports/agent.port +4 -0
- data/lib/hecks/ports/agent.rb +167 -0
- data/lib/hecks/ports/authentication.port +4 -0
- data/lib/hecks/ports/authentication.rb +50 -0
- data/lib/hecks/ports/authorization.port +4 -0
- data/lib/hecks/ports/authorization.rb +59 -0
- data/lib/hecks/ports/clock.port +4 -0
- data/lib/hecks/ports/clock.rb +62 -0
- data/lib/hecks/ports/extraction.port +4 -0
- data/lib/hecks/ports/extraction.rb +37 -0
- data/lib/hecks/ports/identity_assignment.port +4 -0
- data/lib/hecks/ports/identity_assignment.rb +45 -0
- data/lib/hecks/ports/identity_generation.port +4 -0
- data/lib/hecks/ports/identity_generation.rb +49 -0
- data/lib/hecks/ports/identity_resolution.port +4 -0
- data/lib/hecks/ports/identity_resolution.rb +40 -0
- data/lib/hecks/ports/loading.port +4 -0
- data/lib/hecks/ports/loading.rb +13 -0
- data/lib/hecks/ports/persistence/append_only.rb +102 -0
- data/lib/hecks/ports/persistence/binding_policy.rb +56 -0
- data/lib/hecks/ports/persistence/execution.rb +18 -0
- data/lib/hecks/ports/persistence/lineage.rb +277 -0
- data/lib/hecks/ports/persistence/null_saga_store.rb +25 -0
- data/lib/hecks/ports/persistence/remote_runtime.rb +42 -0
- data/lib/hecks/ports/persistence/repository_factory.rb +30 -0
- data/lib/hecks/ports/persistence.port +4 -0
- data/lib/hecks/ports/persistence.rb +39 -0
- data/lib/hecks/ports/projection.port +4 -0
- data/lib/hecks/ports/projection.rb +76 -0
- data/lib/hecks/ports/query/in_memory.rb +60 -0
- data/lib/hecks/ports/query/ordering.rb +41 -0
- data/lib/hecks/ports/query.rb +36 -0
- data/lib/hecks/ports.rb +27 -0
- data/lib/hecks/projections/diagrams.rb +413 -0
- data/lib/hecks/projections/ir.rb +18 -0
- data/lib/hecks/projections/model/deviations.rb +98 -0
- data/lib/hecks/projections/model.rb +145 -0
- data/lib/hecks/projections/oidc.rb +110 -0
- data/lib/hecks/projections/parser_table.rb +159 -0
- data/lib/hecks/projections/reference.rb +38 -0
- data/lib/hecks/projections/shape.rb +31 -0
- data/lib/hecks/projections/statements.rb +110 -0
- data/lib/hecks/projections/vocabulary.rb +100 -0
- data/lib/hecks/projections.rb +32 -0
- data/lib/hecks/projector/cli_projector.rb +437 -0
- data/lib/hecks/projector/docs_projector.rb +321 -0
- data/lib/hecks/projector/exporter.rb +141 -0
- data/lib/hecks/projector/ir_projector.rb +18 -0
- data/lib/hecks/projector/narrate_projector.rb +243 -0
- data/lib/hecks/projector/target.rb +97 -0
- data/lib/hecks/projector.rb +186 -0
- data/lib/hecks/query_ir.rb +392 -0
- data/lib/hecks/query_specification/common/authorization_spec.rb +9 -0
- data/lib/hecks/query_specification/common/comparators.rb +25 -0
- data/lib/hecks/query_specification/common/comparison.rb +174 -0
- data/lib/hecks/query_specification/common/cursor_spec.rb +9 -0
- data/lib/hecks/query_specification/common/dsl.rb +58 -0
- data/lib/hecks/query_specification/common/inspection_spec.rb +9 -0
- data/lib/hecks/query_specification/common/limit_spec.rb +9 -0
- data/lib/hecks/query_specification/common/null_policy.rb +80 -0
- data/lib/hecks/query_specification/common/null_semantics.rb +11 -0
- data/lib/hecks/query_specification/common/offset_spec.rb +9 -0
- data/lib/hecks/query_specification/common/options.rb +32 -0
- data/lib/hecks/query_specification/common/order_by.rb +9 -0
- data/lib/hecks/query_specification/common/specification.rb +10 -0
- data/lib/hecks/query_specification/common/where_clause.rb +9 -0
- data/lib/hecks/query_specification/field_path.rb +89 -0
- data/lib/hecks/query_specification/hop_path.rb +130 -0
- data/lib/hecks/query_specification/read_model/specification.rb +18 -0
- data/lib/hecks/query_specification.rb +14 -0
- data/lib/hecks/rendering.rb +48 -0
- data/lib/hecks/router/namespace_installer.rb +157 -0
- data/lib/hecks/router.rb +70 -0
- data/lib/hecks/runtime/caller.rb +50 -0
- data/lib/hecks/runtime/capability_graph.rb +44 -0
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +124 -0
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +248 -0
- data/lib/hecks/runtime/command_interpreter.rb +413 -0
- data/lib/hecks/runtime/command_rules/admissibility.rb +307 -0
- data/lib/hecks/runtime/command_rules/arithmetic.rb +254 -0
- data/lib/hecks/runtime/command_rules/authorization.rb +64 -0
- data/lib/hecks/runtime/command_rules/emission.rb +34 -0
- data/lib/hecks/runtime/command_rules/references.rb +181 -0
- data/lib/hecks/runtime/command_rules.rb +28 -0
- data/lib/hecks/runtime/dependency_planning.rb +231 -0
- data/lib/hecks/runtime/dispatcher.rb +286 -0
- data/lib/hecks/runtime/entity_element.rb +253 -0
- data/lib/hecks/runtime/entity_interpreter.rb +228 -0
- data/lib/hecks/runtime/era_check.rb +136 -0
- data/lib/hecks/runtime/era_guard/shape_diff.rb +128 -0
- data/lib/hecks/runtime/era_guard.rb +193 -0
- data/lib/hecks/runtime/era_tamper.rb +61 -0
- data/lib/hecks/runtime/errors.rb +100 -0
- data/lib/hecks/runtime/event.rb +51 -0
- data/lib/hecks/runtime/identity.rb +128 -0
- data/lib/hecks/runtime/instance.rb +120 -0
- data/lib/hecks/runtime/interpreting.rb +69 -0
- data/lib/hecks/runtime/loader.rb +143 -0
- data/lib/hecks/runtime/policy_interpreter.rb +366 -0
- data/lib/hecks/runtime/port_operation_interpreter.rb +210 -0
- data/lib/hecks/runtime/query_interpreter.rb +254 -0
- data/lib/hecks/runtime/reaction_invocation.rb +245 -0
- data/lib/hecks/runtime/read_model_interpreter.rb +266 -0
- data/lib/hecks/runtime/rebuild_sweep.rb +74 -0
- data/lib/hecks/runtime/reference_hop.rb +99 -0
- data/lib/hecks/runtime/refusal_wording.rb +124 -0
- data/lib/hecks/runtime/registry/saga_persistence.rb +70 -0
- data/lib/hecks/runtime/registry/verification.rb +158 -0
- data/lib/hecks/runtime/registry.rb +218 -0
- data/lib/hecks/runtime/remote_dispatcher.rb +143 -0
- data/lib/hecks/runtime/routing.rb +96 -0
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +97 -0
- data/lib/hecks/runtime/saga_interpreter.rb +301 -0
- data/lib/hecks/runtime/storage_shape.rb +120 -0
- data/lib/hecks/runtime/tenant_check.rb +84 -0
- data/lib/hecks/runtime/tenant_scope.rb +55 -0
- data/lib/hecks/runtime/value/admission.rb +112 -0
- data/lib/hecks/runtime/value/coercion.rb +500 -0
- data/lib/hecks/runtime/value/invariant_violation.rb +5 -0
- data/lib/hecks/runtime/value.rb +125 -0
- data/lib/hecks/runtime.rb +95 -0
- data/lib/hecks/translation/audit/approval_digest.rb +31 -0
- data/lib/hecks/translation/audit/layer_one.rb +37 -0
- data/lib/hecks/translation/audit/layer_two.rb +77 -0
- data/lib/hecks/translation/audit/unfed_report.rb +42 -0
- data/lib/hecks/translation/audit.rb +70 -0
- data/lib/hecks/translation/reattest.rb +72 -0
- data/lib/hecks/translation/rule_compiler.rb +120 -0
- data/lib/hecks/translation/scaffold/differ.rb +183 -0
- data/lib/hecks/translation/scaffold/renderer.rb +41 -0
- data/lib/hecks/translation/scaffold/writer.rb +28 -0
- data/lib/hecks/translation/scaffold.rb +30 -0
- data/lib/hecks/translation.rb +11 -0
- data/lib/hecks/version.rb +16 -0
- data/lib/hecks/vocabulary.rb +213 -0
- data/lib/hecks.rb +118 -6
- metadata +401 -222
- data/bin/hecks +0 -7
- data/bin/hecks-package +0 -65
- data/bin/hecks_console +0 -12
- data/bin/hecks_serverless +0 -6
- data/lib/cli/build.rb +0 -14
- data/lib/cli/command_runner.rb +0 -28
- data/lib/cli/console.rb +0 -10
- data/lib/cli/generate.rb +0 -37
- data/lib/cli/hecks-cli.rb +0 -27
- data/lib/cli/test.rb +0 -57
- data/lib/console/commands.rb +0 -8
- data/lib/console/hecks-console.rb +0 -1
- data/lib/packager/README.md +0 -0
- data/lib/packager/app_runner.rb +0 -21
- data/lib/packager/args.rb +0 -26
- data/lib/packager/compatibility/fixnum.rb +0 -6
- data/lib/packager/hecks.rb +0 -39
- data/lib/packager/query_runner.rb +0 -21
- data/lib/packager/resources/Dockerfile +0 -11
- data/lib/packager/resources/app_binary +0 -7
- data/lib/packager/resources/bundle_config +0 -3
- data/lib/packager/resources/traveling-ruby-20150715-2.2.2-linux-x86_64.tar.gz +0 -0
- data/lib/packager/resources/traveling-ruby-20150715-2.2.2-osx.tar.gz +0 -0
- data/lib/serverless/Domain +0 -32
- data/lib/serverless/cli.rb +0 -75
- data/lib/serverless/resources/command_name.js +0 -5
- data/lib/serverless/resources/environment.js +0 -7
- data/lib/serverless/resources/handler.js.tt +0 -28
- data/lib/serverless/resources/run_binary.js +0 -22
- data/lib/serverless/resources/serverless.yml +0 -20
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Runtime
|
|
3
|
+
# Every DomainRefusal wording that is not already data — `given`/
|
|
4
|
+
# `ensures`/a declared `invariant` already carry their own description,
|
|
5
|
+
# read at dispatch time off the command or value object that declared
|
|
6
|
+
# them. These are different in kind: LANGUAGE-LEVEL refusals, the same
|
|
7
|
+
# wording for every domain, not authored per-bluebook.
|
|
8
|
+
#
|
|
9
|
+
# Declared the same way in Vocabulary::RefusalTemplate
|
|
10
|
+
# (language/bluebook/vocabulary.bluebook) — spec/refusal_wording_
|
|
11
|
+
# conformance_spec holds this table equal to the language, both
|
|
12
|
+
# directions. Hand-typed rather than read live off the meta-domain at
|
|
13
|
+
# every dispatch, the same reason Expression::Evaluator::
|
|
14
|
+
# SIGN_TEST_OPERATORS is hand-typed beside Vocabulary::SignTest.
|
|
15
|
+
module RefusalWording
|
|
16
|
+
TEMPLATES = {
|
|
17
|
+
%w[NotFound creating_no_identity] =>
|
|
18
|
+
"{command} creates a {aggregate} — pass {identity}:",
|
|
19
|
+
%w[AlreadyExists creating_duplicate] =>
|
|
20
|
+
"{command} creates a {aggregate} that already exists — {identity} {offered}",
|
|
21
|
+
%w[AlreadyExists entity_duplicate] =>
|
|
22
|
+
"a {entity} already exists on {aggregate} — {identity} {offered}",
|
|
23
|
+
%w[NotFound acting_no_identity] =>
|
|
24
|
+
"{command} acts on an existing {aggregate} — pass {identity}:",
|
|
25
|
+
%w[NotFound record_missing] =>
|
|
26
|
+
"no {aggregate} with {identity} {offered}",
|
|
27
|
+
%w[NotFound entity_parent_no_identity] =>
|
|
28
|
+
"{command} acts on a {aggregate}'s {entity} — pass {identity}:",
|
|
29
|
+
%w[UnknownVerb entity_unknown] =>
|
|
30
|
+
"{aggregate} has no entity {entity}",
|
|
31
|
+
%w[NotFound entity_element_no_identity] =>
|
|
32
|
+
"{command} acts on one {entity} — pass {identity}:",
|
|
33
|
+
%w[NotFound entity_element_missing] =>
|
|
34
|
+
"no {entity} with {identity} {wants} on {aggregate} {parent_id}",
|
|
35
|
+
%w[NotFound reference_target_missing] =>
|
|
36
|
+
"no {target} with {heads} {key}",
|
|
37
|
+
%w[NotFound read_model_reference_missing] =>
|
|
38
|
+
"no {aggregate} with reference {offered}",
|
|
39
|
+
%w[TypeMismatch read_model_object_reference] =>
|
|
40
|
+
"{query} refused — a reference is an id, and {field} arrived as an object",
|
|
41
|
+
%w[UnknownVerb no_query] =>
|
|
42
|
+
"{aggregate} has no query {query}",
|
|
43
|
+
%w[UnknownVerb entity_query_missing] =>
|
|
44
|
+
"{entity} has no query {query}",
|
|
45
|
+
%w[UnknownVerb entity_holds_no_list] =>
|
|
46
|
+
"{aggregate} holds no list of {entity}",
|
|
47
|
+
%w[UnknownVerb entity_no_command] =>
|
|
48
|
+
"{entity} has no command {command}",
|
|
49
|
+
%w[UnknownVerb aggregate_no_command] =>
|
|
50
|
+
"{aggregate} has no command {command}",
|
|
51
|
+
%w[UnknownVerb port_no_operation] =>
|
|
52
|
+
"{port} has no operation {operation}",
|
|
53
|
+
%w[UnknownVerb no_domain] =>
|
|
54
|
+
"no domain {domain} loaded (verb {verb})",
|
|
55
|
+
%w[UnknownVerb no_read_model] =>
|
|
56
|
+
"{domain} has no read model {query}",
|
|
57
|
+
%w[UnknownVerb not_fully_qualified] =>
|
|
58
|
+
"{verb} is not a fully-qualified verb (Domain::Aggregate.Command)",
|
|
59
|
+
%w[UnknownVerb no_aggregate] =>
|
|
60
|
+
"{domain} has no aggregate {aggregate}",
|
|
61
|
+
%w[LifecycleRefused transition_blocked] =>
|
|
62
|
+
"{command} refused — {field} is {current}, and {command} moves it only from {allowed}",
|
|
63
|
+
%w[TypeMismatch value_object_shape] =>
|
|
64
|
+
"{name} is a {type} — pass its fields as an object, not {offered}",
|
|
65
|
+
%w[TypeMismatch reference_as_object] =>
|
|
66
|
+
"{command} refused — a reference is an id, and {attribute} arrived as an object{known_by}",
|
|
67
|
+
%w[TypeMismatch multi_field_scalar] =>
|
|
68
|
+
"{type} has multiple fields and cannot stand in for a scalar",
|
|
69
|
+
%w[TypeMismatch composite_identity] =>
|
|
70
|
+
"{type} is a composite identity — an identity must have exactly one field",
|
|
71
|
+
%w[TypeMismatch numeric_field] =>
|
|
72
|
+
"{type}.{field} expects {expected}, got {offered}",
|
|
73
|
+
%w[TypeMismatch pattern_mismatch] =>
|
|
74
|
+
"{type}.{field} must match {pattern}, got {offered}",
|
|
75
|
+
%w[TypeMismatch arithmetic_amount] =>
|
|
76
|
+
"{op} of {target} needs an Integer, got {offered}",
|
|
77
|
+
%w[TypeMismatch arithmetic_current] =>
|
|
78
|
+
"{op} of {target} needs an Integer {target}, got {offered}",
|
|
79
|
+
%w[TypeMismatch arithmetic_shared_field] =>
|
|
80
|
+
"{op} of {target} needs a value object with one shared Integer field",
|
|
81
|
+
%w[UnknownArgument unknown_args] =>
|
|
82
|
+
"{command} does not declare {unknown} — it takes {declared}",
|
|
83
|
+
%w[AbsentArgument absent_args] =>
|
|
84
|
+
"{command} was not given {absent} — it takes {declared}",
|
|
85
|
+
%w[InvariantViolation closed_set_member] =>
|
|
86
|
+
"{type} admits {admitted} — got {offered}",
|
|
87
|
+
%w[InvariantViolation value_object_invariant] =>
|
|
88
|
+
"{name} invariant violated — {description} (given {offered})",
|
|
89
|
+
%w[InvariantViolation admits_declared_set] =>
|
|
90
|
+
"{name} admits {admits} — {admitted} — got {offered}",
|
|
91
|
+
%w[InvariantViolation undeclared_set] =>
|
|
92
|
+
"{name} admits {admits}, which this chapter does not declare — a closed set is named " \
|
|
93
|
+
"Aggregate::SetName, and it must be one the bluebook actually holds",
|
|
94
|
+
%w[Unauthorized tenant_required] =>
|
|
95
|
+
"{query} declares authorize with tenant: {field} — pass {field}: to name which " \
|
|
96
|
+
"{field} this ask is scoped to",
|
|
97
|
+
%w[Unauthorized role_mismatch] =>
|
|
98
|
+
"{command} refused — role: {role}, and the caller stated {caller_role}",
|
|
99
|
+
%w[AttributeAbsent absent_read] =>
|
|
100
|
+
"{aggregate} {field} is absent on this record — declared, not optional, and " \
|
|
101
|
+
"added since it was written. Backfill it in a translation (backfill :{field}, " \
|
|
102
|
+
"default: ...), or declare it optional: true",
|
|
103
|
+
%w[ProjectionAbsent absent_read] =>
|
|
104
|
+
"{aggregate} {field} is not yet projected on this record — declared via " \
|
|
105
|
+
"projects :{field}, but no rebuild sweep has populated it. Run the sweep, " \
|
|
106
|
+
"or read {reference}.{remote_field} directly if this rule cannot wait"
|
|
107
|
+
}.freeze
|
|
108
|
+
|
|
109
|
+
module_function
|
|
110
|
+
|
|
111
|
+
# Plain text substitution, never expression syntax — a template is
|
|
112
|
+
# read, not evaluated. `render` computes the placeholder VALUES via
|
|
113
|
+
# whatever the call site already had (a joined list, a rendered
|
|
114
|
+
# identity reading, …) and this only replaces the markers.
|
|
115
|
+
def render(refusal, site, **values)
|
|
116
|
+
template = TEMPLATES.fetch([refusal, site]) do
|
|
117
|
+
raise KeyError, "no refusal template for #{refusal}/#{site} — declare it in " \
|
|
118
|
+
"Vocabulary::RefusalTemplate first"
|
|
119
|
+
end
|
|
120
|
+
values.reduce(template) { |text, (key, value)| text.gsub("{#{key}}", value.to_s) }
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Runtime
|
|
3
|
+
class Registry
|
|
4
|
+
# SAGA PERSISTENCE — no new DSL verb, no new world setting.
|
|
5
|
+
# Whatever adapter a domain's own aggregates already use for
|
|
6
|
+
# durability, its sagas use the same one, automatically: three
|
|
7
|
+
# OPTIONAL adapter methods (`save_saga`/`delete_saga`/`each_saga`),
|
|
8
|
+
# `respond_to?`-checked the same way `Ports::Persistence::AppendOnly`
|
|
9
|
+
# already treats an adapter's own optional methods
|
|
10
|
+
# (`find`/`all`/`count`/`reset!`/`events`/`record_event`).
|
|
11
|
+
module SagaPersistence
|
|
12
|
+
# Resolved and memoized per domain — the FIRST real aggregate
|
|
13
|
+
# declared in the domain's own bluebook is the resolution
|
|
14
|
+
# anchor. `Ports::Persistence::BindingPolicy.resolve` for that
|
|
15
|
+
# aggregate already falls back to a domain-level default bind
|
|
16
|
+
# (§0's `Hecksagon#bind_for`) before falling back to Memory, so
|
|
17
|
+
# this needs no separate "was a default declared" branch of its
|
|
18
|
+
# own — whatever adapter that anchor resolves to already IS the
|
|
19
|
+
# domain's own default in the normal case (every aggregate
|
|
20
|
+
# shares one adapter), and is the honest, documented fallback
|
|
21
|
+
# for the rarer domain genuinely split across more than one
|
|
22
|
+
# local adapter with no default declared.
|
|
23
|
+
def saga_persistence(domain)
|
|
24
|
+
(@saga_persistence ||= {})[domain.to_s] ||= resolve_saga_persistence(domain.to_s)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# WALKS EVERY LOADED DOMAIN, repopulating `saga_instances` from
|
|
28
|
+
# whatever `saga_persistence(domain)` resolves to — a real store
|
|
29
|
+
# for a domain whose adapter answers the capability, `each_saga`
|
|
30
|
+
# yielding real rows; `NULL_SAGA_STORE`'s own `each_saga` for
|
|
31
|
+
# everything else, which never yields at all, so this needs no
|
|
32
|
+
# `respond_to?` branch of its own — the SAME reason every other
|
|
33
|
+
# call site in this capability never needs one. Called once at
|
|
34
|
+
# boot (`Loader.boot`, between `verify!` and dispatcher
|
|
35
|
+
# construction) — a process that's been running has no reason to
|
|
36
|
+
# re-walk its own already-current `saga_instances`.
|
|
37
|
+
def rehydrate_sagas!
|
|
38
|
+
@hecksagons.each_key do |domain|
|
|
39
|
+
saga_persistence(domain).each_saga do |process_manager, correlation, state, memory|
|
|
40
|
+
@saga_instances[process_manager][correlation] = { state: state, memory: memory }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
self
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def resolve_saga_persistence(domain)
|
|
49
|
+
anchor = hecksagon(domain) && bluebook(domain)&.aggregates&.first
|
|
50
|
+
return Ports::Persistence::NULL_SAGA_STORE unless anchor
|
|
51
|
+
|
|
52
|
+
bind = Ports::Persistence::BindingPolicy.resolve(self, domain, anchor)
|
|
53
|
+
return Ports::Persistence::NULL_SAGA_STORE if adapter_class(bind.adapter) <= Ports::Persistence::RemoteRuntime
|
|
54
|
+
|
|
55
|
+
adapter = repository(domain, anchor).adapter
|
|
56
|
+
adapter.respond_to?(:save_saga) ? adapter : Ports::Persistence::NULL_SAGA_STORE
|
|
57
|
+
rescue WiringError
|
|
58
|
+
# A domain not cleanly wired for its own anchor aggregate's
|
|
59
|
+
# persistence (a test fixture binding only the aggregates it
|
|
60
|
+
# exercises, say) degrades saga persistence to the same no-op
|
|
61
|
+
# default an unbound domain gets — never raises out of a
|
|
62
|
+
# dispatch that would otherwise have succeeded. Matches this
|
|
63
|
+
# capability's own framing throughout: automatic wherever it's
|
|
64
|
+
# cleanly supported, silently absent wherever it isn't.
|
|
65
|
+
Ports::Persistence::NULL_SAGA_STORE
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
require_relative "../../bluebook/hexagon"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Runtime
|
|
5
|
+
class Registry
|
|
6
|
+
# The wiring gate: every bind names a declared aggregate, every
|
|
7
|
+
# adapter satisfies the verb its port declares, every world setting is
|
|
8
|
+
# a field the adapter admits, and the default adapter is usable at
|
|
9
|
+
# all. Included into Registry — `verify!` is what a boot calls after
|
|
10
|
+
# loading, and the smaller checks are also called piecemeal by the
|
|
11
|
+
# repository factory.
|
|
12
|
+
module Verification
|
|
13
|
+
def verify!
|
|
14
|
+
verify_default_adapter!
|
|
15
|
+
|
|
16
|
+
@hecksagons.each_value do |hexagon|
|
|
17
|
+
refuse_ungoverned_roles!(hexagon)
|
|
18
|
+
|
|
19
|
+
hexagon.binds.each do |bind|
|
|
20
|
+
# A domain-level default (§0) — `persisted_by "Heki"` bare,
|
|
21
|
+
# applying to whichever aggregates don't override it — names
|
|
22
|
+
# no aggregate of its own, so there's nothing to look up in
|
|
23
|
+
# the bluebook for THIS row specifically. Still validate its
|
|
24
|
+
# own adapter/verb shape (the same reason
|
|
25
|
+
# `verify_default_adapter!` checks the framework-wide
|
|
26
|
+
# default the same way, aggregate-less). Coverage of real
|
|
27
|
+
# aggregates that only resolve THROUGH this default comes
|
|
28
|
+
# from their own dispatch-time `BindingPolicy.resolve` —
|
|
29
|
+
# deliberately not required to be exhaustive here, the same
|
|
30
|
+
# leniency this method already extended to any aggregate
|
|
31
|
+
# left out of an explicit bind list entirely (real test
|
|
32
|
+
# fixtures bind only the aggregates they exercise).
|
|
33
|
+
if bind.aggregate.nil?
|
|
34
|
+
check_verb(bind)
|
|
35
|
+
next
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
aggregate = bluebook(hexagon.domain)&.aggregate(bind.aggregate_name)
|
|
39
|
+
raise WiringError, "#{bind.aggregate} is bound but not declared in the bluebook" unless aggregate
|
|
40
|
+
|
|
41
|
+
check_verb(bind)
|
|
42
|
+
|
|
43
|
+
repository(hexagon.domain, aggregate)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
self
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def verify_default_adapter!
|
|
50
|
+
name = Ports::Persistence::DEFAULT_ADAPTER
|
|
51
|
+
|
|
52
|
+
check_verb(
|
|
53
|
+
Bluebook::Bind.new(
|
|
54
|
+
aggregate: "(default)",
|
|
55
|
+
verb: Ports::Persistence::VERB,
|
|
56
|
+
adapter: name
|
|
57
|
+
)
|
|
58
|
+
)
|
|
59
|
+
adapter_class(name)
|
|
60
|
+
self
|
|
61
|
+
rescue WiringError => error
|
|
62
|
+
raise WiringError,
|
|
63
|
+
"the default persistence adapter (#{name}) is not usable, so an " \
|
|
64
|
+
"aggregate with no bind could not be given one: #{error.message}"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def check_verb(bind)
|
|
68
|
+
port = port_for(bind)
|
|
69
|
+
return if port.verb.to_s == bind.verb.to_s
|
|
70
|
+
|
|
71
|
+
raise WiringError,
|
|
72
|
+
"#{bind.adapter} implements the #{port.name} port (verb #{port.verb}) " \
|
|
73
|
+
"and cannot satisfy #{bind.verb}"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def check_settings(bind, settings)
|
|
77
|
+
adapter = @adapters[bind.adapter]
|
|
78
|
+
return unless adapter
|
|
79
|
+
|
|
80
|
+
declared = settings.keys - [:adapter]
|
|
81
|
+
unknown = declared.reject { |field| adapter.declares?(field) }
|
|
82
|
+
return if unknown.empty?
|
|
83
|
+
|
|
84
|
+
raise WiringError,
|
|
85
|
+
"#{bind.adapter} does not declare #{unknown.map(&:inspect).join(', ')} — " \
|
|
86
|
+
"it declares #{adapter.all_fields.map(&:inspect).join(', ')}. " \
|
|
87
|
+
"Add the field to the adapter, or remove it from the world."
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def port_for(bind)
|
|
91
|
+
adapter = @adapters[bind.adapter]
|
|
92
|
+
raise WiringError, "unknown adapter #{bind.adapter.inspect}" unless adapter
|
|
93
|
+
|
|
94
|
+
@ports[adapter.port] ||
|
|
95
|
+
raise(WiringError, "adapter #{bind.adapter} declares unknown port #{adapter.port.inspect}")
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def adapter_class(name)
|
|
99
|
+
Adapters.const_get(name)
|
|
100
|
+
rescue NameError
|
|
101
|
+
raise WiringError, "no Ruby adapter implementation for #{name.inspect} " \
|
|
102
|
+
"(expected Hecks::Adapters::#{name})"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
private
|
|
106
|
+
|
|
107
|
+
# `role` IS REAL ACCESS CONTROL ONLY WHEN GOVERNANCE CAN CHECK IT
|
|
108
|
+
# AGAINST SOMETHING — a command that declares a role but whose
|
|
109
|
+
# domain never attaches Governance would leave that role forever
|
|
110
|
+
# unchecked, exactly the defect ADR 0025 §9 names ("role gates
|
|
111
|
+
# access control by exact string equality ... Governance ...
|
|
112
|
+
# connected to none of it"). Checked here, at `verify!` — RECOVERED
|
|
113
|
+
# and MOVED, not new: this used to run per-block, at HECKSAGON
|
|
114
|
+
# build time (Bluebook::DSL::HecksagonBuilder#build), which broke
|
|
115
|
+
# the moment a domain could be split across multiple hecksagon
|
|
116
|
+
# blocks (base + an `environments/<name>.hecksagon` overlay,
|
|
117
|
+
# Runtime::Loader.boot's `environment:` — see its own comment for
|
|
118
|
+
# the recovery provenance): every block but the one declaring
|
|
119
|
+
# `uses_framework "Governance"` would be refused there, even
|
|
120
|
+
# though `Registry#add_hecksagon` merges every block for a domain
|
|
121
|
+
# into ONE Hecksagon before anything ever dispatches against it.
|
|
122
|
+
# Checking the MERGED result once, here, after every file for
|
|
123
|
+
# this domain has loaded, is both more permissive (no need to
|
|
124
|
+
# repeat `uses_framework` in every file) and strictly more
|
|
125
|
+
# correct (a check against an incomplete, not-yet-merged
|
|
126
|
+
# hecksagon can never see the real final shape).
|
|
127
|
+
#
|
|
128
|
+
# GOVERNANCE ITSELF IS EXEMPT — it cannot `uses_framework` its own
|
|
129
|
+
# aggregates, and it IS the source of truth a role check runs
|
|
130
|
+
# against, the same self-reference `CommandRules::Authorization
|
|
131
|
+
# #governance_attached?` grants it at dispatch time.
|
|
132
|
+
def refuse_ungoverned_roles!(hexagon)
|
|
133
|
+
return if hexagon.domain == "Governance"
|
|
134
|
+
return if hexagon.framework_members.include?("Governance")
|
|
135
|
+
|
|
136
|
+
bluebook_ir = bluebook(hexagon.domain)
|
|
137
|
+
return unless bluebook_ir
|
|
138
|
+
|
|
139
|
+
offender = commands_in(bluebook_ir).find { |command| !command.role.to_s.empty? }
|
|
140
|
+
return unless offender
|
|
141
|
+
|
|
142
|
+
raise WiringError,
|
|
143
|
+
"#{offender.hecks_fqn} declares role #{offender.role.inspect}, but " \
|
|
144
|
+
"#{hexagon.domain}'s hecksagon never uses_framework \"Governance\" — role is only " \
|
|
145
|
+
"real access control once Governance is attached to check it against; without that " \
|
|
146
|
+
"it is silent decoration, the exact defect this refusal exists to catch"
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Every command this domain declares, an aggregate's own AND every
|
|
150
|
+
# entity nested inside one — the same reach `refuse_role_mismatch`
|
|
151
|
+
# itself needs at dispatch time, just walked ahead of time here.
|
|
152
|
+
def commands_in(bluebook_ir)
|
|
153
|
+
bluebook_ir.aggregates.flat_map { |aggregate| aggregate.commands + aggregate.entities.flat_map(&:commands) }
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
require_relative "registry/verification"
|
|
2
|
+
require_relative "registry/saga_persistence"
|
|
3
|
+
|
|
4
|
+
module Hecks
|
|
5
|
+
module Runtime
|
|
6
|
+
class WiringError < StandardError; end
|
|
7
|
+
|
|
8
|
+
# The collections a boot gathers — bluebooks, hexagons, ports, adapters,
|
|
9
|
+
# worlds, the logs — and how a repository is resolved from them. The
|
|
10
|
+
# wiring gate lives in registry/verification.rb, saga persistence
|
|
11
|
+
# resolution in registry/saga_persistence.rb.
|
|
12
|
+
class Registry
|
|
13
|
+
include Verification
|
|
14
|
+
include SagaPersistence
|
|
15
|
+
|
|
16
|
+
attr_reader :root, :bluebooks, :hecksagons, :ports, :adapters, :worlds, :event_log,
|
|
17
|
+
:reaction_log, :saga_log, :saga_instances, :translations, :saga_mutex,
|
|
18
|
+
:saga_dispatch_log, :policy_dispatch_log
|
|
19
|
+
|
|
20
|
+
def initialize(root: nil)
|
|
21
|
+
@root = root
|
|
22
|
+
@bluebooks = {}
|
|
23
|
+
@hecksagons = {}
|
|
24
|
+
@ports = {}
|
|
25
|
+
@adapters = {}
|
|
26
|
+
@worlds = {}
|
|
27
|
+
@translations = []
|
|
28
|
+
@event_log = []
|
|
29
|
+
@reaction_log = []
|
|
30
|
+
@saga_log = []
|
|
31
|
+
# ADDITIVE, RUBY-ONLY — never merged into saga_log/reaction_log.
|
|
32
|
+
# rust/src/kernel/orchestrate.rs ports THOSE two arrays' exact
|
|
33
|
+
# shape byte-for-byte (spec/rust_conformance_spec.rb's own
|
|
34
|
+
# equality check) — a landmine found by reading that spec before
|
|
35
|
+
# touching anything, not by hitting it. These carry the raw
|
|
36
|
+
# inputs a dispatch's own argument binding was resolved from
|
|
37
|
+
# (SagaInterpreter#deliver_saga_dispatch / PolicyInterpreter#
|
|
38
|
+
# trigger_args), for Properties.dispatch_binding_fidelity's own
|
|
39
|
+
# independent re-derivation — a fact neither existing log
|
|
40
|
+
# records at all, so there is nothing here for Rust to have
|
|
41
|
+
# matched or drifted from.
|
|
42
|
+
@saga_dispatch_log = []
|
|
43
|
+
@policy_dispatch_log = []
|
|
44
|
+
@saga_instances = Hash.new { |h, k| h[k] = {} }
|
|
45
|
+
# GUARDS `saga_instances`' OWN mutation+checkpoint sequence
|
|
46
|
+
# (`SagaInterpreter`'s 4 write points, §7) — the same shape of
|
|
47
|
+
# hazard this codebase's own prior audit already flagged for
|
|
48
|
+
# `@reaction_depth`, a thread-shared dispatcher ivar with no
|
|
49
|
+
# lock, made meaningfully easier to hit once a persistence write
|
|
50
|
+
# sits in the same critical section. Held across the in-memory
|
|
51
|
+
# mutation AND the checkpoint write together, never across a
|
|
52
|
+
# saga's own dispatch cascade — see `SagaInterpreter#advance_saga`'s
|
|
53
|
+
# own comment for why that distinction matters (non-reentrant
|
|
54
|
+
# Mutex, recursive re-entry is real).
|
|
55
|
+
@saga_mutex = Mutex.new
|
|
56
|
+
@repositories = {}
|
|
57
|
+
@projection_repositories = {}
|
|
58
|
+
@bluebook_builders = {}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# THE BUILDER STAYS OPEN FOR THE LIFE OF THIS REGISTRY, keyed by chapter
|
|
62
|
+
# name — see the comment on `BluebookBuilder.build`. A chapter split across
|
|
63
|
+
# several files (`language/bluebook/*.bluebook`, all `Hecks.bluebook "Bluebook"`)
|
|
64
|
+
# needs its declarations to accumulate into ONE builder rather than each
|
|
65
|
+
# file minting its own and silently discarding the one before.
|
|
66
|
+
def bluebook_builder(name)
|
|
67
|
+
@bluebook_builders[name.to_s] ||= yield
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def add_bluebook(item) = @bluebooks[item.name] = item
|
|
71
|
+
|
|
72
|
+
# MERGED, NOT REPLACED — RECOVERED, not new (see Runtime::Loader
|
|
73
|
+
# .boot's own comment for the provenance). A domain's hecksagon can
|
|
74
|
+
# now load in more than one block for the same domain (base file
|
|
75
|
+
# plus an `environments/<name>.hecksagon` overlay), and the second
|
|
76
|
+
# block should ADD to what the first declared, not silently
|
|
77
|
+
# discard it.
|
|
78
|
+
def add_hecksagon(item)
|
|
79
|
+
existing = @hecksagons[item.domain]
|
|
80
|
+
@hecksagons[item.domain] = existing ? merge_hecksagons(existing, item) : item
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def add_port(item) = @ports[item.name] = item
|
|
84
|
+
def add_adapter(item) = @adapters[item.name] = item
|
|
85
|
+
|
|
86
|
+
# MERGED, NOT REPLACED — the same generalization for `World` that
|
|
87
|
+
# `add_hecksagon` above recovers for `Hecksagon`: an
|
|
88
|
+
# `environments/<name>.world` overlay (or a host-owned tenancy
|
|
89
|
+
# overlay world, same mechanism) can now add or override settings
|
|
90
|
+
# for a domain a base `.world` file already declared, without
|
|
91
|
+
# restating everything the base file said. Settings merge shallow,
|
|
92
|
+
# keyed exactly the way WorldBuilder already stores them (both the
|
|
93
|
+
# bare verb key and the `"verb:adapter"` qualified key point at the
|
|
94
|
+
# same resolved hash) — an overlay's key wins over the base's same
|
|
95
|
+
# key; a key only the base declares survives untouched.
|
|
96
|
+
def add_world(item)
|
|
97
|
+
existing = @worlds[item.domain]
|
|
98
|
+
@worlds[item.domain] = existing ? merge_worlds(existing, item) : item
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def add_translation(item) = @translations << item
|
|
102
|
+
|
|
103
|
+
# {domain name => era ordinal} as resolved by the boot-time era
|
|
104
|
+
# gate. A lineage adapter writes into ITS OWN era's partition —
|
|
105
|
+
# which, for an old checkout booting a held-but-superseded shape,
|
|
106
|
+
# is not the newest one.
|
|
107
|
+
def resolved_eras = @resolved_eras ||= {}
|
|
108
|
+
|
|
109
|
+
def bluebook(name) = @bluebooks[name.to_s]
|
|
110
|
+
def hecksagon(name) = @hecksagons[name.to_s]
|
|
111
|
+
def world(name) = @worlds[name.to_s]
|
|
112
|
+
|
|
113
|
+
def verbs = @bluebooks.values.flat_map(&:verbs).sort
|
|
114
|
+
|
|
115
|
+
def repository(domain, aggregate)
|
|
116
|
+
@repositories[[domain.to_s, aggregate.hecks_name]] ||= Ports::Persistence.repository(self, domain, aggregate)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# EVERYTHING A DISPATCH WROTE, CLEARED; NOTHING A BOOT DECLARED,
|
|
120
|
+
# TOUCHED. Bluebooks, hecksagons, ports, adapters, worlds and the
|
|
121
|
+
# resolved eras are what loading the files produced and stay as
|
|
122
|
+
# they are; the logs, the saga instances and the repositories are
|
|
123
|
+
# what running commands against them produced, and go back to
|
|
124
|
+
# exactly what a fresh boot of the same files hands out. Dropping
|
|
125
|
+
# the repositories (rather than emptying each) is deliberate: a
|
|
126
|
+
# fresh boot's own repositories are new adapter instances too, so
|
|
127
|
+
# a Memory adapter starts empty and a durable one sees whatever
|
|
128
|
+
# it persisted — the same reading either way. Sagas rehydrate off
|
|
129
|
+
# that store again, the way `Loader.boot_files` does after
|
|
130
|
+
# `verify!`.
|
|
131
|
+
#
|
|
132
|
+
# What this is for: a test runner that used to boot a runtime per
|
|
133
|
+
# test to get isolation (`Behaviors::Expectations.run_one`) — ~2s a
|
|
134
|
+
# boot, 76 chess behaviours = two and a half minutes of booting the
|
|
135
|
+
# same two files — can now boot once and reset between tests.
|
|
136
|
+
def reset_runtime_state!
|
|
137
|
+
@event_log.clear
|
|
138
|
+
@reaction_log.clear
|
|
139
|
+
@saga_log.clear
|
|
140
|
+
@saga_dispatch_log.clear
|
|
141
|
+
@policy_dispatch_log.clear
|
|
142
|
+
@saga_instances.clear
|
|
143
|
+
@repositories = {}
|
|
144
|
+
@projection_repositories = {}
|
|
145
|
+
rehydrate_sagas!
|
|
146
|
+
self
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def capability_graph
|
|
150
|
+
@capability_graph ||= CapabilityGraph.new(self)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def read_repository(domain, aggregate)
|
|
154
|
+
key = [domain.to_s, aggregate.hecks_name]
|
|
155
|
+
binding = Ports::Projection.binds_for(self, domain, aggregate).first
|
|
156
|
+
return repository(domain, aggregate) unless binding
|
|
157
|
+
|
|
158
|
+
projection = (@projection_repositories[key] ||= begin
|
|
159
|
+
projection = Ports::Persistence::RepositoryFactory.build(self, domain, aggregate, binding,
|
|
160
|
+
recover: true, settings_verb: Ports::Projection::VERB)
|
|
161
|
+
projection
|
|
162
|
+
end)
|
|
163
|
+
authoritative = repository(domain, aggregate)
|
|
164
|
+
projection_current?(projection, authoritative) ? projection : authoritative
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def projection_current?(projection, authoritative)
|
|
168
|
+
projected_entries = projection.entries
|
|
169
|
+
source_entries = authoritative.entries
|
|
170
|
+
return false unless projected_entries.length == source_entries.length
|
|
171
|
+
return false unless projected_entries.zip(source_entries).all? do |projected, source|
|
|
172
|
+
projected.operation == source.operation && projected.id == source.id && projected.state == source.state
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
projected_rows = projection.all.map(&:to_h).sort_by { |row| row.fetch(:id).to_s }
|
|
176
|
+
source_rows = authoritative.all.map(&:to_h).sort_by { |row| row.fetch(:id).to_s }
|
|
177
|
+
projected_rows == source_rows
|
|
178
|
+
rescue StandardError
|
|
179
|
+
false
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# RECOVERED — see `add_hecksagon`'s own comment for provenance.
|
|
183
|
+
# Concatenates every list-shaped fact; `binds` in particular is
|
|
184
|
+
# additive because an overlay REBINDING an aggregate (a new
|
|
185
|
+
# `persisted_by` for the same aggregate/verb) is meant to shadow
|
|
186
|
+
# the base's own bind at resolution time, not erase it outright —
|
|
187
|
+
# `Ports::Persistence::BindingPolicy.resolve`'s own "exactly one
|
|
188
|
+
# authoritative bind" check is what actually catches a genuine
|
|
189
|
+
# double-bind; this merge only concatenates, it does not itself
|
|
190
|
+
# decide which of two binds for the same aggregate wins.
|
|
191
|
+
def merge_hecksagons(a, b)
|
|
192
|
+
Bluebook::Hecksagon.new(
|
|
193
|
+
domain: a.domain,
|
|
194
|
+
binds: a.binds + b.binds,
|
|
195
|
+
subscriptions: a.subscriptions + b.subscriptions,
|
|
196
|
+
framework_members: a.framework_members + b.framework_members,
|
|
197
|
+
vendored_bluebooks: a.vendored_bluebooks + b.vendored_bluebooks
|
|
198
|
+
)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# RECOVERED AND GENERALIZED — see `add_world`'s own comment. `realm`/
|
|
202
|
+
# `latest` are scalars, so the overlay's value wins when present,
|
|
203
|
+
# else the base's survives; `settings` is a shallow merge keyed by
|
|
204
|
+
# verb (and `"verb:adapter"`) — an overlay entry for a key the base
|
|
205
|
+
# also declares REPLACES that key's whole resolved hash (the same
|
|
206
|
+
# all-or-nothing shape `WorldBuilder#method_missing` already builds
|
|
207
|
+
# each entry as), it does not deep-merge field by field within it.
|
|
208
|
+
def merge_worlds(a, b)
|
|
209
|
+
Bluebook::World.new(
|
|
210
|
+
domain: a.domain,
|
|
211
|
+
realm: b.realm || a.realm,
|
|
212
|
+
latest: b.latest || a.latest,
|
|
213
|
+
settings: a.settings.merge(b.settings)
|
|
214
|
+
)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|