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
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
require_relative "../runtime/errors"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Doors
|
|
5
|
+
# Turns `path=value` command-line words into a nested, typed argument Hash.
|
|
6
|
+
# Types come from the projection, never from guessing at the value ("99" may be a String).
|
|
7
|
+
module CliDoor
|
|
8
|
+
# The words a Boolean is spelled with, and what each means.
|
|
9
|
+
BOOLEAN_WORDS = { "true" => true, "yes" => true, "1" => true, "on" => true,
|
|
10
|
+
"false" => false, "no" => false, "0" => false, "off" => false }.freeze
|
|
11
|
+
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
# Builds a verb's nested, typed argument Hash from its `name=value` words.
|
|
15
|
+
#
|
|
16
|
+
# arguments(spec, ["reference.value=A-1", "sequence.value=99"])
|
|
17
|
+
# # => { reference: { value: "A-1" }, sequence: { value: 99 } }
|
|
18
|
+
#
|
|
19
|
+
# @param spec [Hash{Symbol => Object}] one verb's entry from `Projector::CliProjector`
|
|
20
|
+
# @param pairs [Array<String>] the words after the verb: `name=value` pairs, where a path may
|
|
21
|
+
# be the short form of a single-field value object (`reference` for `reference.value`);
|
|
22
|
+
# `--name` for a Boolean; and at most one bare word, which fills the verb's first argument
|
|
23
|
+
# @return [Hash{Symbol => Object}] the arguments nested by path, leaves cast to type
|
|
24
|
+
# @raise [Runtime::NotFound] if a path, flag or bare word does not fit the verb
|
|
25
|
+
# @raise [Runtime::TypeMismatch] if a value does not parse as its Integer or Float
|
|
26
|
+
def arguments(spec, pairs)
|
|
27
|
+
# Extra accepted arguments stay out of help, which teaches only to=....
|
|
28
|
+
options = (spec[:arguments] + Array(spec[:legacy_arguments])).to_h do |argument|
|
|
29
|
+
[argument[:path], argument]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
normalize(spec, pairs, options).each_with_object({}) do |pair, args|
|
|
33
|
+
path, value = split(pair)
|
|
34
|
+
# key? rather than `||`, so the lookup below honors the same spelling `full` chose.
|
|
35
|
+
full = options.key?(path) ? path : expand(path, options)
|
|
36
|
+
argument = options.key?(full) ? options[full] : raise(Runtime::NotFound, unknown(path, options.keys))
|
|
37
|
+
|
|
38
|
+
next words(args, full.split("."), value, argument[:type]) if argument[:words]
|
|
39
|
+
next append(args, full.split("."), cast(value, argument[:type])) if argument[:list]
|
|
40
|
+
|
|
41
|
+
bury(args, full.split("."), cast(value, argument[:type]))
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Rewrites the short forms into `name=value`: `--name` for a Boolean (a following `yes`,
|
|
46
|
+
# `no`, `true`, `false`, `on`, `off`, `1` or `0` is that flag's value, not an argument),
|
|
47
|
+
# `--name=value` as `name=value`, and one bare word as the verb's first argument (`to` for
|
|
48
|
+
# a command on an existing aggregate, the first attribute for one that creates).
|
|
49
|
+
def normalize(spec, words, options)
|
|
50
|
+
queue = words.dup
|
|
51
|
+
pairs = []
|
|
52
|
+
bare = []
|
|
53
|
+
until queue.empty?
|
|
54
|
+
word = queue.shift
|
|
55
|
+
if word.start_with?("--") && word.include?("=")
|
|
56
|
+
pairs << underscored(word.delete_prefix("--"), options)
|
|
57
|
+
elsif word.start_with?("--")
|
|
58
|
+
path = flag(word.delete_prefix("--"), options)
|
|
59
|
+
value = BOOLEAN_WORDS.key?(queue.first.to_s.downcase) ? queue.shift : "true"
|
|
60
|
+
pairs << "#{path}=#{value}"
|
|
61
|
+
elsif word.include?("=")
|
|
62
|
+
pairs << word
|
|
63
|
+
else
|
|
64
|
+
bare << word
|
|
65
|
+
pairs << word
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
raise Runtime::NotFound, too_many_bare(bare) if bare.length > 1
|
|
69
|
+
|
|
70
|
+
pairs.map { |pair| bare.include?(pair) && !pair.include?("=") ? positional(pair, spec) : pair }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# A `name=value` pair whose name is spelled with dashes (`seed-start=3`), as the argument
|
|
74
|
+
# spelled with underscores, unless an argument is named with the dashes.
|
|
75
|
+
def underscored(pair, options)
|
|
76
|
+
name, value = pair.split("=", 2)
|
|
77
|
+
options.key?(name) ? pair : "#{name.tr('-', '_')}=#{value}"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# A `--name` flag, as the path of the Boolean argument it stands for. A dashed name
|
|
81
|
+
# (`--gem-only`) is the argument spelled with underscores (`gem_only`).
|
|
82
|
+
def flag(name, options)
|
|
83
|
+
name = name.tr("-", "_") unless options.key?(name) || options.keys.any? { |key| key.start_with?("#{name}.") }
|
|
84
|
+
path = options.key?(name) ? name : expand(name, options)
|
|
85
|
+
return path if options.dig(path, :type) == "Boolean"
|
|
86
|
+
|
|
87
|
+
raise Runtime::NotFound, "--#{name} is a flag, but this verb has no Boolean argument #{name.inspect}"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# The one bare word, as a pair for the verb's first argument the launcher does not mint.
|
|
91
|
+
def positional(word, spec)
|
|
92
|
+
first = spec[:arguments].find { |argument| !argument[:minted] }
|
|
93
|
+
return "#{first[:path]}=#{word}" if first
|
|
94
|
+
|
|
95
|
+
if spec[:arguments].any?
|
|
96
|
+
raise Runtime::NotFound, "#{word.inspect} is not name=value; this verb's only argument is its run key, " \
|
|
97
|
+
"which is minted when omitted (run=<key> names one)"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
raise Runtime::NotFound, "#{word.inspect} is not name=value, and this verb takes no arguments"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Words the refusal for more than one unnamed argument.
|
|
104
|
+
def too_many_bare(bare)
|
|
105
|
+
"only one argument may go unnamed, not #{bare.map(&:inspect).join(', ')}; name the rest as name=value"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Cuts one word at its first `=`, so a value may itself contain `=`.
|
|
109
|
+
def split(pair)
|
|
110
|
+
name, value = pair.split("=", 2)
|
|
111
|
+
raise Runtime::NotFound, "#{pair.inspect} is not name=value" if value.nil?
|
|
112
|
+
|
|
113
|
+
[name, value]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Expands a bare value-object name into the path of its only field.
|
|
117
|
+
# Left unchanged when several fields match, since guessing which was meant is worse.
|
|
118
|
+
def expand(path, options)
|
|
119
|
+
candidates = options.keys.select { |key| key.start_with?("#{path}.") }
|
|
120
|
+
candidates.length == 1 ? candidates.first : path
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Converts one value to its declared type; only Integer, Float and Boolean convert.
|
|
124
|
+
def cast(value, type)
|
|
125
|
+
case type
|
|
126
|
+
when "Integer" then Integer(value)
|
|
127
|
+
when "Float" then Float(value)
|
|
128
|
+
when "Boolean" then boolean(value)
|
|
129
|
+
else value
|
|
130
|
+
end
|
|
131
|
+
rescue ArgumentError
|
|
132
|
+
raise Runtime::TypeMismatch, "#{value.inspect} is not #{type} — the chapter declares this field as #{type}"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Reads a Boolean word: `true`, `yes`, `1` or `on` is true; `false`, `no`, `0` or `off` is
|
|
136
|
+
# false; case does not matter. Anything else is refused, since guessing would turn a typo
|
|
137
|
+
# (`dry_run=ture`) into a silent false.
|
|
138
|
+
#
|
|
139
|
+
# @param value [String] the word
|
|
140
|
+
# @return [Boolean]
|
|
141
|
+
# @raise [Runtime::TypeMismatch] if the word is not one of those eight
|
|
142
|
+
def boolean(value)
|
|
143
|
+
BOOLEAN_WORDS.fetch(value.to_s.downcase) do
|
|
144
|
+
raise Runtime::TypeMismatch,
|
|
145
|
+
"#{value.inspect} is not Boolean — use true, false, yes, no, 1, 0, on or off"
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Appends one element to a list argument, creating the list on first use.
|
|
150
|
+
#
|
|
151
|
+
# Repeats must grow the list (`bury` would silently keep only the last), and a single
|
|
152
|
+
# element is still a one-item list, since a `list_of` attribute must never see a bare
|
|
153
|
+
# object. Multi-field elements are left to `JsonDoor`: a flat command line cannot say
|
|
154
|
+
# which `a.x=` pairs with which `a.y=`.
|
|
155
|
+
def append(hash, path, value)
|
|
156
|
+
*branches, leaf = path.map(&:to_sym)
|
|
157
|
+
holder = branches[0..-2].reduce(hash) { |node, key| node[key] ||= {} }
|
|
158
|
+
list = holder[branches.last] ||= []
|
|
159
|
+
|
|
160
|
+
list << { leaf => value }
|
|
161
|
+
hash
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Adds the words of a `list_of` scalar argument to its Array: a comma-separated value is
|
|
165
|
+
# several elements and a repeated name adds more, each cast to the element type. The
|
|
166
|
+
# runtime refuses a lone scalar for a list, so the door is where every spelling becomes an
|
|
167
|
+
# Array.
|
|
168
|
+
#
|
|
169
|
+
# @param hash [Hash{Symbol => Object}] the arguments built so far
|
|
170
|
+
# @param path [Array<String>] the argument's dotted path
|
|
171
|
+
# @param value [String] the word after `=`
|
|
172
|
+
# @param type [String] the element type
|
|
173
|
+
# @return [Hash{Symbol => Object}] `hash`
|
|
174
|
+
def words(hash, path, value, type)
|
|
175
|
+
*branches, leaf = path.map(&:to_sym)
|
|
176
|
+
holder = branches.reduce(hash) { |node, key| node[key] ||= {} }
|
|
177
|
+
list = holder[leaf] ||= []
|
|
178
|
+
list.concat(value.split(",").map { |word| cast(word.strip, type) })
|
|
179
|
+
hash
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Sets one value at a nested path, creating intermediate Hashes and overwriting the leaf.
|
|
183
|
+
def bury(hash, path, value)
|
|
184
|
+
*branches, leaf = path.map(&:to_sym)
|
|
185
|
+
target = branches.reduce(hash) { |node, key| node[key] ||= {} }
|
|
186
|
+
target[leaf] = value
|
|
187
|
+
hash
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Words the refusal for an argument the verb does not take, listing what it does.
|
|
191
|
+
def unknown(path, known)
|
|
192
|
+
"no argument #{path.inspect} — this verb takes #{known.sort.join(', ')}"
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require_relative "cli_door"
|
|
3
|
+
require_relative "command_request"
|
|
4
|
+
require_relative "json_door"
|
|
5
|
+
require_relative "launcher_options"
|
|
6
|
+
require_relative "../projector"
|
|
7
|
+
require_relative "../ports/clock"
|
|
8
|
+
|
|
9
|
+
module Hecks
|
|
10
|
+
module Doors
|
|
11
|
+
# Parses and dispatches a command line against a `Projector::CliProjector` projection.
|
|
12
|
+
# Does no IO: it answers `[text, status]` and leaves printing and exiting to its callers.
|
|
13
|
+
module CliRunner
|
|
14
|
+
# The class an adapter raises when the tool it wraps refuses.
|
|
15
|
+
TOOL_REFUSAL = "Hecks::Adapters::ConsoleCapture::Failure".freeze
|
|
16
|
+
|
|
17
|
+
module_function
|
|
18
|
+
|
|
19
|
+
# Runs one command line against a booted domain and answers the text to print.
|
|
20
|
+
# The booted domain's own chapter is projected, or an attached one its first word names.
|
|
21
|
+
#
|
|
22
|
+
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted domain
|
|
23
|
+
# @param argv [Array<String>] an optional `ask`, the verb or question name, then
|
|
24
|
+
# `path=value` pairs; `--help`, `-h`, `help` or nothing answers usage
|
|
25
|
+
# @param program [String] how the caller was invoked, echoed in usage and hints
|
|
26
|
+
# @return [Array(String, Integer)] the text and the status: 0 answered, 1 refused or misused
|
|
27
|
+
# @raise [Runtime::WiringError] if a repository or the clock adapter cannot be resolved
|
|
28
|
+
# @raise [Runtime::StaleWrite] if concurrent writers beat the command through every retry
|
|
29
|
+
def call(runtime:, argv:, program: "hecks run")
|
|
30
|
+
plan = resolve(runtime, argv, program)
|
|
31
|
+
return plan[:answer] if plan[:answer]
|
|
32
|
+
|
|
33
|
+
dispatch(runtime, plan[:spec], plan[:name], plan[:rest], plan[:program], plan[:asking],
|
|
34
|
+
bluebook: plan[:bluebook], launcher: plan[:launcher])
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Answers a command line that asks only for usage, from the projection alone.
|
|
38
|
+
#
|
|
39
|
+
# Needs a registry and no bound adapter, so a `Runtime::Loader::Described` serves: the
|
|
40
|
+
# help text, the no-argument usage, a verb's `--help` and an unknown verb's hint all come
|
|
41
|
+
# out byte-identical to `call`'s.
|
|
42
|
+
#
|
|
43
|
+
# @param runtime [#registry] a booted domain, or what `Hecks.describe` answers
|
|
44
|
+
# @param argv [Array<String>] as for `call`
|
|
45
|
+
# @param program [String] how the caller was invoked
|
|
46
|
+
# @return [Array(String, Integer), nil] the text and status, or nil when the line would
|
|
47
|
+
# run a verb or question and so needs a booted domain
|
|
48
|
+
def usage(runtime:, argv:, program: "hecks run")
|
|
49
|
+
resolve(runtime, argv, program)[:answer]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Parses a command line against the projection: either the answer it gives without
|
|
53
|
+
# running anything, or the verb it would run.
|
|
54
|
+
#
|
|
55
|
+
# @return [Hash] `{answer: [text, status]}`, or `{spec:, name:, rest:, asking:, program:,
|
|
56
|
+
# bluebook:, launcher:}` for a verb to dispatch
|
|
57
|
+
def resolve(runtime, argv, program)
|
|
58
|
+
bluebook, argv, program = chapter_for(runtime, argv, program)
|
|
59
|
+
launcher = LauncherOptions.settings(runtime, bluebook.name)
|
|
60
|
+
options = LauncherOptions.projection(launcher, program)
|
|
61
|
+
cli = Projector.call(:cli, bluebook: bluebook, options: options)
|
|
62
|
+
|
|
63
|
+
name = argv.first
|
|
64
|
+
return { answer: [cli[:usage], 0] } if name.nil? || %w[--help -h help].include?(name)
|
|
65
|
+
|
|
66
|
+
# `ask` gives questions their own namespace; a chapter may declare a command and query
|
|
67
|
+
# of one name.
|
|
68
|
+
asking = name == "ask"
|
|
69
|
+
argv = argv[1..] if asking
|
|
70
|
+
name = argv.first
|
|
71
|
+
return { answer: [cli[:usage], 1] } if name.nil?
|
|
72
|
+
|
|
73
|
+
# A question answers to its bare name too; `ask` is needed only when a command shares it.
|
|
74
|
+
asking ||= !cli[:names][:command].key?(name) && cli[:names][:question].key?(name)
|
|
75
|
+
|
|
76
|
+
# The alias map lets `create_pizza` and `order.create_pizza` reach the same verb.
|
|
77
|
+
pool = asking ? cli[:questions] : cli[:verbs]
|
|
78
|
+
key = cli[:names][asking ? :question : :command][name]
|
|
79
|
+
spec = pool[key]
|
|
80
|
+
return { answer: [unknown(cli, name, asking, program), 1] } unless spec
|
|
81
|
+
|
|
82
|
+
rest = argv[1..]
|
|
83
|
+
if rest.include?("--help")
|
|
84
|
+
help = Projector.call(:cli, bluebook: bluebook,
|
|
85
|
+
options: options.merge(verb: name, ask: asking))[:usage]
|
|
86
|
+
return { answer: [help, 0] }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
{ spec: spec, name: name, rest: rest, asking: asking, program: program, bluebook: bluebook,
|
|
90
|
+
launcher: launcher }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# The chapter a command line speaks to: the booted domain's own, or, when the first word
|
|
94
|
+
# names a chapter its hecksagon attaches or uses as a framework member (`deploy`,
|
|
95
|
+
# `governance`), that chapter, with the word dropped and added to the program name.
|
|
96
|
+
def chapter_for(runtime, argv, program)
|
|
97
|
+
own = runtime.registry.bluebooks.values.first
|
|
98
|
+
attached = Array(runtime.registry.hecksagon(own.name)&.member_chapters)
|
|
99
|
+
target = attached.find { |name| Naming.snake(name) == argv.first }
|
|
100
|
+
return [own, argv, program] unless target
|
|
101
|
+
|
|
102
|
+
[runtime.registry.bluebook(target), argv[1..], "#{program} #{argv.first}"]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Parses one resolved verb's arguments, runs it as a query or command, and turns the
|
|
106
|
+
# outcome, or the domain's refusal, into `[json_or_message, status]`. With `--wait`, a
|
|
107
|
+
# question whose report names a gap (`LauncherOptions.gap_reported?`) answers status 1.
|
|
108
|
+
#
|
|
109
|
+
# @param bluebook [Bluebook::Chapter] the chapter the verb belongs to
|
|
110
|
+
# @param launcher [Hash, nil] the chapter's `launcher` world setting; nil when not opted in
|
|
111
|
+
def dispatch(runtime, spec, name, rest, program, asking, bluebook: nil, launcher: nil)
|
|
112
|
+
rest, wait = LauncherOptions.take_wait(spec, rest) if launcher
|
|
113
|
+
args = stamp_time(runtime, spec, CliDoor.arguments(spec, rest))
|
|
114
|
+
|
|
115
|
+
return answer_query(runtime, spec, args, wait) if spec[:kind] == :query
|
|
116
|
+
|
|
117
|
+
args, minted = LauncherOptions.run_key(runtime, spec, args, launcher)
|
|
118
|
+
# Answers with only this verb's outcome; a full store dump is every record there is.
|
|
119
|
+
request = CommandRequest.normalize(args, receiver: spec[:receiver],
|
|
120
|
+
legacy_receiver: spec[:legacy_receiver])
|
|
121
|
+
handle = runtime.dispatch_flat(spec[:verb], request)
|
|
122
|
+
extra = refused_answer(handle)
|
|
123
|
+
extra = { run: minted }.merge(extra) if minted
|
|
124
|
+
return settled(runtime, spec, handle, bluebook, launcher, extra) if wait
|
|
125
|
+
return [JSON.pretty_generate(answered(handle).merge(extra)), 0] if handle.state.nil?
|
|
126
|
+
|
|
127
|
+
[JSON.pretty_generate({ id: handle.id,
|
|
128
|
+
state: JsonDoor.materialize(handle.state),
|
|
129
|
+
events: handle.events.map(&:name) }.merge(extra)), 0]
|
|
130
|
+
rescue Runtime::NotFound, Runtime::TypeMismatch => e
|
|
131
|
+
# A bad argument and a missing record both need the same next step: read the help.
|
|
132
|
+
["#{e.message}\n\n #{program} #{'ask ' if asking}#{name} --help", 1]
|
|
133
|
+
rescue *Runtime::DOMAIN_REFUSALS => e
|
|
134
|
+
# The refusal is the chapter's own sentence, verbatim.
|
|
135
|
+
[e.message, 1]
|
|
136
|
+
rescue StandardError => e
|
|
137
|
+
# So is a wrapped tool's, when a question's adapter refuses to answer. Matched by name: the
|
|
138
|
+
# adapters belong to the Hecks chapter, which a client's runtime never loads.
|
|
139
|
+
raise unless e.class.ancestors.map(&:name).include?(TOOL_REFUSAL)
|
|
140
|
+
|
|
141
|
+
[e.message, 1]
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Runs a question and answers its rows, or its text when an adapter answered in text.
|
|
145
|
+
#
|
|
146
|
+
# @param wait [Boolean, nil] whether `--wait` was given: a report naming a gap then fails
|
|
147
|
+
# @return [Array(String, Integer)] the answer and the status
|
|
148
|
+
def answer_query(runtime, spec, args, wait)
|
|
149
|
+
rows = runtime.query(spec[:verb], **args)
|
|
150
|
+
text = text_answer(spec, rows)
|
|
151
|
+
[text || JSON.pretty_generate(rows.map { |row| JsonDoor.materialize(row) }),
|
|
152
|
+
wait && LauncherOptions.gap_reported?(text) ? 1 : 0]
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# The answer `--wait` gives: the record re-read after every reaction has run, with all its
|
|
156
|
+
# events, and a status of 1 when its lifecycle ended in a failure state, a refused reaction
|
|
157
|
+
# blocks the run or a reaction crashed.
|
|
158
|
+
#
|
|
159
|
+
# A refusal blocks unless a sibling reaction delivered another command on the same
|
|
160
|
+
# aggregate (`Runtime::ReactionOutcome`). The settled record is always the text, so a failed
|
|
161
|
+
# run pipes like a passing one; the reason a human reads is a third element.
|
|
162
|
+
#
|
|
163
|
+
# @return [Array(String, Integer)] the JSON and the status, plus (Array(String, Integer,
|
|
164
|
+
# String)) why the run failed when it did
|
|
165
|
+
def settled(runtime, spec, handle, bluebook, launcher, extra)
|
|
166
|
+
why = reactions_failed(handle)
|
|
167
|
+
return finish(JSON.pretty_generate(answered(handle).merge(extra)), why) if handle.state.nil?
|
|
168
|
+
|
|
169
|
+
aggregate = aggregate_of(bluebook, spec)
|
|
170
|
+
state = reread(runtime, bluebook, aggregate, handle) || handle.state
|
|
171
|
+
fqn = "#{bluebook.name}::#{aggregate&.hecks_name}"
|
|
172
|
+
events = runtime.events.select { |event| event.aggregate == fqn && event.id == handle.id }
|
|
173
|
+
answer = { id: handle.id, state: JsonDoor.materialize(state),
|
|
174
|
+
events: (events.empty? ? handle.events : events).map(&:name) }.merge(extra)
|
|
175
|
+
if LauncherOptions.failed?(aggregate, state, launcher)
|
|
176
|
+
field = aggregate.lifecycle.field
|
|
177
|
+
why += ["#{aggregate.hecks_name} ended in the failure state #{state[field.to_sym].to_s.inspect}"]
|
|
178
|
+
end
|
|
179
|
+
finish(JSON.pretty_generate(answer), why)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# The answer and its status: 0 when nothing failed, else 1 with the reasons joined.
|
|
183
|
+
def finish(text, reasons)
|
|
184
|
+
reasons.empty? ? [text, 0] : [text, 1, reasons.join("\n")]
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# What the reactions of the run the handle reports did wrong: each refusal that blocks it
|
|
188
|
+
# and each crash, as one sentence apiece.
|
|
189
|
+
def reactions_failed(handle)
|
|
190
|
+
blocking = handle.respond_to?(:blocking_reactions) ? handle.blocking_reactions : []
|
|
191
|
+
crashed = handle.respond_to?(:reaction_defects) ? handle.reaction_defects : []
|
|
192
|
+
blocking.map { |row| "reaction #{row[:policy]} was refused (#{row[:trigger]}): #{row[:reason]}" } +
|
|
193
|
+
crashed.map { |row| "reaction #{row[:policy]} crashed (#{row[:error_class]}): #{row[:reason]}" }
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Whether a reaction the domain refused blocks the run the handle reports.
|
|
197
|
+
def blocked?(handle)
|
|
198
|
+
handle.respond_to?(:blocking_reactions) && !handle.blocking_reactions.empty?
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# The aggregate a top-level command belongs to; nil for an entity command or a port.
|
|
202
|
+
def aggregate_of(bluebook, spec)
|
|
203
|
+
head = spec[:verb].split("::", 2).last
|
|
204
|
+
return if head.count(".") != 1
|
|
205
|
+
|
|
206
|
+
bluebook.aggregates.find { |aggregate| aggregate.hecks_name == head.split(".").first }
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# The record as its repository holds it now, or nil when it cannot be read.
|
|
210
|
+
def reread(runtime, bluebook, aggregate, handle)
|
|
211
|
+
return unless aggregate && runtime.respond_to?(:registry)
|
|
212
|
+
|
|
213
|
+
runtime.registry.repository(bluebook.name, aggregate)&.find(handle.id)&.to_h
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# The text a query answered by a port gave, when that is the whole answer.
|
|
217
|
+
#
|
|
218
|
+
# A query declared `returns Document` (one value object of the single String attribute
|
|
219
|
+
# `text`) answers one document; printing it raw keeps the document (JSON, Markdown,
|
|
220
|
+
# sentences) readable and pipeable instead of quoted inside another JSON document. What a
|
|
221
|
+
# query declares decides it, not how many rows came back: any other query prints JSON,
|
|
222
|
+
# so a script reading it sees an array whether it held one row or two.
|
|
223
|
+
#
|
|
224
|
+
# @param spec [Hash] the question's projected spec
|
|
225
|
+
# @param rows [Array<Hash>] the query's rows
|
|
226
|
+
# @return [String, nil] the text, or nil when the query does not return a Document
|
|
227
|
+
def text_answer(spec, rows)
|
|
228
|
+
return unless spec[:returns] == "Document" && rows.length == 1 && rows.first.keys == [:text]
|
|
229
|
+
return unless rows.first[:text].is_a?(String)
|
|
230
|
+
|
|
231
|
+
rows.first[:text]
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Fills in a `now` argument the caller left out, from the clock port; an explicit one wins.
|
|
235
|
+
#
|
|
236
|
+
# Done at the door, not in the runtime: a clock read inside the interpreter would give a
|
|
237
|
+
# replayed corpus step the replay day's time. Matched by the argument's name alone.
|
|
238
|
+
def stamp_time(runtime, spec, args)
|
|
239
|
+
return args unless spec[:arguments].any? { |argument| argument[:path].start_with?("now.") }
|
|
240
|
+
return args if args.key?(:now)
|
|
241
|
+
|
|
242
|
+
args.merge(now: { value: Ports::Clock.now(runtime.registry) })
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# The reactions one dispatch caused that the domain refused, as `refused_reactions:`.
|
|
246
|
+
#
|
|
247
|
+
# A policy's trigger that a `given` refuses is not the command's own refusal: the command
|
|
248
|
+
# has already persisted. The dispatch result carries them (`Result#refused_reactions`);
|
|
249
|
+
# without this the answer would say nothing of it. A defect (a crash) is shown apart, as
|
|
250
|
+
# `reaction_defects:`, and fails `--wait`.
|
|
251
|
+
#
|
|
252
|
+
# @param handle [Runtime::Dispatcher::Result, Runtime::RemoteDispatcher::Result] the outcome
|
|
253
|
+
# @return [Hash] `refused_reactions:` each with the `policy`, its `trigger` and the `reason`;
|
|
254
|
+
# empty when every reaction was delivered (or a remote host sent no per-step log), plus
|
|
255
|
+
# `reaction_defects:` when one crashed
|
|
256
|
+
def refused_answer(handle)
|
|
257
|
+
refused = handle.respond_to?(:refused_reactions) ? handle.refused_reactions : []
|
|
258
|
+
defects = handle.respond_to?(:reaction_defects) ? handle.reaction_defects : []
|
|
259
|
+
answer = refused.empty? ? {} : { refused_reactions: refused }
|
|
260
|
+
defects.empty? ? answer : answer.merge(reaction_defects: defects)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# Shapes a port operation's outcome, which has no state: each event with its full payload.
|
|
264
|
+
#
|
|
265
|
+
# The answer lives only in the payloads, so names alone would drop it. Exit status stays 0
|
|
266
|
+
# for refusal events too, since a healthy no is an answer, not a misuse.
|
|
267
|
+
def answered(handle)
|
|
268
|
+
# A port operation hydrates no instance, so the handle's `id` is nil by design.
|
|
269
|
+
{ id: handle.id || handle.events.first&.id,
|
|
270
|
+
events: handle.events.map do |event|
|
|
271
|
+
{ name: event.name, payload: JsonDoor.materialize(event.payload) }
|
|
272
|
+
end }
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# Words the answer for an unknown verb or question, suggesting up to five near names.
|
|
276
|
+
# Ranked by shared prefix, which survives a dropped letter where a substring match would not.
|
|
277
|
+
def unknown(cli, name, asking, program)
|
|
278
|
+
# Both spellings are candidates: `order.create_piza` shares no prefix with `create_pizza`.
|
|
279
|
+
pool = cli[:names][asking ? :question : :command].keys
|
|
280
|
+
near = pool.map { |candidate| [shared_prefix(candidate, name), candidate] }
|
|
281
|
+
.select { |shared, _| shared >= [name.length / 2, 3].max }
|
|
282
|
+
.sort_by { |shared, candidate| [-shared, candidate] }
|
|
283
|
+
.map(&:last)
|
|
284
|
+
|
|
285
|
+
lines = ["no such #{asking ? 'question' : 'verb'}: #{name}"]
|
|
286
|
+
lines += ["", "did you mean:", *near.first(5).map { |candidate| " #{candidate}" }] unless near.empty?
|
|
287
|
+
lines += ["", " #{program}#{' ask' if asking} for the full list"]
|
|
288
|
+
lines.join("\n")
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def shared_prefix(one, other)
|
|
292
|
+
length = [one.length, other.length].min
|
|
293
|
+
(0...length).find { |index| one[index] != other[index] } || length
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
end
|
|
@@ -1,43 +1,25 @@
|
|
|
1
1
|
require_relative "../runtime/errors"
|
|
2
2
|
|
|
3
3
|
module Hecks
|
|
4
|
-
module
|
|
5
|
-
# Turns an external command request into the dispatcher's
|
|
6
|
-
# envelope. Human-facing doors may accept their old flat id spelling at
|
|
7
|
-
# the edge, but every call leaving this boundary has one shape:
|
|
4
|
+
module Doors
|
|
5
|
+
# Turns an external command request into the dispatcher's `to:`/`with:` envelope.
|
|
8
6
|
#
|
|
9
7
|
# aggregate command: { to: "record-id", with: { declared: "facts" } }
|
|
10
|
-
# entity command: { to: { aggregate: "...", entity: "..." },
|
|
11
|
-
# with: { declared: "facts" } }
|
|
12
|
-
#
|
|
13
|
-
# The command interpreter remains the authority on which facts are
|
|
14
|
-
# declared. This helper only prevents routing fields from leaking into the
|
|
15
|
-
# fact payload and gives every external door the same wire contract.
|
|
8
|
+
# entity command: { to: { aggregate: "...", entity: "..." }, with: { ... } }
|
|
16
9
|
module CommandRequest
|
|
17
10
|
module_function
|
|
18
11
|
|
|
19
|
-
# Splits
|
|
20
|
-
#
|
|
21
|
-
# not fit the kind of command being called.
|
|
22
|
-
#
|
|
23
|
-
# Keys are symbolized at every depth first, so a parsed JSON body and a Ruby Hash
|
|
24
|
-
# are treated alike. In a flat Hash everything but `to` (or the legacy receiver
|
|
25
|
-
# keys) is a fact.
|
|
12
|
+
# Splits a request, explicit envelope or flat Hash, into routing (`to:`) and facts (`with:`).
|
|
13
|
+
# Keys are symbolized at every depth; in a flat Hash everything but the receiver is a fact.
|
|
26
14
|
#
|
|
27
|
-
# @param input [Hash
|
|
28
|
-
#
|
|
29
|
-
# @param
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
# @
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
# an `:entity` receiver; `nil` accepts no legacy spelling
|
|
36
|
-
# @return [Hash{Symbol => Object}] `{ with: facts }`, plus `to:` holding the route
|
|
37
|
-
# whenever `receiver` is not `nil`; ready to pass to `Dispatcher#dispatch_flat`
|
|
38
|
-
# @raise [Runtime::TypeMismatch] if `input` or its `with:` is not a Hash, if an
|
|
39
|
-
# explicit envelope carries keys other than `to:` and `with:`, or if the route
|
|
40
|
-
# is missing, blank, malformed, or given to a command that takes no receiver
|
|
15
|
+
# @param input [Hash] the request, with String or Symbol keys
|
|
16
|
+
# @param receiver [Symbol, nil] `:aggregate`, `:entity`, or `nil` for a command with none
|
|
17
|
+
# @param legacy_receiver [Symbol, String, Hash, nil] the flat key that may carry the
|
|
18
|
+
# receiver instead of `to:`: a name for `:aggregate`, `{ aggregate:, entity: }` for
|
|
19
|
+
# `:entity`
|
|
20
|
+
# @return [Hash{Symbol => Object}] `{ with: facts }`, plus `to:` when `receiver` is set
|
|
21
|
+
# @raise [Runtime::TypeMismatch] if the request is malformed or the route does not fit
|
|
22
|
+
# the receiver kind
|
|
41
23
|
# @raise [ArgumentError] if `receiver` is not `nil`, `:aggregate` or `:entity`
|
|
42
24
|
def normalize(input, receiver:, legacy_receiver: nil)
|
|
43
25
|
request = symbolize(input)
|
|
@@ -87,9 +69,7 @@ module Hecks
|
|
|
87
69
|
end
|
|
88
70
|
private_class_method :take_legacy_route
|
|
89
71
|
|
|
90
|
-
#
|
|
91
|
-
# :aggregate, :entity) plus the impossible-kind backstop — each
|
|
92
|
-
# branch is its own self-contained validation for that one kind.
|
|
72
|
+
# One self-contained check per closed receiver kind, plus a backstop.
|
|
93
73
|
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
94
74
|
def validate_route!(route, receiver)
|
|
95
75
|
case receiver
|