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
|
@@ -4,85 +4,23 @@ require "rack"
|
|
|
4
4
|
|
|
5
5
|
module Hecks
|
|
6
6
|
module Adapters
|
|
7
|
-
#
|
|
8
|
-
# rather than code the domain reaches out through. Every existing
|
|
9
|
-
# file under `adapters/driven/` is the latter: a store or reader a
|
|
10
|
-
# `persisted_by`/`port` binding resolves to, called by this
|
|
11
|
-
# framework's own runtime. Nothing under this repository has ever
|
|
12
|
-
# been the mirror image before — code that receives a request from
|
|
13
|
-
# the outside world and turns it into a dispatch — so this is the
|
|
14
|
-
# first entry, and the directory itself is new.
|
|
7
|
+
# Driving adapters: code an outside caller reaches in through.
|
|
15
8
|
module Driving
|
|
16
|
-
#
|
|
17
|
-
# `
|
|
18
|
-
# this class's own pull-side sibling) already draws for itself:
|
|
19
|
-
# this file owns proving a request really came from GitHub and
|
|
20
|
-
# unwrapping GitHub's own webhook envelope (`X-GitHub-Event`, the
|
|
21
|
-
# JSON body, GitHub's own automatic `ping` check) — never which
|
|
22
|
-
# commands to dispatch about what it finds inside. That is exactly
|
|
23
|
-
# as domain-specific as `GithubChecks#run` turning `check-runs`
|
|
24
|
-
# JSON into green-or-raise, and lives exactly where that class's
|
|
25
|
-
# own header explains such logic belongs: outside this library,
|
|
26
|
-
# in `qa/adapters/github_ci_webhook.rb`, the subclass of this file
|
|
27
|
-
# that actually knows what a `QualityControl::Clearance` is.
|
|
9
|
+
# A GitHub webhook receiver, transport only: a rack app that verifies the signature.
|
|
10
|
+
# A subclass implements `#handle_event(event, action, payload)` returning `[status, body]`.
|
|
28
11
|
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
# A plain rack app (`#call(env)`) — no Sinatra, no Rails — the same
|
|
32
|
-
# shape `Hecks::Forms::App` (lib/hecks/forms/app.rb) already
|
|
33
|
-
# established for the one other HTTP-facing surface this library
|
|
34
|
-
# ships. `rack` is a lazy Gemfile dependency for exactly the reason
|
|
35
|
-
# that file's own header gives: this file is never required by
|
|
36
|
-
# `require "hecks"` (nothing under `adapters.rb`'s own eager
|
|
37
|
-
# `adapters/driven` load names it — see that file's own header),
|
|
38
|
-
# so a project that never mounts a driving adapter never needs
|
|
39
|
-
# `rack` installed, the same "opt in by requiring the file at all"
|
|
40
|
-
# contract `hecks/forms.rb` already has for `Forms::App`.
|
|
41
|
-
#
|
|
42
|
-
# ## Subclass responsibility
|
|
43
|
-
#
|
|
44
|
-
# Implement `#handle_event(event, action,
|
|
45
|
-
# payload)`, returning `[http_status, response_body_hash]`. Called
|
|
46
|
-
# only after the signature has verified and the body has parsed as
|
|
47
|
-
# JSON — a subclass never has to re-check either. `event` is
|
|
48
|
-
# GitHub's own `X-GitHub-Event` header value ("check_suite",
|
|
49
|
-
# "check_run", "pull_request", ...); `action` is the payload's own
|
|
50
|
-
# top-level `"action"` field when it has one (GitHub's webhooks
|
|
51
|
-
# nearly all carry one — "completed", "requested", "opened", ...)
|
|
52
|
-
# and nil when it does not. `ping` — GitHub's own automatic
|
|
53
|
-
# connectivity check, sent once when a webhook is first saved in
|
|
54
|
-
# repository settings — is answered here and never reaches a
|
|
55
|
-
# subclass at all; there is nothing domain-specific to decide
|
|
56
|
-
# about it.
|
|
12
|
+
# Not required by `require "hecks"`, so `rack` stays an opt-in dependency.
|
|
57
13
|
class GithubWebhook
|
|
58
|
-
#
|
|
59
|
-
# everywhere else in this codebase (`Runtime::DOMAIN_REFUSALS`,
|
|
60
|
-
# `Forms::App`'s own `{error:, message:}` JSON body for a bad
|
|
61
|
-
# command). A request that cannot prove it came from GitHub gets
|
|
62
|
-
# a real 401 and a named reason, never a silent 200 that would
|
|
63
|
-
# let a forged "CI passed" payload regress nothing while looking
|
|
64
|
-
# exactly like success in a log nobody re-reads.
|
|
14
|
+
# Raised for a request that cannot prove it came from GitHub; answered with a 401.
|
|
65
15
|
class InvalidSignature < StandardError; end
|
|
66
16
|
|
|
67
|
-
#
|
|
68
|
-
# this body genuinely came from whoever signed it (checked
|
|
69
|
-
# first, before parsing ever runs — see `#call`), and simply
|
|
70
|
-
# is not JSON. Still refused, never guessed at.
|
|
17
|
+
# Raised when a correctly signed body is not JSON; answered with a 400.
|
|
71
18
|
class MalformedPayload < StandardError; end
|
|
72
19
|
|
|
73
20
|
SIGNATURE_HEADER = "HTTP_X_HUB_SIGNATURE_256".freeze
|
|
74
21
|
EVENT_HEADER = "HTTP_X_GITHUB_EVENT".freeze
|
|
75
22
|
|
|
76
|
-
# `secret:` has no default
|
|
77
|
-
# `GoogleAuthentication`'s own header states for its own
|
|
78
|
-
# `ENV.fetch`, restated here because the consequence is worse for
|
|
79
|
-
# a webhook: an unverified signature check is not "half
|
|
80
|
-
# configured", it is no verification at all, silently accepting
|
|
81
|
-
# anything claiming to be GitHub. A caller passes the real
|
|
82
|
-
# secret explicitly — from `ENV.fetch("GITHUB_WEBHOOK_SECRET")`
|
|
83
|
-
# or wherever it keeps one — rather than this class reaching into
|
|
84
|
-
# the environment itself and hiding that requirement inside a
|
|
85
|
-
# default.
|
|
23
|
+
# `secret:` has no default: a missing secret would silently disable verification.
|
|
86
24
|
# @param secret [String] the shared webhook secret (e.g. `ENV.fetch
|
|
87
25
|
# ("GITHUB_WEBHOOK_SECRET")`), checked against each request's `X-Hub-Signature-256`
|
|
88
26
|
# @raise [ArgumentError] if `secret` is nil or empty
|
|
@@ -120,13 +58,7 @@ module Hecks
|
|
|
120
58
|
|
|
121
59
|
private
|
|
122
60
|
|
|
123
|
-
# Constant-time compare
|
|
124
|
-
# the moment it finds the first mismatching byte, so how long
|
|
125
|
-
# that took leaks how many leading bytes of a forged signature
|
|
126
|
-
# were already right to anyone timing the response — GitHub's
|
|
127
|
-
# own webhook documentation calls this out by name and recommends
|
|
128
|
-
# exactly the constant-time compare `Rack::Utils.secure_compare`
|
|
129
|
-
# already gives for free, reused rather than hand-rolled.
|
|
61
|
+
# Constant-time compare: `==` leaks how many leading bytes of a forged signature matched.
|
|
130
62
|
def verify_signature!(request, body)
|
|
131
63
|
header = request.get_header(SIGNATURE_HEADER)
|
|
132
64
|
raise InvalidSignature, "missing #{SIGNATURE_HEADER.sub('HTTP_', '').tr('_', '-')} header" if header.to_s.empty?
|
data/lib/hecks/behaviors/dsl.rb
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
require_relative "ir"
|
|
2
2
|
|
|
3
|
-
# The authoring surface for `Hecks.behaviors "Name" do ... end
|
|
4
|
-
# `test "description" do ... end` block per case, `tests`/`setup`/`input`/
|
|
5
|
-
# `expect` inside. `instance_eval`-based, the same shape every other
|
|
6
|
-
# hecks DSL builder uses (`WorldBuilder`, `HecksagonBuilder`).
|
|
3
|
+
# The authoring surface for `Hecks.behaviors "Name" do ... end`, built with `instance_eval`.
|
|
7
4
|
module Hecks
|
|
8
5
|
module Behaviors
|
|
9
|
-
# Raised at
|
|
10
|
-
# `loads` line names the fix directly rather than failing later, deep
|
|
11
|
-
# inside a boot, over a suite that was never going to be scoped.
|
|
6
|
+
# Raised at build time, so a missing `vision` or `loads` fails with the fix named.
|
|
12
7
|
class Malformed < StandardError; end
|
|
13
8
|
|
|
14
|
-
# The `test "description" do ... end`
|
|
15
|
-
# `tests`/`setup`/`input`/`expect` calls and builds a `TestCase` (ir.rb).
|
|
16
|
-
# `validate_expect!` (private, below) is where a malformed or empty
|
|
17
|
-
# `expect` is refused at build time rather than silently passing later.
|
|
9
|
+
# The `test "description" do ... end` receiver; builds a `TestCase` (ir.rb).
|
|
18
10
|
class TestCaseBuilder
|
|
19
11
|
# @param description [String] the `test "description" do ... end` text
|
|
20
12
|
def initialize(description)
|
|
@@ -82,14 +74,8 @@ module Hecks
|
|
|
82
74
|
|
|
83
75
|
private
|
|
84
76
|
|
|
85
|
-
#
|
|
86
|
-
#
|
|
87
|
-
# nothing and passes whenever dispatch doesn't raise; `count:` on a
|
|
88
|
-
# command and `emits:` on a query are each read by neither runner
|
|
89
|
-
# (Expectations#run_command/#run_query), so they're accepted here
|
|
90
|
-
# and then silently ignored at run time. Checking both at build
|
|
91
|
-
# time, not in the runners, makes them errors on the file that
|
|
92
|
-
# wrote them rather than green checks nobody questions.
|
|
77
|
+
# Refuses expectations that would pass silently: an empty `expect`, `count:` on a command,
|
|
78
|
+
# or `emits:` on a query (neither runner reads those). Checked at build time.
|
|
93
79
|
def validate_expect!
|
|
94
80
|
if @expect.empty?
|
|
95
81
|
raise Malformed, "test #{@description.inspect} has no `expect` — say what this " \
|
|
@@ -108,9 +94,7 @@ module Hecks
|
|
|
108
94
|
end
|
|
109
95
|
end
|
|
110
96
|
|
|
111
|
-
# The top-level `Hecks.behaviors
|
|
112
|
-
# `vision`/`loads`/`test` calls and builds a `BehaviorsSuite` (ir.rb),
|
|
113
|
-
# refusing to build one missing either `vision` or `loads` (`#build`).
|
|
97
|
+
# The top-level `Hecks.behaviors` receiver; builds a `BehaviorsSuite` (ir.rb).
|
|
114
98
|
class BehaviorsBuilder
|
|
115
99
|
# @param name [String] the suite's declared name
|
|
116
100
|
# @param source_path [String] the `.behaviors` file's own path
|
|
@@ -129,9 +113,7 @@ module Hecks
|
|
|
129
113
|
# @return [String] `text`, unchanged
|
|
130
114
|
def vision(text) = @vision = text
|
|
131
115
|
|
|
132
|
-
#
|
|
133
|
-
# or a same-stem convention — scope is a fact this file declares,
|
|
134
|
-
# not one a runner infers.
|
|
116
|
+
# Paths are relative to this `.behaviors` file, never the cwd.
|
|
135
117
|
#
|
|
136
118
|
# @param paths [Array<String>] paths to the files this suite's domain boots
|
|
137
119
|
# from, relative to the `.behaviors` file
|
|
@@ -5,45 +5,11 @@ require_relative "../runtime/value"
|
|
|
5
5
|
require_relative "../runtime/reaction_invocation"
|
|
6
6
|
require_relative "../ports/persistence/binding_policy"
|
|
7
7
|
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# ## What it does
|
|
11
|
-
#
|
|
12
|
-
# One test case, start to finish: take the suite's own runtime (booted
|
|
13
|
-
# once for exactly what its `loads` names, reset to nothing between
|
|
14
|
-
# tests — `runtime_for`), replay `setup` dispatches, dispatch (or query)
|
|
15
|
-
# the command under test, check `expect`. Split out of
|
|
16
|
-
# runner.rb the same way the file-count/sweep concern is split from a
|
|
17
|
-
# single test's own execution.
|
|
18
|
-
#
|
|
19
|
-
# ## Deviations from a prior port
|
|
20
|
-
#
|
|
21
|
-
# Two deliberate deviations from a prior port of this same idea (read
|
|
22
|
-
# before writing this, not reinvented):
|
|
23
|
-
#
|
|
24
|
-
# 1. A `setup` refusal and a refusal from the command under test are
|
|
25
|
-
# caught in separate rescue scopes. The prior port wrapped the whole
|
|
26
|
-
# test body — setups included — in one `rescue *REFUSAL_CLASSES`, so a
|
|
27
|
-
# broken setup could spuriously satisfy `expect refused: "..."` if its
|
|
28
|
-
# own refusal happened to match the expected substring. Here, any
|
|
29
|
-
# domain refusal during `setup` is unconditionally an error — the
|
|
30
|
-
# example never got to the situation it claims to test.
|
|
31
|
-
#
|
|
32
|
-
# 2. `emits:` is read off a `registry.event_log` diff around the tested
|
|
33
|
-
# dispatch, not off `Result#events` alone. `Result#events` only holds
|
|
34
|
-
# the events the outermost dispatch announced; a policy's own cascade
|
|
35
|
-
# reenters through the same `Dispatcher#dispatch` (`PolicyInterpreter
|
|
36
|
-
# #deliver` → `door.reenter` → `dispatch`), and every dispatch's
|
|
37
|
-
# events — outer and reentrant alike — land in the one shared
|
|
38
|
-
# `registry.event_log`, in order (`CommandRules::Emission#emit`,
|
|
39
|
-
# `PortOperationInterpreter#emit`/`#call`). Diffing that log's length
|
|
40
|
-
# before/after the tested dispatch is the one read that actually sees
|
|
41
|
-
# a cascade — this DSL has no `kind: :cascade` because cascades are
|
|
42
|
-
# always on and `emits:` is expected to see them.
|
|
8
|
+
# Runs one behaviors test case: replays its setup dispatches, dispatches (or
|
|
9
|
+
# queries) the command under test, and checks the result against `expect`.
|
|
43
10
|
module Hecks
|
|
44
11
|
module Behaviors
|
|
45
|
-
#
|
|
46
|
-
# two deliberate deviations from the prior port it's built from.
|
|
12
|
+
# Runs one behaviors test case: replays its setup, dispatches the command, checks `expect`.
|
|
47
13
|
module Expectations
|
|
48
14
|
module_function
|
|
49
15
|
|
|
@@ -66,6 +32,8 @@ module Hecks
|
|
|
66
32
|
|
|
67
33
|
current_setup = nil
|
|
68
34
|
begin
|
|
35
|
+
# Caught separately from the tested command's own refusal, so a broken
|
|
36
|
+
# setup can't spuriously satisfy `expect refused: "..."` by coincidence.
|
|
69
37
|
test.setups.each do |setup|
|
|
70
38
|
current_setup = setup
|
|
71
39
|
dispatch_command(runtime, qualify(setup.command, nil, bluebooks, kind: :command), setup.args)
|
|
@@ -79,20 +47,9 @@ module Hecks
|
|
|
79
47
|
error_result(test, "#{e.class}: #{e.message}")
|
|
80
48
|
end
|
|
81
49
|
|
|
82
|
-
#
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
# instead of ~2s of loading, verifying and era-checking the same
|
|
86
|
-
# bluebook again (chess: 76 behaviours, 155s of which was booting
|
|
87
|
-
# `chess.bluebook` 76 times). Keyed by the suite's own `loads` and
|
|
88
|
-
# their mtimes, so an edited bluebook boots fresh on the next test
|
|
89
|
-
# rather than running against a stale one — the property a watch
|
|
90
|
-
# loop or a long rspec session actually relies on. `runtime:` lets
|
|
91
|
-
# a caller that already holds a booted runtime (a spec, a REPL)
|
|
92
|
-
# hand it in; it is reset the same way.
|
|
93
|
-
# Not frozen — a real cache, mutated below (`RUNTIMES[key] ||=
|
|
94
|
-
# boot_and_guard(files)`) and by #reset!. False positive for
|
|
95
|
-
# Style/MutableConstant.
|
|
50
|
+
# Cached per suite, keyed by `loads`' file paths and mtimes, so a boot is
|
|
51
|
+
# reused across tests but an edited bluebook boots fresh on the next one.
|
|
52
|
+
# Mutated in place (`RUNTIMES[key] ||= boot_and_guard(files)`, and #reset!).
|
|
96
53
|
# rubocop:disable-next Style/MutableConstant
|
|
97
54
|
RUNTIMES = {}
|
|
98
55
|
RUNTIMES_LOCK = Mutex.new
|
|
@@ -115,16 +72,8 @@ module Hecks
|
|
|
115
72
|
end
|
|
116
73
|
end
|
|
117
74
|
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
# `@records` is a plain per-instance ivar, so a fresh object really
|
|
121
|
-
# is a fresh store. Against anything else (Sqlite, Postgres) the
|
|
122
|
-
# rows themselves stay put: tests leak into each other, and a
|
|
123
|
-
# suite booted against a domain's real hecksagon writes to a real
|
|
124
|
-
# database. Refusing that wiring here, at boot, is the same shape
|
|
125
|
-
# of guard `BindingPolicy` already applies to a missing bind — the
|
|
126
|
-
# project's identity is refusing bad wiring up front, not
|
|
127
|
-
# discovering it mid-suite.
|
|
75
|
+
# Refuses at boot when any aggregate binds to a non-Memory adapter —
|
|
76
|
+
# `reset_runtime_state!` only resets Memory's own per-instance store.
|
|
128
77
|
#
|
|
129
78
|
# @param files [Array<String>] absolute paths to the bluebook/hecksagon/world
|
|
130
79
|
# files to boot
|
|
@@ -209,17 +158,8 @@ module Hecks
|
|
|
209
158
|
check_ok(test) || check_fields(test, settled_state(runtime, verb, result)) || pass_result(test)
|
|
210
159
|
end
|
|
211
160
|
|
|
212
|
-
#
|
|
213
|
-
#
|
|
214
|
-
# always on" reading `emits:` already commits to. `Result#state` is
|
|
215
|
-
# the wrong source for that: it snapshots the instance the outer
|
|
216
|
-
# dispatch saved, and a policy's own reentrant dispatch (a ply
|
|
217
|
-
# advancing off a Moved event, a move count bumping) hydrates and
|
|
218
|
-
# saves a fresh record afterward — so a field the cascade wrote
|
|
219
|
-
# read back stale (found live: `expect move_count: 1` got 0 while
|
|
220
|
-
# `emits:` saw MoveCountBumped in the same test). The repository
|
|
221
|
-
# holds the settled record; read it back by the id the dispatch
|
|
222
|
-
# itself answered with.
|
|
161
|
+
# Reads the aggregate back from the repository, not `Result#state` — a
|
|
162
|
+
# policy's own reentrant dispatch can re-save the record afterward.
|
|
223
163
|
#
|
|
224
164
|
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the suite's
|
|
225
165
|
# booted runtime
|
|
@@ -228,7 +168,7 @@ module Hecks
|
|
|
228
168
|
# the dispatch's own result
|
|
229
169
|
# @return [Hash{Symbol => Object}] the settled record's current state, read
|
|
230
170
|
# back from the repository; `result.state` (or `{}`) when the result has no
|
|
231
|
-
# id or the repository
|
|
171
|
+
# id or the repository does not have that aggregate/record
|
|
232
172
|
def settled_state(runtime, verb, result)
|
|
233
173
|
return result.state || {} unless result.respond_to?(:id) && result.id
|
|
234
174
|
|
|
@@ -241,44 +181,15 @@ module Hecks
|
|
|
241
181
|
record ? record.state : (result.state || {})
|
|
242
182
|
end
|
|
243
183
|
|
|
244
|
-
#
|
|
245
|
-
#
|
|
246
|
-
# (`label: "g", id: "wn", to: { file: 2, rank: 2 }`) — and since
|
|
247
|
-
# #335 the dispatcher's own `to:` keyword is the routing envelope,
|
|
248
|
-
# so forwarding those kwargs loose collides the moment a domain
|
|
249
|
-
# declares a command fact named `to` (chess does: every Move's own
|
|
250
|
-
# destination). Found live: every such test failed with "to: does
|
|
251
|
-
# not recognize file, rank" while this guide promised the spelling
|
|
252
|
-
# works. `ReactionInvocation.build` is #335's own seam for turning
|
|
253
|
-
# mixed facts into the strict envelope — identities lifted into
|
|
254
|
-
# `to:`, declared facts into `with:` — so a behaviors dispatch now
|
|
255
|
-
# goes through the exact same separation a policy's projection
|
|
256
|
-
# does. A verb that names a port operation (checked explicitly,
|
|
257
|
-
# below — "Pizzas::Order.PaymentGateway.Receive") keeps the loose
|
|
258
|
-
# passthrough instead: its own input already spells the port
|
|
259
|
-
# form's `to:`/`with:`, which the dispatcher's port branch reads
|
|
260
|
-
# directly, and `ReactionInvocation.build`'s explicit envelope
|
|
261
|
-
# expects a command's own declared attributes at the top level,
|
|
262
|
-
# not a port operation's already-wrapped `to:`/`with:` shape.
|
|
184
|
+
# Splits a mixed dispatch (receiver identity plus declared facts, e.g.
|
|
185
|
+
# `to: { file: 2, rank: 2 }`) into the dispatcher's strict `to:`/`with:` envelope.
|
|
263
186
|
#
|
|
264
|
-
#
|
|
265
|
-
# `resolve_target` to raise `UnknownVerb` for one — a refusal that held
|
|
266
|
-
# only so long as nothing else ever asked it to resolve a port-operation
|
|
267
|
-
# verb. Now that a `policy` can legitimately `trigger` a port operation
|
|
268
|
-
# (`ReactionInvocation#resolve_target`'s own port-operation branch),
|
|
269
|
-
# that raise no longer happens, so this checks directly instead.
|
|
270
|
-
#
|
|
271
|
-
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the suite's
|
|
272
|
-
# booted runtime
|
|
187
|
+
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the suite's booted runtime
|
|
273
188
|
# @param verb [String] the command's dotted FQN, or a port operation's
|
|
274
|
-
# @param args [Hash{Symbol => Object}] the
|
|
275
|
-
#
|
|
276
|
-
# @
|
|
277
|
-
#
|
|
278
|
-
# @raise [Runtime::UnknownVerb] if `verb` is not fully qualified or names an
|
|
279
|
-
# undeclared domain, aggregate, command, entity or port operation
|
|
280
|
-
# @raise [StandardError] any class in `Runtime::DOMAIN_REFUSALS` when the
|
|
281
|
-
# domain refuses the call
|
|
189
|
+
# @param args [Hash{Symbol => Object}] the facts, mixing identity and declared arguments
|
|
190
|
+
# @return [Runtime::Dispatcher::Result, Runtime::RemoteDispatcher::Result] the dispatch result
|
|
191
|
+
# @raise [Runtime::UnknownVerb] if `verb` names an undeclared construct
|
|
192
|
+
# @raise [StandardError] any `Runtime::DOMAIN_REFUSALS` class, when the domain refuses
|
|
282
193
|
def dispatch_command(runtime, verb, args)
|
|
283
194
|
return runtime.dispatch_flat(verb, args) if port_operation?(runtime, verb)
|
|
284
195
|
|
|
@@ -297,10 +208,8 @@ module Hecks
|
|
|
297
208
|
end
|
|
298
209
|
end
|
|
299
210
|
|
|
300
|
-
# The same "Head.Rest"
|
|
301
|
-
#
|
|
302
|
-
# domain/aggregate lookup plus a port-name lookup, no command
|
|
303
|
-
# resolution needed since all this asks is whether one exists.
|
|
211
|
+
# The same "Head.Rest" split `Dispatcher#dispatch` already uses — just a
|
|
212
|
+
# domain/aggregate and port-name lookup, no command resolution needed.
|
|
304
213
|
#
|
|
305
214
|
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the suite's
|
|
306
215
|
# booted runtime
|
|
@@ -358,12 +267,8 @@ module Hecks
|
|
|
358
267
|
test.expect.keys.any? { |key| !SPECIAL_KEYS.include?(key) }
|
|
359
268
|
end
|
|
360
269
|
|
|
361
|
-
# A field expectation on a query
|
|
362
|
-
#
|
|
363
|
-
# reason `expect status: "sold"` on a multi-row answer would be
|
|
364
|
-
# ambiguous about which row it's describing. `check_fields` itself
|
|
365
|
-
# stays row-shaped (it already is, for `run_command`'s settled
|
|
366
|
-
# state); this picks which row it reads.
|
|
270
|
+
# A field expectation on a query only makes sense once it has settled on
|
|
271
|
+
# exactly one row; this picks that row (or fails naming the row count).
|
|
367
272
|
#
|
|
368
273
|
# @param test [Behaviors::TestCase] the test case, for the fail message
|
|
369
274
|
# @param rows [Array<Hash>, Hash] the query's own result
|
|
@@ -437,12 +342,8 @@ module Hecks
|
|
|
437
342
|
fail_result(test, "expected refused: #{expected.inspect}, got #{msg.inspect}")
|
|
438
343
|
end
|
|
439
344
|
|
|
440
|
-
#
|
|
441
|
-
# `
|
|
442
|
-
# wrapped (`expect kind: { value: "bishop" }`). A live record's
|
|
443
|
-
# field always comes back as a `Hecks::Runtime::Value`;
|
|
444
|
-
# normalizing both sides to the same bare-scalar-or-plain-hash
|
|
445
|
-
# shape is the one comparison that accepts either spelling.
|
|
345
|
+
# Normalizes both `expect` spellings (bare or `{value: ...}`) and a live
|
|
346
|
+
# `Hecks::Runtime::Value` field to the same bare-scalar-or-plain-hash shape.
|
|
446
347
|
#
|
|
447
348
|
# @param value [Object] a stored field's value, or an `expect` value to compare
|
|
448
349
|
# it against
|
|
@@ -455,13 +356,8 @@ module Hecks
|
|
|
455
356
|
value
|
|
456
357
|
end
|
|
457
358
|
|
|
458
|
-
#
|
|
459
|
-
#
|
|
460
|
-
# aggregate across every bluebook the suite booted for the one that
|
|
461
|
-
# actually declares the command. `on:` (when given, only ever on
|
|
462
|
-
# the tested command — `setup` never receives it, see the DSL
|
|
463
|
-
# contract) narrows the search to one aggregate by name instead of
|
|
464
|
-
# searching all of them.
|
|
359
|
+
# Searches every aggregate of every bluebook the suite booted for the one
|
|
360
|
+
# declaring `command`; `on:` narrows the search to one aggregate by name.
|
|
465
361
|
#
|
|
466
362
|
# @param command [String, Symbol] a bare verb, or an already-dotted FQN
|
|
467
363
|
# @param on_aggregate [String, Symbol, nil] the aggregate to search, or nil to
|
|
@@ -478,11 +374,8 @@ module Hecks
|
|
|
478
374
|
disambiguate_qualified_name(candidates, command, kind, bluebooks)
|
|
479
375
|
end
|
|
480
376
|
|
|
481
|
-
# Finds every aggregate that
|
|
482
|
-
#
|
|
483
|
-
# **The search** — every (bluebook, aggregate) pair that declares a
|
|
484
|
-
# command/query named `command`, narrowed to `on_aggregate` by name
|
|
485
|
-
# when given.
|
|
377
|
+
# Finds every (bluebook, aggregate) pair that declares a command/query
|
|
378
|
+
# named `command`, narrowed to `on_aggregate` by name when given.
|
|
486
379
|
#
|
|
487
380
|
# @param command [String, Symbol] the bare verb to search for
|
|
488
381
|
# @param on_aggregate [String, Symbol, nil] the aggregate to search, or nil to
|
|
@@ -502,11 +395,8 @@ module Hecks
|
|
|
502
395
|
pairs.select { |_, agg| agg.public_send(members).any? { |m| m.hecks_name == command.to_s } }
|
|
503
396
|
end
|
|
504
397
|
|
|
505
|
-
# Resolves a search's candidates to exactly one dotted FQN
|
|
506
|
-
#
|
|
507
|
-
# **The report** — zero candidates and more-than-one candidates both
|
|
508
|
-
# refuse (with a different message); exactly one resolves to its
|
|
509
|
-
# dotted FQN.
|
|
398
|
+
# Resolves a search's candidates to exactly one dotted FQN; zero or more
|
|
399
|
+
# than one both refuse, each with a different message.
|
|
510
400
|
#
|
|
511
401
|
# @param candidates [Array<Array(Bluebook::Chapter, Bluebook::Aggregate)>] the
|
|
512
402
|
# matching (chapter, aggregate) pairs found by `qualify_candidates`
|
data/lib/hecks/behaviors/ir.rb
CHANGED
|
@@ -1,29 +1,17 @@
|
|
|
1
1
|
# Hecks::Behaviors::{TestSetup,TestCase,BehaviorsSuite}
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
# not part of `Hecks::IR`/`emits_ir` — a behaviors suite is never
|
|
6
|
-
# collected into a domain Registry (see `Hecks.behaviors`), never
|
|
7
|
-
# dispatched through MetaValidator, and carries none of the self-hosted
|
|
8
|
-
# round-trip machinery a real bluebook construct does. It is a test
|
|
9
|
-
# artifact a runner reads on demand, not a domain a boot needs.
|
|
3
|
+
# Plain Structs holding what `Hecks.behaviors "Name" do ... end` collected; a suite is never
|
|
4
|
+
# collected into a domain Registry.
|
|
10
5
|
module Hecks
|
|
11
|
-
# The `.behaviors` toolkit:
|
|
12
|
-
# (`Hecks.behaviors`, dsl.rb), the plain-Struct IR it builds (this file), the runner
|
|
13
|
-
# that boots a suite's domain and checks each test (runner.rb, expectations.rb), and
|
|
14
|
-
# an rspec shim for a consuming app's own suite (rspec.rb).
|
|
6
|
+
# The `.behaviors` toolkit: DSL, IR, runner and rspec shim.
|
|
15
7
|
module Behaviors
|
|
16
8
|
TestSetup = Struct.new(:command, :args, keyword_init: true)
|
|
17
9
|
|
|
18
10
|
TestCase = Struct.new(:description, :tests_command, :on_aggregate, :kind,
|
|
19
11
|
:setups, :input, :expect, keyword_init: true) do
|
|
20
|
-
#
|
|
12
|
+
# Whether `tests_command` is already a fully qualified verb name.
|
|
21
13
|
#
|
|
22
|
-
#
|
|
23
|
-
# composes with the domain name resolved once setups/the tested
|
|
24
|
-
# dispatch actually run (see Expectations — the domain isn't known
|
|
25
|
-
# until `loads` is booted, so it's a runtime concern, not a field
|
|
26
|
-
# here).
|
|
14
|
+
# @return [Boolean] true when `tests_command` contains a `.`
|
|
27
15
|
#
|
|
28
16
|
# @return [Boolean] true when `tests_command` contains a `.`
|
|
29
17
|
def dotted? = tests_command.to_s.include?(".")
|
|
@@ -34,9 +22,8 @@ module Hecks
|
|
|
34
22
|
def query? = kind == :query
|
|
35
23
|
end
|
|
36
24
|
|
|
37
|
-
# `loads`
|
|
38
|
-
#
|
|
39
|
-
# the `.behaviors` file itself, kept for error messages.
|
|
25
|
+
# `loads` holds absolute paths resolved against the `.behaviors` file's directory;
|
|
26
|
+
# `path` is that file, kept for error messages.
|
|
40
27
|
BehaviorsSuite = Struct.new(:name, :vision, :loads, :tests, :path, keyword_init: true)
|
|
41
28
|
end
|
|
42
29
|
end
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
require_relative "../behaviors"
|
|
2
2
|
|
|
3
|
-
# Hecks::Behaviors::RSpec.describe_file(path)
|
|
4
|
-
# `bundle exec rspec`
|
|
5
|
-
#
|
|
6
|
-
# shape `spec/guides_spec.rb` uses for doctested guides: the file is
|
|
7
|
-
# parsed at collection time (cheap — `Behaviors.parse`, no test actually
|
|
8
|
-
# run yet, just enough to know the `it` names), and each test's own
|
|
9
|
-
# `Expectations.run_one` runs lazily inside its own `it`, exactly when
|
|
10
|
-
# rspec actually executes it.
|
|
3
|
+
# Hecks::Behaviors::RSpec.describe_file(path) registers one `it` per test in a `.behaviors` file,
|
|
4
|
+
# so a consumer's `bundle exec rspec` runs it. The file is parsed at collection time; each test
|
|
5
|
+
# runs lazily inside its own `it`.
|
|
11
6
|
#
|
|
12
7
|
# require "hecks/behaviors/rspec"
|
|
13
8
|
#
|
|
@@ -16,19 +11,13 @@ require_relative "../behaviors"
|
|
|
16
11
|
# end
|
|
17
12
|
module Hecks
|
|
18
13
|
module Behaviors
|
|
19
|
-
#
|
|
20
|
-
# how a consumer wires it into their own `bundle exec rspec` run.
|
|
14
|
+
# Registers one example per test in a `.behaviors` file so a consumer's suite runs it.
|
|
21
15
|
module RSpec
|
|
22
16
|
module_function
|
|
23
17
|
|
|
24
|
-
#
|
|
25
|
-
# `it` per test named by the test's own description.
|
|
18
|
+
# Registers an rspec example group for one `.behaviors` file, one `it` per test.
|
|
26
19
|
#
|
|
27
|
-
#
|
|
28
|
-
# @return [void]
|
|
29
|
-
# @raise [RuntimeError] not raised by this call itself; wraps `parsed.parse_error`'s
|
|
30
|
-
# message and is raised only when rspec later runs the generated "loads without a
|
|
31
|
-
# parse error" example, so it surfaces as that example's failure
|
|
20
|
+
# A parse error surfaces as a failing "loads without a parse error" example.
|
|
32
21
|
def describe_file(path)
|
|
33
22
|
parsed = Behaviors.parse(path)
|
|
34
23
|
|
|
@@ -2,14 +2,9 @@ require_relative "expectations"
|
|
|
2
2
|
|
|
3
3
|
# Hecks::Behaviors.run(path) / .run_all(dir)
|
|
4
4
|
#
|
|
5
|
-
#
|
|
6
|
-
# that actually touches a live runtime; this file finds `.behaviors`
|
|
7
|
-
# files, loads them, and maps their tests through it.
|
|
5
|
+
# Finds `.behaviors` files, loads them, and runs their tests through `Expectations.run_one`.
|
|
8
6
|
module Hecks
|
|
9
|
-
#
|
|
10
|
-
# discovery-and-aggregation half of the `.behaviors` authoring surface;
|
|
11
|
-
# `Expectations` (expectations.rb) is the impure edge that actually runs
|
|
12
|
-
# one test against a live runtime.
|
|
7
|
+
# The `.behaviors` toolkit: DSL, IR, runner and rspec shim.
|
|
13
8
|
module Behaviors
|
|
14
9
|
FileResult = Struct.new(:path, :parse_error, :runs, keyword_init: true)
|
|
15
10
|
SweepResult = Struct.new(:root, :files_swept, :files, :summary, keyword_init: true)
|
|
@@ -18,33 +13,16 @@ module Hecks
|
|
|
18
13
|
class LoadOutsideRunner < StandardError; end
|
|
19
14
|
|
|
20
15
|
class << self
|
|
21
|
-
# The `.behaviors` file
|
|
22
|
-
#
|
|
23
|
-
# never left set. `Hecks.behaviors` (lib/hecks/behaviors.rb)
|
|
24
|
-
# reads this to resolve its own `loads` line relative to the right
|
|
25
|
-
# file, and to refuse a `.behaviors` file loaded any other way.
|
|
16
|
+
# The `.behaviors` file being `Kernel.load`ed; `Hecks.behaviors` reads it to resolve
|
|
17
|
+
# `loads` and to refuse a file loaded any other way.
|
|
26
18
|
attr_accessor :loading_path
|
|
27
19
|
|
|
28
|
-
# The suite the
|
|
29
|
-
#
|
|
30
|
-
# to nil before every load so a file that loads without calling
|
|
31
|
-
# `Hecks.behaviors` at all is never confused for a stale previous
|
|
32
|
-
# suite (a real bug in a prior port of this idea: compared only
|
|
33
|
-
# against nil, so after the first successful file in a sweep it
|
|
34
|
-
# stayed non-nil forever).
|
|
20
|
+
# The suite the latest `Hecks.behaviors` call built. `parse` resets it before each load so
|
|
21
|
+
# a file that never calls `Hecks.behaviors` is not mistaken for the previous suite.
|
|
35
22
|
attr_accessor :last_suite
|
|
36
23
|
|
|
37
|
-
# `Kernel.load`s one `.behaviors` file and returns its suite
|
|
38
|
-
#
|
|
39
|
-
# caller that only needs to know what tests exist (the rspec shim,
|
|
40
|
-
# naming its `it`s at collection time) doesn't pay for running them
|
|
41
|
-
# until it actually wants to. `Behaviors.loading_path` is bound only
|
|
42
|
-
# for the duration of the load, and `last_suite` is reset to nil
|
|
43
|
-
# before it — a file that loads without ever calling
|
|
44
|
-
# `Hecks.behaviors` is unambiguously a parse error, never a stale
|
|
45
|
-
# suite from whatever loaded before it in a sweep (a real bug in a
|
|
46
|
-
# prior port of this idea: compared only against nil, so after the
|
|
47
|
-
# first successful file in a sweep it stayed non-nil forever).
|
|
24
|
+
# `Kernel.load`s one `.behaviors` file and returns its suite without running any test.
|
|
25
|
+
#
|
|
48
26
|
# @param path [String] the `.behaviors` file's path
|
|
49
27
|
# @return [Behaviors::ParseResult] `suite` holding the built `BehaviorsSuite` and
|
|
50
28
|
# `parse_error` nil on success; `suite` nil and `parse_error` a String describing
|
|
@@ -83,10 +61,8 @@ module Hecks
|
|
|
83
61
|
FileResult.new(path: parsed.path, parse_error: nil, runs: runs)
|
|
84
62
|
end
|
|
85
63
|
|
|
86
|
-
# Sweeps every `.behaviors` file under `dir
|
|
87
|
-
#
|
|
88
|
-
# much it looked at is indistinguishable from one that found
|
|
89
|
-
# nothing to look at.
|
|
64
|
+
# Sweeps every `.behaviors` file under `dir`, reporting the file count so a green sweep
|
|
65
|
+
# that found nothing is distinguishable.
|
|
90
66
|
#
|
|
91
67
|
# @param dir [String] the directory to search, recursively, for `.behaviors` files
|
|
92
68
|
# @return [Behaviors::SweepResult] `files_swept` the count found, `files` one
|
data/lib/hecks/behaviors.rb
CHANGED
|
@@ -2,17 +2,13 @@ require_relative "behaviors/dsl"
|
|
|
2
2
|
require_relative "behaviors/expectations"
|
|
3
3
|
require_relative "behaviors/runner"
|
|
4
4
|
|
|
5
|
-
# The `.behaviors` toolkit `bin/behaviors` and
|
|
6
|
-
#
|
|
7
|
-
# purpose — a booted domain never needs it, the same reason `Fuzzing` (see
|
|
5
|
+
# The `.behaviors` toolkit driven by `bin/behaviors` and `hecks/behaviors/rspec`.
|
|
6
|
+
# Opt-in: not required by lib/hecks.rb, like `Fuzzing`.
|
|
8
7
|
# lib/hecks/fuzzing.rb, the shape this file mirrors) is opt-in too.
|
|
9
8
|
module Hecks
|
|
10
9
|
class << self
|
|
11
|
-
# `Hecks.behaviors "Name" do ... end`
|
|
12
|
-
#
|
|
13
|
-
# behaviors suite is a test artifact a runner reads on demand, never
|
|
14
|
-
# a thing a live domain boot needs, so it has no business landing in
|
|
15
|
-
# `Runtime.current_registry`.
|
|
10
|
+
# `Hecks.behaviors "Name" do ... end`. Not routed through `collect`: a behaviors suite is a
|
|
11
|
+
# test artifact, so it stays out of `Runtime.current_registry`.
|
|
16
12
|
#
|
|
17
13
|
# @param name [String] the suite's declared name
|
|
18
14
|
# @yield the suite's body, evaluated against a `Behaviors::BehaviorsBuilder`
|