hecks 2.8.0 → 3.0.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 +50 -7
- 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 +32 -100
- 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 +10 -32
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
- data/lib/hecks/adapters/driven/postgres/shared_connection.rb +180 -0
- data/lib/hecks/adapters/driven/postgres.rb +149 -136
- data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
- data/lib/hecks/adapters/driven/prism.rb +48 -38
- 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 +76 -62
- 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/bench/cli.rb +62 -0
- data/lib/hecks/bench/environment.rb +71 -0
- data/lib/hecks/bench/postgres_probe.rb +30 -0
- data/lib/hecks/bench/report.rb +76 -0
- data/lib/hecks/bench/ruby_runner.rb +93 -0
- data/lib/hecks/bench/run.rb +55 -0
- data/lib/hecks/bench/rust_runner.rb +101 -0
- data/lib/hecks/bench/stats.rb +46 -0
- data/lib/hecks/bench/suite.rb +98 -0
- data/lib/hecks/bench/workload.rb +89 -0
- data/lib/hecks/bench.rb +17 -0
- 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 +56 -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 +39 -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 +11 -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 +16 -50
- data/lib/hecks/bluebook/command.rb +23 -81
- data/lib/hecks/bluebook/domain_port.rb +49 -67
- 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 +96 -708
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +63 -169
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +3 -1
- 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 +42 -89
- 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 +148 -197
- 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 +38 -43
- 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 +46 -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 +39 -661
- data/lib/hecks/bluebook/hexagon.rb +18 -33
- 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 +25 -176
- data/lib/hecks/bluebook/meta_validator/shapes.rb +53 -267
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +47 -306
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
- data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +246 -0
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
- data/lib/hecks/bluebook/meta_validator.rb +87 -352
- data/lib/hecks/bluebook/model_check/client_profile.rb +6 -21
- data/lib/hecks/bluebook/model_check.rb +122 -596
- data/lib/hecks/bluebook/pattern_subset.rb +49 -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 +27 -51
- data/lib/hecks/bluebook/read_model.rb +57 -81
- data/lib/hecks/bluebook/reference.rb +7 -41
- data/lib/hecks/bluebook/smoke_test.rb +9 -99
- 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/canonical_json.rb +30 -0
- data/lib/hecks/chapters.rb +102 -0
- data/lib/hecks/cli/behaviors.rb +61 -0
- data/lib/hecks/cli/canonicalise.rb +25 -0
- data/lib/hecks/cli/check_engine_agreement.rb +32 -0
- data/lib/hecks/cli/check_era.rb +88 -0
- data/lib/hecks/cli/console.rb +66 -0
- data/lib/hecks/cli/deploy_template_diff.rb +40 -0
- data/lib/hecks/cli/doc_coverage.rb +32 -0
- data/lib/hecks/cli/document.rb +5 -23
- data/lib/hecks/cli/expression_projection.rb +41 -0
- data/lib/hecks/cli/follow.rb +88 -0
- data/lib/hecks/cli/history.rb +44 -0
- data/lib/hecks/cli/ir.rb +4 -6
- data/lib/hecks/cli/mcp.rb +5 -6
- data/lib/hecks/cli/model_check.rb +24 -88
- data/lib/hecks/cli/pattern_cases.rb +47 -0
- data/lib/hecks/cli/present.rb +39 -0
- data/lib/hecks/cli/project_cli.rb +223 -64
- data/lib/hecks/cli/project_diagrams.rb +4 -10
- data/lib/hecks/cli/project_glossary.rb +44 -0
- data/lib/hecks/cli/reference.rb +46 -0
- data/lib/hecks/cli/refresh_projections.rb +44 -0
- data/lib/hecks/cli/refresh_rspec_runtime_baseline.rb +150 -0
- data/lib/hecks/cli/release.rb +70 -0
- data/lib/hecks/cli/release_gem.rb +59 -0
- data/lib/hecks/cli/rspec_io_parallel_files.rb +137 -0
- data/lib/hecks/cli/rspec_shard_files.rb +66 -0
- data/lib/hecks/cli/run.rb +15 -61
- data/lib/hecks/cli/seed_semantics_corpus.rb +71 -0
- data/lib/hecks/cli/serve_query_ir_mcp.rb +30 -0
- data/lib/hecks/cli/shape.rb +70 -0
- data/lib/hecks/cli/smoke_http.rb +230 -0
- data/lib/hecks/cli/smoke_test.rb +7 -19
- data/lib/hecks/cli/spec_example.rb +27 -0
- data/lib/hecks/cli/statements.rb +32 -0
- data/lib/hecks/cli/stores.rb +4 -6
- data/lib/hecks/cli/stress_concurrency_specs.rb +177 -0
- data/lib/hecks/cli.rb +216 -73
- data/lib/hecks/codemod.rb +324 -0
- data/lib/hecks/construct.rb +8 -34
- data/lib/hecks/corpus.rb +423 -0
- data/lib/hecks/deploy/bluebook/deploy.bluebook +617 -158
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +41 -11
- data/lib/hecks/deploy/oidc.json +82 -2
- data/lib/hecks/doc/reference.rb +351 -0
- data/lib/hecks/doors/cli_door.rb +196 -0
- data/lib/hecks/doors/cli_runner.rb +297 -0
- data/lib/hecks/{facade → doors}/command_request.rb +14 -34
- data/lib/hecks/doors/handle.rb +206 -0
- data/lib/hecks/doors/json_door.rb +132 -0
- data/lib/hecks/doors/launcher_options.rb +129 -0
- data/lib/hecks/{cli → doors}/mcp_door.rb +25 -120
- data/lib/hecks/doors/mcp_door_scope.rb +97 -0
- data/lib/hecks/doors/ruby_door/aggregate_door.rb +162 -0
- data/lib/hecks/doors/ruby_door/chapter.rb +72 -0
- data/lib/hecks/doors/ruby_door.rb +77 -0
- data/lib/hecks/doors.rb +61 -0
- 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 +5 -72
- data/lib/hecks/engine_agreement.rb +156 -0
- data/lib/hecks/facade.rb +23 -51
- data/lib/hecks/forms/app.rb +21 -76
- data/lib/hecks/forms/banking_presentation.rb +50 -0
- 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 +31 -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/fuzzing/bounded_exhaustive_expressions.rb +393 -0
- data/lib/hecks/fuzzing/combination_miner.rb +86 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +214 -0
- data/lib/hecks/fuzzing/coverage_campaign.rb +143 -0
- data/lib/hecks/fuzzing/differential.rb +156 -0
- data/lib/hecks/fuzzing/domain_generator.rb +615 -0
- data/lib/hecks/fuzzing/era_boundary.rb +71 -0
- data/lib/hecks/fuzzing/form_census.rb +136 -0
- data/lib/hecks/fuzzing/generated_domain_check.rb +93 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +64 -0
- data/lib/hecks/fuzzing/isolated_boot.rb +301 -0
- data/lib/hecks/fuzzing/nondeterministic.rb +51 -0
- data/lib/hecks/fuzzing/outside_world.rb +45 -0
- data/lib/hecks/fuzzing/persistence_parity.rb +102 -0
- data/lib/hecks/fuzzing/properties/corrections.rb +88 -0
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +314 -0
- data/lib/hecks/fuzzing/properties/guards.rb +190 -0
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +300 -0
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +93 -0
- data/lib/hecks/fuzzing/properties/outbox.rb +89 -0
- data/lib/hecks/fuzzing/properties/querying.rb +175 -0
- data/lib/hecks/fuzzing/properties.rb +162 -0
- data/lib/hecks/fuzzing/qa_settings.rb +123 -0
- data/lib/hecks/fuzzing/replay.rb +505 -0
- data/lib/hecks/fuzzing/rotation_priority.rb +48 -0
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +108 -0
- data/lib/hecks/fuzzing/self_consistency.rb +339 -0
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +385 -0
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +132 -0
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +95 -0
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +77 -0
- data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +109 -0
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +237 -0
- data/lib/hecks/fuzzing/sequence_generator.rb +189 -0
- data/lib/hecks/fuzzing/shrinker.rb +166 -0
- data/lib/hecks/fuzzing/structural_skips.rb +33 -0
- data/lib/hecks/fuzzing/sweep_depth.rb +43 -0
- data/lib/hecks/fuzzing/target_capabilities.rb +163 -0
- data/lib/hecks/fuzzing/value_generator.rb +216 -0
- data/lib/hecks/fuzzing.rb +23 -0
- data/lib/hecks/grammar/evolve.rb +336 -0
- 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/hecks/adapters/codebase/codemods.rb +45 -0
- data/lib/hecks/hecks/adapters/codebase/conformance.rb +71 -0
- data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +202 -0
- data/lib/hecks/hecks/adapters/codebase/gem_registry.rb +80 -0
- data/lib/hecks/hecks/adapters/codebase/kernel_tables.rb +54 -0
- data/lib/hecks/hecks/adapters/codebase/language.rb +240 -0
- data/lib/hecks/hecks/adapters/codebase/npm_registry.rb +102 -0
- data/lib/hecks/hecks/adapters/codebase/publishing.rb +142 -0
- data/lib/hecks/hecks/adapters/codebase/regeneration.rb +67 -0
- data/lib/hecks/hecks/adapters/codebase/release_facts.rb +90 -0
- data/lib/hecks/hecks/adapters/codebase/ruby_child.rb +83 -0
- data/lib/hecks/hecks/adapters/codebase/secret_vault.rb +36 -0
- data/lib/hecks/hecks/adapters/codebase/source_tree.adapter +7 -0
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +189 -0
- data/lib/hecks/hecks/adapters/codebase/sqlite_fixture.rb +71 -0
- data/lib/hecks/hecks/adapters/codebase/style.rb +171 -0
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +55 -0
- data/lib/hecks/hecks/adapters/codebase/test_suite.rb +165 -0
- data/lib/hecks/hecks/adapters/codebase/tree.rb +111 -0
- data/lib/hecks/hecks/adapters/console_capture.rb +79 -0
- data/lib/hecks/hecks/adapters/deploy_toolchain.adapter +7 -0
- data/lib/hecks/hecks/adapters/deploy_toolchain.rb +108 -0
- data/lib/hecks/hecks/adapters/git.adapter +7 -0
- data/lib/hecks/hecks/adapters/git.rb +102 -0
- data/lib/hecks/hecks/adapters/host_http.adapter +7 -0
- data/lib/hecks/hecks/adapters/host_http.rb +65 -0
- data/lib/hecks/hecks/adapters/in_process_boot.adapter +7 -0
- data/lib/hecks/hecks/adapters/in_process_boot.rb +197 -0
- data/lib/hecks/hecks/adapters/in_process_operations.rb +202 -0
- data/lib/hecks/hecks/adapters/journal_store/changes.rb +143 -0
- data/lib/hecks/hecks/adapters/journal_store/compaction.rb +145 -0
- data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +132 -0
- data/lib/hecks/hecks/adapters/journal_store/examination.rb +135 -0
- data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +163 -0
- data/lib/hecks/hecks/adapters/journal_store/readings.rb +187 -0
- data/lib/hecks/hecks/adapters/journal_store.adapter +7 -0
- data/lib/hecks/hecks/adapters/journal_store.rb +64 -0
- data/lib/hecks/hecks/adapters/local_files.adapter +7 -0
- data/lib/hecks/hecks/adapters/local_files.rb +46 -0
- data/lib/hecks/hecks/adapters/pg_admin.adapter +7 -0
- data/lib/hecks/hecks/adapters/pg_admin.rb +245 -0
- data/lib/hecks/hecks/adapters/process_pool.adapter +7 -0
- data/lib/hecks/hecks/adapters/process_pool.rb +119 -0
- data/lib/hecks/hecks/adapters/rust_toolchain.adapter +7 -0
- data/lib/hecks/hecks/adapters/rust_toolchain.rb +179 -0
- data/lib/hecks/hecks/adapters/rust_workspace.rb +150 -0
- data/lib/hecks/hecks/adapters/shell.adapter +7 -0
- data/lib/hecks/hecks/adapters/shell.rb +49 -0
- data/lib/hecks/hecks/adapters/terminal.adapter +7 -0
- data/lib/hecks/hecks/adapters/terminal.rb +74 -0
- data/lib/hecks/hecks/codebase.behaviors +172 -0
- data/lib/hecks/hecks/codebase.bluebook +2453 -0
- data/lib/hecks/hecks/context_map.hecksagon +13 -0
- data/lib/hecks/hecks/custodian.behaviors +179 -0
- data/lib/hecks/hecks/custodian.bluebook +1847 -0
- data/lib/hecks/hecks/deploy.behaviors +74 -0
- data/lib/hecks/hecks/deploy.world +12 -0
- data/lib/hecks/hecks/environments/memory.world +10 -0
- data/lib/hecks/hecks/governance.world +7 -0
- data/lib/hecks/hecks/hecks.behaviors +62 -0
- data/lib/hecks/hecks/hecks.bluebook +124 -0
- data/lib/hecks/hecks/hecks.hecksagon +390 -0
- data/lib/hecks/hecks/hecks.world +42 -0
- data/lib/hecks/ir.rb +14 -83
- data/lib/hecks/kernel_capabilities.rb +194 -0
- 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 +26 -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 +19 -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 +83 -198
- data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
- data/lib/hecks/language/hecksagon/domain_port.bluebook +14 -19
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +26 -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_stdio_guard.rb +4 -41
- data/lib/hecks/naming.rb +29 -137
- data/lib/hecks/persistence_legacy_fixture/regenerate.rb +161 -0
- data/lib/hecks/persistence_legacy_fixture.rb +113 -0
- 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 +44 -144
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +20 -85
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +6 -25
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +18 -24
- 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 +19 -76
- 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 +43 -127
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +131 -555
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
- data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +227 -0
- 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 +50 -47
- 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/translation.rb +1 -0
- 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 +47 -108
- 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/projection_files.rb +285 -0
- data/lib/hecks/projections/bootstrap_table.rb +12 -44
- 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 +40 -207
- data/lib/hecks/projections/deploy/lambda.rb +191 -708
- 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/preview.sh.erb +1 -1
- 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 +3 -59
- data/lib/hecks/projections/deploy/preview.rb +2 -67
- data/lib/hecks/projections/deploy/scripts/deploy-service.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/scripts/expected-era.header +1 -1
- data/lib/hecks/projections/deploy/scripts/hosting.mk.tmpl +3 -3
- data/lib/hecks/projections/deploy/scripts/settings.rb +3 -25
- data/lib/hecks/projections/deploy/scripts/smoke-after-deploy.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/scripts.rb +4 -57
- data/lib/hecks/projections/deploy/shared.rb +48 -147
- 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 +22 -584
- 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 +16 -43
- data/lib/hecks/projections/model.rb +12 -51
- data/lib/hecks/projections/oidc.rb +12 -56
- data/lib/hecks/projections/parser_table.rb +13 -32
- data/lib/hecks/projections/reference.rb +4 -18
- data/lib/hecks/projections/rust_vocabulary.rb +13 -251
- 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 +154 -400
- 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/quality_control/adapters/agent.adapter +7 -0
- data/lib/hecks/quality_control/adapters/agent.rb +58 -0
- data/lib/hecks/quality_control/adapters/angle_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/angle_tools.rb +15 -0
- data/lib/hecks/quality_control/adapters/clearance_recorder.rb +36 -0
- data/lib/hecks/quality_control/adapters/clearance_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/clearance_tools.rb +17 -0
- data/lib/hecks/quality_control/adapters/git_pr.adapter +7 -0
- data/lib/hecks/quality_control/adapters/git_pr.rb +226 -0
- data/lib/hecks/quality_control/adapters/github_checks.adapter +9 -0
- data/lib/hecks/quality_control/adapters/github_checks.rb +83 -0
- data/lib/hecks/quality_control/adapters/github_ci_webhook.rb +56 -0
- data/lib/hecks/quality_control/adapters/github_issues.adapter +7 -0
- data/lib/hecks/quality_control/adapters/github_issues.rb +49 -0
- data/lib/hecks/quality_control/adapters/qa_tool.rb +59 -0
- data/lib/hecks/quality_control/adapters/sweep_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/sweep_tools.rb +51 -0
- data/lib/hecks/quality_control/adapters/target_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/target_tools.rb +43 -0
- data/lib/hecks/quality_control/cli/child.rb +45 -0
- data/lib/hecks/quality_control/cli/qa_concurrency_racer.rb +45 -0
- data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +248 -0
- data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +163 -0
- data/lib/hecks/quality_control/cli/qa_generated_domains.rb +450 -0
- data/lib/hecks/quality_control/cli/qa_log_bug.rb +195 -0
- data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +220 -0
- data/lib/hecks/quality_control/cli/qa_open_pr.rb +305 -0
- data/lib/hecks/quality_control/cli/qa_postgres_migrate.rb +154 -0
- data/lib/hecks/quality_control/cli/qa_postgres_role.rb +159 -0
- data/lib/hecks/quality_control/cli/qa_pr_check.rb +232 -0
- data/lib/hecks/quality_control/cli/qa_seed_angles.rb +142 -0
- data/lib/hecks/quality_control/cli/qa_seed_targets.rb +69 -0
- data/lib/hecks/quality_control/cli/qa_sweep/all_mode.rb +280 -0
- data/lib/hecks/quality_control/cli/qa_sweep/checks.rb +329 -0
- data/lib/hecks/quality_control/cli/qa_sweep/dials.rb +92 -0
- data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +117 -0
- data/lib/hecks/quality_control/cli/qa_sweep/parsing.rb +175 -0
- data/lib/hecks/quality_control/cli/qa_sweep/release_mode.rb +60 -0
- data/lib/hecks/quality_control/cli/qa_sweep.rb +611 -0
- data/lib/hecks/quality_control/cli/qa_tick.rb +274 -0
- data/lib/hecks/quality_control/quality_control.bluebook +2224 -0
- data/lib/hecks/quality_control/quality_control.hecksagon +13 -0
- data/lib/hecks/quality_control/quality_control.ports.hecksagon +54 -0
- data/lib/hecks/query_ir.rb +346 -0
- data/lib/hecks/query_ir_mcp.rb +198 -0
- 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 +5 -8
- data/lib/hecks/release/runner/clean_tree.rb +42 -0
- data/lib/hecks/release/runner/commands.rb +2 -4
- data/lib/hecks/release/runner/console.rb +6 -0
- data/lib/hecks/release/runner/gem_publisher.rb +15 -9
- data/lib/hecks/release/runner/npm_publisher.rb +9 -53
- data/lib/hecks/release/runner/preflight.rb +29 -20
- data/lib/hecks/release/runner/published.rb +8 -26
- data/lib/hecks/release/runner/tagger.rb +31 -23
- data/lib/hecks/release/runner.rb +60 -38
- data/lib/hecks/rendering.rb +4 -26
- data/lib/hecks/router/namespace_installer.rb +15 -39
- data/lib/hecks/router.rb +4 -8
- data/lib/hecks/runtime/adapter_lookup.rb +124 -0
- 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 +27 -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 +50 -104
- data/lib/hecks/runtime/dispatcher.rb +134 -243
- 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 +14 -70
- data/lib/hecks/runtime/invocation.rb +28 -164
- data/lib/hecks/runtime/loader.rb +109 -194
- data/lib/hecks/runtime/outbox.rb +39 -262
- data/lib/hecks/runtime/policy_interpreter.rb +43 -202
- data/lib/hecks/runtime/port_operation_interpreter.rb +28 -97
- data/lib/hecks/runtime/query_interpreter.rb +122 -187
- data/lib/hecks/runtime/reaction_invocation.rb +36 -138
- data/lib/hecks/runtime/reaction_outcome.rb +89 -0
- 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 +28 -87
- data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
- data/lib/hecks/runtime/registry/verification.rb +213 -227
- data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
- data/lib/hecks/runtime/registry.rb +156 -247
- data/lib/hecks/runtime/remote_dispatcher.rb +68 -154
- 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 +162 -760
- data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
- data/lib/hecks/runtime/value/field_checks.rb +172 -0
- data/lib/hecks/runtime/value.rb +15 -84
- data/lib/hecks/runtime.rb +35 -69
- data/lib/hecks/rust_build/conformance.rb +158 -0
- data/lib/hecks/rust_build/conformance_fuzz.rb +71 -0
- data/lib/hecks/rust_build/coverage.rb +218 -0
- data/lib/hecks/rust_build/native_build.rb +124 -0
- data/lib/hecks/rust_build/project_rust/root_mod.rb +120 -0
- data/lib/hecks/rust_build/project_rust.rb +332 -0
- data/lib/hecks/rust_build/wasm.rb +105 -0
- data/lib/hecks/rust_build/wasm_browser.rb +113 -0
- data/lib/hecks/rust_build.rb +144 -0
- data/lib/hecks/storehouse.rb +155 -582
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +10 -34
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
- data/lib/hecks/three_zero/forms.yml +127 -0
- data/lib/hecks/three_zero.rb +12 -0
- data/lib/hecks/tools/argument_gate_matrix.rb +316 -0
- data/lib/hecks/tools/comment_style.rb +1070 -0
- data/lib/hecks/tools/corpus_report.rb +87 -0
- data/lib/hecks/tools/deploy_recipe.rb +255 -0
- data/lib/hecks/tools/deploy_recipe_lint.rb +282 -0
- data/lib/hecks/tools/drop_implicit_append_fields.rb +95 -0
- data/lib/hecks/tools/era_projection_backfill.rb +94 -0
- data/lib/hecks/tools/era_reattest.rb +146 -0
- data/lib/hecks/tools/evolve_run.rb +189 -0
- data/lib/hecks/tools/fuzz_sweep.rb +373 -0
- data/lib/hecks/tools/heki_compaction.rb +100 -0
- data/lib/hecks/tools/hoist_local_givens.rb +246 -0
- data/lib/hecks/tools/journal_compaction.rb +89 -0
- data/lib/hecks/tools/oidc_manifests.rb +70 -0
- data/lib/hecks/tools/query_ir_run.rb +81 -0
- data/lib/hecks/tools/regeneration_run.rb +166 -0
- data/lib/hecks/tools/rust_comment_style.rb +642 -0
- data/lib/hecks/tools/sequence_script.rb +62 -0
- data/lib/hecks/tools/tail_merge.rb +117 -0
- data/lib/hecks/tools/tenant_provisioning.rb +142 -0
- data/lib/hecks/tools/translation_audit.rb +208 -0
- data/lib/hecks/tools/translation_scaffold.rb +131 -0
- data/lib/hecks/tools.rb +86 -0
- 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 +52 -90
- 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
- data/qa/settings.yml +66 -0
- data/rust/.cargo/config.toml +6 -0
- data/rust/.gitignore +2 -0
- data/rust/Cargo.lock +54 -0
- data/rust/Cargo.toml +61 -0
- data/rust/build/.cargo/config.toml +3 -0
- data/rust/build/.gitignore +1 -0
- data/rust/build/Cargo.lock +7 -0
- data/rust/build/Cargo.toml +19 -0
- data/rust/build/src/build_artifact.rs +47 -0
- data/rust/build/src/cargo_sync.rs +248 -0
- data/rust/build/src/json.rs +421 -0
- data/rust/build/src/lineage_pass.rs +285 -0
- data/rust/build/src/main.rs +51 -0
- data/rust/build/src/optional_pass.rs +150 -0
- data/rust/build/src/pipeline.rs +264 -0
- data/rust/build/src/reserved_names.rs +76 -0
- data/rust/build/src/resolve.rs +124 -0
- data/rust/build/src/root.rs +25 -0
- data/rust/build/src/sidecars.rs +73 -0
- data/rust/build/src/subprocess.rs +51 -0
- data/rust/build/src/tmp.rs +31 -0
- data/rust/codegen/.cargo/config.toml +3 -0
- data/rust/codegen/.gitignore +1 -0
- data/rust/codegen/Cargo.lock +7 -0
- data/rust/codegen/Cargo.toml +20 -0
- data/rust/codegen/src/attr.rs +32 -0
- data/rust/codegen/src/bridging.rs +481 -0
- data/rust/codegen/src/commands.rs +1226 -0
- data/rust/codegen/src/constraints.rs +109 -0
- data/rust/codegen/src/dependency_planning.rs +209 -0
- data/rust/codegen/src/domain_generator.rs +1288 -0
- data/rust/codegen/src/exemplar.rs +230 -0
- data/rust/codegen/src/expr_emitter.rs +87 -0
- data/rust/codegen/src/fielded.rs +192 -0
- data/rust/codegen/src/hecks_naming.rs +33 -0
- data/rust/codegen/src/json.rs +335 -0
- data/rust/codegen/src/json_codec.rs +697 -0
- data/rust/codegen/src/literal.rs +207 -0
- data/rust/codegen/src/main.rs +290 -0
- data/rust/codegen/src/manifest.rs +227 -0
- data/rust/codegen/src/mutations.rs +784 -0
- data/rust/codegen/src/naming.rs +286 -0
- data/rust/codegen/src/ports.rs +208 -0
- data/rust/codegen/src/prelude.rs +116 -0
- data/rust/codegen/src/queries.rs +786 -0
- data/rust/codegen/src/reactions.rs +532 -0
- data/rust/codegen/src/read_models.rs +697 -0
- data/rust/codegen/src/reference_specs.rs +40 -0
- data/rust/codegen/src/registry.rs +966 -0
- data/rust/codegen/src/reserved_names.rs +76 -0
- data/rust/codegen/src/shared.rs +93 -0
- data/rust/codegen/src/skip_reason.rs +25 -0
- data/rust/codegen/src/types.rs +362 -0
- data/rust/host/.cargo/config.toml +3 -0
- data/rust/host/.gitignore +1 -0
- data/rust/host/Cargo.lock +4297 -0
- data/rust/host/Cargo.toml +61 -0
- data/rust/host/HECKS_RELEASE +1 -0
- data/rust/host/rds-ca-bundle.pem +2736 -0
- data/rust/host/src/api.rs +1596 -0
- data/rust/host/src/approval.rs +652 -0
- data/rust/host/src/auth.rs +1287 -0
- data/rust/host/src/bin/expr_harness.rs +46 -0
- data/rust/host/src/bin/lineage_harness.rs +119 -0
- data/rust/host/src/bin/mint_harness.rs +135 -0
- data/rust/host/src/checkout.rs +265 -0
- data/rust/host/src/dispatch.rs +1443 -0
- data/rust/host/src/expr_json.rs +1117 -0
- data/rust/host/src/field_hints.rs +52 -0
- data/rust/host/src/ir.rs +733 -0
- data/rust/host/src/journal.rs +874 -0
- data/rust/host/src/lambda_client.rs +378 -0
- data/rust/host/src/log.rs +147 -0
- data/rust/host/src/main.rs +474 -0
- data/rust/host/src/mint.rs +1865 -0
- data/rust/host/src/payments/keystore.rs +330 -0
- data/rust/host/src/payments/tests.rs +1400 -0
- data/rust/host/src/payments.rs +767 -0
- data/rust/host/src/presentation.rs +698 -0
- data/rust/host/src/presentation_write.rs +1431 -0
- data/rust/host/src/query_step.rs +149 -0
- data/rust/host/src/rate_limit.rs +904 -0
- data/rust/host/src/reference_transform.rs +241 -0
- data/rust/host/src/reference_validate.rs +338 -0
- data/rust/host/src/resend.rs +296 -0
- data/rust/host/src/secrets.rs +106 -0
- data/rust/host/src/server.rs +663 -0
- data/rust/host/src/storage_shape.rs +175 -0
- data/rust/host/src/test_pg.rs +40 -0
- data/rust/host/src/ui_schema.rs +1351 -0
- data/rust/host/src/wasm_runner.rs +191 -0
- data/rust/host/src/web/newsletter/cooldown.rs +126 -0
- data/rust/host/src/web/newsletter/tests.rs +104 -0
- data/rust/host/src/web/newsletter.rs +310 -0
- data/rust/host/src/web/newsletter_send.rs +357 -0
- data/rust/host/src/web/registrations.rs +1505 -0
- data/rust/host/src/web.rs +2937 -0
- data/rust/lsp/.gitignore +1 -0
- data/rust/lsp/Cargo.lock +7 -0
- data/rust/lsp/Cargo.toml +16 -0
- data/rust/lsp/README.md +133 -0
- data/rust/lsp/src/diagnostics.rs +165 -0
- data/rust/lsp/src/json.rs +385 -0
- data/rust/lsp/src/main.rs +399 -0
- data/rust/lsp/src/outline.rs +177 -0
- data/rust/lsp/src/rpc.rs +67 -0
- data/rust/parser/.cargo/config.toml +5 -0
- data/rust/parser/.gitignore +1 -0
- data/rust/parser/Cargo.lock +7 -0
- data/rust/parser/Cargo.toml +21 -0
- data/rust/parser/src/build/closed_sets.rs +27 -0
- data/rust/parser/src/build/identity.rs +276 -0
- data/rust/parser/src/build/mod.rs +12 -0
- data/rust/parser/src/build/naming.rs +141 -0
- data/rust/parser/src/build/pattern_subset.rs +161 -0
- data/rust/parser/src/build/query_derive.rs +72 -0
- data/rust/parser/src/build/query_inference.rs +269 -0
- data/rust/parser/src/build/query_options.rs +100 -0
- data/rust/parser/src/build/read_model.rs +94 -0
- data/rust/parser/src/build/references.rs +32 -0
- data/rust/parser/src/canonical.rs +114 -0
- data/rust/parser/src/diag.rs +55 -0
- data/rust/parser/src/emit.rs +953 -0
- data/rust/parser/src/expr/ast_json.rs +147 -0
- data/rust/parser/src/expr/evaluator.rs +81 -0
- data/rust/parser/src/expr/mod.rs +84 -0
- data/rust/parser/src/expr/resolver.rs +497 -0
- data/rust/parser/src/ir.rs +387 -0
- data/rust/parser/src/keywords.rs +585 -0
- data/rust/parser/src/lex.rs +662 -0
- data/rust/parser/src/main.rs +252 -0
- data/rust/parser/src/parse/aggregate.rs +612 -0
- data/rust/parser/src/parse/chapter.rs +517 -0
- data/rust/parser/src/parse/command.rs +637 -0
- data/rust/parser/src/parse/domain_port.rs +127 -0
- data/rust/parser/src/parse/entity.rs +527 -0
- data/rust/parser/src/parse/file.rs +67 -0
- data/rust/parser/src/parse/hecksagon.rs +231 -0
- data/rust/parser/src/parse/lifecycle.rs +162 -0
- data/rust/parser/src/parse/mod.rs +1262 -0
- data/rust/parser/src/parse/policy.rs +87 -0
- data/rust/parser/src/parse/process_manager.rs +305 -0
- data/rust/parser/src/parse/query.rs +100 -0
- data/rust/parser/src/parse/read_model.rs +214 -0
- data/rust/parser/src/parse/value_object.rs +250 -0
- data/rust/parser/src/ruby_value.rs +422 -0
- data/rust/project/bridging.rb +257 -0
- data/rust/project/commands.rb +877 -0
- data/rust/project/constraints.rb +86 -0
- data/rust/project/dependency_planning.rb +178 -0
- data/rust/project/domain_generator.rb +980 -0
- data/rust/project/exemplar.rb +148 -0
- data/rust/project/expr_emitter.rb +72 -0
- data/rust/project/fielded.rb +141 -0
- data/rust/project/json_codec.rb +641 -0
- data/rust/project/mutations.rb +721 -0
- data/rust/project/naming.rb +205 -0
- data/rust/project/ports.rb +65 -0
- data/rust/project/queries.rb +493 -0
- data/rust/project/reactions.rb +317 -0
- data/rust/project/read_models.rb +690 -0
- data/rust/project/reference_specs.rb +27 -0
- data/rust/project/registry.rb +396 -0
- data/rust/project/skip_reason.rb +29 -0
- data/rust/project/types.rb +225 -0
- data/rust/project/write_if_changed.rb +83 -0
- data/rust/project.rb +30 -0
- data/rust/project_rust_pipeline.rb +383 -0
- data/rust/src/exemplar/commands.rs +279 -0
- data/rust/src/exemplar/constraints.rs +18 -0
- data/rust/src/exemplar/fielded.rs +280 -0
- data/rust/src/exemplar/json.rs +277 -0
- data/rust/src/exemplar/mod.rs +17 -0
- data/rust/src/exemplar/mutations.rs +80 -0
- data/rust/src/exemplar/queries.rs +33 -0
- data/rust/src/exemplar/reactions.rs +88 -0
- data/rust/src/exemplar/read_models.rs +48 -0
- data/rust/src/exemplar/registry.rs +166 -0
- data/rust/src/exemplar/types.rs +44 -0
- data/rust/src/kernel/attribute_shapes/composite.rs +88 -0
- data/rust/src/kernel/attribute_shapes/list.rs +18 -0
- data/rust/src/kernel/attribute_shapes/mod.rs +44 -0
- data/rust/src/kernel/attribute_shapes/optional.rs +9 -0
- data/rust/src/kernel/attribute_shapes/scalar.rs +39 -0
- data/rust/src/kernel/cli.rs +585 -0
- data/rust/src/kernel/dispatch.rs +1090 -0
- data/rust/src/kernel/expr.rs +458 -0
- data/rust/src/kernel/expression_operators/arithmetic.rs +128 -0
- data/rust/src/kernel/expression_operators/comparison.rs +48 -0
- data/rust/src/kernel/expression_operators/enumeration.rs +232 -0
- data/rust/src/kernel/expression_operators/logical.rs +13 -0
- data/rust/src/kernel/expression_operators/membership.rs +18 -0
- data/rust/src/kernel/expression_operators/mod.rs +68 -0
- data/rust/src/kernel/expression_operators/pattern_match.rs +73 -0
- data/rust/src/kernel/expression_operators/positional.rs +126 -0
- data/rust/src/kernel/expression_operators/presence.rs +101 -0
- data/rust/src/kernel/expression_operators/sign_test.rs +16 -0
- data/rust/src/kernel/expression_operators/sized.rs +35 -0
- data/rust/src/kernel/expression_operators/text.rs +79 -0
- data/rust/src/kernel/expression_operators/to_string.rs +18 -0
- data/rust/src/kernel/json.rs +779 -0
- data/rust/src/kernel/mod.rs +133 -0
- data/rust/src/kernel/named_query.rs +219 -0
- data/rust/src/kernel/orchestrate.rs +1810 -0
- data/rust/src/kernel/pattern.rs +307 -0
- data/rust/src/kernel/query_comparators.rs +206 -0
- data/rust/src/kernel/query_ordering.rs +103 -0
- data/rust/src/kernel/read_model.rs +914 -0
- data/rust/src/kernel/reference_lookup.rs +149 -0
- data/rust/src/kernel/repository.rs +468 -0
- data/rust/src/kernel/routing.rs +612 -0
- data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +118 -0
- data/rust/src/kernel/vocab/entity_dispatch_order.rs +114 -0
- data/rust/src/kernel/vocab/field_hint.rs +70 -0
- data/rust/src/kernel/vocab/mod.rs +20 -0
- data/rust/src/kernel/vocab/query_comparator.rs +65 -0
- data/rust/src/kernel/vocab/refusal_template.rs +1592 -0
- data/rust/src/lib.rs +8 -0
- data/rust/src/main.rs +16 -0
- data/rust/web/.gitignore +1 -0
- data/rust/web/Cargo.lock +119 -0
- data/rust/web/Cargo.toml +18 -0
- data/rust/web/src/lib.rs +8 -0
- metadata +478 -12
- data/lib/hecks/facade/cli_door.rb +0 -178
- data/lib/hecks/facade/cli_runner.rb +0 -271
- data/lib/hecks/facade/handle.rb +0 -293
- data/lib/hecks/facade/json_door.rb +0 -247
- data/lib/hecks/facade/surface/aggregate_door.rb +0 -222
- data/lib/hecks/facade/surface/chapter.rb +0 -116
- data/lib/hecks/facade/surface.rb +0 -61
- data/lib/hecks/mcp_door_scope.rb +0 -176
|
@@ -3,151 +3,36 @@ require_relative "model_check/client_profile"
|
|
|
3
3
|
|
|
4
4
|
module Hecks
|
|
5
5
|
module Bluebook
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# every process manager a declared protocol, and both are already
|
|
9
|
-
# data, not code, so they can be model-checked rather than merely
|
|
10
|
-
# executed. Static analysis only — no bluebook boots twice, no
|
|
11
|
-
# runtime is touched — over what meta_validator/judge.rb and the
|
|
12
|
-
# builders' own `validate!` methods leave uncovered (an undeclared
|
|
13
|
-
# transition target, a dispatch to nowhere, a compensation nothing
|
|
14
|
-
# can ever reach).
|
|
15
|
-
#
|
|
16
|
-
# **The rare property this rests on**: the model is the implementation.
|
|
17
|
-
# A checker over TLA+ verifies a spec a human keeps in sync with
|
|
18
|
-
# code by hand ; this verifies the same IR the runtime dispatches
|
|
19
|
-
# against, so there is no second copy to drift.
|
|
6
|
+
# Static formal checks over the assembled bluebook IR: lifecycles as state
|
|
7
|
+
# machines, process managers as protocols, checked as data without booting a runtime.
|
|
20
8
|
module ModelCheck
|
|
21
9
|
Finding = Struct.new(:kind, :severity, :subject, :message, keyword_init: true) do
|
|
22
10
|
def to_s = "#{severity.to_s.upcase.ljust(7)} #{kind.to_s.ljust(20)} #{subject} — #{message}"
|
|
23
11
|
end
|
|
24
12
|
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
|
|
30
|
-
# constant, so the tool and the spec can never drift apart.
|
|
31
|
-
#
|
|
32
|
-
# "banking"/ExternalSettlement — found on the first real run:
|
|
33
|
-
# ExternalSettlement declares `ends_on "ExternalTransferSent"`, and
|
|
34
|
-
# ExternalTransfer.Send genuinely emits it — the event is real, and
|
|
35
|
-
# the aggregate reaches "sent" (its own, separate lifecycle) — but
|
|
36
|
-
# the saga's protocol has no `on "ExternalTransferSent"` handler, so
|
|
37
|
-
# its own `state "sent"` is unreachable through the chain the
|
|
38
|
-
# checker walks, and the saga's own bookkeeping (saga_log, ends_on)
|
|
39
|
-
# never closes it. Real domain activity is unaffected; the saga's
|
|
40
|
-
# own tracking of it is not. Left named rather than redesigning a
|
|
41
|
-
# corpus fixture that is not this checker's to redesign.
|
|
42
|
-
# S7, ADR 0025 — an ExternalSettlement finding once lived here as an
|
|
43
|
-
# allowlist entry and is gone now, not just quieted: its "sent" state
|
|
44
|
-
# was a `state "x"` line never named by any handler's own from:/to:, a
|
|
45
|
-
# pure declaration-drift artifact. States are derived from the
|
|
46
|
-
# transitions that name them now (ProcessManagerBuilder#derived_
|
|
47
|
-
# states), so a state nothing ever transitions into or out of does
|
|
48
|
-
# not exist to be unreachable — the finding that entry allowlisted
|
|
49
|
-
# cannot occur, by construction.
|
|
50
|
-
#
|
|
51
|
-
# "banking"/NotifyOnClosure, FlagKeyReturn — gone from here, moved
|
|
52
|
-
# to banking. `across "Notifications"` names a domain that does not
|
|
53
|
-
# exist anywhere in this repo, deliberately (it exercises the
|
|
54
|
-
# undelivered-reaction runtime path — `spec/runtime/policy_spec.rb`,
|
|
55
|
-
# "records a reaction it cannot deliver rather than swallowing it").
|
|
56
|
-
# That expectation is now declared on the two policies themselves —
|
|
57
|
-
# `across "Notifications", expect_undelivered: true` — and
|
|
58
|
-
# `expected_undelivered_findings` below holds it in both directions:
|
|
59
|
-
# the unknown-target and unacknowledged-relationship findings are
|
|
60
|
-
# expected, and a declaration whose target turns out reachable is a
|
|
61
|
-
# `stale_undelivered_expectation` error. A domain's own allowance
|
|
62
|
-
# lives in its own source, never in a core table keyed by its name.
|
|
63
|
-
#
|
|
64
|
-
# Pinned empty (spec/model_check_spec.rb), the way `bin/fuzz`'s
|
|
65
|
-
# `KNOWN_FUZZ_FINDINGS` is: a finding a domain means to keep belongs
|
|
66
|
-
# in that domain's own declaration.
|
|
67
|
-
ALLOWED_FINDINGS = {
|
|
68
|
-
# QualityControl was the first domain in this corpus to trigger an
|
|
69
|
-
# `asks`/`tells` port operation from a `policy`, and once carried
|
|
70
|
-
# two entries here for it — both gone now, not just quieted:
|
|
71
|
-
#
|
|
72
|
-
# `deaf_policy` (ClearOnPass, RefuseOnFail, RecordTheIssue,
|
|
73
|
-
# RecordTheRefusal) went first: `emitted_events` below now reads an
|
|
74
|
-
# outbound operation's `.answers`/`.refuses` the same way it already
|
|
75
|
-
# read a command's `.emits`, so `Clearance.SuitePassed`/`SuiteFailed`
|
|
76
|
-
# and `Ticket.IssueFiled`/`IssueFilingRefused` enter the known-emits
|
|
77
|
-
# set for real — the same fix `bin/qa_pr_check`'s own move to
|
|
78
|
-
# dispatching through the CI port (rather than `Clearance::Passed`/
|
|
79
|
-
# `Failed` directly) needed to make these two policies actually fire.
|
|
80
|
-
#
|
|
81
|
-
# `unknown_trigger` (FileWhenSubmitted, AskOnceMore) — BUG#23 — was
|
|
82
|
-
# never actually a `Naming`/`PolicyBuilder` defect, confirmed by
|
|
83
|
-
# tracing the real dispatch path rather than trusting a stale
|
|
84
|
-
# comment that once sat here: `Naming.command_ref`'s bare-constant
|
|
85
|
-
# rewrite does leave `trigger Ticket::IssueTracker::File` (aggregate,
|
|
86
|
-
# port, operation) as "Ticket::IssueTracker.File", a leftover `::`
|
|
87
|
-
# past the aggregate — but `PolicyInterpreter#deliver` re-qualifies
|
|
88
|
-
# every trigger with this domain's own name before dispatch
|
|
89
|
-
# ("QualityControl::Ticket::IssueTracker.File"), and `Naming.
|
|
90
|
-
# split_verb` already folds that reintroduced `::` into the
|
|
91
|
-
# dot-joined tail correctly (the same fix `ReactionInvocation#
|
|
92
|
-
# resolve_target` already carries) — confirmed live: a real
|
|
93
|
-
# dispatch through `Ticket.Submit` fires
|
|
94
|
-
# `IssueFiled`/`TicketFiled` exactly as declared. The actual gap was
|
|
95
|
-
# entirely in this checker: `verbs_of` never enumerated a port
|
|
96
|
-
# operation as a triggerable verb at all, and `policy_findings`
|
|
97
|
-
# compared raw strings instead of `Naming.split_verb` triples the
|
|
98
|
-
# way `handler_findings`'s own `unknown_dispatch` check already does
|
|
99
|
-
# (BUG#6). Fixed with `port_verbs_of`/`triggerable_verbs`, scoped
|
|
100
|
-
# entirely to this file — no change to `Naming` or `PolicyBuilder`
|
|
101
|
-
# was needed or made.
|
|
102
|
-
}.freeze
|
|
13
|
+
# Findings a domain deliberately allows, enforced in both directions by
|
|
14
|
+
# spec/model_check_spec.rb: an error this checker reports but isn't listed
|
|
15
|
+
# here is a regression; an entry listed but never reported is stale. Pinned
|
|
16
|
+
# empty — a domain's own allowance belongs on its own declaration. (ADR 0025)
|
|
17
|
+
ALLOWED_FINDINGS = {}.freeze
|
|
103
18
|
|
|
104
19
|
# The profiles `call` accepts; `nil` is the default, unprofiled run.
|
|
105
20
|
PROFILES = %i[client].freeze
|
|
106
21
|
|
|
107
22
|
module_function
|
|
108
23
|
|
|
109
|
-
# Runs every static model check over `bluebook` and returns what it
|
|
110
|
-
# finds.
|
|
111
|
-
#
|
|
112
|
-
# `hecksagon:`/`known_domains:` — both optional, both `nil`-safe
|
|
113
|
-
# (every existing caller with no sibling hecksagon, or checking one
|
|
114
|
-
# domain in isolation, behaves exactly as before). `hecksagon` is
|
|
115
|
-
# this bluebook's own sibling wiring file, if the caller loaded one
|
|
116
|
-
# (see `emitted_events`'s own comment on why a caller that didn't
|
|
117
|
-
# simply finds none, correctly). `known_domains` is the caller's
|
|
118
|
-
# own corpus-wide view — every bluebook/hecksagon name it has
|
|
119
|
-
# booted anywhere, across every domain it has looked at, not just
|
|
120
|
-
# this one — used only to catch a typo'd `across`/`uses_framework`
|
|
121
|
-
# target; see `cross_domain_policy_findings`'s own comment for why
|
|
122
|
-
# this can only ever be a corpus-scoped heuristic, never a general
|
|
123
|
-
# correctness guarantee.
|
|
124
|
-
#
|
|
125
|
-
# `rust_target:`/`strict:` — both default false, both only change the
|
|
126
|
-
# severity of `rust_reserved_name` findings (see
|
|
127
|
-
# `rust_reserved_name_findings`); every other finding is unaffected.
|
|
24
|
+
# Runs every static model check over `bluebook` and returns what it finds.
|
|
128
25
|
#
|
|
129
26
|
# @param bluebook [Bluebook::Chapter] the assembled chapter to check
|
|
130
|
-
# @param hecksagon [Bluebook::Hecksagon, nil] this bluebook's own
|
|
131
|
-
#
|
|
132
|
-
# @param known_domains [Set<String>, nil] every bluebook/hecksagon
|
|
133
|
-
#
|
|
134
|
-
# @param global_emitted_events [Set<String>, nil] every
|
|
135
|
-
#
|
|
136
|
-
#
|
|
137
|
-
#
|
|
138
|
-
#
|
|
139
|
-
# single-domain-only check
|
|
140
|
-
# @param rust_target [Boolean] whether this domain has a real Rust
|
|
141
|
-
# target, raising `rust_reserved_name` findings to error
|
|
142
|
-
# @param strict [Boolean] whether to raise every `rust_reserved_name`
|
|
143
|
-
# finding to error regardless of `rust_target`
|
|
144
|
-
# @param profile [Symbol, nil] `:client` adds `ClientProfile`'s error
|
|
145
|
-
# findings for constructs known to answer wrongly without refusing;
|
|
146
|
-
# `nil` (the default) adds nothing and leaves every other finding as
|
|
147
|
-
# it was
|
|
148
|
-
# @return [Array<Finding>] every finding this bluebook triggers,
|
|
149
|
-
# across its lifecycles, sagas, policies, and Rust-reserved names
|
|
150
|
-
# @raise [ArgumentError] if `profile` is neither `nil` nor `:client`
|
|
27
|
+
# @param hecksagon [Bluebook::Hecksagon, nil] this bluebook's own sibling wiring
|
|
28
|
+
# file, if the caller loaded one
|
|
29
|
+
# @param known_domains [Set<String>, nil] every bluebook/hecksagon name booted
|
|
30
|
+
# anywhere in this corpus scan, checked to catch a typo'd across/uses_framework target
|
|
31
|
+
# @param global_emitted_events [Set<String>, nil] every event emitted anywhere in
|
|
32
|
+
# the corpus, checked as a fallback so a translates reaction isn't flagged deaf
|
|
33
|
+
# @param profile [Symbol, nil] :client adds ClientProfile's error findings
|
|
34
|
+
# @return [Array<Finding>] every finding this bluebook triggers
|
|
35
|
+
# @raise [ArgumentError] if profile is neither nil nor :client
|
|
151
36
|
def call(bluebook, hecksagon: nil, known_domains: nil, global_emitted_events: nil, rust_target: false, strict: false,
|
|
152
37
|
profile: nil)
|
|
153
38
|
unless profile.nil? || PROFILES.include?(profile)
|
|
@@ -166,45 +51,43 @@ module Hecks
|
|
|
166
51
|
findings.concat(rust_reserved_name_findings(domain_name: bluebook.name,
|
|
167
52
|
aggregate_names: bluebook.aggregates.map(&:hecks_name),
|
|
168
53
|
rust_target: rust_target, strict: strict))
|
|
54
|
+
findings.concat(external_query_findings(bluebook, rust_target: rust_target))
|
|
169
55
|
findings.concat(ClientProfile.call(bluebook, hecksagon: hecksagon)) if profile == :client
|
|
170
56
|
findings
|
|
171
57
|
end
|
|
172
58
|
|
|
173
|
-
#
|
|
174
|
-
#
|
|
175
|
-
# A name that becomes a bare Rust module identifier with no `r#`
|
|
176
|
-
# escape hatch: an aggregate (`pub mod <name.downcase>;` plus its
|
|
177
|
-
# `<name.downcase>.rs` file) and a domain (`pub mod <name>;` and a
|
|
178
|
-
# Cargo `[features]` key). Field names are not checked — both
|
|
179
|
-
# generators already raw-escape those (`rust_ident_field`).
|
|
180
|
-
#
|
|
181
|
-
# The words come from the `RustReservedWord`/`CargoReservedName`
|
|
182
|
-
# vocabularies, the same tables `rust/project/naming.rb` and
|
|
183
|
-
# hecks-codegen's generated `reserved_names.rs` read. Both Rust
|
|
184
|
-
# generators refuse through this check (`Projector.
|
|
185
|
-
# reserved_name_refusal`, and its hecks-codegen port in `naming.rs`).
|
|
59
|
+
# Flags a query the hecksagon binds to a port's adapter. Only the Ruby runtime asks an
|
|
60
|
+
# adapter for an answer; a Rust host would find nothing behind the query.
|
|
186
61
|
#
|
|
187
|
-
#
|
|
188
|
-
#
|
|
189
|
-
#
|
|
190
|
-
#
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
62
|
+
# @param bluebook [Bluebook::Chapter] the assembled chapter, its hecksagon's ports attached
|
|
63
|
+
# @param rust_target [Boolean] whether this domain has a real Rust target; a domain without
|
|
64
|
+
# one is not checked, since nothing but the Ruby runtime serves it
|
|
65
|
+
# @return [Array<Finding>] one :external_query error per bound query, none off a Rust target
|
|
66
|
+
def external_query_findings(bluebook, rust_target: false)
|
|
67
|
+
return [] unless rust_target
|
|
68
|
+
|
|
69
|
+
bluebook.aggregates.flat_map do |aggregate|
|
|
70
|
+
aggregate.ports.flat_map do |port|
|
|
71
|
+
port.answered_queries.map do |answer|
|
|
72
|
+
Finding.new(kind: :external_query, severity: :error,
|
|
73
|
+
subject: "#{aggregate.hecks_name}.#{answer.name}",
|
|
74
|
+
message: "the #{port.name} port's adapter answers this query, and only the Ruby " \
|
|
75
|
+
"runtime asks an adapter — the Rust host cannot serve it")
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Flags an aggregate or domain name that collides with a Rust keyword or reserved
|
|
82
|
+
# Cargo key — both become bare module identifiers with no `r#` escape hatch.
|
|
196
83
|
#
|
|
197
|
-
# @param domain_name [String, Symbol, nil] the domain's own name, or
|
|
198
|
-
#
|
|
199
|
-
# @param aggregate_names [Array<String, Symbol>] every aggregate name
|
|
200
|
-
#
|
|
201
|
-
#
|
|
202
|
-
#
|
|
203
|
-
# @
|
|
204
|
-
# regardless of `rust_target`
|
|
205
|
-
# @return [Array<Finding>] one `:rust_reserved_name` finding per
|
|
206
|
-
# aggregate or domain name that collides with a Rust or Cargo
|
|
207
|
-
# reserved word
|
|
84
|
+
# @param domain_name [String, Symbol, nil] the domain's own name, or nil to skip
|
|
85
|
+
# the domain-level check
|
|
86
|
+
# @param aggregate_names [Array<String, Symbol>] every aggregate name to check
|
|
87
|
+
# @param rust_target [Boolean] raises severity to error when this domain has a
|
|
88
|
+
# real Rust target
|
|
89
|
+
# @param strict [Boolean] raises severity to error regardless of rust_target
|
|
90
|
+
# @return [Array<Finding>] one :rust_reserved_name finding per colliding name
|
|
208
91
|
def rust_reserved_name_findings(domain_name: nil, aggregate_names: [], rust_target: false, strict: false)
|
|
209
92
|
severity = rust_target || strict ? :error : :warning
|
|
210
93
|
keywords = Hecks::Vocabulary.fetch("RustReservedWord")
|
|
@@ -221,17 +104,7 @@ module Hecks
|
|
|
221
104
|
findings
|
|
222
105
|
end
|
|
223
106
|
|
|
224
|
-
#
|
|
225
|
-
# from `rust_reserved_name_findings`'s own per-aggregate check.
|
|
226
|
-
#
|
|
227
|
-
# @param domain_name [String, Symbol] the domain's own name
|
|
228
|
-
# @param keywords [Array<String>] the admitted `RustReservedWord`
|
|
229
|
-
# vocabulary rows
|
|
230
|
-
# @param severity [Symbol] the finding severity to use, `:error` or
|
|
231
|
-
# `:warning`
|
|
232
|
-
# @return [Array<Finding>] a single-element array holding a
|
|
233
|
-
# `:rust_reserved_name` finding, or `[]` if `domain_name` collides
|
|
234
|
-
# with no reserved word or Cargo key
|
|
107
|
+
# The domain-level half of rust_reserved_name_findings — same tables, one name.
|
|
235
108
|
def domain_reserved_name_findings(domain_name, keywords, severity)
|
|
236
109
|
module_name = rust_module_name(domain_name)
|
|
237
110
|
table = if keywords.include?(module_name)
|
|
@@ -246,27 +119,10 @@ module Hecks
|
|
|
246
119
|
"rename the domain")]
|
|
247
120
|
end
|
|
248
121
|
|
|
249
|
-
# Derives the Rust module identifier both generators derive from a
|
|
250
|
-
# name.
|
|
251
|
-
#
|
|
252
|
-
# @param name [String, Symbol] an aggregate or domain name
|
|
253
|
-
# @return [String] the Rust module identifier both generators derive
|
|
254
|
-
# from `name`
|
|
255
122
|
def rust_module_name(name) = name.to_s.downcase
|
|
256
123
|
|
|
257
|
-
#
|
|
258
|
-
|
|
259
|
-
# Runs every lifecycle-shaped model check for one declaring
|
|
260
|
-
# construct's own lifecycle, if it has one.
|
|
261
|
-
#
|
|
262
|
-
# @param aggregate [Bluebook::Aggregate] the root aggregate `declaring`
|
|
263
|
-
# belongs to
|
|
264
|
-
# @param declaring [Bluebook::Aggregate, Bluebook::Entity] the
|
|
265
|
-
# construct whose own lifecycle to check — `aggregate` itself, or
|
|
266
|
-
# one of its entities
|
|
267
|
-
# @return [Array<Finding>] `[]` if `declaring` has no lifecycle;
|
|
268
|
-
# otherwise every `unknown_command`/`unreachable_state`/
|
|
269
|
-
# `dead_transition`/`stuck_state` finding its lifecycle triggers
|
|
124
|
+
# Runs every lifecycle-shaped check for one declaring construct — the aggregate
|
|
125
|
+
# itself, or one of its entities — if it declares a lifecycle at all.
|
|
270
126
|
def lifecycle_findings(aggregate, declaring)
|
|
271
127
|
lifecycle = declaring.lifecycle
|
|
272
128
|
return [] unless lifecycle
|
|
@@ -285,15 +141,7 @@ module Hecks
|
|
|
285
141
|
findings
|
|
286
142
|
end
|
|
287
143
|
|
|
288
|
-
# Finds every declared state
|
|
289
|
-
# never reaches.
|
|
290
|
-
#
|
|
291
|
-
# @param lifecycle [Bluebook::Lifecycle] the lifecycle being checked
|
|
292
|
-
# @param full [Array<String>] every state `full_states` declares
|
|
293
|
-
# @param reached [Set<String>] every state `reachable_states` reaches
|
|
294
|
-
# @param subject [String] the finding's own subject label
|
|
295
|
-
# @return [Array<Finding>] one `:unreachable_state` finding per
|
|
296
|
-
# declared state that `reached` does not cover
|
|
144
|
+
# Finds every declared state the lifecycle's own reachability walk never reaches.
|
|
297
145
|
def unreachable_state_findings(lifecycle, full, reached, subject)
|
|
298
146
|
(full - reached.to_a).map do |state|
|
|
299
147
|
Finding.new(kind: :unreachable_state, severity: :error, subject: subject,
|
|
@@ -302,13 +150,7 @@ module Hecks
|
|
|
302
150
|
end
|
|
303
151
|
end
|
|
304
152
|
|
|
305
|
-
# Finds every constrained transition
|
|
306
|
-
#
|
|
307
|
-
# @param lifecycle [Bluebook::Lifecycle] the lifecycle being checked
|
|
308
|
-
# @param reached [Set<String>] every state `reachable_states` reaches
|
|
309
|
-
# @param subject [String] the finding's own subject label
|
|
310
|
-
# @return [Array<Finding>] one `:dead_transition` finding per
|
|
311
|
-
# constrained transition whose `from:` states are all unreached
|
|
153
|
+
# Finds every constrained transition whose from: states are all unreached.
|
|
312
154
|
def dead_transition_findings(lifecycle, reached, subject)
|
|
313
155
|
lifecycle.transitions.filter_map do |command, transition|
|
|
314
156
|
next unless transition.constrained?
|
|
@@ -320,14 +162,8 @@ module Hecks
|
|
|
320
162
|
end
|
|
321
163
|
end
|
|
322
164
|
|
|
323
|
-
# Finds every reached state with no transition ever leaving it
|
|
324
|
-
#
|
|
325
|
-
# @param lifecycle [Bluebook::Lifecycle] the lifecycle being checked
|
|
326
|
-
# @param reached [Set<String>] every state `reachable_states` reaches
|
|
327
|
-
# @param subject [String] the finding's own subject label
|
|
328
|
-
# @return [Array<Finding>] one `:stuck_state` finding per reached,
|
|
329
|
-
# non-exempt state with no transition ever leaving it (skipped
|
|
330
|
-
# entirely when any transition is unconstrained)
|
|
165
|
+
# Finds every reached state with no transition ever leaving it, unless any
|
|
166
|
+
# transition in this lifecycle is unconstrained.
|
|
331
167
|
def stuck_state_findings(lifecycle, reached, subject)
|
|
332
168
|
any_unconstrained = lifecycle.transitions.any? { |_, t| !t.constrained? }
|
|
333
169
|
(reached - terminal_exempt(lifecycle)).filter_map do |state|
|
|
@@ -340,15 +176,7 @@ module Hecks
|
|
|
340
176
|
end
|
|
341
177
|
end
|
|
342
178
|
|
|
343
|
-
# Finds every transition named after a command
|
|
344
|
-
# not declare.
|
|
345
|
-
#
|
|
346
|
-
# @param lifecycle [Bluebook::Lifecycle] the lifecycle being checked
|
|
347
|
-
# @param commands [Array<String>] every command name the declaring
|
|
348
|
-
# construct actually declares
|
|
349
|
-
# @param subject [String] the finding's own subject label
|
|
350
|
-
# @return [Array<Finding>] one `:unknown_command` finding per
|
|
351
|
-
# transition named after a command `commands` does not list
|
|
179
|
+
# Finds every transition named after a command the construct doesn't declare.
|
|
352
180
|
def unknown_transition_commands(lifecycle, commands, subject)
|
|
353
181
|
lifecycle.transitions.filter_map do |command, _transition|
|
|
354
182
|
next if commands.include?(command)
|
|
@@ -358,14 +186,11 @@ module Hecks
|
|
|
358
186
|
end
|
|
359
187
|
end
|
|
360
188
|
|
|
361
|
-
#
|
|
362
|
-
#
|
|
363
|
-
# exactly the hole `Lifecycle#states` leaves: it answers default
|
|
364
|
-
# plus targets only.
|
|
189
|
+
# Every state `lifecycle` declares in any role — default, target, or from,
|
|
190
|
+
# unique.
|
|
365
191
|
#
|
|
366
192
|
# @param lifecycle [Bluebook::Lifecycle] the lifecycle being checked
|
|
367
|
-
# @return [Array<String>] every state
|
|
368
|
-
# role (default, target, or from), unique
|
|
193
|
+
# @return [Array<String>] every state lifecycle declares, unique
|
|
369
194
|
def full_states(lifecycle)
|
|
370
195
|
(
|
|
371
196
|
[lifecycle.default] +
|
|
@@ -374,21 +199,14 @@ module Hecks
|
|
|
374
199
|
).uniq
|
|
375
200
|
end
|
|
376
201
|
|
|
377
|
-
# Least fixpoint from the default state: an unconstrained
|
|
378
|
-
#
|
|
379
|
-
# constrained one fires once any of its named sources is reached.
|
|
380
|
-
#
|
|
381
|
-
# @param lifecycle [Bluebook::Lifecycle] the lifecycle being checked
|
|
382
|
-
# @return [Set<String>] every state reachable from `lifecycle`'s own
|
|
383
|
-
# default state
|
|
202
|
+
# Least fixpoint from the default state: an unconstrained transition always
|
|
203
|
+
# fires; a constrained one fires once any of its named sources is reached.
|
|
384
204
|
def reachable_states(lifecycle)
|
|
385
205
|
reached = Set.new([lifecycle.default])
|
|
386
206
|
loop do
|
|
387
207
|
grown = false
|
|
388
|
-
# `transitions` is an Array of [from, transition]
|
|
389
|
-
#
|
|
390
|
-
# `each_value` rewrite assumed otherwise from the block shape
|
|
391
|
-
# alone. False positive.
|
|
208
|
+
# `transitions` is an Array of [from, transition] pairs, not a Hash, so
|
|
209
|
+
# Style/HashEachMethods' each_value rewrite is a false positive here.
|
|
392
210
|
# rubocop:disable-next Style/HashEachMethods
|
|
393
211
|
lifecycle.transitions.each do |_, transition|
|
|
394
212
|
next if reached.include?(transition.target)
|
|
@@ -402,26 +220,10 @@ module Hecks
|
|
|
402
220
|
reached
|
|
403
221
|
end
|
|
404
222
|
|
|
405
|
-
#
|
|
406
|
-
#
|
|
407
|
-
#
|
|
408
|
-
#
|
|
409
|
-
# forever, since nothing about *entering* it via default implies
|
|
410
|
-
# anything must eventually move it, unlike a state a transition
|
|
411
|
-
# explicitly delivered somewhere.
|
|
412
|
-
#
|
|
413
|
-
# Scoped to `default` alone, and only when the lifecycle actually
|
|
414
|
-
# declares real transitions elsewhere: an empty lifecycle (no
|
|
415
|
-
# transitions at all) doesn't get this exemption — that's not "a
|
|
416
|
-
# machine whose entry point deliberately awaits external action,"
|
|
417
|
-
# it's much more likely a lifecycle nobody finished wiring, and
|
|
418
|
-
# should still warn (see spec/fixtures/model_check/lifecycle_
|
|
419
|
-
# findings.bluebook's own Widget::Part, which stays warned on
|
|
420
|
-
# purpose).
|
|
421
|
-
#
|
|
422
|
-
# @param lifecycle [Bluebook::Lifecycle] the lifecycle being checked
|
|
423
|
-
# @return [Array<String>] `[lifecycle.default]` when it is exempt from
|
|
424
|
-
# the stuck-state warning, `[]` otherwise
|
|
223
|
+
# Exempts only the lifecycle's own default state, and only when the lifecycle
|
|
224
|
+
# declares real transitions elsewhere: entering default implies nothing about
|
|
225
|
+
# ever leaving it, unlike a state some transition explicitly delivered to. An
|
|
226
|
+
# empty lifecycle still warns — that reads as unfinished wiring, not a deliberate rest.
|
|
425
227
|
def terminal_exempt(lifecycle)
|
|
426
228
|
return [] if lifecycle.transitions.empty?
|
|
427
229
|
|
|
@@ -429,24 +231,12 @@ module Hecks
|
|
|
429
231
|
outgoing_sources.include?(lifecycle.default) ? [] : [lifecycle.default]
|
|
430
232
|
end
|
|
431
233
|
|
|
432
|
-
#
|
|
433
|
-
|
|
434
|
-
# Runs every saga-shaped model check for one process manager.
|
|
435
|
-
#
|
|
436
|
-
# @param bluebook [Bluebook::Chapter] the assembled chapter
|
|
437
|
-
# `process_manager` belongs to
|
|
438
|
-
# @param process_manager [Bluebook::ProcessManager] the process
|
|
439
|
-
# manager to check
|
|
440
|
-
# @return [Array<Finding>] every `deaf_trigger`/`unreachable_pm_state`/
|
|
441
|
-
# `deaf_handler`/`unknown_dispatch`/`unarmed_compensation`/
|
|
442
|
-
# `dead_compensation` finding this process manager triggers
|
|
234
|
+
# Runs every saga-shaped check for one process manager.
|
|
443
235
|
def saga_findings(bluebook, process_manager)
|
|
444
236
|
emitted = emitted_events(bluebook)
|
|
445
|
-
# (domain, aggregate, command) triples, not
|
|
446
|
-
#
|
|
447
|
-
#
|
|
448
|
-
# spellings (`Naming.command_ref`'s own `::`-then-`.` rewrite vs.
|
|
449
|
-
# `verbs_of`'s own all-`.` one) compare equal.
|
|
237
|
+
# Parsed as (domain, aggregate, command) triples via Naming.split_verb, not
|
|
238
|
+
# compared as raw strings — that's what makes an entity verb's two legitimate
|
|
239
|
+
# spellings compare equal (see handler_findings' unknown_dispatch check).
|
|
450
240
|
verbs = verbs_of(bluebook).map { |verb| Naming.split_verb(verb) }
|
|
451
241
|
reached = pm_reachable_states(process_manager, emitted)
|
|
452
242
|
|
|
@@ -460,15 +250,7 @@ module Hecks
|
|
|
460
250
|
findings
|
|
461
251
|
end
|
|
462
252
|
|
|
463
|
-
# Finds every declared
|
|
464
|
-
# emits.
|
|
465
|
-
#
|
|
466
|
-
# @param process_manager [Bluebook::ProcessManager] the process
|
|
467
|
-
# manager being checked
|
|
468
|
-
# @param emitted [Array<String>] every bare event name this domain
|
|
469
|
-
# actually emits
|
|
470
|
-
# @return [Array<Finding>] one `:deaf_trigger` finding per declared
|
|
471
|
-
# `starts_on`/`ends_on` event this domain never emits
|
|
253
|
+
# Finds every declared starts_on/ends_on event this domain never emits.
|
|
472
254
|
def deaf_trigger_findings(process_manager, emitted)
|
|
473
255
|
[process_manager.starts_on, process_manager.ends_on].compact.filter_map do |event|
|
|
474
256
|
next if emitted.include?(bare(event))
|
|
@@ -480,13 +262,6 @@ module Hecks
|
|
|
480
262
|
end
|
|
481
263
|
|
|
482
264
|
# Finds every declared state no handler chain ever reaches.
|
|
483
|
-
#
|
|
484
|
-
# @param process_manager [Bluebook::ProcessManager] the process
|
|
485
|
-
# manager being checked
|
|
486
|
-
# @param reached [Set<String>] every state `pm_reachable_states`
|
|
487
|
-
# reaches
|
|
488
|
-
# @return [Array<Finding>] one `:unreachable_pm_state` finding per
|
|
489
|
-
# declared state that `reached` does not cover
|
|
490
265
|
def unreachable_pm_state_findings(process_manager, reached)
|
|
491
266
|
(Array(process_manager.states) - reached.to_a).map do |state|
|
|
492
267
|
Finding.new(kind: :unreachable_pm_state, severity: :error, subject: process_manager.name,
|
|
@@ -495,33 +270,12 @@ module Hecks
|
|
|
495
270
|
end
|
|
496
271
|
end
|
|
497
272
|
|
|
498
|
-
# One handler's own
|
|
499
|
-
# per dispatch), and unarmed_compensation (one per compensating
|
|
500
|
-
# dispatch), pulled out of saga_findings' own handler loop; each
|
|
501
|
-
# check reads only this handler plus the domain-wide emitted/verbs
|
|
502
|
-
# sets saga_findings already resolved once, no state shared between
|
|
503
|
-
# handlers.
|
|
504
|
-
#
|
|
505
|
-
# @param bluebook [Bluebook::Chapter] the assembled chapter
|
|
506
|
-
# `process_manager` belongs to
|
|
507
|
-
# @param process_manager [Bluebook::ProcessManager] the process
|
|
508
|
-
# manager `handler` belongs to
|
|
509
|
-
# @param emitted [Array<String>] every bare event name this domain
|
|
510
|
-
# actually emits
|
|
511
|
-
# @param verbs [Array<Array(String, String, String)>] every command
|
|
512
|
-
# this domain declares, parsed as `Naming.split_verb`
|
|
513
|
-
# `[domain, aggregate, command]` triples
|
|
514
|
-
# @param handler [Bluebook::ProcessManagerHandler] the handler to
|
|
515
|
-
# check
|
|
516
|
-
# @return [Array<Finding>] every `deaf_handler`/`unknown_dispatch`/
|
|
517
|
-
# `unarmed_compensation` finding this handler triggers
|
|
273
|
+
# One handler's own deaf_handler/unknown_dispatch/unarmed_compensation findings.
|
|
518
274
|
def handler_findings(bluebook, process_manager, emitted, verbs, handler)
|
|
519
275
|
findings = []
|
|
520
276
|
|
|
521
|
-
# The compensating leg answers `REFUSED`, a synthetic trigger no
|
|
522
|
-
#
|
|
523
|
-
# a deaf handler, the one handler this domain's own events can
|
|
524
|
-
# never satisfy on purpose.
|
|
277
|
+
# The compensating leg answers `REFUSED`, a synthetic trigger no command
|
|
278
|
+
# ever emits by name — deliberately exempt from the deaf-handler check.
|
|
525
279
|
if handler.event_type != ProcessManager::REFUSED && !emitted.include?(bare(handler.event_type))
|
|
526
280
|
findings << Finding.new(kind: :deaf_handler, severity: :error, subject: process_manager.name,
|
|
527
281
|
message: "a handler answers #{handler.event_type.inspect}, which no command " \
|
|
@@ -529,37 +283,9 @@ module Hecks
|
|
|
529
283
|
end
|
|
530
284
|
|
|
531
285
|
handler.dispatches.each do |dispatch|
|
|
532
|
-
#
|
|
533
|
-
#
|
|
534
|
-
#
|
|
535
|
-
# command_ref`'s own rewrite as "already qualified" and leaving it
|
|
536
|
-
# alone — would hit the exact same string-shape ambiguity that
|
|
537
|
-
# `SagaInterpreter#qualified`'s own comment explains at length
|
|
538
|
-
# (a same-domain entity command reference and a genuinely
|
|
539
|
-
# cross-domain one are textually indistinguishable after that
|
|
540
|
-
# rewrite). Confirmed against the entire corpus, same as that
|
|
541
|
-
# fix: no saga anywhere ever dispatches genuinely cross-domain,
|
|
542
|
-
# so this checker qualifies exactly the way the runtime
|
|
543
|
-
# actually dispatches — unconditionally against `bluebook.name`
|
|
544
|
-
# — instead of maintaining its own, independently-wrong copy of
|
|
545
|
-
# the same guess.
|
|
546
|
-
#
|
|
547
|
-
# Compared as a triple, not a string — `Naming.command_ref`'s
|
|
548
|
-
# own rewrite of an entity reference (`Manifest::Slot::Fill`)
|
|
549
|
-
# collapses to "Manifest::Slot.Fill" (`::` between aggregate and
|
|
550
|
-
# entity, `.` before the command); `verbs_of`'s own entity
|
|
551
|
-
# spelling, below, joins aggregate/entity/command all with `.`
|
|
552
|
-
# instead (matching `fuzzing/sequence_generator/catalog.rb`'s own
|
|
553
|
-
# independent convention, its comment's own "the same spelling"
|
|
554
|
-
# claim). Both are legitimate, and `Naming.split_verb` already
|
|
555
|
-
# parses either to the identical (domain, aggregate, command)
|
|
556
|
-
# triple (its own comment: "past the already-resolved domain
|
|
557
|
-
# boundary, any leftover `::` is unambiguous... folding it into
|
|
558
|
-
# the dot-joined tail") — the same reading `ReactionInvocation.
|
|
559
|
-
# resolve_target` relies on at runtime. A bare string `include?`
|
|
560
|
-
# would falsely flag every entity dispatch as unknown_dispatch
|
|
561
|
-
# even once correctly domain-qualified, comparing two spellings
|
|
562
|
-
# of the same verb as though they were different ones.
|
|
286
|
+
# Always qualified against this domain's own name (no saga dispatches
|
|
287
|
+
# cross-domain) and compared as a parsed triple, not a string, so an
|
|
288
|
+
# entity verb's two legitimate spellings compare equal.
|
|
563
289
|
qualified = Naming.split_verb("#{bluebook.name}::#{dispatch.command_name}")
|
|
564
290
|
next if verbs.include?(qualified)
|
|
565
291
|
|
|
@@ -569,14 +295,9 @@ module Hecks
|
|
|
569
295
|
"checker's scope, same as CommandRules#resolve_references")
|
|
570
296
|
end
|
|
571
297
|
|
|
572
|
-
#
|
|
573
|
-
#
|
|
574
|
-
#
|
|
575
|
-
# model-check time instead of discovered in production. No
|
|
576
|
-
# handler anywhere answers `REFUSED` (`process_manager.saga?` false) means
|
|
577
|
-
# `SagaInterpreter#unwind` never runs for this process
|
|
578
|
-
# manager at all, so a declared `compensates` is structurally
|
|
579
|
-
# unreachable — not a warning about style, a dead declaration.
|
|
298
|
+
# No handler anywhere answers `REFUSED` means SagaInterpreter#unwind never
|
|
299
|
+
# runs for this process manager, so a declared compensates is structurally
|
|
300
|
+
# unreachable — a dead declaration, not a style warning.
|
|
580
301
|
if !process_manager.saga? && handler.dispatches.any?(&:compensates)
|
|
581
302
|
handler.dispatches.select(&:compensates).each do |dispatch|
|
|
582
303
|
findings << Finding.new(kind: :unarmed_compensation, severity: :error, subject: process_manager.name,
|
|
@@ -589,16 +310,7 @@ module Hecks
|
|
|
589
310
|
findings
|
|
590
311
|
end
|
|
591
312
|
|
|
592
|
-
# Checks whether a saga's own compensation leaves an unreachable
|
|
593
|
-
# state.
|
|
594
|
-
#
|
|
595
|
-
# @param process_manager [Bluebook::ProcessManager] the process
|
|
596
|
-
# manager being checked
|
|
597
|
-
# @param reached [Set<String>] every state `pm_reachable_states`
|
|
598
|
-
# reaches
|
|
599
|
-
# @return [Array<Finding>] a single-element array holding a
|
|
600
|
-
# `:dead_compensation` finding, or `[]` if `process_manager` has no
|
|
601
|
-
# saga or its own compensation leaves a reached state
|
|
313
|
+
# Checks whether a saga's own compensation leaves an unreachable state.
|
|
602
314
|
def dead_compensation_findings(process_manager, reached)
|
|
603
315
|
return [] unless process_manager.saga? && !reached.include?(process_manager.saga.from_state)
|
|
604
316
|
|
|
@@ -607,22 +319,10 @@ module Hecks
|
|
|
607
319
|
"handler chain ever reaches — a refusal here can never fire it")]
|
|
608
320
|
end
|
|
609
321
|
|
|
610
|
-
#
|
|
611
|
-
#
|
|
612
|
-
# event
|
|
613
|
-
#
|
|
614
|
-
# pair reads as connected even though nothing can ever traverse
|
|
615
|
-
# it, which would hide exactly the states this walk exists to
|
|
616
|
-
# catch (a state only "reachable" through a handler that itself
|
|
617
|
-
# never fires).
|
|
618
|
-
#
|
|
619
|
-
# @param process_manager [Bluebook::ProcessManager] the process
|
|
620
|
-
# manager being checked
|
|
621
|
-
# @param emitted [Array<String>] every bare event name this domain
|
|
622
|
-
# actually emits
|
|
623
|
-
# @return [Set<String>] every state reachable from
|
|
624
|
-
# `process_manager`'s own first declared state, through handlers
|
|
625
|
-
# that can actually fire
|
|
322
|
+
# Only a handler that can actually fire extends the closure — `REFUSED` always
|
|
323
|
+
# can (it's a compensation trigger, not an event); any other handler needs its
|
|
324
|
+
# event genuinely emitted. Otherwise a deaf handler's edge would read as
|
|
325
|
+
# connected even though nothing can ever traverse it.
|
|
626
326
|
def pm_reachable_states(process_manager, emitted)
|
|
627
327
|
return Set.new if Array(process_manager.states).empty?
|
|
628
328
|
|
|
@@ -642,80 +342,25 @@ module Hecks
|
|
|
642
342
|
reached
|
|
643
343
|
end
|
|
644
344
|
|
|
645
|
-
# ── policies ───────────────────────────────────────────────────────
|
|
646
|
-
|
|
647
345
|
# Runs every same-domain policy check, or defers to
|
|
648
|
-
#
|
|
649
|
-
# domain.
|
|
650
|
-
#
|
|
651
|
-
# @param bluebook [Bluebook::Chapter] the assembled chapter `policy`
|
|
652
|
-
# belongs to
|
|
653
|
-
# @param policy [Bluebook::Policy] the policy to check
|
|
654
|
-
# @param hecksagon [Bluebook::Hecksagon, nil] this bluebook's own
|
|
655
|
-
# sibling wiring file, if the caller loaded one
|
|
656
|
-
# @param known_domains [Set<String>, nil] every bluebook/hecksagon
|
|
657
|
-
# name the caller has booted anywhere in this corpus scan
|
|
658
|
-
# @param global_emitted_events [Set<String>, nil] every event name emitted anywhere in the
|
|
659
|
-
# corpus, consulted only as a fallback for a `translates` reaction; nil when the caller
|
|
660
|
-
# has no cross-domain set to offer
|
|
661
|
-
# @return [Array<Finding>] every `deaf_policy`/`unknown_trigger`
|
|
662
|
-
# finding this policy triggers, or `cross_domain_policy_findings`'s
|
|
663
|
-
# own return for a cross-domain policy
|
|
346
|
+
# cross_domain_policy_findings for a cross-domain policy.
|
|
664
347
|
def policy_findings(bluebook, policy, hecksagon, known_domains, global_emitted_events = nil)
|
|
665
348
|
return cross_domain_policy_findings(policy, hecksagon, known_domains) if policy.target_domain
|
|
666
349
|
|
|
667
350
|
emitted = emitted_events(bluebook)
|
|
668
351
|
findings = []
|
|
669
352
|
|
|
670
|
-
# `policy.event_name
|
|
671
|
-
#
|
|
672
|
-
#
|
|
673
|
-
# stores whatever was typed, verbatim — see `on "Account.
|
|
674
|
-
# AccountFrozen"`), and `bare` left it untouched, silently comparing
|
|
675
|
-
# "Account.AccountFrozen" against a list of bare emitted names that
|
|
676
|
-
# can never contain it. Latent until now : banking's one aggregate-
|
|
677
|
-
# scoped same-domain policy check would have caught it, but its only
|
|
678
|
-
# prior aggregate-scoped policy (ReviewOnFreeze) is also cross-domain
|
|
679
|
-
# (`across "Compliance"`), which exits this method one line above
|
|
680
|
-
# before the mismatch is ever reached.
|
|
681
|
-
# `global_emitted_events` — a `translates` (hecksagon-level)
|
|
682
|
-
# reaction is built as the exact same same-domain-target `Policy`
|
|
683
|
-
# an ordinary `policy` block is (no `target_domain`, so it never
|
|
684
|
-
# reaches `cross_domain_policy_findings` above), but its whole
|
|
685
|
-
# point is to react to a foreign domain's own event — this
|
|
686
|
-
# domain's own `emitted_events(bluebook)` was never going to
|
|
687
|
-
# contain it. Checked only as a fallback, after the local check
|
|
688
|
-
# already failed, so a real same-domain typo still gets flagged
|
|
689
|
-
# exactly as before whenever the caller has no cross-domain set
|
|
690
|
-
# to offer (nil — every existing call site, unchanged) or the
|
|
691
|
-
# event genuinely isn't emitted anywhere this run examined.
|
|
353
|
+
# `policy.event_name`, not `bare` — an aggregate-scoped on_event carries a
|
|
354
|
+
# "." qualifier bare doesn't strip. `global_emitted_events` is a fallback for
|
|
355
|
+
# a translates reaction, whose whole point is reacting to a foreign event.
|
|
692
356
|
unless emitted.include?(policy.event_name) || global_emitted_events&.include?(policy.event_name)
|
|
693
357
|
findings << Finding.new(kind: :deaf_policy, severity: :error, subject: policy.name,
|
|
694
358
|
message: "on #{policy.on_event.inspect}, which no command in this domain emits")
|
|
695
359
|
end
|
|
696
360
|
|
|
697
|
-
#
|
|
698
|
-
#
|
|
699
|
-
#
|
|
700
|
-
# independently, so the two spellings have to be compared as FQNs.
|
|
701
|
-
#
|
|
702
|
-
# Compared as a triple, not a string — `handler_findings`'s own
|
|
703
|
-
# `unknown_dispatch` check (BUG#6) already applies this fix for a
|
|
704
|
-
# saga's dispatch; a policy's `trigger` needed the identical one. A
|
|
705
|
-
# policy triggering an `asks`/`tells` port operation (`Aggregate::
|
|
706
|
-
# Port::Operation`, three colon-joined segments — `Naming.command_ref`'s
|
|
707
|
-
# bare-constant rewrite turns this into "Aggregate::Port.Operation",
|
|
708
|
-
# a leftover `::` past the aggregate) is a real, working dispatch —
|
|
709
|
-
# `PolicyInterpreter#deliver` qualifies it with this domain's own
|
|
710
|
-
# name before `Naming.split_verb` ever sees it, and `split_verb`
|
|
711
|
-
# already folds that leftover `::` into the dot-joined tail
|
|
712
|
-
# correctly (fixed for `ReactionInvocation#resolve_target`, PR
|
|
713
|
-
# #520) — but this check compared raw strings against `verbs_of`,
|
|
714
|
-
# which never enumerated port operations at all, so it reported
|
|
715
|
-
# every port-operation trigger as unknown regardless. `triggerable_
|
|
716
|
-
# verbs` now includes both, and both sides are parsed through
|
|
717
|
-
# `Naming.split_verb` before comparing, the same reading
|
|
718
|
-
# `resolve_target` relies on at runtime.
|
|
361
|
+
# Completed to the same FQN verbs_of builds, then compared as a parsed
|
|
362
|
+
# triple — not a string — so a port-operation trigger's spelling matches
|
|
363
|
+
# the same way an entity command's does (see handler_findings above).
|
|
719
364
|
qualified = Naming.split_verb("#{bluebook.name}::#{policy.trigger_command}")
|
|
720
365
|
unless qualified && triggerable_verbs(bluebook).include?(qualified)
|
|
721
366
|
findings << Finding.new(kind: :unknown_trigger, severity: :error, subject: policy.name,
|
|
@@ -726,35 +371,9 @@ module Hecks
|
|
|
726
371
|
findings
|
|
727
372
|
end
|
|
728
373
|
|
|
729
|
-
#
|
|
730
|
-
#
|
|
731
|
-
#
|
|
732
|
-
# merges X's own bluebook into this registry, no boundary. A cross-
|
|
733
|
-
# domain `policy ... across: "X"` already is a Customer/Supplier
|
|
734
|
-
# relationship — it dispatches into X over real cross-Lambda RPC in
|
|
735
|
-
# the Rust host (`rust/host/src/lambda_client.rs`). Neither is a new
|
|
736
|
-
# word; this makes the choice between them checked instead of a
|
|
737
|
-
# prose comment nobody enforces (`examples/banking/bluebook/
|
|
738
|
-
# banking.hecksagon`'s own hand-written note explaining why
|
|
739
|
-
# Compliance is reached via `across`, never `uses_framework`).
|
|
740
|
-
#
|
|
741
|
-
# No new keyword anywhere — ADR 0025 principle 1 ("one idea, one
|
|
742
|
-
# spelling") refuses a `relationship:`/`as:` argument that would
|
|
743
|
-
# just restate, as a string, the fact the chosen keyword (
|
|
744
|
-
# `uses_framework` vs `across`) already states completely. The
|
|
745
|
-
# DDD vocabulary (Shared Kernel, Customer/Supplier) lives here, in
|
|
746
|
-
# the finding's own name and this comment, and in prose docs — not
|
|
747
|
-
# in the grammar.
|
|
748
|
-
#
|
|
749
|
-
# @param policy [Bluebook::Policy] the cross-domain policy to check
|
|
750
|
-
# @param hecksagon [Bluebook::Hecksagon, nil] this bluebook's own
|
|
751
|
-
# sibling wiring file, if the caller loaded one
|
|
752
|
-
# @param known_domains [Set<String>, nil] every bluebook/hecksagon
|
|
753
|
-
# name the caller has booted anywhere in this corpus scan
|
|
754
|
-
# @return [Array<Finding>] `expected_undelivered_findings`'s own
|
|
755
|
-
# return for a policy declared `expect_undelivered`; otherwise every
|
|
756
|
-
# `contradictory_relationship`/`unacknowledged_relationship`/
|
|
757
|
-
# `unknown_target_domain` finding this policy triggers
|
|
374
|
+
# `uses_framework` is a Shared Kernel relationship (X merges in, no boundary);
|
|
375
|
+
# `across` is Customer/Supplier (real cross-Lambda RPC). This checks the choice
|
|
376
|
+
# between them instead of leaving it as an unenforced comment. (ADR 0025)
|
|
758
377
|
def cross_domain_policy_findings(policy, hecksagon, known_domains)
|
|
759
378
|
return expected_undelivered_findings(policy, hecksagon, known_domains) if policy.expect_undelivered
|
|
760
379
|
# No sibling hecksagon loaded — nothing to check a relationship against.
|
|
@@ -764,12 +383,9 @@ module Hecks
|
|
|
764
383
|
findings = []
|
|
765
384
|
|
|
766
385
|
if hecksagon.framework_members.include?(target)
|
|
767
|
-
# Shared
|
|
768
|
-
#
|
|
769
|
-
#
|
|
770
|
-
# separate deployment, reached only by RPC." Declaring both is
|
|
771
|
-
# either a pointless RPC to a domain already local, or a
|
|
772
|
-
# `uses_framework` that isn't really doing what its name says.
|
|
386
|
+
# Shared Kernel and Customer/Supplier are mutually exclusive claims about
|
|
387
|
+
# the same target — declaring both means either a pointless RPC to a
|
|
388
|
+
# domain already local, or a uses_framework that isn't doing its job.
|
|
773
389
|
findings << Finding.new(kind: :contradictory_relationship, severity: :error, subject: policy.name,
|
|
774
390
|
message: "across #{target.inspect} dispatches over RPC (Customer/Supplier), " \
|
|
775
391
|
"but this hecksagon also uses_framework #{target.inspect} (Shared " \
|
|
@@ -777,11 +393,8 @@ module Hecks
|
|
|
777
393
|
"relationship declarations contradict each other for the same " \
|
|
778
394
|
"target domain")
|
|
779
395
|
elsif hecksagon.subscriptions.none? { |subscribed| Naming.qualifier(subscribed) == target }
|
|
780
|
-
#
|
|
781
|
-
#
|
|
782
|
-
# (nothing dispatches off a `subscribe` line; see hecksagon.md's
|
|
783
|
-
# own "checked, not routed" section). ADR 0025 names `subscribe`
|
|
784
|
-
# by number as failing the corpus-use bar; this is the real use.
|
|
396
|
+
# Checked here at model-check time; subscribe itself is never routed at
|
|
397
|
+
# runtime (see hecksagon.md's own "checked, not routed" section).
|
|
785
398
|
findings << Finding.new(kind: :unacknowledged_relationship, severity: :error, subject: policy.name,
|
|
786
399
|
message: "across #{target.inspect} declares a Customer/Supplier " \
|
|
787
400
|
"relationship, but nothing in this hecksagon records the " \
|
|
@@ -790,17 +403,9 @@ module Hecks
|
|
|
790
403
|
"attach it in-process instead")
|
|
791
404
|
end
|
|
792
405
|
|
|
793
|
-
#
|
|
794
|
-
#
|
|
795
|
-
#
|
|
796
|
-
# shaped case) lives in a genuinely separate repository this
|
|
797
|
-
# corpus scan can never see, so a target this check cannot find
|
|
798
|
-
# is "unknown to THIS corpus," never proof of a typo. A target
|
|
799
|
-
# undefined by design (the corpus's own "Notifications", used
|
|
800
|
-
# deliberately to exercise the undelivered-reaction runtime path)
|
|
801
|
-
# declares so on its own policy — `expect_undelivered: true`,
|
|
802
|
-
# checked by `expected_undelivered_findings` — rather than being
|
|
803
|
-
# named in a core allowlist.
|
|
406
|
+
# A heuristic, not proof: an external hecks consumer's own domain lives in a
|
|
407
|
+
# repo this corpus scan can't see, so "unknown here" isn't necessarily a typo.
|
|
408
|
+
# A domain undefined on purpose declares so via expect_undelivered instead.
|
|
804
409
|
if known_domains && !known_domains.include?(target)
|
|
805
410
|
findings << Finding.new(kind: :unknown_target_domain, severity: :error, subject: policy.name,
|
|
806
411
|
message: "across #{target.inspect} names a domain nowhere in the corpus " \
|
|
@@ -812,25 +417,10 @@ module Hecks
|
|
|
812
417
|
findings
|
|
813
418
|
end
|
|
814
419
|
|
|
815
|
-
#
|
|
816
|
-
#
|
|
817
|
-
#
|
|
818
|
-
#
|
|
819
|
-
# going stale: the target is a domain this corpus actually booted,
|
|
820
|
-
# or the sibling hecksagon attaches or subscribes to it — either way
|
|
821
|
-
# the reaction can be delivered, and the declaration is now a lie.
|
|
822
|
-
# `known_domains` is nil for a single-target run, which can then only
|
|
823
|
-
# check the hecksagon half.
|
|
824
|
-
#
|
|
825
|
-
# @param policy [Bluebook::Policy] the `expect_undelivered` policy to
|
|
826
|
-
# check
|
|
827
|
-
# @param hecksagon [Bluebook::Hecksagon, nil] this bluebook's own
|
|
828
|
-
# sibling wiring file, if the caller loaded one
|
|
829
|
-
# @param known_domains [Set<String>, nil] every bluebook/hecksagon
|
|
830
|
-
# name the caller has booted anywhere in this corpus scan
|
|
831
|
-
# @return [Array<Finding>] a single-element array holding a
|
|
832
|
-
# `:stale_undelivered_expectation` finding, or `[]` if the target
|
|
833
|
-
# remains genuinely unreachable
|
|
420
|
+
# Holds an expect_undelivered declaration to its word: the two findings an
|
|
421
|
+
# unreachable target would otherwise raise are suppressed (that's the point of
|
|
422
|
+
# the declaration), but if the target turns out reachable after all, the stale
|
|
423
|
+
# declaration itself is now the error.
|
|
834
424
|
def expected_undelivered_findings(policy, hecksagon, known_domains)
|
|
835
425
|
target = policy.target_domain
|
|
836
426
|
reached = []
|
|
@@ -849,33 +439,12 @@ module Hecks
|
|
|
849
439
|
"what reaches #{target}")]
|
|
850
440
|
end
|
|
851
441
|
|
|
852
|
-
#
|
|
853
|
-
|
|
854
|
-
# A port operation emits too — the primary/driving port an adapter
|
|
855
|
-
# outside the bluebook calls through (see hecksagon_builder.rb) is a
|
|
856
|
-
# second, real source of events, alongside a command's own `emits`.
|
|
857
|
-
# Ports attach to the aggregate/bluebook from the sibling `.hecksagon`
|
|
858
|
-
# file, not this one — a caller that boots only the `.bluebook` (as
|
|
859
|
-
# the fixtures under spec/fixtures/model_check/ do, having no
|
|
860
|
-
# hecksagon at all) simply finds none, which is correct : nothing
|
|
861
|
-
# can be deaf to an event that isn't even wired up yet.
|
|
442
|
+
# Every bare event name this domain's commands and port operations emit,
|
|
443
|
+
# answer, or refuse — an outbound `asks` port operation has no `.emits`.
|
|
862
444
|
#
|
|
863
|
-
#
|
|
864
|
-
#
|
|
865
|
-
#
|
|
866
|
-
# `.answers`/`.refuses` instead (`PortOperation#initialize`). Reading
|
|
867
|
-
# only `.emits` left every `asks`'s own two events invisible to this
|
|
868
|
-
# method — real, live events a policy genuinely reacts to
|
|
869
|
-
# (`Clearance.SuitePassed`/`SuiteFailed`, `Ticket.IssueFiled`/
|
|
870
|
-
# `IssueFilingRefused`), reported as `deaf_policy` findings until this
|
|
871
|
-
# read both. `.compact` because an inbound operation's `.answers`/
|
|
872
|
-
# `.refuses` are always nil (there is no channel back to tell), which
|
|
873
|
-
# would otherwise seed every emitted-events set with a stray nil.
|
|
874
|
-
#
|
|
875
|
-
# @param bluebook [Bluebook::Chapter] the assembled chapter to
|
|
876
|
-
# enumerate
|
|
877
|
-
# @return [Array<String>] every bare event name this domain's
|
|
878
|
-
# commands and port operations emit, answer, or refuse, unique
|
|
445
|
+
# @param bluebook [Bluebook::Chapter] the assembled chapter to enumerate
|
|
446
|
+
# @return [Array<String>] every bare event name emitted, answered, or refused
|
|
447
|
+
# across this domain, unique
|
|
879
448
|
def emitted_events(bluebook)
|
|
880
449
|
aggregate_emits = bluebook.aggregates.flat_map do |aggregate|
|
|
881
450
|
aggregate.commands.map(&:emits) +
|
|
@@ -887,32 +456,14 @@ module Hecks
|
|
|
887
456
|
(aggregate_emits + chapter_emits).flatten.compact.uniq
|
|
888
457
|
end
|
|
889
458
|
|
|
890
|
-
# One
|
|
891
|
-
#
|
|
892
|
-
# (`PortOperationBuilder#refuse_wrong_words!` refuses one that
|
|
893
|
-
# tries) and names its two real endings `.answers`/`.refuses`
|
|
894
|
-
# instead. Pulled out of `emitted_events` above purely to keep that
|
|
895
|
-
# method's own branching low enough to read at a glance — every
|
|
896
|
-
# port, aggregate-owned or chapter-level, asks this the same way.
|
|
897
|
-
#
|
|
898
|
-
# @param ports [Array<Bluebook::DomainPort>] the ports to enumerate
|
|
899
|
-
# @return [Array<String, nil>] every event name each operation emits,
|
|
900
|
-
# answers, or refuses; `nil` entries included (compacted by the
|
|
901
|
-
# caller)
|
|
459
|
+
# One port's own emitted/answered/refused event names — an inbound operation's
|
|
460
|
+
# answers/refuses are always nil, compacted by the caller.
|
|
902
461
|
def port_operation_events(ports)
|
|
903
462
|
ports.flat_map { |port| port.operations.flat_map { |op| [*op.emits, op.answers, op.refuses] } }
|
|
904
463
|
end
|
|
905
464
|
|
|
906
|
-
#
|
|
907
|
-
#
|
|
908
|
-
# independently for the same reason: a saga dispatch and a fuzzer
|
|
909
|
-
# step both have to name a verb the same way the door does.
|
|
910
|
-
#
|
|
911
|
-
# @param bluebook [Bluebook::Chapter] the assembled chapter to
|
|
912
|
-
# enumerate
|
|
913
|
-
# @return [Array<String>] every command's own fully-qualified verb,
|
|
914
|
-
# `"Domain::Aggregate.Command"` or
|
|
915
|
-
# `"Domain::Aggregate.Entity.Command"`
|
|
465
|
+
# Every command's own fully-qualified verb: "Domain::Aggregate.Command" or
|
|
466
|
+
# "Domain::Aggregate.Entity.Command".
|
|
916
467
|
def verbs_of(bluebook)
|
|
917
468
|
bluebook.aggregates.flat_map do |aggregate|
|
|
918
469
|
verbs = aggregate.commands.map { |command| "#{bluebook.name}::#{aggregate.hecks_name}.#{command.hecks_name}" }
|
|
@@ -924,20 +475,9 @@ module Hecks
|
|
|
924
475
|
end
|
|
925
476
|
end
|
|
926
477
|
|
|
927
|
-
#
|
|
928
|
-
#
|
|
929
|
-
#
|
|
930
|
-
# Port.Operation", the aggregate then the port then the operation,
|
|
931
|
-
# dot-joined past the domain) an entity command uses, checked first,
|
|
932
|
-
# same order `Dispatcher#dispatch` already resolves a live verb in.
|
|
933
|
-
# Only an aggregate's own ports (`aggregate.ports`) are in scope here
|
|
934
|
-
# — a policy's `trigger` always names one aggregate, never a chapter-
|
|
935
|
-
# level port with no owner to address through.
|
|
936
|
-
#
|
|
937
|
-
# @param bluebook [Bluebook::Chapter] the assembled chapter to
|
|
938
|
-
# enumerate
|
|
939
|
-
# @return [Array<String>] every aggregate-owned port operation's own
|
|
940
|
-
# fully-qualified verb, `"Domain::Aggregate.Port.Operation"`
|
|
478
|
+
# Every aggregate-owned port operation's own fully-qualified verb — only an
|
|
479
|
+
# aggregate's own ports are in scope; a policy trigger never names a
|
|
480
|
+
# chapter-level port directly.
|
|
941
481
|
def port_verbs_of(bluebook)
|
|
942
482
|
bluebook.aggregates.flat_map do |aggregate|
|
|
943
483
|
aggregate.ports.flat_map do |port|
|
|
@@ -948,27 +488,13 @@ module Hecks
|
|
|
948
488
|
end
|
|
949
489
|
end
|
|
950
490
|
|
|
951
|
-
# Every triggerable verb
|
|
952
|
-
#
|
|
953
|
-
# through `Naming.split_verb` so a caller never has to compare two
|
|
954
|
-
# spellings of the same verb as strings (see `policy_findings`'s own
|
|
955
|
-
# `unknown_trigger` check for why that comparison has to happen this
|
|
956
|
-
# way, not as `include?` on a raw string).
|
|
957
|
-
#
|
|
958
|
-
# @param bluebook [Bluebook::Chapter] the assembled chapter to
|
|
959
|
-
# enumerate
|
|
960
|
-
# @return [Set<Array(String, String, String)>] every triggerable
|
|
961
|
-
# verb, parsed as `Naming.split_verb` `[domain, aggregate,
|
|
962
|
-
# command]` triples
|
|
491
|
+
# Every triggerable verb — ordinary/entity commands plus port operations —
|
|
492
|
+
# parsed through Naming.split_verb so callers never compare raw spellings.
|
|
963
493
|
def triggerable_verbs(bluebook)
|
|
964
494
|
(verbs_of(bluebook) + port_verbs_of(bluebook)).to_set { |verb| Naming.split_verb(verb) }
|
|
965
495
|
end
|
|
966
496
|
|
|
967
497
|
# Strips a domain qualifier off an event name.
|
|
968
|
-
#
|
|
969
|
-
# @param event [String, Symbol] an event name, qualified
|
|
970
|
-
# (`"Domain::Event"`) or bare
|
|
971
|
-
# @return [String] `event`'s own bare name, past the last `::`
|
|
972
498
|
def bare(event) = event.to_s.split("::").last
|
|
973
499
|
end
|
|
974
500
|
end
|