hecks 2.7.0 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/exe/hecks +11 -0
- data/lib/hecks/adapters/driven/claude_code.rb +4 -104
- data/lib/hecks/adapters/driven/d1.rb +44 -237
- data/lib/hecks/adapters/driven/folder.rb +17 -76
- data/lib/hecks/adapters/driven/google_authentication.rb +15 -49
- data/lib/hecks/adapters/driven/governance_authorization.rb +23 -70
- data/lib/hecks/adapters/driven/heki/journal.rb +8 -41
- data/lib/hecks/adapters/driven/heki/saga_store.rb +12 -54
- data/lib/hecks/adapters/driven/heki/snapshot.rb +4 -12
- data/lib/hecks/adapters/driven/heki.rb +13 -42
- data/lib/hecks/adapters/driven/identity_registry.rb +3 -14
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +9 -26
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +2 -14
- data/lib/hecks/adapters/driven/lambda/client.rb +10 -51
- data/lib/hecks/adapters/driven/lambda.rb +10 -84
- data/lib/hecks/adapters/driven/local_storage.rb +5 -40
- data/lib/hecks/adapters/driven/memory.rb +18 -99
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +5 -21
- data/lib/hecks/adapters/driven/postgres/codec.rb +6 -28
- data/lib/hecks/adapters/driven/postgres/outbox.rb +3 -12
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +4 -30
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
- data/lib/hecks/adapters/driven/postgres.rb +145 -134
- data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
- data/lib/hecks/adapters/driven/prism.rb +7 -30
- data/lib/hecks/adapters/driven/sql_query_builder.rb +21 -121
- data/lib/hecks/adapters/driven/sqlite/codec.rb +8 -40
- data/lib/hecks/adapters/driven/sqlite/projection.rb +26 -82
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +40 -84
- data/lib/hecks/adapters/driven/sqlite.rb +128 -65
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +15 -37
- data/lib/hecks/adapters/driven.rb +5 -36
- data/lib/hecks/adapters/driving/github_webhook.rb +8 -76
- data/lib/hecks/behaviors/dsl.rb +7 -25
- data/lib/hecks/behaviors/expectations.rb +32 -142
- data/lib/hecks/behaviors/ir.rb +7 -20
- data/lib/hecks/behaviors/rspec.rb +6 -17
- data/lib/hecks/behaviors/runner.rb +10 -34
- data/lib/hecks/behaviors.rb +4 -8
- data/lib/hecks/bluebook/aggregate.rb +9 -75
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +9 -38
- data/lib/hecks/bluebook/assembly/build.rb +4 -15
- data/lib/hecks/bluebook/assembly/contract.rb +15 -136
- data/lib/hecks/bluebook/assembly/contracts.rb +54 -174
- data/lib/hecks/bluebook/assembly/marks.rb +31 -223
- data/lib/hecks/bluebook/assembly/specializer.rb +6 -52
- data/lib/hecks/bluebook/assembly.rb +6 -45
- data/lib/hecks/bluebook/attribute.rb +12 -61
- data/lib/hecks/bluebook/behaviour/aggregate.rb +10 -39
- data/lib/hecks/bluebook/behaviour/attribute.rb +3 -14
- data/lib/hecks/bluebook/behaviour/chapter.rb +17 -42
- data/lib/hecks/bluebook/behaviour/command.rb +14 -66
- data/lib/hecks/bluebook/behaviour/domain_port.rb +4 -20
- data/lib/hecks/bluebook/behaviour/entity.rb +7 -27
- data/lib/hecks/bluebook/behaviour/hexagon.rb +23 -41
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +15 -40
- data/lib/hecks/bluebook/behaviour/policy.rb +23 -52
- data/lib/hecks/bluebook/behaviour/process_manager.rb +30 -57
- data/lib/hecks/bluebook/behaviour/read_model.rb +38 -26
- data/lib/hecks/bluebook/behaviour/traits.rb +27 -64
- data/lib/hecks/bluebook/behaviour/value_object.rb +5 -24
- data/lib/hecks/bluebook/capabilities.rb +17 -49
- data/lib/hecks/bluebook/chapter.rb +10 -48
- data/lib/hecks/bluebook/command.rb +23 -81
- data/lib/hecks/bluebook/domain_port.rb +14 -63
- data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +50 -229
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +50 -377
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +33 -204
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +17 -65
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +86 -708
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +56 -170
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +1 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +74 -546
- data/lib/hecks/bluebook/dsl/const_shim.rb +8 -41
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +12 -87
- data/lib/hecks/bluebook/dsl/entity_builder.rb +44 -330
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +20 -269
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +26 -189
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +13 -88
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +6 -20
- data/lib/hecks/bluebook/dsl/policy_builder.rb +16 -88
- data/lib/hecks/bluebook/dsl/port_builder.rb +2 -6
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +22 -96
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +31 -184
- data/lib/hecks/bluebook/dsl/query_builder.rb +11 -42
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +75 -201
- data/lib/hecks/bluebook/dsl/rule_reference.rb +22 -163
- data/lib/hecks/bluebook/dsl/translation_builder.rb +28 -114
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +10 -124
- data/lib/hecks/bluebook/dsl/word_gate.rb +16 -162
- data/lib/hecks/bluebook/dsl/world_builder.rb +19 -106
- data/lib/hecks/bluebook/entity.rb +13 -71
- data/lib/hecks/bluebook/expression/ast_json.rb +24 -190
- data/lib/hecks/bluebook/expression/ast_reader.rb +12 -54
- data/lib/hecks/bluebook/expression/canonical_form.rb +11 -88
- data/lib/hecks/bluebook/expression/evaluator.rb +47 -295
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +30 -147
- data/lib/hecks/bluebook/expression/resolver.rb +37 -660
- data/lib/hecks/bluebook/hexagon.rb +5 -28
- data/lib/hecks/bluebook/lifecycle.rb +5 -16
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -7
- data/lib/hecks/bluebook/meta_validator/judge.rb +111 -361
- data/lib/hecks/bluebook/meta_validator/plan.rb +33 -162
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -7
- data/lib/hecks/bluebook/meta_validator/readings.rb +56 -342
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -176
- data/lib/hecks/bluebook/meta_validator/shapes.rb +54 -268
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +47 -308
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
- data/lib/hecks/bluebook/meta_validator.rb +79 -349
- data/lib/hecks/bluebook/model_check/client_profile.rb +10 -91
- data/lib/hecks/bluebook/model_check.rb +102 -600
- data/lib/hecks/bluebook/pattern_subset.rb +11 -69
- data/lib/hecks/bluebook/process_manager.rb +13 -83
- data/lib/hecks/bluebook/project_loader.rb +3 -39
- data/lib/hecks/bluebook/project_register.rb +12 -60
- data/lib/hecks/bluebook/query.rb +9 -48
- data/lib/hecks/bluebook/read_model.rb +57 -81
- data/lib/hecks/bluebook/reference.rb +7 -41
- data/lib/hecks/bluebook/smoke_test.rb +8 -98
- data/lib/hecks/bluebook/synthesizer.rb +13 -62
- data/lib/hecks/bluebook/translation.rb +26 -62
- data/lib/hecks/bluebook/value_object.rb +11 -36
- data/lib/hecks/bluebook.rb +5 -22
- data/lib/hecks/cache_dir.rb +84 -0
- data/lib/hecks/cli/document.rb +86 -0
- data/lib/hecks/cli/ir.rb +45 -0
- data/lib/hecks/cli/mcp.rb +31 -0
- data/lib/hecks/cli/mcp_door.rb +366 -0
- data/lib/hecks/cli/model_check.rb +203 -0
- data/lib/hecks/cli/project_cli.rb +90 -0
- data/lib/hecks/cli/project_diagrams.rb +36 -0
- data/lib/hecks/cli/run.rb +173 -0
- data/lib/hecks/cli/smoke_test.rb +45 -0
- data/lib/hecks/cli/stores.rb +53 -0
- data/lib/hecks/cli.rb +160 -0
- data/lib/hecks/construct.rb +8 -34
- data/lib/hecks/deploy/bluebook/deploy.bluebook +33 -142
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +2 -9
- data/lib/hecks/embryonaut_bluebook/lock.rb +3 -35
- data/lib/hecks/embryonaut_bluebook/shape.rb +1 -17
- data/lib/hecks/embryonaut_bluebook/vendor.rb +8 -53
- data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +4 -19
- data/lib/hecks/embryonaut_bluebook.rb +4 -71
- data/lib/hecks/facade/cli_door.rb +24 -106
- data/lib/hecks/facade/cli_runner.rb +28 -172
- data/lib/hecks/facade/command_request.rb +13 -33
- data/lib/hecks/facade/handle.rb +34 -121
- data/lib/hecks/facade/json_door.rb +31 -146
- data/lib/hecks/facade/surface/aggregate_door.rb +39 -104
- data/lib/hecks/facade/surface/chapter.rb +10 -54
- data/lib/hecks/facade/surface.rb +8 -23
- data/lib/hecks/facade.rb +10 -25
- data/lib/hecks/forms/app.rb +15 -70
- data/lib/hecks/forms/command_form_renderer.rb +10 -29
- data/lib/hecks/forms/field_renderer.rb +17 -156
- data/lib/hecks/forms/field_shape.rb +30 -222
- data/lib/hecks/forms/html.rb +12 -47
- data/lib/hecks/forms/index_renderer.rb +1 -4
- data/lib/hecks/forms/page.rb +3 -9
- data/lib/hecks/forms/params.rb +14 -60
- data/lib/hecks/forms/port_argument.rb +2 -19
- data/lib/hecks/forms/query_form_renderer.rb +4 -79
- data/lib/hecks/forms/record_renderer.rb +8 -75
- data/lib/hecks/forms/record_table.rb +7 -50
- data/lib/hecks/forms/reference_options.rb +3 -30
- data/lib/hecks/forms/value_object_shape.rb +8 -35
- data/lib/hecks/forms.rb +5 -33
- data/lib/hecks/fqn.rb +5 -12
- data/lib/hecks/framework/bluebook/compliance.bluebook +16 -65
- data/lib/hecks/framework/bluebook/console_settings.bluebook +47 -134
- data/lib/hecks/framework/bluebook/framework.hecksagon +5 -14
- data/lib/hecks/framework/bluebook/governance.bluebook +13 -34
- data/lib/hecks/framework/bluebook/identity.bluebook +11 -26
- data/lib/hecks/framework/bluebook/privacy.bluebook +8 -33
- data/lib/hecks/framework.rb +11 -80
- data/lib/hecks/freezer.rb +5 -39
- data/lib/hecks/grammar/expression.bluebook +15 -40
- data/lib/hecks/grammar/translation.bluebook +3 -6
- data/lib/hecks/grammar.rb +16 -41
- data/lib/hecks/ir.rb +14 -83
- data/lib/hecks/language/adapter.bluebook +2 -7
- data/lib/hecks/language/bluebook/aggregate.bluebook +56 -221
- data/lib/hecks/language/bluebook/attaches/paging.bluebook +6 -15
- data/lib/hecks/language/bluebook/bluebook.bluebook +15 -83
- data/lib/hecks/language/bluebook/bluebook.hecksagon +3 -15
- data/lib/hecks/language/bluebook/command.bluebook +27 -144
- data/lib/hecks/language/bluebook/entity.bluebook +24 -97
- data/lib/hecks/language/bluebook/policy.bluebook +16 -67
- data/lib/hecks/language/bluebook/process_manager.bluebook +28 -151
- data/lib/hecks/language/bluebook/projection.bluebook +126 -60
- data/lib/hecks/language/bluebook/query.bluebook +12 -43
- data/lib/hecks/language/bluebook/shape.bluebook +23 -73
- data/lib/hecks/language/bluebook/syntax.bluebook +51 -262
- data/lib/hecks/language/bluebook/vocabulary.bluebook +98 -197
- data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
- data/lib/hecks/language/hecksagon/domain_port.bluebook +4 -19
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +6 -25
- data/lib/hecks/language/hecksagon/port_operation.bluebook +3 -14
- data/lib/hecks/language/oidc.json +35 -0
- data/lib/hecks/language/port.bluebook +4 -17
- data/lib/hecks/language/translation/translation.bluebook +3 -9
- data/lib/hecks/language/translation/translation_aggregate.bluebook +5 -24
- data/lib/hecks/language/world/wiring.bluebook +1 -3
- data/lib/hecks/language/world/world.bluebook +5 -7
- data/lib/hecks/literal.rb +8 -50
- data/lib/hecks/mcp_door_scope.rb +95 -0
- data/lib/hecks/mcp_stdio_guard.rb +4 -41
- data/lib/hecks/naming.rb +29 -137
- data/lib/hecks/ports/access_control.rb +7 -23
- data/lib/hecks/ports/agent/answers.rb +5 -23
- data/lib/hecks/ports/agent.rb +47 -172
- data/lib/hecks/ports/authentication.rb +12 -39
- data/lib/hecks/ports/authorization.rb +16 -43
- data/lib/hecks/ports/clock.rb +5 -45
- data/lib/hecks/ports/extraction.rb +7 -12
- data/lib/hecks/ports/identity_assignment.rb +8 -29
- data/lib/hecks/ports/identity_generation.rb +6 -29
- data/lib/hecks/ports/identity_resolution.rb +2 -10
- data/lib/hecks/ports/key_vault.rb +9 -45
- data/lib/hecks/ports/persistence/append_only.rb +62 -65
- data/lib/hecks/ports/persistence/binding_policy.rb +4 -14
- data/lib/hecks/ports/persistence/codec_boundary.rb +20 -80
- data/lib/hecks/ports/persistence/null_saga_store.rb +4 -20
- data/lib/hecks/ports/persistence/plugin.rb +13 -42
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +38 -141
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +16 -84
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +7 -26
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +3 -22
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +55 -181
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/events.rb +77 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +12 -122
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +28 -128
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +73 -474
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +31 -124
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +45 -219
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +13 -108
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +18 -75
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +9 -49
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +6 -123
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +23 -55
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +14 -72
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +7 -16
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +14 -115
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +127 -554
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +2 -17
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +3 -14
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +13 -58
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -17
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -26
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +9 -25
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +22 -122
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +34 -123
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +5 -12
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +3 -9
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -14
- data/lib/hecks/ports/persistence/plugins/era.rb +4 -23
- data/lib/hecks/ports/persistence/postgres_dump/connection.rb +7 -23
- data/lib/hecks/ports/persistence/postgres_dump.rb +4 -35
- data/lib/hecks/ports/persistence/remote_runtime.rb +2 -24
- data/lib/hecks/ports/persistence/repository_factory.rb +8 -27
- data/lib/hecks/ports/persistence/state_codec.rb +23 -103
- data/lib/hecks/ports/persistence.rb +7 -14
- data/lib/hecks/ports/projection.rb +42 -84
- data/lib/hecks/ports/query/in_memory.rb +10 -58
- data/lib/hecks/ports/query/ordering.rb +11 -32
- data/lib/hecks/ports/query.rb +14 -37
- data/lib/hecks/ports.rb +2 -8
- data/lib/hecks/projections/bootstrap_table.rb +11 -43
- data/lib/hecks/projections/deploy/fargate/assembly.rb +2 -15
- data/lib/hecks/projections/deploy/fargate/cdn.rb +1 -48
- data/lib/hecks/projections/deploy/fargate/cdn_default.rb +2 -11
- data/lib/hecks/projections/deploy/fargate/check.rb +5 -14
- data/lib/hecks/projections/deploy/fargate/containers.rb +3 -78
- data/lib/hecks/projections/deploy/fargate/extras.rb +3 -44
- data/lib/hecks/projections/deploy/fargate/monitoring.rb +0 -44
- data/lib/hecks/projections/deploy/fargate/settings.rb +2 -54
- data/lib/hecks/projections/deploy/fargate/yaml.rb +13 -58
- data/lib/hecks/projections/deploy/fargate.rb +35 -202
- data/lib/hecks/projections/deploy/lambda.rb +151 -679
- data/lib/hecks/projections/deploy/preview/access_yaml.rb +1 -33
- data/lib/hecks/projections/deploy/preview/containers.rb +6 -111
- data/lib/hecks/projections/deploy/preview/routing_yaml.rb +4 -56
- data/lib/hecks/projections/deploy/preview/script.rb +2 -14
- data/lib/hecks/projections/deploy/preview/settings.rb +3 -33
- data/lib/hecks/projections/deploy/preview/task_yaml.rb +2 -38
- data/lib/hecks/projections/deploy/preview/template.rb +2 -58
- data/lib/hecks/projections/deploy/preview.rb +2 -67
- data/lib/hecks/projections/deploy/scripts/settings.rb +3 -25
- data/lib/hecks/projections/deploy/scripts.rb +4 -57
- data/lib/hecks/projections/deploy/shared.rb +47 -146
- data/lib/hecks/projections/deploy/smoke.rb +4 -61
- data/lib/hecks/projections/deploy/template_diff/comparer.rb +3 -25
- data/lib/hecks/projections/deploy/template_diff/loader.rb +2 -11
- data/lib/hecks/projections/deploy/template_diff.rb +5 -57
- data/lib/hecks/projections/diagrams.rb +20 -582
- data/lib/hecks/projections/glossary/html.rb +14 -130
- data/lib/hecks/projections/glossary/markdown.rb +8 -69
- data/lib/hecks/projections/glossary/mermaid.rb +9 -66
- data/lib/hecks/projections/glossary/sensitivity.rb +5 -34
- data/lib/hecks/projections/glossary/sentences.rb +14 -159
- data/lib/hecks/projections/glossary.rb +30 -222
- data/lib/hecks/projections/ir.rb +3 -10
- data/lib/hecks/projections/model/deviations.rb +15 -42
- data/lib/hecks/projections/model.rb +12 -51
- data/lib/hecks/projections/oidc.rb +12 -56
- data/lib/hecks/projections/parser_table.rb +9 -28
- data/lib/hecks/projections/reference.rb +9 -16
- data/lib/hecks/projections/rust_vocabulary.rb +12 -250
- data/lib/hecks/projections/shape.rb +3 -29
- data/lib/hecks/projections/statements.rb +7 -86
- data/lib/hecks/projections/vocabulary.rb +6 -22
- data/lib/hecks/projections.rb +2 -16
- data/lib/hecks/projector/cli_projector.rb +60 -375
- data/lib/hecks/projector/docs_projector.rb +10 -201
- data/lib/hecks/projector/exporter.rb +46 -138
- data/lib/hecks/projector/ir_projector.rb +2 -9
- data/lib/hecks/projector/narrate_projector.rb +15 -189
- data/lib/hecks/projector/target.rb +13 -76
- data/lib/hecks/projector.rb +30 -121
- data/lib/hecks/query_specification/common/comparators.rb +5 -26
- data/lib/hecks/query_specification/common/comparison.rb +35 -143
- data/lib/hecks/query_specification/common/dsl.rb +8 -24
- data/lib/hecks/query_specification/common/null_policy.rb +20 -70
- data/lib/hecks/query_specification/common/options.rb +2 -6
- data/lib/hecks/query_specification/common/where_clause.rb +2 -10
- data/lib/hecks/query_specification/field_path.rb +37 -77
- data/lib/hecks/query_specification/hop_path.rb +12 -76
- data/lib/hecks/release/gem_pin.rb +2 -26
- data/lib/hecks/release/runner/ci_publisher.rb +93 -0
- data/lib/hecks/release/runner/commands.rb +66 -0
- data/lib/hecks/release/runner/console.rb +52 -0
- data/lib/hecks/release/runner/gem_publisher.rb +43 -0
- data/lib/hecks/release/runner/git.rb +48 -0
- data/lib/hecks/release/runner/npm_publisher.rb +75 -0
- data/lib/hecks/release/runner/preflight.rb +104 -0
- data/lib/hecks/release/runner/published.rb +55 -0
- data/lib/hecks/release/runner/tagger.rb +80 -0
- data/lib/hecks/release/runner.rb +215 -0
- data/lib/hecks/rendering.rb +4 -26
- data/lib/hecks/router/namespace_installer.rb +12 -36
- data/lib/hecks/router.rb +4 -8
- data/lib/hecks/runtime/aggregate_lock.rb +8 -33
- data/lib/hecks/runtime/boot_gates.rb +3 -18
- data/lib/hecks/runtime/caller.rb +19 -59
- data/lib/hecks/runtime/capability_graph.rb +5 -21
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +23 -160
- data/lib/hecks/runtime/command_interpreter.rb +125 -307
- data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
- data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
- data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
- data/lib/hecks/runtime/command_rules/emission.rb +8 -21
- data/lib/hecks/runtime/command_rules/references.rb +23 -202
- data/lib/hecks/runtime/dependency_planning.rb +30 -102
- data/lib/hecks/runtime/dispatcher.rb +82 -241
- data/lib/hecks/runtime/entity_element.rb +40 -462
- data/lib/hecks/runtime/entity_interpreter.rb +65 -220
- data/lib/hecks/runtime/errors.rb +32 -115
- data/lib/hecks/runtime/event.rb +3 -25
- data/lib/hecks/runtime/identity.rb +18 -154
- data/lib/hecks/runtime/instance.rb +18 -150
- data/lib/hecks/runtime/interpreting.rb +11 -70
- data/lib/hecks/runtime/invocation.rb +28 -164
- data/lib/hecks/runtime/loader.rb +48 -186
- data/lib/hecks/runtime/outbox.rb +38 -261
- data/lib/hecks/runtime/policy_interpreter.rb +38 -200
- data/lib/hecks/runtime/port_operation_interpreter.rb +26 -87
- data/lib/hecks/runtime/query_interpreter.rb +45 -187
- data/lib/hecks/runtime/reaction_invocation.rb +36 -138
- data/lib/hecks/runtime/read_model_interpreter.rb +139 -208
- data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
- data/lib/hecks/runtime/reference_hop.rb +8 -81
- data/lib/hecks/runtime/refusal_wording.rb +8 -89
- data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
- data/lib/hecks/runtime/registry/verification.rb +52 -225
- data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
- data/lib/hecks/runtime/registry.rb +103 -244
- data/lib/hecks/runtime/remote_dispatcher.rb +37 -152
- data/lib/hecks/runtime/routing.rb +4 -15
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
- data/lib/hecks/runtime/saga_interpreter.rb +79 -263
- data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
- data/lib/hecks/runtime/tenant_check.rb +6 -68
- data/lib/hecks/runtime/tenant_scope.rb +3 -28
- data/lib/hecks/runtime/value/admission.rb +16 -67
- data/lib/hecks/runtime/value/coercion.rb +155 -641
- data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
- data/lib/hecks/runtime/value.rb +12 -84
- data/lib/hecks/runtime.rb +17 -65
- data/lib/hecks/storehouse.rb +146 -567
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +8 -32
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
- data/lib/hecks/vendoring/git_environment.rb +2 -8
- data/lib/hecks/vendoring/git_source.rb +4 -10
- data/lib/hecks/vendoring.rb +7 -46
- data/lib/hecks/version.rb +3 -14
- data/lib/hecks/vocabulary.rb +5 -0
- data/lib/hecks.rb +32 -82
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
- metadata +30 -59
- data/lib/hecks/bench/cli.rb +0 -74
- data/lib/hecks/bench/environment.rb +0 -100
- data/lib/hecks/bench/postgres_probe.rb +0 -41
- data/lib/hecks/bench/report.rb +0 -116
- data/lib/hecks/bench/ruby_runner.rb +0 -151
- data/lib/hecks/bench/run.rb +0 -55
- data/lib/hecks/bench/rust_runner.rb +0 -165
- data/lib/hecks/bench/stats.rb +0 -75
- data/lib/hecks/bench/suite.rb +0 -172
- data/lib/hecks/bench/workload.rb +0 -137
- data/lib/hecks/bench.rb +0 -28
- data/lib/hecks/codemod.rb +0 -443
- data/lib/hecks/corpus.rb +0 -479
- data/lib/hecks/doc/reference.rb +0 -585
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -684
- data/lib/hecks/fuzzing/combination_miner.rb +0 -178
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +0 -469
- data/lib/hecks/fuzzing/coverage_campaign.rb +0 -161
- data/lib/hecks/fuzzing/differential.rb +0 -192
- data/lib/hecks/fuzzing/domain_generator.rb +0 -871
- data/lib/hecks/fuzzing/era_boundary.rb +0 -154
- data/lib/hecks/fuzzing/form_census.rb +0 -285
- data/lib/hecks/fuzzing/generated_domain_check.rb +0 -171
- data/lib/hecks/fuzzing/invalid_value_generator.rb +0 -125
- data/lib/hecks/fuzzing/isolated_boot.rb +0 -658
- data/lib/hecks/fuzzing/nondeterministic.rb +0 -79
- data/lib/hecks/fuzzing/persistence_parity.rb +0 -253
- data/lib/hecks/fuzzing/properties/corrections.rb +0 -125
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +0 -688
- data/lib/hecks/fuzzing/properties/guards.rb +0 -398
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +0 -399
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +0 -137
- data/lib/hecks/fuzzing/properties/outbox.rb +0 -180
- data/lib/hecks/fuzzing/properties/querying.rb +0 -260
- data/lib/hecks/fuzzing/properties.rb +0 -267
- data/lib/hecks/fuzzing/qa_settings.rb +0 -164
- data/lib/hecks/fuzzing/replay.rb +0 -930
- data/lib/hecks/fuzzing/rotation_priority.rb +0 -114
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +0 -139
- data/lib/hecks/fuzzing/self_consistency.rb +0 -825
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +0 -532
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +0 -171
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +0 -127
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +0 -116
- data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +0 -138
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +0 -344
- data/lib/hecks/fuzzing/sequence_generator.rb +0 -311
- data/lib/hecks/fuzzing/shrinker.rb +0 -309
- data/lib/hecks/fuzzing/structural_skips.rb +0 -53
- data/lib/hecks/fuzzing/sweep_depth.rb +0 -61
- data/lib/hecks/fuzzing/target_capabilities.rb +0 -255
- data/lib/hecks/fuzzing/value_generator.rb +0 -340
- data/lib/hecks/fuzzing.rb +0 -23
- data/lib/hecks/grammar/evolve.rb +0 -524
- data/lib/hecks/query_ir.rb +0 -482
|
@@ -2,13 +2,8 @@ require_relative "behaviour/read_model"
|
|
|
2
2
|
|
|
3
3
|
module Hecks
|
|
4
4
|
module Bluebook
|
|
5
|
-
# A read model
|
|
6
|
-
#
|
|
7
|
-
# Like a query it crosses over as an instance, and for the same reason: its
|
|
8
|
-
# body is inherited from `QuerySpecification::ReadModel::Specification`, whose
|
|
9
|
-
# readers the runtime and the SQLite adapter both call on the object. It gains
|
|
10
|
-
# an identity and an owner ; it keeps the name it always had, because only a
|
|
11
|
-
# class ever had a competing answer for `name`.
|
|
5
|
+
# A read model: an ask that gathers heads from more than one aggregate.
|
|
6
|
+
# Crosses over as an instance, like a query.
|
|
12
7
|
class ReadModel < QuerySpecification::ReadModel::Specification
|
|
13
8
|
include Construct
|
|
14
9
|
|
|
@@ -27,31 +22,25 @@ module Hecks
|
|
|
27
22
|
)
|
|
28
23
|
|
|
29
24
|
attr_reader :name, :description, :reference_name, :reference_target, :aggregate_heads, :group_by,
|
|
30
|
-
:count, :median_field
|
|
25
|
+
:count, :median_field, :sum_field, :avg_field, :min_field, :max_field,
|
|
26
|
+
:percentile_field, :percentile_at, :any_field, :all_field
|
|
31
27
|
|
|
32
|
-
# `reference_name
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
28
|
+
# Nil `reference_name`/`reference_target` mean a rootless read model, so `&.` keeps
|
|
29
|
+
# them nil. `count` through `all_field` are `Behaviour::ReadModel::REDUCTION_FIELDS`,
|
|
30
|
+
# one keyword per wire field: `Assembly::Build`/`Reconstruction` construct this
|
|
31
|
+
# generically off the contract's own flat field list (contracts.rb), so grouping
|
|
32
|
+
# them into a nested object would break that reflection.
|
|
37
33
|
#
|
|
38
34
|
# @param name [String, Symbol] the read model's declared name
|
|
39
|
-
# @param
|
|
40
|
-
# @param
|
|
41
|
-
#
|
|
42
|
-
# @param
|
|
43
|
-
#
|
|
44
|
-
# @param aggregate_heads [Array<Hash{Symbol => Object}>] the included aggregates, each
|
|
45
|
-
# a row with `:aggregate` (the aggregate's name), `:as` (the output field name) and
|
|
46
|
-
# `:many` (whether this head is a many-side join)
|
|
47
|
-
# @param group_by [Array<Hash{field: Symbol}>] the declared group-by fields, one row
|
|
48
|
-
# per field
|
|
49
|
-
# @param count [Boolean, nil] whether this read model reduces to a row count; `nil`
|
|
50
|
-
# when undeclared
|
|
51
|
-
# @param median_field [Symbol, nil] the field this read model reduces to the median
|
|
52
|
-
# of, or `nil` when undeclared
|
|
35
|
+
# @param reference_name [Symbol, String, nil] the local reference attribute it roots at
|
|
36
|
+
# @param reference_target [String, Symbol, nil] the rooted aggregate's name
|
|
37
|
+
# @param aggregate_heads [Array<Hash{Symbol => Object}>] rows of `:aggregate`, `:as`, `:many`
|
|
38
|
+
# @param group_by [Array<Hash{field: Symbol}>] the declared group-by fields, one row each
|
|
39
|
+
# rubocop:disable-next Metrics/ParameterLists
|
|
53
40
|
def initialize(name:, description: nil, reference_name: nil, reference_target: nil, aggregate_heads: [],
|
|
54
|
-
group_by: [], count: nil, median_field: nil,
|
|
41
|
+
group_by: [], count: nil, median_field: nil, sum_field: nil, avg_field: nil,
|
|
42
|
+
min_field: nil, max_field: nil, percentile_field: nil, percentile_at: nil,
|
|
43
|
+
any_field: nil, all_field: nil, **)
|
|
55
44
|
super(joins: aggregate_heads, **)
|
|
56
45
|
@name = name.to_s
|
|
57
46
|
@hecks_name = @name
|
|
@@ -59,71 +48,58 @@ module Hecks
|
|
|
59
48
|
@reference_name = reference_name&.to_sym
|
|
60
49
|
@reference_target = reference_target&.to_s
|
|
61
50
|
@aggregate_heads = aggregate_heads
|
|
62
|
-
#
|
|
63
|
-
# `group_by_fields` is the convenience reader everything but
|
|
64
|
-
# `to_h`/the Judge's own generic walk actually wants.
|
|
51
|
+
# Rows are `{field: :agg}` hashes, the same shape as `aggregate_heads`.
|
|
65
52
|
@group_by = group_by
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
# (MetaValidator::Judge#setters skips a setter whose every
|
|
70
|
-
# source is `nil`, so an always-false `@count` would dispatch
|
|
71
|
-
# `ReadModel.Count` on every read model that never wrote the
|
|
72
|
-
# word at all). `!!count` collapses a truthy DSL `true` (or a
|
|
73
|
-
# reconstructed `true`) to the same `true`/`nil` pair either path
|
|
74
|
-
# produces — never `false`.
|
|
75
|
-
@count = count ? true : nil
|
|
76
|
-
@median_field = median_field&.to_sym
|
|
53
|
+
assign_reductions(count: count, median_field: median_field, sum_field: sum_field, avg_field: avg_field,
|
|
54
|
+
min_field: min_field, max_field: max_field, percentile_field: percentile_field,
|
|
55
|
+
percentile_at: percentile_at, any_field: any_field, all_field: all_field)
|
|
77
56
|
end
|
|
78
57
|
|
|
79
|
-
#
|
|
80
|
-
#
|
|
81
|
-
# replayed-from-the-meta-domain path every real boot actually
|
|
82
|
-
# goes through) reads the field back as a String, the same way
|
|
83
|
-
# `aggregate_heads`' own `:as` does. Row hash keys built by
|
|
84
|
-
# `Value.materialize_unwrapped` are symbols (`attr.name`), so
|
|
85
|
-
# this has to be too, or `row[field]` in `nest` silently misses.
|
|
86
|
-
|
|
87
|
-
# `wheres`/`order_by`/`limit` are spelled explicitly here, the same
|
|
88
|
-
# mechanism `Query#to_h` already uses (query.rb, read directly
|
|
89
|
-
# before this was written) — always present, `wheres` a (possibly
|
|
90
|
-
# empty) array and `order_by`/`limit` nil when undeclared, exactly
|
|
91
|
-
# like a Query's. `extra_options_to_h` still excludes these three by
|
|
92
|
-
# name (options.rb), so nothing here double-spells them; the two
|
|
93
|
-
# constructs now share one encoding for the same three words rather
|
|
94
|
-
# than a reader needing to learn a second one. See this file's own
|
|
95
|
-
# `filtered_head_name` and language/bluebook/syntax.bluebook's
|
|
96
|
-
# `ReadModel` `where`/`order_by`/`limit` member rows for the history
|
|
97
|
-
# of why this was narrower, and 2026-08-11's read-model where/
|
|
98
|
-
# order_by/limit task for why it stopped being.
|
|
99
|
-
# Same dynamic tail as a Query's, plus two collections whose
|
|
100
|
-
# rows are plain hashes rather than constructs — they are
|
|
101
|
-
# normalised here rather than by `many`, which recurses through
|
|
102
|
-
# `to_h` and would have nothing to call.
|
|
103
|
-
# `count`/`median_field` are absent (no key at all), not `nil`,
|
|
104
|
-
# when undeclared — unlike `group_by` (always a `[]`, never
|
|
105
|
-
# missing) they are the first fields this construct has ever
|
|
106
|
-
# added that no older real corpus member could possibly carry, so
|
|
107
|
-
# merging them unconditionally would have put a `null` onto every
|
|
108
|
-
# existing read model's own wire shape (banking's
|
|
109
|
-
# `ComplianceDashboard`/`CustomerPortfolio`, the language's own
|
|
110
|
-
# `WholeBluebook`, ...) for a fact nothing about them changed —
|
|
111
|
-
# the exact `extra_options_to_h` reads for `cursor`/`offset`/etc,
|
|
112
|
-
# applied here for the same reason.
|
|
58
|
+
# Every reduction is omitted from the export, not nil, when undeclared, so
|
|
59
|
+
# existing read models keep their wire shape.
|
|
113
60
|
#
|
|
114
61
|
# @return [Hash] the declared emission, with `aggregate_heads`/`group_by` rows
|
|
115
|
-
# stringified,
|
|
62
|
+
# stringified, each reduction merged in only when declared, and
|
|
116
63
|
# `extra_options_to_h`'s own dynamic tail merged last
|
|
117
64
|
def to_h
|
|
118
|
-
reductions = {}
|
|
119
|
-
reductions[:count] = true if @count
|
|
120
|
-
reductions[:median_field] = @median_field.to_s if @median_field
|
|
121
65
|
super
|
|
122
66
|
.merge(aggregate_heads: @aggregate_heads.map { |head| head.merge(as: head[:as].to_s) })
|
|
123
67
|
.merge(group_by: @group_by.map { |row| row.merge(field: row[:field].to_s) })
|
|
124
|
-
.merge(
|
|
68
|
+
.merge(reduction_pairs)
|
|
125
69
|
.merge(extra_options_to_h)
|
|
126
70
|
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
# Stays nil (never false) when undeclared: the Judge skips setters whose source is nil.
|
|
75
|
+
def assign_reductions(count:, median_field:, sum_field:, avg_field:, min_field:, max_field:,
|
|
76
|
+
percentile_field:, percentile_at:, any_field:, all_field:)
|
|
77
|
+
@count = count ? true : nil
|
|
78
|
+
@median_field = median_field&.to_sym
|
|
79
|
+
@sum_field = sum_field&.to_sym
|
|
80
|
+
@avg_field = avg_field&.to_sym
|
|
81
|
+
@min_field = min_field&.to_sym
|
|
82
|
+
@max_field = max_field&.to_sym
|
|
83
|
+
@percentile_field = percentile_field&.to_sym
|
|
84
|
+
@percentile_at = percentile_at&.to_f
|
|
85
|
+
@any_field = any_field&.to_sym
|
|
86
|
+
@all_field = all_field&.to_sym
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def reduction_pairs
|
|
90
|
+
pairs = {}
|
|
91
|
+
pairs[:count] = true if @count
|
|
92
|
+
pairs[:median_field] = @median_field.to_s if @median_field
|
|
93
|
+
pairs[:sum_field] = @sum_field.to_s if @sum_field
|
|
94
|
+
pairs[:avg_field] = @avg_field.to_s if @avg_field
|
|
95
|
+
pairs[:min_field] = @min_field.to_s if @min_field
|
|
96
|
+
pairs[:max_field] = @max_field.to_s if @max_field
|
|
97
|
+
pairs[:percentile_field] = @percentile_field.to_s if @percentile_field
|
|
98
|
+
pairs[:percentile_at] = @percentile_at if @percentile_field
|
|
99
|
+
pairs[:any_field] = @any_field.to_s if @any_field
|
|
100
|
+
pairs[:all_field] = @all_field.to_s if @all_field
|
|
101
|
+
pairs
|
|
102
|
+
end
|
|
127
103
|
end
|
|
128
104
|
end
|
|
129
105
|
end
|
|
@@ -2,32 +2,13 @@ module Hecks
|
|
|
2
2
|
module Bluebook
|
|
3
3
|
# An attribute that points at another aggregate's head.
|
|
4
4
|
#
|
|
5
|
-
# Holds the target
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# would otherwise mint from the real constant just handed in. That string
|
|
9
|
-
# would need five separate readers to parse back apart — a regex in the
|
|
10
|
-
# command interpreter, string equality in the read-model interpreter and
|
|
11
|
-
# the SQLite adapter, and `delete_prefix` in the bluebook builder — one
|
|
12
|
-
# writer inventing a spelling, five readers reparsing it.
|
|
13
|
-
#
|
|
14
|
-
# Resolution is lazy and deliberately so: `reference_to
|
|
15
|
-
# Customer` may name an aggregate declared lower in the file — banking's
|
|
16
|
-
# Account points at Customer and survives only because Customer happens to
|
|
17
|
-
# be written above — so the edge cannot be resolved at declaration time.
|
|
18
|
-
# It resolves through the chapter's own IR (`Bluebook#aggregate`),
|
|
19
|
-
# which is scoped by construction : the lookup cannot walk anywhere but
|
|
20
|
-
# this chapter's declared heads, so a same-named aggregate in another
|
|
21
|
-
# loaded domain is unreachable rather than defended against.
|
|
22
|
-
#
|
|
23
|
-
# `to_s` still spells `"Reference<Customer>"`, because `Attribute#to_h` is
|
|
24
|
-
# part of the pinned byte-for-byte export contract. IR objects in
|
|
25
|
-
# the graph, strings in the export.
|
|
5
|
+
# Holds the target name and resolves lazily through the chapter's own IR, since
|
|
6
|
+
# `reference_to` may name an aggregate declared later in the file.
|
|
7
|
+
# `to_s` spells `"Reference<Customer>"`, which the export contract pins.
|
|
26
8
|
class Reference
|
|
27
9
|
attr_reader :target_name
|
|
28
10
|
|
|
29
|
-
# The Aggregate whose declaration carries this reference
|
|
30
|
-
# up to the chapter, stamped once every sibling has been read.
|
|
11
|
+
# The Aggregate whose declaration carries this reference, stamped once siblings are read.
|
|
31
12
|
attr_accessor :declared_in
|
|
32
13
|
|
|
33
14
|
# @param target_name [Module, String, Symbol] the aggregate constant this
|
|
@@ -36,9 +17,7 @@ module Hecks
|
|
|
36
17
|
@target_name = Naming.demodulise(target_name).to_s
|
|
37
18
|
end
|
|
38
19
|
|
|
39
|
-
# The Aggregate this points at, or nil when the target belongs to
|
|
40
|
-
# another domain — a cross-domain target may legitimately not be loaded,
|
|
41
|
-
# the same reading `across` policies get.
|
|
20
|
+
# The Aggregate this points at, or nil when the target belongs to another domain.
|
|
42
21
|
#
|
|
43
22
|
# @return [Bluebook::Aggregate, nil] the target aggregate, or `nil` when
|
|
44
23
|
# `declared_in`'s own chapter does not declare one by this name
|
|
@@ -61,21 +40,8 @@ module Hecks
|
|
|
61
40
|
def to_s = "Reference<#{@target_name}>"
|
|
62
41
|
def inspect = "#<Reference #{@target_name}>"
|
|
63
42
|
|
|
64
|
-
# Value equality
|
|
65
|
-
#
|
|
66
|
-
# N's own boot and a held era's own shadow reconstruction,
|
|
67
|
-
# coverage_check.rb's own comparison), are different objects and
|
|
68
|
-
# compare unequal by Ruby's default `==`. `EraGuard::ShapeDiff
|
|
69
|
-
# #diff_type`'s `held_type != current_type` check then reads as
|
|
70
|
-
# true for every reference_to attribute on every mint, regardless
|
|
71
|
-
# of whether the reference actually changed — a real refusal for
|
|
72
|
-
# attributes nothing about. `declared_in` (which aggregate carries
|
|
73
|
-
# this reference) is deliberately excluded: the same reference
|
|
74
|
-
# attribute exists once, but its `held_type`/`current_type` come
|
|
75
|
-
# from separately-parsed bluebooks with structurally different
|
|
76
|
-
# (if same-shaped) owning aggregates, and `declared_in` is a
|
|
77
|
-
# cross-reference for `resolve`, not part of what this attribute
|
|
78
|
-
# itself is.
|
|
43
|
+
# Value equality on the target; `declared_in` is excluded so references parsed from
|
|
44
|
+
# separate reads compare equal (EraGuard's shape diff depends on it).
|
|
79
45
|
#
|
|
80
46
|
# @param other [Object] the value to compare against
|
|
81
47
|
# @return [Boolean] whether `other` is a `Reference` to the same target
|
|
@@ -4,43 +4,8 @@ require "fileutils"
|
|
|
4
4
|
|
|
5
5
|
module Hecks
|
|
6
6
|
module Bluebook
|
|
7
|
-
# Boots a
|
|
8
|
-
#
|
|
9
|
-
# is structurally sound (no dead states, no unreachable transitions)
|
|
10
|
-
# without ever running a single command; this proves it works,
|
|
11
|
-
# catching exactly the class of bug static analysis structurally
|
|
12
|
-
# cannot see. Built and proved inside `bin/interview`'s own `smoke`
|
|
13
|
-
# command, where it first found two real bugs — a lifecycle field
|
|
14
|
-
# colliding with an explicit attribute, and a non-creating command
|
|
15
|
-
# missing `Command.ActsOn` — that reloaded clean, checked clean,
|
|
16
|
-
# and only broke once something actually dispatched against them.
|
|
17
|
-
#
|
|
18
|
-
# Synthesized args, never random (`Synthesizer`) — a String becomes
|
|
19
|
-
# a fixed marker, a closed set uses its own first admitted member, a
|
|
20
|
-
# reference reuses whatever this same run already minted for that
|
|
21
|
-
# target. One call per declared command, one call per declared
|
|
22
|
-
# report, per created root; every failure is collected and reported
|
|
23
|
-
# together rather than stopping at the first.
|
|
24
|
-
#
|
|
25
|
-
# ## What this cannot catch
|
|
26
|
-
#
|
|
27
|
-
# A call that dispatches cleanly but answers wrong — the read-model join
|
|
28
|
-
# bug this exact tool's own first user hit is the textbook case, a query
|
|
29
|
-
# that silently returned an empty array rather than raising.
|
|
30
|
-
# Smoke-testing proves nothing crashes; it does not prove the answer is
|
|
31
|
-
# the right one.
|
|
32
|
-
#
|
|
33
|
-
# ## False positives from a naive synthesizer
|
|
34
|
-
#
|
|
35
|
-
# A failure here is not necessarily a domain bug, either — it may
|
|
36
|
-
# just be `Synthesizer`'s own naive values (0, an empty list,
|
|
37
|
-
# "smoke-test") not satisfying a real invariant this simple
|
|
38
|
-
# generator was never built to reason about (a positive amount, a
|
|
39
|
-
# non-empty topping list, an email pattern). Confirmed against
|
|
40
|
-
# Banking and Pizzas: both real domains, both showing exactly that
|
|
41
|
-
# class of failure, neither one an actual bug. Building a
|
|
42
|
-
# synthesizer that respects arbitrary declared invariants is real
|
|
43
|
-
# constraint-aware generation — a separate, larger tool than this.
|
|
7
|
+
# Boots a bluebook and dispatches synthesized calls against it, collecting every failure.
|
|
8
|
+
# Catches what static checks miss, but not wrong answers, and naive values may trip invariants.
|
|
44
9
|
module SmokeTest
|
|
45
10
|
Failure = Struct.new(:domain, :aggregate, :command, :error, keyword_init: true) do
|
|
46
11
|
def to_s = "#{domain}::#{aggregate}.#{command}: #{error}"
|
|
@@ -48,44 +13,8 @@ module Hecks
|
|
|
48
13
|
|
|
49
14
|
module_function
|
|
50
15
|
|
|
51
|
-
# `dir`
|
|
52
|
-
#
|
|
53
|
-
# for exactly this purpose. Plain `Hecks.boot`, not
|
|
54
|
-
# `Router.boot` — proven all night, and it works against a
|
|
55
|
-
# domain with framework-attached chapters (`uses_framework
|
|
56
|
-
# "Governance"`, say) that `Router.boot` refuses outright: those
|
|
57
|
-
# chapters carry no `.world` of their own, which `Router`'s
|
|
58
|
-
# per-domain realm resolution requires and a plain boot never
|
|
59
|
-
# did. Reports are reachable the identical way regardless of
|
|
60
|
-
# boot path — `dispatcher.query("Domain.report_name", **args)`
|
|
61
|
-
# is the same `Dispatcher#query` either boot hands back; Router
|
|
62
|
-
# was never actually required for this, only its own namespace
|
|
63
|
-
# sugar (`Domain.report_name(...)` as a bare method call) was.
|
|
64
|
-
#
|
|
65
|
-
# Never boots `dir`'s own real bindings — measured, not a
|
|
66
|
-
# precaution taken on spec: pointed at `examples/pizzas` (a real,
|
|
67
|
-
# persistent store carrying real accumulated records), a
|
|
68
|
-
# synthesized `CreatePizza` collided with an actual pre-existing
|
|
69
|
-
# record literally named "smoke-test" — a coincidence this time,
|
|
70
|
-
# data pollution the next. `isolate!` copies only the `.bluebook`
|
|
71
|
-
# (and `.world`, for its realm) into a throwaway directory and
|
|
72
|
-
# deliberately leaves the real `.hecksagon` behind — with none
|
|
73
|
-
# present, `Ports::Persistence::BindingPolicy.default_binding`
|
|
74
|
-
# binds every aggregate to Memory on its own, no settings needed.
|
|
75
|
-
# Whatever `dir` is really bound to in production is never
|
|
76
|
-
# touched.
|
|
77
|
-
# `install_facade: false` — this only ever dispatches by FQN string
|
|
78
|
-
# (below), so it never needs the `Widget::Item.Add(...)` Ruby
|
|
79
|
-
# sugar `Hecks.boot` installs by default. Skipping it matters
|
|
80
|
-
# here specifically: that sugar lands as a bare global constant on
|
|
81
|
-
# `Object` per domain and per aggregate name, with no scoping and
|
|
82
|
-
# no cleanup — and this tool boots throwaway domains under
|
|
83
|
-
# whatever generic names the caller's `.bluebook` happens to use
|
|
84
|
-
# ("Widget", "Item", "Tag" in this tool's own specs). Installing
|
|
85
|
-
# the facade would leak those names into the rest of the process —
|
|
86
|
-
# measured, not hypothetical: it once left a stale `Widget` constant
|
|
87
|
-
# that corrupted an unrelated spec's own unrelated use of the same
|
|
88
|
-
# bare name.
|
|
16
|
+
# Boots an isolated copy of `dir` on Memory bindings, so real stores are never touched.
|
|
17
|
+
# Skips the facade: its bare global constants would leak into the rest of the process.
|
|
89
18
|
#
|
|
90
19
|
# @param dir [String] path to a bootable hecks app directory — a saved
|
|
91
20
|
# domain, or a throwaway one rendered into a temp dir for this purpose
|
|
@@ -102,18 +31,8 @@ module Hecks
|
|
|
102
31
|
end
|
|
103
32
|
end
|
|
104
33
|
|
|
105
|
-
#
|
|
106
|
-
#
|
|
107
|
-
# real adapter it names, not to Memory; copied verbatim, they
|
|
108
|
-
# apply to the wrong binding and refuse with a `WiringError`
|
|
109
|
-
# ("Memory does not declare :dir") — measured, not assumed, the
|
|
110
|
-
# first time this ran against Banking. A plain `Hecks.boot`
|
|
111
|
-
# needs no realm to address anything (unlike `Router.boot`), so
|
|
112
|
-
# leaving the world out entirely is the correct minimal isolation,
|
|
113
|
-
# not a workaround. `translations/*.bluebook` also deliberately
|
|
114
|
-
# excluded — those are earlier, superseded versions of the same
|
|
115
|
-
# domain; smoke-testing the current declaration is the point, not
|
|
116
|
-
# its whole history.
|
|
34
|
+
# Copies only the `.bluebook` files: a real `.world` carries settings keyed to its own
|
|
35
|
+
# adapter and refuses under Memory.
|
|
117
36
|
#
|
|
118
37
|
# @param dir [String] path to the source hecks app directory to isolate
|
|
119
38
|
# @param scratch [String] path to the throwaway directory to copy the
|
|
@@ -126,16 +45,7 @@ module Hecks
|
|
|
126
45
|
Dir.glob(File.join(source, "*.bluebook")).each { |file| FileUtils.cp(file, target) }
|
|
127
46
|
end
|
|
128
47
|
|
|
129
|
-
#
|
|
130
|
-
# a later aggregate's own shape needs from an earlier one (a
|
|
131
|
-
# `TripItem` needing a real `PackingItem`, say) already exists by
|
|
132
|
-
# the time that aggregate's own turn comes, the same reason
|
|
133
|
-
# `Synthesizer#args_for`'s `created` map is threaded through in
|
|
134
|
-
# this same order.
|
|
135
|
-
# `created` and `failures` thread through both the aggregate loop and the
|
|
136
|
-
# read_models loop below in the declaration order the comment above already
|
|
137
|
-
# documents; splitting would turn that ordering into an implicit contract on
|
|
138
|
-
# parameter/return passing instead of one visible sequence.
|
|
48
|
+
# Walks aggregates in declaration order so later ones can reference earlier `created` ids.
|
|
139
49
|
# rubocop:disable-next Metrics/AbcSize
|
|
140
50
|
#
|
|
141
51
|
# @param dispatcher [Runtime::Dispatcher, Runtime::RemoteDispatcher] the
|
|
@@ -178,7 +88,7 @@ module Hecks
|
|
|
178
88
|
|
|
179
89
|
chapter.read_models.each do |model|
|
|
180
90
|
root_id = created[model.reference_target]
|
|
181
|
-
#
|
|
91
|
+
# No root was created, so there is nothing to query yet.
|
|
182
92
|
next unless root_id
|
|
183
93
|
|
|
184
94
|
dispatcher.query("#{domain}.#{model.query_name}", model.reference_name => root_id)
|
|
@@ -1,58 +1,21 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
# `Attribute`, `ValueObject`), so it works against any
|
|
6
|
-
# loaded bluebook, not one particular domain: a String becomes a
|
|
7
|
-
# fixed marker; an Integer becomes 0; a closed set uses its own
|
|
8
|
-
# first admitted member (never a value a real `one_of` would
|
|
9
|
-
# legitimately refuse); a reference uses whatever id the caller
|
|
10
|
-
# already knows for that target (`created`), so a command whose
|
|
11
|
-
# own shape depends on an earlier-created record — a `TripItem`
|
|
12
|
-
# needing a real `PackingItem`, say — gets something real to work
|
|
13
|
-
# with instead of a guess that would only ever fail.
|
|
14
|
-
#
|
|
15
|
-
# Extracted from `bin/interview`'s own smoke test, where this first
|
|
16
|
-
# got built and proved — the logic never actually knew anything
|
|
17
|
-
# about the Interview domain or any one app; it only ever read IR.
|
|
3
|
+
# Synthesizes one argument per declared attribute from the IR: fixed markers for scalars,
|
|
4
|
+
# a closed set's first member, and a known `created` id for references. Never random.
|
|
18
5
|
module Synthesizer
|
|
19
|
-
#
|
|
20
|
-
# a value object's own field names is another value object,
|
|
21
|
-
# nested (`Pizza.price_cents: Price`, itself wrapping `cents:
|
|
22
|
-
# Integer` — a real, ordinary shape, not an edge case). Missing
|
|
23
|
-
# this the first time round meant a nested field silently
|
|
24
|
-
# synthesized as the bare string `"smoke-test"` instead of the
|
|
25
|
-
# real nested structure a command actually declares — caught
|
|
26
|
-
# only by testing against Pizzas instead of re-testing against
|
|
27
|
-
# the one simpler domain this was first built for, where every
|
|
28
|
-
# value object happened to be single-field and primitive-typed.
|
|
29
|
-
# The same list `Attribute::PRIMITIVES` holds, referenced rather
|
|
30
|
-
# than repeated. It was a second, byte-identical copy — and only
|
|
31
|
-
# the Attribute one is held to `vocabulary.bluebook` by
|
|
32
|
-
# spec/vocabulary_conformance_spec, so this copy could drift from
|
|
33
|
-
# the language and nothing would say so.
|
|
6
|
+
# Shared with `Attribute::PRIMITIVES` so the two lists cannot drift.
|
|
34
7
|
PRIMITIVES = Attribute::PRIMITIVES
|
|
35
8
|
|
|
36
9
|
module_function
|
|
37
10
|
|
|
38
|
-
# `
|
|
39
|
-
#
|
|
40
|
-
# name back to its own declared shape. `created` maps an
|
|
41
|
-
# aggregate name to a real id already minted for it earlier in
|
|
42
|
-
# the same run; a reference whose target isn't in there yet gets
|
|
43
|
-
# a placeholder instead of failing outright, since the caller may
|
|
44
|
-
# not care about that particular argument's real value.
|
|
11
|
+
# Synthesizes arguments for `command`. A reference whose target is not in `created`
|
|
12
|
+
# gets a placeholder id.
|
|
45
13
|
#
|
|
46
|
-
# @param chapter [Bluebook::Chapter]
|
|
47
|
-
# belongs to, needed to resolve a value object type name back to its
|
|
48
|
-
# own declared shape
|
|
14
|
+
# @param chapter [Bluebook::Chapter] resolves value object type names to their shapes
|
|
49
15
|
# @param aggregate [Bluebook::Aggregate] the aggregate `command` belongs to
|
|
50
|
-
# @param command [Class] the
|
|
51
|
-
#
|
|
52
|
-
# @
|
|
53
|
-
# id already minted for it earlier in this same run
|
|
54
|
-
# @return [Hash{Symbol => Object}] one synthesized value per declared
|
|
55
|
-
# attribute, keyed by attribute name
|
|
16
|
+
# @param command [Class] the `Bluebook::Command` subclass to synthesize arguments for
|
|
17
|
+
# @param created [Hash{String => Object}] aggregate name mapped to an id minted earlier
|
|
18
|
+
# @return [Hash{Symbol => Object}] one synthesized value per declared attribute
|
|
56
19
|
def args_for(chapter, aggregate, command, created = {})
|
|
57
20
|
command.attributes.to_h do |attribute|
|
|
58
21
|
if attribute.reference?
|
|
@@ -63,13 +26,8 @@ module Hecks
|
|
|
63
26
|
end
|
|
64
27
|
end
|
|
65
28
|
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
# refused), otherwise one synthesized scalar per declared field.
|
|
69
|
-
# Falls back to searching every aggregate in the chapter, the
|
|
70
|
-
# same tolerance `bin/interview shape`'s own lookup already
|
|
71
|
-
# needed: a value object referenced by name doesn't have to be
|
|
72
|
-
# declared on the same aggregate using it.
|
|
29
|
+
# Synthesizes a value object: its first member if a closed set, else one value per field.
|
|
30
|
+
# Falls back to searching every aggregate, since a value object need not be local.
|
|
73
31
|
#
|
|
74
32
|
# @param chapter [Bluebook::Chapter] the chapter to search when
|
|
75
33
|
# `type_name` is not declared on `aggregate` itself
|
|
@@ -91,11 +49,7 @@ module Hecks
|
|
|
91
49
|
value_object.attributes.to_h { |field| [field.name, field_value_for(chapter, aggregate, field.type)] }
|
|
92
50
|
end
|
|
93
51
|
|
|
94
|
-
# A
|
|
95
|
-
# type is one of the true primitives, otherwise that type names
|
|
96
|
-
# another value object, resolved the identical way `value_for`
|
|
97
|
-
# resolves any other one (its own closed set or its own nested
|
|
98
|
-
# fields, however deep that nesting actually goes).
|
|
52
|
+
# A field's value: a scalar for a primitive type, otherwise the nested value object.
|
|
99
53
|
#
|
|
100
54
|
# @param chapter [Bluebook::Chapter] see `value_for`
|
|
101
55
|
# @param aggregate [Bluebook::Aggregate] see `value_for`
|
|
@@ -107,10 +61,7 @@ module Hecks
|
|
|
107
61
|
PRIMITIVES.include?(type_name.to_s) ? scalar_for(type_name) : value_for(chapter, aggregate, type_name)
|
|
108
62
|
end
|
|
109
63
|
|
|
110
|
-
# A
|
|
111
|
-
# closed set's own field (that's `value_for`'s job, reading the
|
|
112
|
-
# set's real first member) — only for a plain scalar field with
|
|
113
|
-
# no declared vocabulary to respect.
|
|
64
|
+
# A primitive's synthesized value; closed-set fields are handled by `value_for`.
|
|
114
65
|
#
|
|
115
66
|
# @param primitive [String, Symbol] the primitive type name, such as
|
|
116
67
|
# `"Integer"` or `"String"`
|
|
@@ -1,79 +1,47 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
|
-
# One field crossing a value-object boundary
|
|
4
|
-
# member, a VO member becoming a scalar, or a rename across the move.
|
|
3
|
+
# One field crossing a value-object boundary, or carried under a new name.
|
|
5
4
|
# Paths are dotted ("price.cents") for a VO member, bare otherwise.
|
|
6
5
|
TranslationMove = Struct.new(:from, :to)
|
|
7
6
|
|
|
8
|
-
# One field whose value,
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# Paths follow `TranslationMove`'s convention (dotted reaches a VO
|
|
12
|
-
# member); `values` maps every old-era value that can appear to its
|
|
13
|
-
# new-era replacement.
|
|
14
|
-
# `:values` shadows Struct#values on purpose (same reasoning as
|
|
15
|
-
# AttributeCollector::OneOf) — no caller reads the built-in behavior.
|
|
7
|
+
# One field whose value changed, bridged by an exhaustive lookup table.
|
|
8
|
+
# Paths follow `TranslationMove`; `values` maps each old-era value to its new-era one.
|
|
9
|
+
# `:values` shadows Struct#values on purpose; no caller reads the built-in.
|
|
16
10
|
# rubocop:disable-next Lint/StructNewOverride
|
|
17
11
|
TranslationConvert = Struct.new(:from, :to, :values)
|
|
18
12
|
|
|
19
|
-
# A value object's or entity's
|
|
20
|
-
#
|
|
21
|
-
# because the attribute kept its name and only the type it points at
|
|
22
|
-
# changed name. Mirrors `was:` one level deeper.
|
|
13
|
+
# A value object's or entity's type name changed with its members unchanged.
|
|
14
|
+
# The attribute kept its name, so `rename`/`move` cannot express it.
|
|
23
15
|
TranslationRetype = Struct.new(:from, :to)
|
|
24
16
|
|
|
25
|
-
# A computed transform
|
|
26
|
-
#
|
|
27
|
-
# inside the compiled Postgres head. No in-process reference exists
|
|
28
|
-
# to check it against; any aggregate carrying one refuses to boot on
|
|
29
|
-
# every other adapter.
|
|
17
|
+
# A computed transform whose only implementation is a SQL expression run inside
|
|
18
|
+
# the compiled Postgres head; an aggregate carrying one refuses to boot elsewhere.
|
|
30
19
|
TranslationCompute = Struct.new(:from, :to, :sql)
|
|
31
20
|
|
|
32
|
-
# The aggregate's own
|
|
33
|
-
#
|
|
34
|
-
# name (`retype`), or a value transform (`compute`): the record's own
|
|
35
|
-
# key. No `from:`/`to:` path, unlike every other rule here, because
|
|
36
|
-
# nothing is being consumed from or moved into `state` — the state
|
|
37
|
-
# a record already holds is untouched; only what identifies it is
|
|
38
|
-
# recomputed. Same SQL-only, Postgres-only, no-in-process-reference
|
|
39
|
-
# shape `compute` already has, and for the same reason: there is
|
|
40
|
-
# nothing to check this against outside the compiled head.
|
|
21
|
+
# The aggregate's own key is recomputed by a SQL expression; stored state is untouched.
|
|
22
|
+
# Postgres-only like `compute`, and it has no `from:`/`to:` path.
|
|
41
23
|
TranslationRekey = Struct.new(:sql)
|
|
42
24
|
|
|
43
|
-
# A newly added, required attribute with no source in old data
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
# reads until the next command against it writes a real one; unlike
|
|
47
|
-
# `compute`, this is adapter-agnostic — applied in-process by
|
|
48
|
-
# `Lineage#translate`, the same as rename/move/drop, because there is
|
|
49
|
-
# nothing to compute, only a value to declare.
|
|
25
|
+
# A newly added, required attribute with no source in old data.
|
|
26
|
+
# `default` is what an existing record reads until a command writes a real value;
|
|
27
|
+
# applied in-process by `Lineage#translate` on every adapter.
|
|
50
28
|
TranslationBackfill = Struct.new(:name, :default)
|
|
51
29
|
|
|
52
|
-
# One aggregate's part of a translation
|
|
53
|
-
#
|
|
54
|
-
# the old era to the new one. `drops` exists so data loss is a
|
|
55
|
-
# declared decision, not a field that quietly stopped being
|
|
56
|
-
# explained.
|
|
30
|
+
# One aggregate's part of a translation between eras.
|
|
31
|
+
# `drops` makes data loss a declared decision.
|
|
57
32
|
class TranslationAggregate
|
|
58
33
|
attr_reader :name, :was, :renames, :moves, :converts, :drops, :retypes, :computes, :rekeys, :backfills
|
|
59
34
|
|
|
60
35
|
# @param name [String, Symbol] the aggregate's name in the destination era
|
|
61
|
-
# @param was [String, Symbol, nil]
|
|
62
|
-
#
|
|
63
|
-
# @param
|
|
64
|
-
# @param
|
|
65
|
-
# boundary
|
|
66
|
-
# @param converts [Array<Bluebook::TranslationConvert>] fields whose value is
|
|
67
|
-
# remapped through a declared lookup table
|
|
36
|
+
# @param was [String, Symbol, nil] its name in the origin era, or `nil` if unchanged
|
|
37
|
+
# @param renames [Hash{Symbol => Symbol}] old field name to new field name
|
|
38
|
+
# @param moves [Array<Bluebook::TranslationMove>] fields crossing a value-object boundary
|
|
39
|
+
# @param converts [Array<Bluebook::TranslationConvert>] fields remapped via a lookup table
|
|
68
40
|
# @param drops [Array<Symbol>] fields deliberately not carried forward
|
|
69
|
-
# @param retypes [Array<Bluebook::TranslationRetype>] value object or entity type
|
|
70
|
-
#
|
|
71
|
-
# @param
|
|
72
|
-
#
|
|
73
|
-
# @param rekeys [Array<Bluebook::TranslationRekey>] SQL expressions that recompute
|
|
74
|
-
# the aggregate's own identity
|
|
75
|
-
# @param backfills [Array<Bluebook::TranslationBackfill>] newly added required
|
|
76
|
-
# fields with no source in old data
|
|
41
|
+
# @param retypes [Array<Bluebook::TranslationRetype>] value object or entity type changes
|
|
42
|
+
# @param computes [Array<Bluebook::TranslationCompute>] fields computed by Postgres-only SQL
|
|
43
|
+
# @param rekeys [Array<Bluebook::TranslationRekey>] SQL recomputing the aggregate's identity
|
|
44
|
+
# @param backfills [Array<Bluebook::TranslationBackfill>] new required fields, no old source
|
|
77
45
|
def initialize(name:, was: nil, renames: {}, moves: [], converts: [], drops: [], retypes: [],
|
|
78
46
|
computes: [], rekeys: [], backfills: [])
|
|
79
47
|
@name = name.to_s
|
|
@@ -89,12 +57,8 @@ module Hecks
|
|
|
89
57
|
end
|
|
90
58
|
end
|
|
91
59
|
|
|
92
|
-
# A declared map from one era of a domain's storage shape to the
|
|
93
|
-
#
|
|
94
|
-
# concept: old records are translated when they are replayed, never
|
|
95
|
-
# rewritten. `retired` acknowledges aggregates that are gone
|
|
96
|
-
# outright — not renamed — so their disappearance is a decision,
|
|
97
|
-
# not an accident.
|
|
60
|
+
# A declared map from one era of a domain's storage shape to the next.
|
|
61
|
+
# Old records are translated on replay, never rewritten; `retired` names aggregates removed.
|
|
98
62
|
class Translation
|
|
99
63
|
attr_reader :domain, :from, :to, :aggregates, :retired
|
|
100
64
|
|
|
@@ -104,7 +68,7 @@ module Hecks
|
|
|
104
68
|
# @param aggregates [Array<Bluebook::TranslationAggregate>] each aggregate's own
|
|
105
69
|
# translation rules
|
|
106
70
|
# @param retired [Array<String>] the names of aggregates gone outright in the
|
|
107
|
-
# destination era
|
|
71
|
+
# destination era
|
|
108
72
|
def initialize(domain:, from:, to:, aggregates: [], retired: [])
|
|
109
73
|
@domain = domain.to_s
|
|
110
74
|
@from = from
|