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,321 @@
|
|
|
1
|
+
require_relative "../naming"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Projector
|
|
5
|
+
# A BLUEBOOK, PROJECTED AS ITS OWN USAGE DOCUMENTATION.
|
|
6
|
+
#
|
|
7
|
+
# WHAT THIS IS FOR. A chapter in this corpus already contains everything a
|
|
8
|
+
# user of it needs: what each aggregate is (`description`), what each verb
|
|
9
|
+
# is for (`goal`) and who issues it (`role`), which states it moves
|
|
10
|
+
# between, what it refuses and in whose words (`given`, `invariant`,
|
|
11
|
+
# `ensures`), and what each list is worth reading (`description` on a
|
|
12
|
+
# query). None of that reaches the person who has to CALL the domain.
|
|
13
|
+
# They read the source, or they read a document somebody wrote beside the
|
|
14
|
+
# source and stopped updating.
|
|
15
|
+
#
|
|
16
|
+
# `docs/implemented/reference/` is the precedent and the contrast. `bin/reference`
|
|
17
|
+
# generates it from the language's own Syntax chapter, so the reference
|
|
18
|
+
# for the DSL cannot drift from the DSL. This is the same trick one level
|
|
19
|
+
# down: the usage document for a DOMAIN, generated from that domain, so it
|
|
20
|
+
# cannot drift from the domain either.
|
|
21
|
+
#
|
|
22
|
+
# WHY IT IS A PROJECTION AND NOT A GENERATOR SCRIPT. `Projector` is the
|
|
23
|
+
# repository's registry of "canonical IR in, external artifact out" (§30
|
|
24
|
+
# of the implementation plan), and this is exactly that shape: one
|
|
25
|
+
# bluebook's IR in, markdown out, no runtime needed and no store touched.
|
|
26
|
+
# Registered as `:docs` beside `:ir`, and reachable the way every
|
|
27
|
+
# projector is — `Projector.call(:docs, bluebook: ...)`.
|
|
28
|
+
#
|
|
29
|
+
# AND AS A METHOD, which is the half that makes it get used.
|
|
30
|
+
# `Facade::Surface` already installs a module per chapter carrying
|
|
31
|
+
# `vision` and `aggregates`; `docs` joins them, so a booted domain answers
|
|
32
|
+
# `QualityControl.docs` and an aggregate door answers
|
|
33
|
+
# `QualityControl::Bug.docs`. A document you have to remember a script for
|
|
34
|
+
# is a document nobody reads.
|
|
35
|
+
#
|
|
36
|
+
# WHAT IT DELIBERATELY DOES NOT DO: invent. Every sentence below comes out
|
|
37
|
+
# of the chapter. Where a chapter says nothing — an aggregate with no
|
|
38
|
+
# `description`, a command with no `goal` — the document says nothing
|
|
39
|
+
# rather than filling the gap with a restatement of the name, because a
|
|
40
|
+
# generated paragraph that only rephrases an identifier teaches a reader
|
|
41
|
+
# to skim the ones that do not.
|
|
42
|
+
module DocsProjector
|
|
43
|
+
module_function
|
|
44
|
+
|
|
45
|
+
# `options[:heading]` sets the top heading level (default 1), so a
|
|
46
|
+
# caller splicing this into a larger document can push it down.
|
|
47
|
+
def call(bluebook:, options: {})
|
|
48
|
+
depth = (options[:heading] || 1).to_i
|
|
49
|
+
only = options[:aggregate]
|
|
50
|
+
|
|
51
|
+
out = []
|
|
52
|
+
out << chapter_header(bluebook, depth) unless only
|
|
53
|
+
Array(aggregates(bluebook, only)).each { |aggregate| out << aggregate_section(aggregate, only ? depth : depth + 1) }
|
|
54
|
+
out << closing(bluebook, depth + 1) unless only
|
|
55
|
+
out.compact.join("\n").rstrip + "\n"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# A NAME THAT NAMES NOTHING IS REFUSED, not answered with an empty
|
|
59
|
+
# document. Shipped the other way first: `options[:aggregate]` that
|
|
60
|
+
# matched no head returned "" and exit 0, which is the silent-wrong-
|
|
61
|
+
# answer shape this repository has already been bitten by twice in the
|
|
62
|
+
# query engine. A misspelling should cost a sentence, not a puzzle.
|
|
63
|
+
def aggregates(bluebook, only)
|
|
64
|
+
return bluebook.aggregates unless only
|
|
65
|
+
|
|
66
|
+
bluebook.aggregates.find { |aggregate| aggregate.hecks_name == only.to_s } ||
|
|
67
|
+
raise(Runtime::NotFound,
|
|
68
|
+
"#{bluebook.name} declares no aggregate named #{only.to_s.inspect} — " \
|
|
69
|
+
"it declares #{bluebook.aggregates.map(&:hecks_name).sort.join(', ')}")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def h(depth, text) = "#{'#' * depth} #{text}"
|
|
73
|
+
|
|
74
|
+
# ── the chapter ───────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
def chapter_header(bluebook, depth)
|
|
77
|
+
out = [h(depth, bluebook.name), ""]
|
|
78
|
+
# THE VISION FIRST AND AS A QUOTE. It is the one sentence in a chapter
|
|
79
|
+
# written for somebody who does not know the domain yet.
|
|
80
|
+
out += ["> #{bluebook.vision}", ""] if bluebook.vision
|
|
81
|
+
out << "#{bluebook.classification.to_s.capitalize} domain." if bluebook.classification
|
|
82
|
+
out << "Previously known as `#{bluebook.formerly_known_as}`." if bluebook.formerly_known_as
|
|
83
|
+
out << ""
|
|
84
|
+
out << "Aggregates: #{bluebook.aggregates.map { |a| "[#{a.hecks_name}](##{anchor(a.hecks_name)})" }.join(', ')}."
|
|
85
|
+
out << ""
|
|
86
|
+
out.join("\n")
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def anchor(name) = Naming.snake(name).tr("_", "-")
|
|
90
|
+
|
|
91
|
+
# WHAT HAPPENS WITHOUT ANYBODY ASKING — the part of a domain a caller
|
|
92
|
+
# cannot discover from any verb list, and the part most likely to surprise
|
|
93
|
+
# them. A policy means one dispatch causes another, sometimes into a
|
|
94
|
+
# different domain entirely; a saga means a sequence is being driven on
|
|
95
|
+
# their behalf and can end in more than one place.
|
|
96
|
+
def closing(bluebook, depth)
|
|
97
|
+
out = []
|
|
98
|
+
|
|
99
|
+
unless bluebook.policies.empty?
|
|
100
|
+
out << h(depth, "Reactions")
|
|
101
|
+
out << ""
|
|
102
|
+
out << "These fire on their own. Issuing the verb on the left also causes the one on the right."
|
|
103
|
+
out << ""
|
|
104
|
+
rows = bluebook.policies.map do |policy|
|
|
105
|
+
["`#{policy.on_event}`", "`#{policy.trigger_command}`", policy.target_domain || bluebook.name]
|
|
106
|
+
end
|
|
107
|
+
out << table(%w[on\ event dispatches in], rows)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
bluebook.process_managers.each do |saga|
|
|
111
|
+
shape = saga.to_h
|
|
112
|
+
out << h(depth, "#{shape[:name]} (a saga)")
|
|
113
|
+
out << ""
|
|
114
|
+
out << "Starts on `#{shape[:starts_on]}`, ends on `#{shape[:ends_on]}`, " \
|
|
115
|
+
"correlated by `#{shape[:correlates_by]}`."
|
|
116
|
+
out << ""
|
|
117
|
+
out << "States: #{Array(shape[:states]).map { |s| "`#{s}`" }.join(' → ')}."
|
|
118
|
+
out << ""
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
out.empty? ? nil : out.join("\n")
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# ── one aggregate ─────────────────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
def aggregate_section(aggregate, depth)
|
|
127
|
+
out = [h(depth, aggregate.hecks_name), ""]
|
|
128
|
+
out += [aggregate.description, ""] if aggregate.description
|
|
129
|
+
|
|
130
|
+
out << "Identified by `#{aggregate.identity_heads.join('`, `')}`." unless aggregate.identity_heads.empty?
|
|
131
|
+
refs = aggregate.attributes.select(&:reference?)
|
|
132
|
+
out << "References #{refs.map { |r| "`#{r.type.target_name}`" }.join(', ')}." unless refs.empty?
|
|
133
|
+
out << ""
|
|
134
|
+
|
|
135
|
+
out << attributes_table(aggregate)
|
|
136
|
+
out << lifecycle_section(aggregate, depth + 1)
|
|
137
|
+
out << verbs_section(aggregate, depth + 1)
|
|
138
|
+
out << queries_section(aggregate.queries, depth + 1, "Questions you can ask")
|
|
139
|
+
|
|
140
|
+
aggregate.entities.each { |entity| out << entity_section(aggregate, entity, depth + 1) }
|
|
141
|
+
|
|
142
|
+
out.compact.join("\n")
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def entity_section(aggregate, entity, depth)
|
|
146
|
+
out = [h(depth, "#{entity.hecks_name} (within #{aggregate.hecks_name})"), ""]
|
|
147
|
+
out += [entity.description, ""] if entity.description
|
|
148
|
+
# THE THING A CALLER GETS WRONG FIRST. An entity has no door of its
|
|
149
|
+
# own: its verb is spelled through the aggregate that holds it, and
|
|
150
|
+
# the parent's id travels alongside the entity's own identity.
|
|
151
|
+
out << "Addressed through its holder — `#{aggregate.hecks_name}.#{entity.hecks_name}.<Verb>`, " \
|
|
152
|
+
"passing the #{aggregate.hecks_name}'s `id` and this element's " \
|
|
153
|
+
"`#{entity.identity_heads.join('`, `')}`."
|
|
154
|
+
out << ""
|
|
155
|
+
out << attributes_table(entity)
|
|
156
|
+
out << lifecycle_section(entity, depth + 1)
|
|
157
|
+
out << verbs_section(entity, depth + 1)
|
|
158
|
+
out << queries_section(entity.queries, depth + 1, "Questions you can ask")
|
|
159
|
+
out.compact.join("\n")
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# ── the shape ─────────────────────────────────────────────────────
|
|
163
|
+
|
|
164
|
+
def attributes_table(holder)
|
|
165
|
+
attributes = holder.attributes.reject(&:reference?)
|
|
166
|
+
return nil if attributes.empty?
|
|
167
|
+
|
|
168
|
+
rows = attributes.map do |attribute|
|
|
169
|
+
["`#{attribute.name}`", shape_of(attribute, holder), rules_of(attribute, holder)]
|
|
170
|
+
end
|
|
171
|
+
table(%w[attribute shape rules], rows)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# A VALUE OBJECT'S FIELDS, NOT ITS NAME. `commit` typed `CommitRef` tells
|
|
175
|
+
# a caller nothing; `{ value: String }` tells them what to send, which is
|
|
176
|
+
# the single most common thing to get wrong at this boundary — a bare
|
|
177
|
+
# scalar where an object is wanted.
|
|
178
|
+
def shape_of(attribute, holder)
|
|
179
|
+
value_object = value_object_for(attribute, holder)
|
|
180
|
+
inner =
|
|
181
|
+
if value_object
|
|
182
|
+
"{ " + value_object.attributes.map { |f| "#{f.name}: #{f.type}" }.join(", ") + " }"
|
|
183
|
+
else
|
|
184
|
+
attribute.type.to_s
|
|
185
|
+
end
|
|
186
|
+
shape = attribute.list? ? "list of #{inner}" : inner
|
|
187
|
+
attribute.optional? ? "#{shape} *(optional)*" : shape
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def rules_of(attribute, holder)
|
|
191
|
+
value_object = value_object_for(attribute, holder)
|
|
192
|
+
rules = []
|
|
193
|
+
rules << "one of #{closed_members(value_object).map { |m| "`#{m}`" }.join(', ')}" if closed_members(value_object).any?
|
|
194
|
+
Array(value_object&.attributes).each do |field|
|
|
195
|
+
rules << "`#{field.name}` matches `#{field.pattern}`" if field.pattern
|
|
196
|
+
rules << "`#{field.name}` defaults to `#{field.default.inspect}`" unless field.default.nil?
|
|
197
|
+
end
|
|
198
|
+
rules += Array(value_object&.invariants).map(&:description)
|
|
199
|
+
rules << "defaults to `#{attribute.default.inspect}`" unless attribute.default.nil?
|
|
200
|
+
rules.empty? ? "" : rules.join("; ")
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def closed_members(value_object)
|
|
204
|
+
return [] unless value_object&.closed_set?
|
|
205
|
+
|
|
206
|
+
value_object.members.flat_map(&:values).uniq
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# An entity holds no value objects of its own — its argument types are
|
|
210
|
+
# declared on the aggregate above it.
|
|
211
|
+
def value_object_for(attribute, holder)
|
|
212
|
+
scopes = [holder, holder.respond_to?(:hecks_owner) ? holder.hecks_owner : nil].compact
|
|
213
|
+
scopes.each do |scope|
|
|
214
|
+
next unless scope.respond_to?(:value_objects)
|
|
215
|
+
|
|
216
|
+
found = scope.value_objects.find { |v| v.hecks_name == attribute.type.to_s }
|
|
217
|
+
return found if found
|
|
218
|
+
end
|
|
219
|
+
nil
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# ── the machine ───────────────────────────────────────────────────
|
|
223
|
+
|
|
224
|
+
def lifecycle_section(holder, depth)
|
|
225
|
+
lifecycle = holder.lifecycle or return nil
|
|
226
|
+
|
|
227
|
+
rows = lifecycle.transitions.map do |name, transition|
|
|
228
|
+
["`#{name}`", "`#{Array(transition.from).join('`, `')}`", "`#{transition.target}`"]
|
|
229
|
+
end
|
|
230
|
+
[h(depth, "Lifecycle (`#{lifecycle.field}`)"), "",
|
|
231
|
+
"Starts at `#{lifecycle.default}`. A verb not listed here can be issued from any state.", "",
|
|
232
|
+
table(%w[verb from to], rows)].join("\n")
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# ── the verbs ─────────────────────────────────────────────────────
|
|
236
|
+
|
|
237
|
+
def verbs_section(holder, depth)
|
|
238
|
+
commands = holder.commands
|
|
239
|
+
return nil if commands.empty?
|
|
240
|
+
|
|
241
|
+
out = [h(depth, "Verbs"), ""]
|
|
242
|
+
commands.each { |command| out << command_entry(command, holder, depth + 1) }
|
|
243
|
+
out.join("\n")
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def command_entry(command, holder, depth)
|
|
247
|
+
out = [h(depth, "#{command.hecks_name}#{command.creates? ? ' *(creates)*' : ''}"), ""]
|
|
248
|
+
out += [command.goal, ""] if command.goal
|
|
249
|
+
out << "Issued by: **#{command.role}**." if command.role
|
|
250
|
+
out << ""
|
|
251
|
+
|
|
252
|
+
arguments = command.attributes
|
|
253
|
+
unless arguments.empty?
|
|
254
|
+
rows = arguments.map do |attribute|
|
|
255
|
+
shape = attribute.reference? ? "id of a `#{attribute.type.target_name}`" : shape_of(attribute, holder)
|
|
256
|
+
["`#{attribute.name}`", shape, attribute.optional? ? "" : "required"]
|
|
257
|
+
end
|
|
258
|
+
out << table(%w[argument shape needed], rows)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
refusals = refusals_of(command, holder)
|
|
262
|
+
unless refusals.empty?
|
|
263
|
+
out << "Refused when:"
|
|
264
|
+
out << ""
|
|
265
|
+
refusals.each { |refusal| out << "- #{refusal}" }
|
|
266
|
+
out << ""
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
out << "Guarantees: #{command.ensures.map(&:description).join('; ')}." unless command.ensures.empty?
|
|
270
|
+
out << "Emits `#{command.emits.join('`, `')}`." unless command.emits.empty?
|
|
271
|
+
out << ""
|
|
272
|
+
out.join("\n")
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# EVERY WAY THIS VERB CAN SAY NO, gathered from the three places a
|
|
276
|
+
# chapter states them — the lifecycle it is an edge of, its own
|
|
277
|
+
# `given`s, and the fact that a reference has to resolve. A caller
|
|
278
|
+
# reading only the argument list learns none of these, and they are
|
|
279
|
+
# most of what a domain is.
|
|
280
|
+
def refusals_of(command, holder)
|
|
281
|
+
refusals = []
|
|
282
|
+
|
|
283
|
+
lifecycle = holder.lifecycle
|
|
284
|
+
froms = lifecycle && lifecycle.transitions.filter_map do |name, transition|
|
|
285
|
+
Array(transition.from) if name.to_s == command.hecks_name
|
|
286
|
+
end.flatten.uniq
|
|
287
|
+
refusals << "`#{lifecycle.field}` is anything other than #{froms.map { |f| "`#{f}`" }.join(' or ')}" if froms && !froms.empty?
|
|
288
|
+
|
|
289
|
+
command.attributes.select(&:reference?).each do |reference|
|
|
290
|
+
refusals << "no `#{reference.type.target_name}` exists for the id given as `#{reference.name}`"
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
refusals + command.givens.map { |given| "not: #{given.description}" }
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# ── the reads ─────────────────────────────────────────────────────
|
|
297
|
+
|
|
298
|
+
def queries_section(queries, depth, title)
|
|
299
|
+
return nil if queries.empty?
|
|
300
|
+
|
|
301
|
+
out = [h(depth, title), ""]
|
|
302
|
+
queries.each do |query|
|
|
303
|
+
shape = query.to_h
|
|
304
|
+
takes = Array(shape[:attributes]).map { |a| "`#{a[:name]}`" }.join(", ")
|
|
305
|
+
out << "**#{query.hecks_name}**#{takes.empty? ? '' : " (#{takes})"} "
|
|
306
|
+
out << (query.description ? "#{query.description} " : "")
|
|
307
|
+
filters = Array(shape[:wheres]).map { |w| "`#{w[:field]} #{w[:op]} #{w[:value].inspect}`" }
|
|
308
|
+
out << "Filters: #{filters.join(', ')}." unless filters.empty?
|
|
309
|
+
out << ""
|
|
310
|
+
end
|
|
311
|
+
out.join("\n")
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def table(headers, rows)
|
|
315
|
+
lines = ["| #{headers.join(' | ')} |", "|#{headers.map { '---' }.join('|')}|"]
|
|
316
|
+
rows.each { |row| lines << "| #{row.join(' | ')} |" }
|
|
317
|
+
(lines + [""]).join("\n")
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require_relative "../runtime/era_check"
|
|
3
|
+
require_relative "../translation/rule_compiler"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module Projector
|
|
7
|
+
module Exporter
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
def call(registry)
|
|
11
|
+
registry.bluebooks.transform_values(&:to_h)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def json(registry)
|
|
15
|
+
JSON.pretty_generate(call(registry))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# A BINDING fact, deliberately NOT folded into `call`/`bluebook.to_h`
|
|
19
|
+
# above — the canonical IR is runtime-independent by design (ADR
|
|
20
|
+
# 0001: it describes what a bluebook DECLARES, never which adapter
|
|
21
|
+
# a deployment happens to bind it to), and "is this aggregate bound
|
|
22
|
+
# to a lineage-capable adapter" is exactly the kind of fact that
|
|
23
|
+
# answer can change per-deployment without the bluebook's own shape
|
|
24
|
+
# changing at all. Consumers that need it (bin/project_rust's own
|
|
25
|
+
# `ir.json` sidecar, rust/host's runtime era-aware seed overlay —
|
|
26
|
+
# dispatch.rs) merge this in as a SEPARATE top-level key, the same
|
|
27
|
+
# way `translations` already sits beside `call`'s output rather than
|
|
28
|
+
# inside it.
|
|
29
|
+
#
|
|
30
|
+
# Reuses `Runtime::EraCheck`'s own capability predicates rather than
|
|
31
|
+
# re-deriving them — the boot-time gate and this export must never
|
|
32
|
+
# answer differently for the same aggregate.
|
|
33
|
+
def lineage(registry, domain_name)
|
|
34
|
+
bluebook = registry.bluebooks.fetch(domain_name)
|
|
35
|
+
capable = bluebook.aggregates.select do |aggregate|
|
|
36
|
+
adapter_name = Runtime::EraCheck.adapter_for(registry, domain_name, aggregate)
|
|
37
|
+
Runtime::EraCheck.lineage_capable?(registry, adapter_name)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
{ capable_aggregates: capable.map { |aggregate| { name: aggregate.name, storage_name: aggregate.storage_name } } }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Translation IR, always as an array, WITH each aggregate's
|
|
44
|
+
# precompiled SQL attached (`compiled_translation_aggregate`) —
|
|
45
|
+
# this is the export a consumer embeds (`ir.json`'s `translations`
|
|
46
|
+
# key), never the bare digest-relevant shape `edge_digest` hashes
|
|
47
|
+
# (see that method's own header for why the two must stay
|
|
48
|
+
# separate). `values:` tables serialize as `[key, value]` pairs,
|
|
49
|
+
# never an object, because JSON object keys are always strings and
|
|
50
|
+
# a convert's keys are typed.
|
|
51
|
+
def translations(registry)
|
|
52
|
+
registry.translations.map { |translation| compiled_translation_hash(translation) }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def compiled_translation_hash(translation)
|
|
56
|
+
{
|
|
57
|
+
domain: translation.domain,
|
|
58
|
+
from: translation.from,
|
|
59
|
+
to: translation.to,
|
|
60
|
+
retired: translation.retired,
|
|
61
|
+
aggregates: translation.aggregates.map { |aggregate| compiled_translation_aggregate(aggregate) }
|
|
62
|
+
}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def translations_json(registry)
|
|
66
|
+
JSON.pretty_generate(translations(registry))
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# THE DIGEST-RELEVANT SHAPE — `ApprovalDigest.edge_digest` hashes
|
|
70
|
+
# EXACTLY this, and only this, for exactly the reason `compiled_
|
|
71
|
+
# translation_aggregate` below must never be used for that
|
|
72
|
+
# purpose: a digest bound to the COMPILED SQL, not just the
|
|
73
|
+
# DECLARED rules, would invalidate an existing human approval the
|
|
74
|
+
# moment `Translation::RuleCompiler`'s own output format changed
|
|
75
|
+
# for any reason — a compiler refactor, a cosmetic SQL-formatting
|
|
76
|
+
# change — even when the declared rules an approver actually
|
|
77
|
+
# reviewed never changed at all. The approval binds to WHAT WAS
|
|
78
|
+
# DECLARED, not to what a particular compiler build happened to
|
|
79
|
+
# emit from it.
|
|
80
|
+
def translation_hash(translation)
|
|
81
|
+
{
|
|
82
|
+
domain: translation.domain,
|
|
83
|
+
from: translation.from,
|
|
84
|
+
to: translation.to,
|
|
85
|
+
retired: translation.retired,
|
|
86
|
+
aggregates: translation.aggregates.map { |aggregate| translation_aggregate(aggregate) }
|
|
87
|
+
}
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def translation_aggregate(aggregate)
|
|
91
|
+
{
|
|
92
|
+
name: aggregate.name,
|
|
93
|
+
was: aggregate.was,
|
|
94
|
+
renames: aggregate.renames.transform_keys(&:to_s).transform_values(&:to_s),
|
|
95
|
+
moves: aggregate.moves.map { |move| { from: move.from, to: move.to } },
|
|
96
|
+
converts: aggregate.converts.map do |convert|
|
|
97
|
+
{ from: convert.from, to: convert.to, values: convert.values.map { |key, value| [key, value] } }
|
|
98
|
+
end,
|
|
99
|
+
drops: aggregate.drops.map(&:to_s),
|
|
100
|
+
retypes: aggregate.retypes.map { |retype| { from: retype.from, to: retype.to } },
|
|
101
|
+
computes: aggregate.computes.map { |compute| { from: compute.from, to: compute.to, sql: compute.sql } },
|
|
102
|
+
# PREVIOUSLY MISSING — found live while planning Rust-side mint
|
|
103
|
+
# support. An edge carrying only a rekey (no compute) had its
|
|
104
|
+
# approval bind to nothing rekey-specific at all: any two
|
|
105
|
+
# rekey edges with otherwise-identical renames/moves/converts/
|
|
106
|
+
# drops/retypes/computes produced the SAME digest regardless
|
|
107
|
+
# of what their `rekey sql:` actually said, and a rekey's own
|
|
108
|
+
# SQL could change without invalidating an existing approval.
|
|
109
|
+
# Same bug shape for `backfills` (present, just never
|
|
110
|
+
# exported). Fixing this CHANGES every existing rekey/
|
|
111
|
+
# backfill edge's digest — any approval already recorded for
|
|
112
|
+
# one is invalidated by this fix and must be re-reviewed.
|
|
113
|
+
rekeys: aggregate.rekeys.map { |rekey| { sql: rekey.sql } },
|
|
114
|
+
backfills: aggregate.backfills.map { |backfill| { name: backfill.name.to_s, default: backfill.default } }
|
|
115
|
+
}
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# THE EXPORT SHAPE — `translation_aggregate`'s own digest-relevant
|
|
119
|
+
# fields, plus the precompiled SQL (`compiled_state_expression`/
|
|
120
|
+
# `compiled_id_expression`) a consumer embedding this JSON
|
|
121
|
+
# (rust/host's own boot-time mint) needs to execute the edge
|
|
122
|
+
# without compiling SQL itself. The SAME call head_compiler.rb's
|
|
123
|
+
# own `compile_rules(declared)`/`id_case(guard, declared)` make at
|
|
124
|
+
# mint time, run here once at build/export time instead —
|
|
125
|
+
# `Translation::RuleCompiler` is the ONE place this expression is
|
|
126
|
+
# built, called from both here and from head_compiler.rb's real
|
|
127
|
+
# per-mint assembly, so a consumer gets Ruby's own compiler's
|
|
128
|
+
# output verbatim, never a second, independently-authored SQL
|
|
129
|
+
# compiler that could drift from this one. `compiled_id_
|
|
130
|
+
# expression` is nil unless this edge rekeys — the bare
|
|
131
|
+
# `aggregate_id` passthrough head_compiler.rb itself falls back to
|
|
132
|
+
# for the overwhelming common case.
|
|
133
|
+
def compiled_translation_aggregate(aggregate)
|
|
134
|
+
translation_aggregate(aggregate).merge(
|
|
135
|
+
compiled_state_expression: Translation::RuleCompiler.compile_rules(aggregate),
|
|
136
|
+
compiled_id_expression: Translation::RuleCompiler.rekeyed?(aggregate) ? Translation::RuleCompiler.compile_id_expression(aggregate) : nil
|
|
137
|
+
)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Projector
|
|
3
|
+
# The trivial case, on purpose: canonical IR projected AS canonical
|
|
4
|
+
# IR. `Bluebook#to_h` already satisfies every §30 acceptance
|
|
5
|
+
# criterion on its own — no live runtime needed to call it, output is
|
|
6
|
+
# deterministic (the same golden fixtures `spec/ir_golden_spec.rb`
|
|
7
|
+
# pins), and target-version metadata rides along for free (`§7`'s
|
|
8
|
+
# `ir_version:` is the first key `to_h` emits). Registering it proves
|
|
9
|
+
# the framework's call shape against a real, already-load-bearing
|
|
10
|
+
# target before any genuinely new projector (`:rust`, `:ul`,
|
|
11
|
+
# `:openid`) has to.
|
|
12
|
+
module IRProjector
|
|
13
|
+
module_function
|
|
14
|
+
|
|
15
|
+
def call(bluebook:, options: {}) = bluebook.to_h
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|