hecks 2.8.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 +4 -6
- 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 +17 -98
- 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 +5 -48
- 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 +31 -141
- 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 +14 -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 +25 -31
- 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 +16 -69
- 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 -194
- 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 -111
- 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 +53 -267
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +43 -305
- 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 +6 -21
- data/lib/hecks/bluebook/model_check.rb +100 -597
- 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 +4 -23
- data/lib/hecks/cli/document.rb +3 -21
- data/lib/hecks/cli/ir.rb +3 -5
- data/lib/hecks/cli/mcp.rb +2 -3
- data/lib/hecks/cli/mcp_door.rb +19 -114
- data/lib/hecks/cli/model_check.rb +18 -85
- data/lib/hecks/cli/project_cli.rb +7 -40
- data/lib/hecks/cli/project_diagrams.rb +0 -7
- data/lib/hecks/cli/run.rb +12 -58
- data/lib/hecks/cli/smoke_test.rb +2 -16
- data/lib/hecks/cli/stores.rb +1 -2
- data/lib/hecks/cli.rb +18 -24
- 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 +82 -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 +9 -90
- 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 +61 -64
- 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 +6 -25
- 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 +18 -124
- 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 +150 -678
- 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 +2 -15
- 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 +42 -134
- 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 +3 -6
- data/lib/hecks/release/runner/commands.rb +2 -4
- data/lib/hecks/release/runner/gem_publisher.rb +2 -3
- data/lib/hecks/release/runner/npm_publisher.rb +6 -20
- data/lib/hecks/release/runner/preflight.rb +4 -19
- data/lib/hecks/release/runner/tagger.rb +2 -9
- data/lib/hecks/release/runner.rb +3 -32
- 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 +124 -306
- 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 +64 -219
- 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 +117 -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 +99 -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 +141 -568
- 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.rb +26 -81
- 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 +3 -2
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
#
|
|
2
|
-
# attached
|
|
3
|
-
# into the ATTACHING domain's own per-boot registry, framework.rb:43-66
|
|
4
|
-
# — since hecks gives each tenant its own separate `Runtime.boot`, a
|
|
5
|
-
# framework-attached Tenant would exist as a single-row copy inside
|
|
6
|
-
# EVERY tenant's own boot, never as one cross-tenant list, defeating
|
|
7
|
-
# this domain's whole point). `Deploy::Tenant` (deploy.bluebook) stays
|
|
8
|
-
# a SEPARATE, validation-only aggregate — a shape check before
|
|
9
|
-
# provisioning; this is the durable, reactable runtime fact that
|
|
10
|
-
# aggregate's own comment named and deliberately deferred. Two
|
|
11
|
-
# different meanings of "Tenant," on purpose, translated at the one
|
|
12
|
-
# explicit seam `tenancy.hecksagon`'s own `translates` line names.
|
|
1
|
+
# Its own bounded context, booted centrally like `Deploy` and never attached via `uses_framework`:
|
|
2
|
+
# an attached Tenant would be a single-row copy in every tenant's boot, not one cross-tenant list.
|
|
13
3
|
Hecks.bluebook "Tenancy" do
|
|
14
4
|
vision "Which tenants exist, which realm and storage each one owns, and whether each is currently allowed to run, is one queryable, historical fact — not scattered across a world-file overlay and a validation-only command."
|
|
15
5
|
|
|
@@ -50,14 +40,8 @@ Hecks.bluebook "Tenancy" do
|
|
|
50
40
|
attribute :value, String, one_of: ["Active", "Suspended", "Retired"]
|
|
51
41
|
end
|
|
52
42
|
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
# reaction once `Deploy::Tenant.Provision`'s adapter has already
|
|
56
|
-
# written the world overlay and proven the domain boots. Driving
|
|
57
|
-
# provisioning FROM this command's own emission would need a
|
|
58
|
-
# process manager reacting to it — the exact durable-saga cost
|
|
59
|
-
# `deploy.bluebook`'s own `Tenant` aggregate comment already argued
|
|
60
|
-
# three local, idempotent, single-process steps don't need.
|
|
43
|
+
# Fires after provisioning, never before: driving provisioning from this command's emission
|
|
44
|
+
# would need a process manager, the durable-saga cost `deploy.bluebook` argues against.
|
|
61
45
|
command "Register" do
|
|
62
46
|
goal "Record that a tenant now exists, after its shape has been validated and its schema provisioned"
|
|
63
47
|
|
|
@@ -74,13 +58,8 @@ Hecks.bluebook "Tenancy" do
|
|
|
74
58
|
emits "TenantRegistered"
|
|
75
59
|
end
|
|
76
60
|
|
|
77
|
-
#
|
|
78
|
-
#
|
|
79
|
-
# running. Spelled out in this command's own `goal`, not just a
|
|
80
|
-
# comment, so it survives into generated docs/corpus rather than
|
|
81
|
-
# quietly reading as enforced when it isn't — the exact "a dial
|
|
82
|
-
# says a check runs when nothing runs it" defect class this
|
|
83
|
-
# project has already been burned by once.
|
|
61
|
+
# Record-only: nothing reads `status` before dispatch or boot yet. Stated in the `goal` too,
|
|
62
|
+
# so generated docs do not read it as enforced.
|
|
84
63
|
command "Suspend" do
|
|
85
64
|
goal "Record that a tenant should no longer be treated as active — nothing yet enforces this at dispatch or boot time"
|
|
86
65
|
|
|
@@ -101,11 +80,8 @@ Hecks.bluebook "Tenancy" do
|
|
|
101
80
|
emits "TenantReactivated"
|
|
102
81
|
end
|
|
103
82
|
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
-
# no-adapter-calls-from-a-handler shape every other command in this
|
|
107
|
-
# corpus holds to. Real teardown, if ever built, is its own
|
|
108
|
-
# separate, deliberately reviewed tool — never implicit here.
|
|
83
|
+
# Record-only: dropping a live schema from a command handler would break the pure, no-adapter
|
|
84
|
+
# handler shape. Teardown, if built, is a separate reviewed tool.
|
|
109
85
|
command "Retire" do
|
|
110
86
|
goal "Record that a tenant is permanently done — real schema teardown is a deliberately separate, manually-run tool, never automatic here"
|
|
111
87
|
|
|
@@ -1,30 +1,13 @@
|
|
|
1
1
|
Hecks.hecksagon "Tenancy" do
|
|
2
2
|
Tenancy::Tenant.persisted_by("LocalStorage")
|
|
3
3
|
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
# `
|
|
7
|
-
#
|
|
8
|
-
# name only, never the domain (confirmed live — a domain-qualified
|
|
9
|
-
# `on` silently never matches).
|
|
4
|
+
# The one seam where Deploy's "Tenant" (a validated request) becomes Tenancy's "Tenant" (a fact).
|
|
5
|
+
# `on` omits the "Deploy::" prefix: PolicyInterpreter matches event and aggregate name only.
|
|
6
|
+
# `with:` is required: the event payload also carries the port's dispatch-time args, which
|
|
7
|
+
# `Register` does not declare and would refuse.
|
|
10
8
|
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# port operation's OWN dispatch-time args (database/adapter,
|
|
14
|
-
# PortOperationInterpreter#ask's `ctx.args.merge(spread(answer))`),
|
|
15
|
-
# so the whole payload forwarded verbatim carries two fields
|
|
16
|
-
# `Register` never declares and gets refused outright.
|
|
17
|
-
# CHECKED TOGETHER, NOT IN ISOLATION — `bin/model_check`'s own
|
|
18
|
-
# `deaf_policy` check only sees ONE domain's own emitted events by
|
|
19
|
-
# default, which would flag this reaction as deaf (it reacts to a
|
|
20
|
-
# FOREIGN event, not one Tenancy itself emits). Fixed by
|
|
21
|
-
# `global_emitted_events:`, a cross-domain set `bin/model_check`
|
|
22
|
-
# builds across every target it boots in one run — run `bin/model_check
|
|
23
|
-
# lib/hecks/deploy lib/hecks/tenancy` (both targets, one invocation),
|
|
24
|
-
# not each directory separately, or this reads as a false positive
|
|
25
|
-
# again. Neither `deploy` nor `tenancy` is in
|
|
26
|
-
# `Hecks::Corpus::MODEL_CHECK_KINDS`, so the automatic sweep never
|
|
27
|
-
# reaches this at all, checked or not.
|
|
9
|
+
# Check with `bin/model_check lib/hecks/deploy lib/hecks/tenancy` in one run; alone, the
|
|
10
|
+
# reaction to a foreign event reads as a deaf policy.
|
|
28
11
|
translates "RegisterProvisionedTenant" do
|
|
29
12
|
on Tenant::TenantProvisioned
|
|
30
13
|
trigger Tenant::Register, with: { slug: :slug, domain: :domain, realm: :realm, schema: :schema }
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Vendoring
|
|
3
|
-
# The environment a `git` subprocess needs
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
# Git exports `GIT_DIR`, `GIT_INDEX_FILE` and their kin to the hooks it
|
|
7
|
-
# runs (a push from a linked worktree runs `pre-push` this way). Every
|
|
8
|
-
# `git` that a hook's process tree starts inherits them, and they outrank
|
|
9
|
-
# both `git -C` and the working directory: a command aimed at a scratch
|
|
10
|
-
# repository would read, or commit into, the repository that was pushing.
|
|
3
|
+
# The environment a `git` subprocess needs to target one repository:
|
|
4
|
+
# inherited GIT_DIR variables outrank `-C` and would redirect it elsewhere.
|
|
11
5
|
module GitEnvironment
|
|
12
6
|
# The variables that pin git to one repository, work tree or index.
|
|
13
7
|
INHERITED = %w[
|
|
@@ -4,14 +4,8 @@ require_relative "git_environment"
|
|
|
4
4
|
|
|
5
5
|
module Hecks
|
|
6
6
|
module Vendoring
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# Everything here names a commit and reads what that commit holds, so a
|
|
11
|
-
# shared checkout with uncommitted edits, or a different branch checked
|
|
12
|
-
# out, cannot leak into what gets vendored. Every command runs under
|
|
13
|
-
# {GitEnvironment.clean}, so a `GIT_DIR` inherited from a hook cannot
|
|
14
|
-
# redirect it to some other repository.
|
|
7
|
+
# Reads one commit of a local git repository via the `git` executable,
|
|
8
|
+
# under {GitEnvironment.clean} so an inherited `GIT_DIR` can't redirect it.
|
|
15
9
|
class GitSource
|
|
16
10
|
# @param path [String] a directory that is a git repository (or a
|
|
17
11
|
# worktree of one)
|
|
@@ -57,8 +51,8 @@ module Hecks
|
|
|
57
51
|
|
|
58
52
|
# Lists the files directly inside a directory of a commit.
|
|
59
53
|
#
|
|
60
|
-
# Subdirectories are
|
|
61
|
-
# top-level files
|
|
54
|
+
# Subdirectories are excluded on purpose; only the subtree's own
|
|
55
|
+
# top-level files are vendored.
|
|
62
56
|
#
|
|
63
57
|
# @param ref [String] the commit-ish to read from
|
|
64
58
|
# @param subtree [String] a repository-relative directory, such as `"payments/bluebook"`
|
data/lib/hecks/vendoring.rb
CHANGED
|
@@ -5,29 +5,7 @@ require_relative "vendoring/git_source"
|
|
|
5
5
|
|
|
6
6
|
module Hecks
|
|
7
7
|
# Pins an exact commit of one directory of another repository into a
|
|
8
|
-
# project's own tree.
|
|
9
|
-
#
|
|
10
|
-
# ## What it is
|
|
11
|
-
#
|
|
12
|
-
# A project that consumes a package from a private repository cannot clone
|
|
13
|
-
# that repository when its image builds, and a live pull would make the build
|
|
14
|
-
# depend on whatever is checked out. So the files are exported from one
|
|
15
|
-
# commit, committed into the consumer, and a marker file beside them records
|
|
16
|
-
# which commit they came from. Only the files directly inside the named
|
|
17
|
-
# subtree are taken; the source's own specs, ports, adapters and data stay
|
|
18
|
-
# where they are.
|
|
19
|
-
#
|
|
20
|
-
# ## Marker
|
|
21
|
-
#
|
|
22
|
-
# `VENDORED_COMMIT` holds the full 40-character commit id and a newline, so
|
|
23
|
-
# `git -C <source> show <id>:<subtree>` reproduces the vendored files. The
|
|
24
|
-
# marker sits in `into`, beside the exported directory.
|
|
25
|
-
#
|
|
26
|
-
# ## Whole-directory replacement
|
|
27
|
-
#
|
|
28
|
-
# A pin replaces `into` entirely, so a file the source dropped does not
|
|
29
|
-
# linger. Nothing is touched until the export succeeded and the optional
|
|
30
|
-
# block accepted it.
|
|
8
|
+
# project's own tree, so a build doesn't depend on a live clone.
|
|
31
9
|
module Vendoring
|
|
32
10
|
# Raised when a pin cannot be made or is refused.
|
|
33
11
|
class Error < StandardError; end
|
|
@@ -45,27 +23,17 @@ module Hecks
|
|
|
45
23
|
# @return [Array<String>] the exported files' names, sorted
|
|
46
24
|
Pin = Struct.new(:commit, :dir, :files, keyword_init: true)
|
|
47
25
|
|
|
48
|
-
# Exports the top-level files of one directory at one commit into `into
|
|
49
|
-
#
|
|
50
|
-
# The exported files land in `into/<last segment of subtree>/`; the marker
|
|
51
|
-
# lands in `into` itself. The optional block sees the staged files before
|
|
52
|
-
# anything is replaced and may raise to refuse the pin.
|
|
26
|
+
# Exports the top-level files of one directory at one commit into `into`,
|
|
27
|
+
# replacing it wholly; the optional block may refuse before that happens.
|
|
53
28
|
#
|
|
54
29
|
# @param from [String] path of the local source repository
|
|
55
|
-
# @param ref [String] the commit-ish to export
|
|
56
|
-
# @param subtree [String] repository-relative directory to export
|
|
57
|
-
# `"payments/bluebook"`
|
|
30
|
+
# @param ref [String] the commit-ish to export
|
|
31
|
+
# @param subtree [String] repository-relative directory to export
|
|
58
32
|
# @param into [String] the directory to replace with the export
|
|
59
|
-
# @param glob [String]
|
|
33
|
+
# @param glob [String] pattern each exported file's name must match
|
|
60
34
|
# @param marker [String] name of the file recording the commit
|
|
61
|
-
# @yield [staged, commit] the staged files, before `into` is touched
|
|
62
|
-
# @yieldparam staged [String] directory holding the exported files
|
|
63
|
-
# @yieldparam commit [String] the resolved commit id
|
|
64
|
-
# @yieldreturn [Hash{String => String}, nil] extra files to write into `into`,
|
|
65
|
-
# keyed by file name; nil for none
|
|
66
35
|
# @return [Pin] the commit and where the files landed
|
|
67
|
-
# @raise [Vendoring::Error] if the source
|
|
68
|
-
# the subtree holds no matching file, or the block refuses
|
|
36
|
+
# @raise [Vendoring::Error] if the source or a matching file is missing
|
|
69
37
|
def self.pin(from:, ref:, subtree:, into:, glob: "*", marker: MARKER)
|
|
70
38
|
source = GitSource.new(from)
|
|
71
39
|
commit = source.commit(ref)
|
|
@@ -83,13 +51,6 @@ module Hecks
|
|
|
83
51
|
end
|
|
84
52
|
|
|
85
53
|
# Replaces `into` with the staged files, the marker and any extras.
|
|
86
|
-
#
|
|
87
|
-
# @param staged [String] directory holding the exported files
|
|
88
|
-
# @param into [String] the directory to replace
|
|
89
|
-
# @param marker [String] marker file name
|
|
90
|
-
# @param commit [String] commit id the marker records
|
|
91
|
-
# @param extras [Hash{String => String}] more files to write into `into`
|
|
92
|
-
# @return [void]
|
|
93
54
|
def self.install(staged, into, marker, commit, extras)
|
|
94
55
|
FileUtils.rm_rf(into)
|
|
95
56
|
FileUtils.mkdir_p(into)
|
data/lib/hecks/version.rb
CHANGED
|
@@ -1,16 +1,5 @@
|
|
|
1
1
|
module Hecks
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
|
|
5
|
-
# gem's own dependencies (prism among them) before Bundler has even
|
|
6
|
-
# resolved what to install. A real, live chicken-and-egg bug caught
|
|
7
|
-
# deploying to a Ruby version that doesn't bundle prism for free
|
|
8
|
-
# (AWS Lambda's ruby3.2): the gemspec's own `require_relative
|
|
9
|
-
# "lib/hecks"` pulled in adapters/driven/prism.rb's
|
|
10
|
-
# unconditional `require "prism"`, which failed before Bundler ever
|
|
11
|
-
# read what the gemspec itself declared as a dependency — declaring
|
|
12
|
-
# it there, or even in the consuming Gemfile, never closed the gap,
|
|
13
|
-
# because gemspec evaluation happens before anything Bundler
|
|
14
|
-
# resolves is actually loadable yet.
|
|
15
|
-
VERSION = "2.8.0".freeze
|
|
2
|
+
# Read directly by the gemspec, so this file must never require the
|
|
3
|
+
# rest of the gem (its dependencies aren't resolved yet at that point).
|
|
4
|
+
VERSION = "2.9.0".freeze
|
|
16
5
|
end
|
data/lib/hecks.rb
CHANGED
|
@@ -42,65 +42,33 @@ require_relative "hecks/embryonaut_bluebook"
|
|
|
42
42
|
# packaged gem leaves it out (ADR 0066).
|
|
43
43
|
Hecks.autoload(:Corpus, File.expand_path("hecks/corpus", __dir__))
|
|
44
44
|
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
# `.world` files, and `.bluebook`/`.hecksagon`/`.port`/`.adapter`/`.world`/
|
|
48
|
-
# `.data_translation` are the top-level declaration words every such file
|
|
49
|
-
# opens with, each collecting its built construct into the currently booting
|
|
50
|
-
# Registry (`#collect`, private below).
|
|
45
|
+
# Root namespace and public facade of the DSL/runtime: `Hecks.boot`/`.boot_files`
|
|
46
|
+
# assemble a running domain from `.bluebook`/`.hecksagon`/`.world` files.
|
|
51
47
|
module Hecks
|
|
52
48
|
class LoadOutsideBoot < StandardError; end
|
|
53
49
|
|
|
54
50
|
class << self
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
# they live.
|
|
59
|
-
# `install_facade:` — see Runtime::Loader.boot. Defaults on; a caller
|
|
60
|
-
# that only dispatches by FQN string can skip the global sugar.
|
|
61
|
-
#
|
|
62
|
-
# `environment:` — recovered, not new: this parameter (and the
|
|
63
|
-
# `environments/<name>.hecksagon` / `.world` overlay it loads —
|
|
64
|
-
# see Adapters::Folder#load_domain) existed on a prior commit of
|
|
65
|
-
# this repo (933d1dd), was vendored out to a real consumer
|
|
66
|
-
# (a client site's domain), and was then lost from this repo's own
|
|
67
|
-
# history (no branch here reaches that commit). Ported forward
|
|
68
|
-
# from the consumer's vendor snapshot — the only surviving copy —
|
|
69
|
-
# and generalized: the original only loaded a `.hecksagon`
|
|
70
|
-
# overlay; this also loads a same-named `.world` overlay, both
|
|
71
|
-
# merged into the base rather than replacing it (Registry#add_hecksagon
|
|
72
|
-
# / #add_world). hecks never reads ENV itself — a caller
|
|
73
|
-
# resolves its own env var name and passes the resulting string
|
|
74
|
-
# straight through, e.g. `Hecks.boot(path, environment:
|
|
75
|
-
# ENV.fetch("MYAPP_ENV", "development"))`.
|
|
76
|
-
#
|
|
51
|
+
# Boots a domain from `path`, assembling a running dispatcher from its
|
|
52
|
+
# `.bluebook`/`.hecksagon`/`.world` files. hecks never reads ENV itself —
|
|
53
|
+
# a caller resolves its own env var name and passes it as `environment`.
|
|
77
54
|
# @param path [String] path to a domain directory, or a file inside one
|
|
78
55
|
# @param shared [String, nil] a shared-root override; see `Runtime::Loader.boot`
|
|
79
|
-
# @param install_facade [Boolean]
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
#
|
|
83
|
-
# domain's own
|
|
84
|
-
# @return [Runtime::Dispatcher, Runtime::RemoteDispatcher] the dispatcher bound
|
|
85
|
-
# to the booted domain
|
|
56
|
+
# @param install_facade [Boolean] install the `Widget::Item.Add`-style facade
|
|
57
|
+
# @param environment [String, nil] env name; its `.hecksagon`/`.world` overlay,
|
|
58
|
+
# if present, loads after the domain's own
|
|
59
|
+
# @return [Runtime::Dispatcher, Runtime::RemoteDispatcher] dispatcher bound to the domain
|
|
86
60
|
def boot(path, shared: nil, install_facade: true, environment: nil)
|
|
87
61
|
Runtime.boot(path, shared: shared, install_facade: install_facade, environment: environment)
|
|
88
62
|
end
|
|
89
63
|
|
|
90
|
-
#
|
|
91
|
-
# `.hecksagon`, optionally a `.world`)
|
|
92
|
-
#
|
|
93
|
-
# why this exists beside `boot` rather than as a special case of it.
|
|
94
|
-
#
|
|
64
|
+
# Boots a domain from an explicit list of files (a `.bluebook`, its
|
|
65
|
+
# `.hecksagon`, optionally a `.world`) instead of a whole directory —
|
|
66
|
+
# see `Runtime::Loader.boot_files` for why this exists beside `boot`.
|
|
95
67
|
# @param paths [String, Array<String>] one or more file paths within the domain
|
|
96
|
-
# to load, instead of the whole directory
|
|
97
68
|
# @param shared [String, nil] a shared-root override; see `Runtime::Loader.boot_files`
|
|
98
|
-
# @param install_facade [Boolean]
|
|
99
|
-
#
|
|
100
|
-
# @
|
|
101
|
-
# selected-file loader
|
|
102
|
-
# @return [Runtime::Dispatcher, Runtime::RemoteDispatcher] the dispatcher bound
|
|
103
|
-
# to the booted domain
|
|
69
|
+
# @param install_facade [Boolean] install the `Widget::Item.Add`-style facade
|
|
70
|
+
# @param environment [String, nil] environment name passed to the selected-file loader
|
|
71
|
+
# @return [Runtime::Dispatcher, Runtime::RemoteDispatcher] dispatcher bound to the domain
|
|
104
72
|
def boot_files(paths, shared: nil, install_facade: true, environment: nil)
|
|
105
73
|
Runtime.boot_files(paths, shared: shared, install_facade: install_facade, environment: environment)
|
|
106
74
|
end
|
|
@@ -117,21 +85,8 @@ module Hecks
|
|
|
117
85
|
# @return [Runtime::Registry, nil] the current registry, or nil outside a boot
|
|
118
86
|
def current_registry = Runtime.current_registry
|
|
119
87
|
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
# default), a command's role stays exactly what it is without this:
|
|
123
|
-
# decoration.
|
|
124
|
-
#
|
|
125
|
-
# `actor_id` is optional — a caller naming only a role is checked by
|
|
126
|
-
# string equality against the command's own `role`, exactly as
|
|
127
|
-
# before. A caller that also names who it is lets the check run
|
|
128
|
-
# against a real Governance `RoleAssignment` instead, once the
|
|
129
|
-
# command's domain has Governance attached — see
|
|
130
|
-
# `CommandRules::Authorization`'s own header.
|
|
131
|
-
#
|
|
132
|
-
# `as_of` and `scope` are optional too, same shape — see
|
|
133
|
-
# `Runtime.as_caller`'s own header for what each does.
|
|
134
|
-
#
|
|
88
|
+
# Binds who is dispatching for the duration of the block, checked against
|
|
89
|
+
# a command's declared `role`; unbound (the default) is a no-op.
|
|
135
90
|
# @param role [String, Symbol] the role to check the caller against
|
|
136
91
|
# @param actor_id [String, nil] who is calling, checked against a real Governance
|
|
137
92
|
# `RoleAssignment` when given; string-equality only against `role` when nil
|
|
@@ -165,26 +120,16 @@ module Hecks
|
|
|
165
120
|
# @return [Bluebook::Hecksagon] the built hecksagon
|
|
166
121
|
# @raise [LoadOutsideBoot] if called outside `Hecks.boot`/`.boot_files`
|
|
167
122
|
def hecksagon(name, &) = collect(:add_hecksagon, Bluebook::DSL::HecksagonBuilder.build(name, &))
|
|
168
|
-
|
|
169
|
-
#
|
|
170
|
-
# `.port` file callers too (the aggregate-scoped `Thing.port(...)`,
|
|
171
|
-
# binding_proxy.rb, already went through this builder). Every real
|
|
172
|
-
# `.port` file only ever spells `verb`/`signal` (no `.port` file
|
|
173
|
-
# declares operations — that's DomainPort's own newer shape), and
|
|
174
|
-
# DomainPortBuilder's own bare-verb branch produces the exact same
|
|
175
|
-
# `Port` object PortBuilder itself did (dsl_spec.rb's own byte-
|
|
176
|
-
# identity check) — a pure repoint, no behavior change for any
|
|
177
|
-
# existing caller reading `.verb`/`.signal` off what comes back.
|
|
123
|
+
|
|
124
|
+
# Declares a port — a `.port` file's top-level word.
|
|
178
125
|
#
|
|
179
|
-
# `legacy_bare_port: true` —
|
|
180
|
-
#
|
|
181
|
-
#
|
|
182
|
-
#
|
|
183
|
-
#
|
|
184
|
-
#
|
|
185
|
-
#
|
|
186
|
-
# the literal top-level `.port` file entry point, keeps the older,
|
|
187
|
-
# looser rule (see `DomainPortBuilder#initialize`'s own comment).
|
|
126
|
+
# `legacy_bare_port: true` — only this entry point allows a completely
|
|
127
|
+
# empty port body; see `DomainPortBuilder#initialize`'s own doc.
|
|
128
|
+
#
|
|
129
|
+
# @param name [String] the port's name
|
|
130
|
+
# @yield the port's body, evaluated against a `Bluebook::DSL::DomainPortBuilder`
|
|
131
|
+
# @return [Bluebook::Port, Bluebook::DomainPort] the built port
|
|
132
|
+
# @raise [LoadOutsideBoot] if called outside `Hecks.boot`/`.boot_files`
|
|
188
133
|
def port(name, &) = collect(:add_port, Bluebook::DSL::DomainPortBuilder.build(name, legacy_bare_port: true, &))
|
|
189
134
|
|
|
190
135
|
# Declares an adapter — an `.adapter` file's top-level word.
|
|
@@ -1,55 +1,15 @@
|
|
|
1
1
|
module RuboCop
|
|
2
2
|
module Cop
|
|
3
3
|
module Hecks
|
|
4
|
-
# Flags
|
|
5
|
-
#
|
|
6
|
-
# the same receiver looked up by two different keys, falling back to
|
|
7
|
-
# the second lookup whenever the first is falsy. `||` cannot tell a
|
|
8
|
-
# genuinely stored `false` from a missing key — both are falsy in
|
|
9
|
-
# Ruby — so a real `false` sitting at `holder[a]` is silently
|
|
10
|
-
# discarded and `holder[b]` (usually absent, so `nil`) is returned
|
|
11
|
-
# instead. Every one of those 8+ instances was the identical
|
|
12
|
-
# AST shape with a different pair of keys (most commonly
|
|
13
|
-
# `h[k.to_sym] || h[k]`, reading a value that could arrive keyed
|
|
14
|
-
# either by symbol or by string off the wire).
|
|
4
|
+
# Flags `holder[a] || holder[b]`: the same receiver looked up by two keys, falling back
|
|
5
|
+
# to the second when the first is falsy.
|
|
15
6
|
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
# The fix this codebase already converged on — see
|
|
19
|
-
# `lib/hecks/query_specification/field_path.rb#read`, the shared
|
|
20
|
-
# digger this whole bug class got consolidated behind: check
|
|
21
|
-
# `key?` first, never fall back through `||`.
|
|
22
|
-
#
|
|
23
|
-
# sym = segment.to_sym
|
|
24
|
-
# return current.key?(sym) ? current[sym] : current[segment]
|
|
25
|
-
#
|
|
26
|
-
# That method's own comment says it plainly: "`key?` first, never
|
|
27
|
-
# `||`, because `||` falls through a genuinely-stored `false` to
|
|
28
|
-
# the other spelling (usually absent) and returns `nil` instead."
|
|
29
|
-
# This cop exists so the next `holder[a] || holder[b]` gets caught
|
|
30
|
-
# mechanically, before it becomes bug #9, rather than found by
|
|
31
|
-
# hand again in a future audit.
|
|
32
|
-
#
|
|
33
|
-
# ## Scope
|
|
34
|
-
#
|
|
35
|
-
# Scoped to the `[]`/`[]` shape only, deliberately — a receiver
|
|
36
|
-
# method-call is compared by AST structure (`==`, which ignores
|
|
37
|
-
# source location), so `hash[a] || hash[b]` is flagged whether
|
|
38
|
-
# `hash` is a local variable, a method call, or a constant, but
|
|
39
|
-
# `a[k] || b[k]` (different receivers) and `value || default`
|
|
40
|
-
# (an ordinary default, not a second lookup at all) are both left
|
|
41
|
-
# alone — neither one can silently drop a stored `false` the way
|
|
42
|
-
# a same-receiver double-lookup can.
|
|
7
|
+
# `||` cannot tell a stored `false` from a missing key, so a real `false` at `holder[a]` is
|
|
8
|
+
# discarded. Different receivers (`a[k] || b[k]`) and plain defaults are left alone.
|
|
43
9
|
#
|
|
44
10
|
# @example
|
|
45
|
-
#
|
|
46
|
-
# hash[:active]
|
|
47
|
-
#
|
|
48
|
-
# # good
|
|
49
|
-
# hash.key?(:active) ? hash[:active] : hash["active"]
|
|
50
|
-
#
|
|
51
|
-
# # good — or the shared digger this codebase already has
|
|
52
|
-
# Hecks::QuerySpecification::FieldPath.dig(hash, "active")
|
|
11
|
+
# hash[:active] || hash["active"] # bad
|
|
12
|
+
# hash.key?(:active) ? hash[:active] : hash["active"] # good
|
|
53
13
|
class FallbackHashLookup < Base
|
|
54
14
|
MSG = "`%<receiver>s[...] || %<receiver>s[...]` falls back to the second lookup " \
|
|
55
15
|
"whenever the first is falsy — but `||` cannot tell a genuinely stored `false` " \
|
|
@@ -96,3 +56,5 @@ module RuboCop
|
|
|
96
56
|
end
|
|
97
57
|
end
|
|
98
58
|
end
|
|
59
|
+
|
|
60
|
+
# AST `==` ignores source location, so two parses of the same receiver compare equal.
|
|
@@ -1,67 +1,17 @@
|
|
|
1
1
|
module RuboCop
|
|
2
2
|
module Cop
|
|
3
3
|
module Hecks
|
|
4
|
-
# Flags `hash[new] = hash.delete(old)` inside a loop
|
|
5
|
-
#
|
|
6
|
-
# in the specific rename/permutation shape this codebase has
|
|
7
|
-
# already lost real data to once.
|
|
8
|
-
#
|
|
9
|
-
# ## The bug this follows up on
|
|
10
|
-
#
|
|
11
|
-
# The bug this is the mechanical follow-up to (M27,
|
|
12
|
-
# docs/audits/2026-08-10-main-bug-audit.md,
|
|
13
|
-
# docs/audits/2026-08-11-bug-triage.md; fixed in
|
|
14
|
-
# `Hecks::Ports::Persistence::Lineage#apply_renames`,
|
|
15
|
-
# lib/hecks/ports/persistence/plugins/era/lineage.rb): a rename
|
|
16
|
-
# rule set applied one rule at a time, `state[new_name] =
|
|
17
|
-
# state.delete(old_name)` per rule, against the same hash it was
|
|
18
|
-
# reading from — so a swap (`rename :a, to: :b` alongside `rename
|
|
19
|
-
# :b, to: :a`) on `{a: 1, b: 2}` collapsed to `{a: 1}`. The first
|
|
20
|
-
# rule wrote `b: 1` (clobbering the real `b: 2`) before the second
|
|
21
|
-
# rule ever got a chance to read the value that was supposed to
|
|
22
|
-
# move to `:a` — sequential edits stepping on each other, not the
|
|
23
|
-
# simultaneous permutation a rename set actually means. The same
|
|
24
|
-
# shape loses data identically for any chain longer than one link
|
|
25
|
-
# (`a->b->c->a`), not just a two-element swap.
|
|
26
|
-
#
|
|
27
|
-
# ## The fix this cop points at
|
|
28
|
-
#
|
|
29
|
-
# Snapshot every old key's value
|
|
30
|
-
# first, delete all old keys, then write all new keys, so no
|
|
31
|
-
# rename in the pass ever reads a key this same pass has already
|
|
32
|
-
# written:
|
|
33
|
-
#
|
|
34
|
-
# snapshot = renames.filter_map { |old_name, new_name| [old_name, new_name, state[old_name]]
|
|
35
|
-
# if state.key?(old_name) }
|
|
36
|
-
# snapshot.each { |old_name, _new_name, _value| state.delete(old_name) }
|
|
37
|
-
# snapshot.each { |_old_name, new_name, value| state[new_name] = value }
|
|
38
|
-
#
|
|
39
|
-
# ## What this cop does not attempt
|
|
40
|
-
#
|
|
41
|
-
# The general checklist item is
|
|
42
|
-
# broader than any AST pattern can safely automate ("any method
|
|
43
|
-
# that both reads and writes the same collection across a loop"
|
|
44
|
-
# covers plenty of correct code too — accumulating into a result
|
|
45
|
-
# hash, memoizing into a cache). This cop stays narrow and
|
|
46
|
-
# precise: only the exact `recv[x] = recv.delete(y)` shape, same
|
|
47
|
-
# receiver both sides, inside something that iterates. A single
|
|
48
|
-
# rename outside any loop is not this bug (nothing else can have
|
|
49
|
-
# run first); `hash.store(...)` instead of `hash[...] =`, or
|
|
50
|
-
# `hash.delete(...)` whose result is not immediately written back
|
|
51
|
-
# into the same hash, are not this shape either and are left to
|
|
52
|
-
# the broader code-review checklist item, not this cop.
|
|
4
|
+
# Flags `hash[new] = hash.delete(old)` inside a loop.
|
|
5
|
+
# Applying renames one at a time to the hash being read loses data on swaps and chains.
|
|
53
6
|
#
|
|
54
7
|
# @example
|
|
55
|
-
# # bad
|
|
56
|
-
# renames.each
|
|
57
|
-
# state[new_name] = state.delete(old_name)
|
|
58
|
-
# end
|
|
8
|
+
# # bad
|
|
9
|
+
# renames.each { |old, new| state[new] = state.delete(old) }
|
|
59
10
|
#
|
|
60
|
-
# # good
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
# snapshot.each { |_old_name, new_name, value| state[new_name] = value }
|
|
11
|
+
# # good: snapshot the values, delete every old key, then write the new keys
|
|
12
|
+
# pairs = renames.select { |old, _| state.key?(old) }.map { |old, new| [new, state[old]] }
|
|
13
|
+
# renames.each_key { |old| state.delete(old) }
|
|
14
|
+
# pairs.each { |new, value| state[new] = value }
|
|
65
15
|
class SequentialHashRenameInLoop < Base
|
|
66
16
|
MSG = "`%<recv>s[new] = %<recv>s.delete(old)` inside a loop applies one rename at a time against the " \
|
|
67
17
|
"SAME hash it reads from — a swap (`{a: :b, b: :a}`) on `{a: 1, b: 2}` collapses to `{a: 1}` " \
|
|
@@ -78,22 +28,12 @@ module RuboCop
|
|
|
78
28
|
inject reduce each_slice each_cons
|
|
79
29
|
].freeze
|
|
80
30
|
|
|
81
|
-
#
|
|
82
|
-
#
|
|
83
|
-
# own grammar): `(send recv :[]= new_key (send recv2 :delete
|
|
84
|
-
# old_key))`. `$_recv`/`$_recv2` are captured (not merely
|
|
85
|
-
# matched) so `same_receiver?` below can compare them
|
|
86
|
-
# structurally — node-pattern has no built-in "same subtree
|
|
87
|
-
# twice" backreference, so the equality check is real Ruby, not
|
|
88
|
-
# the pattern itself.
|
|
31
|
+
# Indexed assignment parses as a `send`. Both receivers are captured because node-pattern
|
|
32
|
+
# has no backreference; `same_receiver?` compares them.
|
|
89
33
|
def_node_matcher :rename_write?, <<~PATTERN
|
|
90
34
|
(send $_recv :[]= _new_key (send $_recv2 :delete _old_key))
|
|
91
35
|
PATTERN
|
|
92
36
|
|
|
93
|
-
# Flags a `recv[new] = recv.delete(old)` write inside a loop, same receiver both sides.
|
|
94
|
-
#
|
|
95
|
-
# @param node [RuboCop::AST::SendNode] the `[]=` send node being visited
|
|
96
|
-
# @return [void]
|
|
97
37
|
def on_send(node)
|
|
98
38
|
rename_write?(node) do |recv, recv2|
|
|
99
39
|
next unless same_receiver?(recv, recv2)
|
|
@@ -105,20 +45,11 @@ module RuboCop
|
|
|
105
45
|
|
|
106
46
|
private
|
|
107
47
|
|
|
108
|
-
# Structural equality (type + children, recursively) — exactly
|
|
109
|
-
# what `Parser::AST::Node#==` already gives, ignoring source
|
|
110
|
-
# location, so `state` vs `state` (two separate `send(nil,
|
|
111
|
-
# :state)` reader calls) and `@state` vs `@state` both count as
|
|
112
|
-
# "the same collection", not just an identical local variable.
|
|
113
48
|
def same_receiver?(recv, recv2)
|
|
114
49
|
recv == recv2
|
|
115
50
|
end
|
|
116
51
|
|
|
117
|
-
#
|
|
118
|
-
# not just the immediate parent, since the offending assignment
|
|
119
|
-
# may sit one or more non-loop nodes deep inside a loop's block
|
|
120
|
-
# (an `if` guarding it, say) and still carries the exact same
|
|
121
|
-
# hazard.
|
|
52
|
+
# Checks every ancestor: a guard node may sit between the assignment and the loop block.
|
|
122
53
|
def in_loop?(node)
|
|
123
54
|
node.each_ancestor(:block, :numblock, :for, :while, :until, :while_post, :until_post).any? do |ancestor|
|
|
124
55
|
loop_ancestor?(ancestor)
|