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
data/lib/hecks/storehouse.rb
CHANGED
|
@@ -2,139 +2,35 @@ require "json"
|
|
|
2
2
|
require "fileutils"
|
|
3
3
|
require "time"
|
|
4
4
|
require_relative "cache_dir"
|
|
5
|
-
require_relative "
|
|
6
|
-
require_relative "
|
|
5
|
+
require_relative "doors/command_request"
|
|
6
|
+
require_relative "doors/json_door"
|
|
7
7
|
require_relative "naming"
|
|
8
8
|
require_relative "projector"
|
|
9
9
|
require_relative "runtime/errors"
|
|
10
10
|
|
|
11
11
|
module Hecks
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# "Storehouse" the single highest-priority gap this repo had: "no
|
|
15
|
-
# per-command tool... the bluebook is the contract, the [door] just
|
|
16
|
-
# projects it." This module is that bus, borrowing its name too — the
|
|
17
|
-
# dispatch/query/state/... surface below is a pure function of a
|
|
18
|
-
# `Runtime::Dispatcher` plus plain Ruby arguments, no IO, no protocol
|
|
19
|
-
# awareness. `bin/hecks_mcp_door` is one projection of it — MCP over
|
|
20
|
-
# stdio — not the whole thing; a plain CLI door, an HTTP door, a second
|
|
21
|
-
# transport of any shape, would sit beside it on the exact same bus,
|
|
22
|
-
# sharing the same audit log and caller-identity handling, without
|
|
23
|
-
# ever needing to speak MCP. (This bus is deliberately not
|
|
24
|
-
# `Facade::McpDoor` — folding it into `Facade` under that name would
|
|
25
|
-
# conflate the bus with its one built transport.)
|
|
26
|
-
#
|
|
27
|
-
# ## The surface
|
|
28
|
-
#
|
|
29
|
-
# catalog — what aggregates a domain declares, and what each can do
|
|
30
|
-
# describe — one aggregate's full command/query/refusal contract
|
|
31
|
-
# validate — is this domain's wiring sound (deep: also model-checks it)
|
|
32
|
-
# domains — every domain directory under a root, discovered not typed
|
|
33
|
-
# dispatch — issue a command (dry_run: preview, steps: batch)
|
|
34
|
-
# query — ask a question
|
|
35
|
-
# state — read what is actually stored, no verb involved
|
|
36
|
-
# events — what happened, with payloads, to one record — not just its
|
|
37
|
-
# current state; events this bus witnessed, sourced from
|
|
38
|
-
# its own audit log, not a full event-sourcing replay
|
|
39
|
-
# history — the full append-only journal, not just current state
|
|
40
|
-
# behaviors — run a domain's hand-curated `.behaviors` examples
|
|
41
|
-
# follow — tail this bus's own dispatch/query/state audit log
|
|
42
|
-
#
|
|
43
|
-
# ## Auditing and caller identity
|
|
44
|
-
#
|
|
45
|
-
# `dispatch`/`query`/`state` each take a `summary` — the survey's "every
|
|
46
|
-
# audit row carries human intent for free" — and `dispatch`/`query`
|
|
47
|
-
# additionally take an optional `source:` (`SOURCE_TAGS`, the survey's
|
|
48
|
-
# `SourceTag`: who is calling) and an optional `role:`/`actor_id:` —
|
|
49
|
-
# a real caller identity, bound for the call's duration via `Hecks.
|
|
50
|
-
# as_caller`, checked against a `role`-gated command's own declared
|
|
51
|
-
# role (`CommandRules::Authorization`) rather than merely documented by
|
|
52
|
-
# `describe`. `dispatch` requires it for any command that declares a
|
|
53
|
-
# role — `require_caller_for_role_gated!` refuses an unbound dispatch
|
|
54
|
-
# against one rather than silently running it unchecked; `query`'s own
|
|
55
|
-
# authorization runs on a separate mechanism (`Runtime::TenantScope`)
|
|
56
|
-
# that `role:` does not gate. This is self-asserted identity, not
|
|
57
|
-
# authentication: the caller states its own `role`/`actor_id`, and this
|
|
58
|
-
# bus checks it consistently once stated — see `bin/hecks_mcp_door`'s
|
|
59
|
-
# header for what that does and does not guarantee. Every call through
|
|
60
|
-
# those three, plus a dry run, is appended to a per-domain JSONL audit
|
|
61
|
-
# log (`record!`) `follow` tails back — a record of what the caller
|
|
62
|
-
# said it was, not independently verified identity. `catalog`/
|
|
63
|
-
# `describe`/`validate`/`domains`/`history`/`behaviors`/`events` need
|
|
64
|
-
# neither — they change nothing and commit nothing to any log.
|
|
65
|
-
#
|
|
66
|
-
# ## Booting is the caller's job
|
|
67
|
-
#
|
|
68
|
-
# A caller hands in an already-booted `runtime`, the same division of
|
|
69
|
-
# labor `Facade::CliRunner` already keeps against `bin/run`: booting a
|
|
70
|
-
# domain from a path is IO the calling `bin/` script owns, this stays a
|
|
71
|
-
# pure function of a `Runtime::Dispatcher` plus plain Ruby arguments —
|
|
72
|
-
# no different from `CliRunner.call(runtime:, argv:)` itself.
|
|
73
|
-
# `validate` and `domains` are the two exceptions: `validate`'s whole
|
|
74
|
-
# job is to attempt the boot and report whether it survived, so it
|
|
75
|
-
# takes the domain path instead and boots it; `domains` has no
|
|
76
|
-
# domain to be handed one of yet, that's what it's answering.
|
|
77
|
-
#
|
|
78
|
-
# ## Built from existing doors
|
|
79
|
-
#
|
|
80
|
-
# No new vocabulary otherwise. Every method here composes doors that
|
|
81
|
-
# already exist — `Projector.call(:cli, ...)` for verb/question alias
|
|
82
|
-
# resolution (the identical table `CliRunner` itself resolves against),
|
|
83
|
-
# `Projector.call(:docs, ...)` for `describe`, `Facade::JsonDoor` for
|
|
84
|
-
# the JSON↔Runtime::Value boundary, `Registry#repository` for `state`/
|
|
85
|
-
# `history`, `Registry#verify!` (run by every boot) for `validate`,
|
|
86
|
-
# `Bluebook::ModelCheck` for `validate(deep: true)`, `Hecks::Behaviors`
|
|
87
|
-
# for `behaviors`, `Adapters::Folder#domain?` for `domains`,
|
|
88
|
-
# `Dispatcher#dry_run?` for `dispatch(dry_run: true)`. A second copy of
|
|
89
|
-
# any of these here would be the exact duplication `Facade::JsonDoor`'s
|
|
90
|
-
# own header already warns against.
|
|
12
|
+
# Dispatch/query/state and introspection over one booted domain, pure
|
|
13
|
+
# beyond its own JSONL audit log — booting stays the caller's job.
|
|
91
14
|
module Storehouse
|
|
92
15
|
module_function
|
|
93
16
|
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
# that omits it gets `source: nil` recorded, honestly, rather than a
|
|
97
|
-
# guessed default.
|
|
17
|
+
# Optional on dispatch/query; omitted, source is recorded as nil rather
|
|
18
|
+
# than guessed.
|
|
98
19
|
SOURCE_TAGS = %w[process-manager operator hook sidequest-agent cascade daemon].freeze
|
|
99
20
|
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
# `dispatch`/`query`/`state`/dry-run call, and `follow` tails it back.
|
|
104
|
-
# It is kept under `Hecks::CacheDir`, not the domain's own directory: this is
|
|
105
|
-
# the bus's record of what was asked of it, not part of the domain's own
|
|
106
|
-
# persisted state, and not the gem's own directory either, which is
|
|
107
|
-
# read-only on an installed gem.
|
|
108
|
-
#
|
|
109
|
-
# @return [String] the absolute path of the `storehouse` directory under
|
|
110
|
-
# the cache root
|
|
21
|
+
# Kept under Hecks::CacheDir, not the domain directory (part of the domain's
|
|
22
|
+
# own state) or the gem directory (read-only once installed).
|
|
23
|
+
# :nodoc:
|
|
111
24
|
def log_root = Hecks::CacheDir.path("storehouse")
|
|
112
25
|
|
|
113
|
-
# The root every
|
|
114
|
-
#
|
|
115
|
-
#
|
|
116
|
-
# files a domain path resolves to, and those are Ruby, not a data
|
|
117
|
-
# format — a caller-supplied path with no confinement at all is an
|
|
118
|
-
# unmarked door out of "a narrower, checked surface" (the README's
|
|
119
|
-
# own pitch for this bus) and into arbitrary code execution from any
|
|
120
|
-
# path on disk. This is a boundary a local convention was standing
|
|
121
|
-
# in for, not new behavior for a caller already confined to the
|
|
122
|
-
# project tree.
|
|
26
|
+
# The root every domain path resolves under; HECKS_STOREHOUSE_ROOT widens it.
|
|
27
|
+
# Hecks.boot Kernel.loads Ruby files here, so an unconfined path is
|
|
28
|
+
# arbitrary code execution, not just a wrong-directory mistake.
|
|
123
29
|
BOOT_ROOT = File.expand_path(ENV["HECKS_STOREHOUSE_ROOT"] || File.expand_path("../..", __dir__))
|
|
124
30
|
|
|
125
|
-
#
|
|
126
|
-
#
|
|
127
|
-
#
|
|
128
|
-
# an honest mistake (a relative path typed against the wrong cwd) or
|
|
129
|
-
# the exact thing this check exists to catch, and both deserve the
|
|
130
|
-
# same clear refusal rather than a silent rewrite to something the
|
|
131
|
-
# caller didn't ask for.
|
|
132
|
-
#
|
|
133
|
-
# @param path [String, #to_s] the caller-supplied path to confine
|
|
134
|
-
# @param label [String] the argument's own name, for the refusal message
|
|
135
|
-
# (e.g. `"domain"`, `"under"`)
|
|
136
|
-
# @return [String] `path` resolved to an absolute path under `BOOT_ROOT`
|
|
137
|
-
# @raise [Runtime::TypeMismatch] if the resolved path is outside `BOOT_ROOT`
|
|
31
|
+
# Refuses a path outside BOOT_ROOT rather than silently clamping it — a
|
|
32
|
+
# stray relative path and a deliberate escape both deserve a clear refusal.
|
|
33
|
+
# :nodoc:
|
|
138
34
|
def confine!(path, label)
|
|
139
35
|
resolved = File.expand_path(path.to_s, BOOT_ROOT)
|
|
140
36
|
return resolved if resolved == BOOT_ROOT || resolved.start_with?("#{BOOT_ROOT}#{File::SEPARATOR}")
|
|
@@ -144,45 +40,24 @@ module Hecks
|
|
|
144
40
|
"own root (HECKS_STOREHOUSE_ROOT to widen it)"
|
|
145
41
|
end
|
|
146
42
|
|
|
147
|
-
#
|
|
148
|
-
|
|
149
|
-
#
|
|
150
|
-
# projects a whole-domain CLI or doc set makes this same assumption
|
|
151
|
-
# (`Facade::CliRunner#call`'s own `bluebook = runtime.registry.
|
|
152
|
-
# bluebooks.values.first`) — one `.hecksagon` names one chapter.
|
|
153
|
-
# The one bluebook this runtime booted.
|
|
154
|
-
#
|
|
155
|
-
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted runtime
|
|
156
|
-
# @return [Bluebook::Chapter] the runtime's one loaded bluebook
|
|
157
|
-
# @raise [Runtime::NotFound] if the runtime's boot loaded no bluebook
|
|
43
|
+
# The one bluebook this runtime booted; every whole-domain door assumes
|
|
44
|
+
# the same one-bluebook-per-boot shape.
|
|
45
|
+
# :nodoc:
|
|
158
46
|
def bluebook_for(runtime)
|
|
159
47
|
runtime.registry.bluebooks.values.first or
|
|
160
48
|
raise Runtime::NotFound, "this boot loaded no bluebook"
|
|
161
49
|
end
|
|
162
50
|
|
|
163
|
-
#
|
|
164
|
-
#
|
|
165
|
-
# @param bluebook [Bluebook::Chapter] the chapter to search
|
|
166
|
-
# @param name [String, Symbol, #to_s] the aggregate's declared name
|
|
167
|
-
# @return [Bluebook::Aggregate] the matching aggregate
|
|
168
|
-
# @raise [Runtime::NotFound] if `bluebook` declares no aggregate named `name`
|
|
51
|
+
# :nodoc:
|
|
169
52
|
def aggregate_ir!(bluebook, name)
|
|
170
53
|
bluebook.aggregate(name) or
|
|
171
54
|
raise Runtime::NotFound, "#{bluebook.name} declares no aggregate named #{name.inspect} — " \
|
|
172
55
|
"known: #{bluebook.aggregates.map(&:hecks_name).sort.join(', ')}"
|
|
173
56
|
end
|
|
174
57
|
|
|
175
|
-
# The same alias table
|
|
176
|
-
#
|
|
177
|
-
#
|
|
178
|
-
# messages) never drift from what a human typing `bin/run` sees.
|
|
179
|
-
#
|
|
180
|
-
# @param cli [Hash{Symbol => Object}] a `Projector.call(:cli, ...)` result
|
|
181
|
-
# @param name [String, Symbol, #to_s] the command or query name, as given by the caller
|
|
182
|
-
# @param asking [Boolean] true to resolve a query (against `cli[:questions]`),
|
|
183
|
-
# false to resolve a command (against `cli[:verbs]`)
|
|
184
|
-
# @return [Hash{Symbol => Object}] the resolved verb/question spec
|
|
185
|
-
# @raise [Runtime::NotFound] if `name` matches no known command or query
|
|
58
|
+
# The same alias table CliRunner resolves against, kept here so dispatch
|
|
59
|
+
# and query never drift from what a human typing hecks run sees.
|
|
60
|
+
# :nodoc:
|
|
186
61
|
def resolve!(cli, name, asking:)
|
|
187
62
|
pool = asking ? cli[:questions] : cli[:verbs]
|
|
188
63
|
key = cli[:names][asking ? :question : :command][name]
|
|
@@ -193,11 +68,8 @@ module Hecks
|
|
|
193
68
|
raise Runtime::NotFound, "no such #{asking ? 'query' : 'command'}: #{name.inspect} — known: #{known}"
|
|
194
69
|
end
|
|
195
70
|
|
|
196
|
-
#
|
|
197
|
-
#
|
|
198
|
-
# @param summary [String, nil] the caller-supplied one-line summary
|
|
199
|
-
# @return [void]
|
|
200
|
-
# @raise [Runtime::TypeMismatch] if `summary` is nil or blank
|
|
71
|
+
# Required on dispatch/query/state: what makes an audit row legible later.
|
|
72
|
+
# :nodoc:
|
|
201
73
|
def require_summary!(summary)
|
|
202
74
|
return unless summary.nil? || summary.to_s.strip.empty?
|
|
203
75
|
|
|
@@ -205,80 +77,34 @@ module Hecks
|
|
|
205
77
|
"a one-line summary: is required on dispatch/query/state — it is what makes an audit row legible later"
|
|
206
78
|
end
|
|
207
79
|
|
|
208
|
-
#
|
|
209
|
-
#
|
|
210
|
-
# @param source [String, Symbol, nil] the caller-supplied source tag
|
|
211
|
-
# @return [void]
|
|
212
|
-
# @raise [Runtime::TypeMismatch] if `source` is present and not in `SOURCE_TAGS`
|
|
80
|
+
# :nodoc:
|
|
213
81
|
def valid_source!(source)
|
|
214
82
|
return if source.nil? || SOURCE_TAGS.include?(source.to_s)
|
|
215
83
|
|
|
216
84
|
raise Runtime::TypeMismatch, "source: #{source.inspect} is not one of #{SOURCE_TAGS.join(', ')}"
|
|
217
85
|
end
|
|
218
86
|
|
|
219
|
-
#
|
|
220
|
-
#
|
|
221
|
-
#
|
|
222
|
-
# `role` would silently do nothing rather than bind a real caller,
|
|
223
|
-
# which is worse than refusing: a caller who thinks they've
|
|
224
|
-
# identified themselves and haven't deserves to be told.
|
|
225
|
-
#
|
|
226
|
-
# @param role [String, Symbol, nil] the caller's self-asserted role
|
|
227
|
-
# @param actor_id [String, nil] the caller's self-asserted identity
|
|
228
|
-
# @return [void]
|
|
229
|
-
# @raise [Runtime::TypeMismatch] if `actor_id` is given without `role`
|
|
87
|
+
# actor_id without role would silently bind nothing rather than a real
|
|
88
|
+
# caller — refusing here beats a caller thinking it identified itself.
|
|
89
|
+
# :nodoc:
|
|
230
90
|
def valid_caller!(role, actor_id)
|
|
231
91
|
return unless actor_id && role.nil?
|
|
232
92
|
|
|
233
93
|
raise Runtime::TypeMismatch, "actor_id: requires role: too — a caller names WHO through WHICH role they hold"
|
|
234
94
|
end
|
|
235
95
|
|
|
236
|
-
#
|
|
237
|
-
#
|
|
238
|
-
#
|
|
239
|
-
# for `query`, exactly as before: `CommandRules::Authorization#
|
|
240
|
-
# refuse_role_mismatch` is opt-in on the domain side (`return unless
|
|
241
|
-
# caller`), and query authorization runs on a wholly separate
|
|
242
|
-
# mechanism (`authorize policy, tenant: :field`, checked against an
|
|
243
|
-
# explicit `tenant:` argument — see `Runtime::TenantScope`), so
|
|
244
|
-
# binding a caller around a query has no effect on it today; it is
|
|
245
|
-
# still accepted here, for symmetry and for the audit log, against
|
|
246
|
-
# the day a read model does check `Caller.current`. For `dispatch`,
|
|
247
|
-
# `require_caller_for_role_gated!` (below) now refuses before this
|
|
248
|
-
# is ever reached when the command declares a role and no caller is
|
|
249
|
-
# bound — so an unbound `dispatch` here means either the command
|
|
250
|
-
# declares no role at all, or a caller-side check let it through.
|
|
251
|
-
#
|
|
252
|
-
# @param role [String, Symbol, nil] the caller's self-asserted role; nil runs
|
|
253
|
-
# `block` unbound
|
|
254
|
-
# @param actor_id [String, nil] the caller's self-asserted identity
|
|
255
|
-
# @yield the dispatch/query to run, bound to the caller for its duration
|
|
256
|
-
# @return [Object] the block's own return value
|
|
96
|
+
# Binds role/actor_id for the block's duration via Hecks.as_caller; role: nil
|
|
97
|
+
# runs the block unbound (query's own authorization does not depend on it).
|
|
98
|
+
# :nodoc:
|
|
257
99
|
def with_caller(role, actor_id, &block)
|
|
258
100
|
return block.call if role.nil?
|
|
259
101
|
|
|
260
102
|
Hecks.as_caller(role: role, actor_id: actor_id, &block)
|
|
261
103
|
end
|
|
262
104
|
|
|
263
|
-
#
|
|
264
|
-
#
|
|
265
|
-
#
|
|
266
|
-
# string match), but changed nothing about a caller who binds no
|
|
267
|
-
# role at all: `refuse_role_mismatch` `return`s immediately when
|
|
268
|
-
# `Caller.current` is nil, so a bus caller who simply omits `role:`
|
|
269
|
-
# sails past a role-gated command unchecked, not denied. That is a
|
|
270
|
-
# property of this bus choosing to dispatch unbound, not of the
|
|
271
|
-
# domain rule — `bin/run`, the human CLI, has no such gap because a
|
|
272
|
-
# human always dispatches through a real `Hecks.as_caller` binding
|
|
273
|
-
# upstream of it. Refusing here, before `with_caller`/`dispatch` are
|
|
274
|
-
# ever reached, makes the bus keep the same promise: a command whose
|
|
275
|
-
# bluebook declares a role is not run through this bus without one.
|
|
276
|
-
#
|
|
277
|
-
# @param spec [Hash{Symbol => Object}] a resolved command spec (from `resolve!`),
|
|
278
|
-
# read for `:role_gated`, `:role` and `:verb`
|
|
279
|
-
# @param role [String, Symbol, nil] the caller's self-asserted role
|
|
280
|
-
# @return [void]
|
|
281
|
-
# @raise [Runtime::Unauthorized] if `spec` is role-gated and `role` is nil
|
|
105
|
+
# A caller who omits role: would otherwise reach a role-gated command
|
|
106
|
+
# unchecked (refuse_role_mismatch no-ops with no bound caller) — refuse here instead.
|
|
107
|
+
# :nodoc:
|
|
282
108
|
def require_caller_for_role_gated!(spec, role)
|
|
283
109
|
return unless spec[:role_gated] && role.nil?
|
|
284
110
|
|
|
@@ -287,26 +113,9 @@ module Hecks
|
|
|
287
113
|
"(role:/actor_id:) is bound; dispatching it unbound is refused, not silently unchecked"
|
|
288
114
|
end
|
|
289
115
|
|
|
290
|
-
#
|
|
291
|
-
#
|
|
292
|
-
#
|
|
293
|
-
# CommandInterpreter/EntityInterpreter's pre-envelope contract,
|
|
294
|
-
# never updated because nothing else needed it to be — a real
|
|
295
|
-
# record of history, not a defect this bus should paper over
|
|
296
|
-
# silently). `Facade::CommandRequest`/`spec[:legacy_receiver]`
|
|
297
|
-
# already know how to name that same flat shape for every receiver
|
|
298
|
-
# kind (a bare id under one string key for :aggregate, a
|
|
299
|
-
# {aggregate:, entity:} pair of keys for :entity) — this is the one
|
|
300
|
-
# door back into it.
|
|
301
|
-
#
|
|
302
|
-
# @param envelope [Hash{Symbol => Object}] a normalized command request, with an
|
|
303
|
-
# optional `:to` route and `:with` facts
|
|
304
|
-
# @param receiver [Symbol, nil] `:aggregate`, `:entity`, or nil for anything else
|
|
305
|
-
# @param legacy_receiver [Symbol, String, Hash{Symbol => Symbol, String}, nil] where
|
|
306
|
-
# the route belongs in the flat shape: a single key for `:aggregate`, an
|
|
307
|
-
# `{aggregate:, entity:}` pair of keys for `:entity`
|
|
308
|
-
# @return [Hash{Symbol => Object}] the facts, with the route merged in under its
|
|
309
|
-
# legacy key(s) when `envelope` carries one
|
|
116
|
+
# dry_run? (Runtime::Dispatcher) only understands the pre-envelope flat
|
|
117
|
+
# args shape; this is the one door back into it from dispatch's own envelope.
|
|
118
|
+
# :nodoc:
|
|
310
119
|
def flatten_legacy(envelope, receiver, legacy_receiver)
|
|
311
120
|
facts = envelope[:with] || {}
|
|
312
121
|
return facts unless envelope.key?(:to)
|
|
@@ -321,36 +130,14 @@ module Hecks
|
|
|
321
130
|
end
|
|
322
131
|
end
|
|
323
132
|
|
|
324
|
-
#
|
|
325
|
-
|
|
326
|
-
# The audit log file one domain's calls are appended to.
|
|
327
|
-
#
|
|
328
|
-
# @param domain_name [String, Symbol, #to_s] the domain name
|
|
329
|
-
# @return [String] the absolute path to that domain's JSONL log file, under
|
|
330
|
-
# `log_root`
|
|
133
|
+
# :nodoc:
|
|
331
134
|
def log_path(domain_name)
|
|
332
135
|
File.join(log_root, "#{domain_name.to_s.gsub(/[^A-Za-z0-9_-]/, '_')}.jsonl")
|
|
333
136
|
end
|
|
334
137
|
|
|
335
|
-
#
|
|
336
|
-
#
|
|
337
|
-
#
|
|
338
|
-
# written — a full disk or a permissions problem is a `follow`
|
|
339
|
-
# feature going dark, not a reason to refuse the dispatch/query/
|
|
340
|
-
# state call that was actually asked for.
|
|
341
|
-
#
|
|
342
|
-
# @param domain_name [String, Symbol, nil] the domain to log against; nil skips
|
|
343
|
-
# logging entirely (a call that never resolved a bluebook)
|
|
344
|
-
# @param tool [String] which tool logged this: `"dispatch"`, `"dry_run"`,
|
|
345
|
-
# `"query"`, or `"state"`
|
|
346
|
-
# @param summary [String, nil] the caller's one-line summary
|
|
347
|
-
# @param source [String, Symbol, nil] the caller's source tag
|
|
348
|
-
# @param outcome [Hash{Symbol => Object}] the call's own result, read for `:ok`,
|
|
349
|
-
# `:id`, `:error` and `:events`
|
|
350
|
-
# @param verb [String, nil] the resolved verb dispatched or queried, when known
|
|
351
|
-
# @param role [String, Symbol, nil] the caller's self-asserted role
|
|
352
|
-
# @param actor_id [String, nil] the caller's self-asserted identity
|
|
353
|
-
# @return [void]
|
|
138
|
+
# Never fails a real call because its own audit log couldn't be written —
|
|
139
|
+
# a full disk silences follow, not the dispatch/query/state call itself.
|
|
140
|
+
# :nodoc:
|
|
354
141
|
def record!(domain_name, tool:, summary:, source:, outcome:, verb: nil, role: nil, actor_id: nil)
|
|
355
142
|
return unless domain_name
|
|
356
143
|
|
|
@@ -363,27 +150,18 @@ module Hecks
|
|
|
363
150
|
nil
|
|
364
151
|
end
|
|
365
152
|
|
|
366
|
-
#
|
|
367
|
-
|
|
368
|
-
# `dry_run: true` answers a different question than a real dispatch
|
|
369
|
-
# does — "would this succeed", not "here is what happened" — so a
|
|
370
|
-
# domain refusal is the legitimate, complete answer (`ok: true,
|
|
371
|
-
# would_succeed: false`), not a failed call. A malformed request
|
|
372
|
-
# (unknown command, a bad args shape) is still a failed call
|
|
373
|
-
# (`ok: false`) either way — it never reached the domain to be asked.
|
|
153
|
+
# Issues a command, or previews it with dry_run: true.
|
|
374
154
|
#
|
|
375
|
-
# @param runtime [Runtime::
|
|
155
|
+
# @param runtime [Runtime::Registry] the booted domain to dispatch against
|
|
376
156
|
# @param command [String, Symbol] the command name, bare or qualified
|
|
377
|
-
# @param summary [String] a one-line
|
|
378
|
-
# @param args [Hash] the command's arguments
|
|
379
|
-
# @param source [String, Symbol, nil] a
|
|
380
|
-
# @param dry_run [Boolean] true
|
|
381
|
-
#
|
|
382
|
-
# @param
|
|
383
|
-
# @
|
|
384
|
-
#
|
|
385
|
-
# (a real dispatch) or `:would_succeed`/`:error` (a dry run); on refusal, the
|
|
386
|
-
# shape `refused` returns
|
|
157
|
+
# @param summary [String] a one-line audit summary; required
|
|
158
|
+
# @param args [Hash] the command's arguments, JSON-shaped
|
|
159
|
+
# @param source [String, Symbol, nil] a SOURCE_TAGS tag naming who is calling
|
|
160
|
+
# @param dry_run [Boolean] true previews; a false would_succeed is not a failure
|
|
161
|
+
# @param role [String, Symbol, nil] the caller's bound role, or nil to run unbound
|
|
162
|
+
# @param actor_id [String, nil] the caller's identity; requires role:
|
|
163
|
+
# @return [Hash] :ok plus :id/:state/:events (real) or :would_succeed/:error
|
|
164
|
+
# (dry run); or the refused shape
|
|
387
165
|
def dispatch(runtime:, command:, summary:, args: {}, source: nil, dry_run: false, role: nil, actor_id: nil)
|
|
388
166
|
bluebook = bluebook_for(runtime)
|
|
389
167
|
tool = dry_run ? "dry_run" : "dispatch"
|
|
@@ -399,24 +177,7 @@ module Hecks
|
|
|
399
177
|
outcome
|
|
400
178
|
end
|
|
401
179
|
|
|
402
|
-
#
|
|
403
|
-
#
|
|
404
|
-
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted runtime
|
|
405
|
-
# @param bluebook [Bluebook::Chapter] the domain's one loaded chapter
|
|
406
|
-
# @param command [String, Symbol] the command name, bare or qualified
|
|
407
|
-
# @param summary [String, nil] a one-line human summary of the call
|
|
408
|
-
# @param args [Hash] the command's arguments, JSON-shaped
|
|
409
|
-
# @param source [String, Symbol, nil] a `SOURCE_TAGS` tag naming who is calling
|
|
410
|
-
# @param dry_run [Boolean] true to check whether the command would succeed,
|
|
411
|
-
# without actually dispatching it
|
|
412
|
-
# @param role [String, Symbol, nil] the caller's self-asserted role
|
|
413
|
-
# @param actor_id [String, nil] the caller's self-asserted identity
|
|
414
|
-
# @return [Hash{Symbol => Object}] the outcome from `real_dispatch`/`dry_run_outcome`,
|
|
415
|
-
# merged with `:verb`
|
|
416
|
-
# @raise [Runtime::TypeMismatch] if `summary`, `source`, or `actor_id` without
|
|
417
|
-
# `role` is invalid
|
|
418
|
-
# @raise [Runtime::NotFound] if `command` names no known command
|
|
419
|
-
# @raise [Runtime::Unauthorized] if `command` is role-gated and `role` is nil
|
|
180
|
+
# :nodoc:
|
|
420
181
|
def perform_dispatch(runtime, bluebook, command, summary, args, source, dry_run, role, actor_id)
|
|
421
182
|
require_summary!(summary)
|
|
422
183
|
valid_source!(source)
|
|
@@ -424,9 +185,9 @@ module Hecks
|
|
|
424
185
|
cli = Projector.call(:cli, bluebook: bluebook, options: { program: "mcp" })
|
|
425
186
|
spec = resolve!(cli, command, asking: false)
|
|
426
187
|
require_caller_for_role_gated!(spec, role)
|
|
427
|
-
envelope =
|
|
428
|
-
|
|
429
|
-
|
|
188
|
+
envelope = Doors::CommandRequest.normalize(Doors::JsonDoor.deep_symbolize(args),
|
|
189
|
+
receiver: spec[:receiver],
|
|
190
|
+
legacy_receiver: spec[:legacy_receiver])
|
|
430
191
|
|
|
431
192
|
result = with_caller(role, actor_id) do
|
|
432
193
|
dry_run ? dry_run_outcome(runtime, spec, envelope, summary: summary) : real_dispatch(runtime, spec, envelope, summary)
|
|
@@ -434,30 +195,16 @@ module Hecks
|
|
|
434
195
|
result.merge(verb: spec[:verb])
|
|
435
196
|
end
|
|
436
197
|
|
|
437
|
-
#
|
|
438
|
-
#
|
|
439
|
-
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted runtime
|
|
440
|
-
# @param spec [Hash{Symbol => Object}] the resolved command spec (from `resolve!`)
|
|
441
|
-
# @param envelope [Hash{Symbol => Object}] the normalized `to:`/`with:` request
|
|
442
|
-
# @param summary [String, nil] a one-line human summary of the call
|
|
443
|
-
# @return [Hash{Symbol => Object}] `:ok`, `:summary`, `:id`, `:state` (JSON-safe,
|
|
444
|
-
# nil for a port operation) and `:events` (name/payload pairs)
|
|
198
|
+
# :nodoc:
|
|
445
199
|
def real_dispatch(runtime, spec, envelope, summary)
|
|
446
200
|
result = runtime.dispatch_flat(spec[:verb], envelope)
|
|
447
201
|
ok(summary: summary,
|
|
448
202
|
id: result.id,
|
|
449
|
-
state: result.state.nil? ? nil :
|
|
450
|
-
events: result.events.map { |event| { name: event.name, payload:
|
|
203
|
+
state: result.state.nil? ? nil : Doors::JsonDoor.materialize(result.state),
|
|
204
|
+
events: result.events.map { |event| { name: event.name, payload: Doors::JsonDoor.materialize(event.payload) } })
|
|
451
205
|
end
|
|
452
206
|
|
|
453
|
-
#
|
|
454
|
-
#
|
|
455
|
-
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted runtime
|
|
456
|
-
# @param spec [Hash{Symbol => Object}] the resolved command spec (from `resolve!`)
|
|
457
|
-
# @param envelope [Hash{Symbol => Object}] the normalized `to:`/`with:` request
|
|
458
|
-
# @param summary [String, nil] a one-line human summary of the call
|
|
459
|
-
# @return [Hash{Symbol => Object}] `:ok`, `:summary`, `:would_succeed`, and
|
|
460
|
-
# `:error` (the refusal message) when it would not succeed
|
|
207
|
+
# :nodoc:
|
|
461
208
|
def dry_run_outcome(runtime, spec, envelope, summary:)
|
|
462
209
|
flat = flatten_legacy(envelope, spec[:receiver], spec[:legacy_receiver])
|
|
463
210
|
runtime.dry_run?(spec[:verb], **flat)
|
|
@@ -466,32 +213,22 @@ module Hecks
|
|
|
466
213
|
ok(summary: summary, would_succeed: false, error: e.message)
|
|
467
214
|
end
|
|
468
215
|
|
|
469
|
-
# Dispatches a
|
|
470
|
-
#
|
|
471
|
-
#
|
|
472
|
-
#
|
|
473
|
-
# account, then fund it) pays one round trip instead of N. Every step
|
|
474
|
-
# goes through `dispatch` itself — same resolution, same audit log
|
|
475
|
-
# line per step, same summary/source stamped on all of them since
|
|
476
|
-
# the batch is what the caller is declaring intent about, not each
|
|
477
|
-
# individual step. Runs every step regardless of an earlier one
|
|
478
|
-
# refusing — a later step naming a record an earlier step never
|
|
479
|
-
# created will refuse honestly on its own account, which is more
|
|
480
|
-
# informative than silently dropping the rest of the batch.
|
|
216
|
+
# Dispatches a sequence of commands as one call, through dispatch itself —
|
|
217
|
+
# same audit log line per step. Runs every step even after an earlier one
|
|
218
|
+
# refuses, since a later step naming a since-refused record will refuse
|
|
219
|
+
# honestly on its own account.
|
|
481
220
|
#
|
|
482
|
-
# @param runtime [Runtime::
|
|
483
|
-
# @param steps [Array<Hash>] each step's
|
|
484
|
-
# @param summary [String] a one-line
|
|
485
|
-
# @param source [String, Symbol, nil] a
|
|
486
|
-
# @param role [String, Symbol, nil] the caller's
|
|
487
|
-
# @param actor_id [String, nil] the caller's
|
|
488
|
-
# @return [Hash
|
|
489
|
-
# true), `:summary` and `:results` (each step's own `dispatch` outcome); on a
|
|
490
|
-
# refusal outside any one step, the shape `refused` returns
|
|
221
|
+
# @param runtime [Runtime::Registry] the booted domain to dispatch against
|
|
222
|
+
# @param steps [Array<Hash>] each step's command/args, JSON-shaped
|
|
223
|
+
# @param summary [String] a one-line audit summary; required
|
|
224
|
+
# @param source [String, Symbol, nil] a SOURCE_TAGS tag naming who is calling
|
|
225
|
+
# @param role [String, Symbol, nil] the caller's bound role, or nil to run unbound
|
|
226
|
+
# @param actor_id [String, nil] the caller's identity; requires role:
|
|
227
|
+
# @return [Hash] :ok (true only if every step's own :ok was true), :results
|
|
491
228
|
def dispatch_batch(runtime:, steps:, summary:, source: nil, role: nil, actor_id: nil)
|
|
492
229
|
require_summary!(summary)
|
|
493
230
|
results = Array(steps).map do |raw|
|
|
494
|
-
step =
|
|
231
|
+
step = Doors::JsonDoor.deep_symbolize(raw)
|
|
495
232
|
dispatch(runtime: runtime, command: step[:command], args: step[:args] || {},
|
|
496
233
|
summary: summary, source: source, role: role, actor_id: actor_id)
|
|
497
234
|
end
|
|
@@ -502,15 +239,14 @@ module Hecks
|
|
|
502
239
|
|
|
503
240
|
# Answers one declared query.
|
|
504
241
|
#
|
|
505
|
-
# @param runtime [Runtime::
|
|
242
|
+
# @param runtime [Runtime::Registry] the booted domain to query
|
|
506
243
|
# @param question [String, Symbol] the query name, bare or qualified
|
|
507
|
-
# @param summary [String] a one-line
|
|
508
|
-
# @param args [Hash] the query's arguments
|
|
509
|
-
# @param source [String, Symbol, nil] a
|
|
510
|
-
# @param role [String, Symbol, nil] the caller's
|
|
511
|
-
# @param actor_id [String, nil] the caller's
|
|
512
|
-
# @return [Hash
|
|
513
|
-
# or the shape `refused` returns
|
|
244
|
+
# @param summary [String] a one-line audit summary; required
|
|
245
|
+
# @param args [Hash] the query's arguments, JSON-shaped
|
|
246
|
+
# @param source [String, Symbol, nil] a SOURCE_TAGS tag naming who is calling
|
|
247
|
+
# @param role [String, Symbol, nil] the caller's bound role, or nil to run unbound
|
|
248
|
+
# @param actor_id [String, nil] the caller's identity; requires role:
|
|
249
|
+
# @return [Hash] :ok and :rows (each JSON-safe); or the refused shape
|
|
514
250
|
def query(runtime:, question:, summary:, args: {}, source: nil, role: nil, actor_id: nil)
|
|
515
251
|
bluebook = bluebook_for(runtime)
|
|
516
252
|
outcome = perform_query(bluebook, runtime, question, summary, args, source, role, actor_id)
|
|
@@ -525,47 +261,27 @@ module Hecks
|
|
|
525
261
|
outcome
|
|
526
262
|
end
|
|
527
263
|
|
|
528
|
-
#
|
|
529
|
-
#
|
|
530
|
-
# @param bluebook [Bluebook::Chapter] the domain's one loaded chapter
|
|
531
|
-
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted runtime
|
|
532
|
-
# @param question [String, Symbol] the query name, bare or qualified
|
|
533
|
-
# @param summary [String, nil] a one-line human summary of the call
|
|
534
|
-
# @param args [Hash] the query's arguments, JSON-shaped
|
|
535
|
-
# @param source [String, Symbol, nil] a `SOURCE_TAGS` tag naming who is calling
|
|
536
|
-
# @param role [String, Symbol, nil] the caller's self-asserted role
|
|
537
|
-
# @param actor_id [String, nil] the caller's self-asserted identity
|
|
538
|
-
# @return [Hash{Symbol => Object}] `:ok`, `:rows` (each JSON-safe) and `:verb`
|
|
539
|
-
# @raise [Runtime::TypeMismatch] if `summary`, `source`, or `actor_id` without
|
|
540
|
-
# `role` is invalid
|
|
541
|
-
# @raise [Runtime::NotFound] if `question` names no known query
|
|
264
|
+
# :nodoc:
|
|
542
265
|
def perform_query(bluebook, runtime, question, summary, args, source, role, actor_id)
|
|
543
266
|
require_summary!(summary)
|
|
544
267
|
valid_source!(source)
|
|
545
268
|
valid_caller!(role, actor_id)
|
|
546
269
|
cli = Projector.call(:cli, bluebook: bluebook, options: { program: "mcp" })
|
|
547
270
|
spec = resolve!(cli, question, asking: true)
|
|
548
|
-
rows = with_caller(role, actor_id) { runtime.query(spec[:verb], **
|
|
271
|
+
rows = with_caller(role, actor_id) { runtime.query(spec[:verb], **Doors::JsonDoor.deep_symbolize(args)) }
|
|
549
272
|
|
|
550
|
-
ok(summary: summary, rows: rows.map { |row|
|
|
273
|
+
ok(summary: summary, rows: rows.map { |row| Doors::JsonDoor.materialize(row) }).merge(verb: spec[:verb])
|
|
551
274
|
end
|
|
552
275
|
|
|
553
|
-
# Reads one aggregate's stored records directly, bypassing any declared
|
|
276
|
+
# Reads one aggregate's stored records directly, bypassing any declared
|
|
277
|
+
# query — id: answers one record, omitted answers every record.
|
|
554
278
|
#
|
|
555
|
-
#
|
|
556
|
-
# itself. `id:` given answers one record (`NotFound` when it names
|
|
557
|
-
# nothing); omitted answers every record the aggregate currently
|
|
558
|
-
# holds. This is the difference `query` can't cover: a query answers a
|
|
559
|
-
# declared question, and an aggregate that never declared "list
|
|
560
|
-
# everything" has no query this could reuse.
|
|
561
|
-
#
|
|
562
|
-
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted runtime
|
|
279
|
+
# @param runtime [Runtime::Registry] the booted domain to read
|
|
563
280
|
# @param aggregate [String, Symbol] the aggregate's declared name
|
|
564
|
-
# @param summary [String] a one-line
|
|
281
|
+
# @param summary [String] a one-line audit summary; required
|
|
565
282
|
# @param id [String, Object, nil] one record's identity, or nil for every record
|
|
566
|
-
# @param source [String, Symbol, nil] a
|
|
567
|
-
# @return [Hash
|
|
568
|
-
# `:count` and `:records` (each JSON-safe); or the shape `refused` returns
|
|
283
|
+
# @param source [String, Symbol, nil] a SOURCE_TAGS tag naming who is calling
|
|
284
|
+
# @return [Hash] :ok and, with id:, :record; without it, :count/:records; or refused
|
|
569
285
|
def state(runtime:, aggregate:, summary:, id: nil, source: nil)
|
|
570
286
|
bluebook = bluebook_for(runtime)
|
|
571
287
|
outcome = perform_state(runtime, bluebook, aggregate, summary, id)
|
|
@@ -578,18 +294,7 @@ module Hecks
|
|
|
578
294
|
outcome
|
|
579
295
|
end
|
|
580
296
|
|
|
581
|
-
#
|
|
582
|
-
#
|
|
583
|
-
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted runtime
|
|
584
|
-
# @param bluebook [Bluebook::Chapter] the domain's one loaded chapter
|
|
585
|
-
# @param aggregate [String, Symbol] the aggregate's declared name
|
|
586
|
-
# @param summary [String, nil] a one-line human summary of the call
|
|
587
|
-
# @param id [String, Object, nil] one record's identity, or nil for every record
|
|
588
|
-
# @return [Hash{Symbol => Object}] `:ok` and, with `id`, `:record`; without it,
|
|
589
|
-
# `:count` and `:records` (each JSON-safe)
|
|
590
|
-
# @raise [Runtime::TypeMismatch] if `summary` is invalid
|
|
591
|
-
# @raise [Runtime::NotFound] if `aggregate` names no known aggregate, or `id`
|
|
592
|
-
# names no record of it
|
|
297
|
+
# :nodoc:
|
|
593
298
|
def perform_state(runtime, bluebook, aggregate, summary, id)
|
|
594
299
|
require_summary!(summary)
|
|
595
300
|
ir = aggregate_ir!(bluebook, aggregate)
|
|
@@ -598,30 +303,19 @@ module Hecks
|
|
|
598
303
|
if id
|
|
599
304
|
instance = repository.find(id) or
|
|
600
305
|
raise Runtime::NotFound, "no #{ir.hecks_name} found for id #{id.inspect}"
|
|
601
|
-
ok(summary: summary, record:
|
|
306
|
+
ok(summary: summary, record: Doors::JsonDoor.materialize(instance.to_h))
|
|
602
307
|
else
|
|
603
|
-
records = repository.all.map { |instance|
|
|
308
|
+
records = repository.all.map { |instance| Doors::JsonDoor.materialize(instance.to_h) }
|
|
604
309
|
ok(summary: summary, count: records.length, records: records)
|
|
605
310
|
end
|
|
606
311
|
end
|
|
607
312
|
|
|
608
|
-
#
|
|
609
|
-
|
|
610
|
-
# Lists every domain directory under a root.
|
|
611
|
-
#
|
|
612
|
-
# **Zoom level zero** — every domain directory a root actually holds,
|
|
613
|
-
# discovered rather than typed from memory. Every other tool takes
|
|
614
|
-
# `domain:` as a directory it assumes the caller already knows; this
|
|
615
|
-
# is how a caller who doesn't finds out. `Adapters::Folder#domain?`
|
|
616
|
-
# is the same predicate `domain_root`/`nearest_domain` already walk
|
|
617
|
-
# up directories checking — a bare `.hecksagon` or one under
|
|
618
|
-
# `bluebook/`, the two real shapes this corpus uses.
|
|
313
|
+
# Lists every domain directory found under a root (discovered, not typed).
|
|
619
314
|
#
|
|
620
|
-
# @param under [String] the directory to search, relative to
|
|
621
|
-
# @return [Hash
|
|
622
|
-
#
|
|
623
|
-
#
|
|
624
|
-
# @raise [Runtime::TypeMismatch] if `under` resolves outside `BOOT_ROOT`
|
|
315
|
+
# @param under [String] the directory to search, relative to BOOT_ROOT
|
|
316
|
+
# @return [Hash] :ok, :under, :domains (each path relative to BOOT_ROOT,
|
|
317
|
+
# sorted); :domains is [] when under does not exist
|
|
318
|
+
# @raise [Runtime::TypeMismatch] if under resolves outside BOOT_ROOT
|
|
625
319
|
def domains(under: "examples")
|
|
626
320
|
root = confine!(under, "under")
|
|
627
321
|
return ok(under: under, domains: []) unless Dir.exist?(root)
|
|
@@ -632,17 +326,12 @@ module Hecks
|
|
|
632
326
|
ok(under: under, domains: found.map { |name| File.join(under, name) })
|
|
633
327
|
end
|
|
634
328
|
|
|
635
|
-
# Lists a domain's aggregates and their command/query names
|
|
329
|
+
# Lists a domain's aggregates and their command/query names, snake_cased
|
|
330
|
+
# exactly as dispatch/query want them.
|
|
636
331
|
#
|
|
637
|
-
#
|
|
638
|
-
#
|
|
639
|
-
#
|
|
640
|
-
# the next level down for what one of them actually takes.
|
|
641
|
-
#
|
|
642
|
-
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted runtime
|
|
643
|
-
# @return [Hash{Symbol => Object}] `:ok`, `:domain`, and `:aggregates` — an Array
|
|
644
|
-
# of `{name:, commands:, queries:}` Hashes, commands and queries sorted,
|
|
645
|
-
# command names suffixed `!`; or the shape `refused` returns
|
|
332
|
+
# @param runtime [Runtime::Registry] the booted domain to describe
|
|
333
|
+
# @return [Hash] :ok, :domain, :aggregates (each {name:, commands:, queries:},
|
|
334
|
+
# command names suffixed !); or the refused shape
|
|
646
335
|
def catalog(runtime:)
|
|
647
336
|
bluebook = bluebook_for(runtime)
|
|
648
337
|
|
|
@@ -656,21 +345,14 @@ module Hecks
|
|
|
656
345
|
refused(e)
|
|
657
346
|
end
|
|
658
347
|
|
|
659
|
-
#
|
|
660
|
-
#
|
|
661
|
-
# **Zoom level two** — the exact same usage document a human gets from
|
|
662
|
-
# `bin/docs <domain> [aggregate]` (`Projector::DocsProjector`, the
|
|
663
|
-
# identical projection `Surface::AggregateDoor#docs` calls one door
|
|
664
|
-
# over): every command's arguments, the states it may be issued
|
|
665
|
-
# from, and every way it can refuse. `aggregate:` omitted answers the
|
|
666
|
-
# whole chapter.
|
|
348
|
+
# One aggregate's (or the whole chapter's) full usage documentation — the
|
|
349
|
+
# same document hecks docs renders for a human.
|
|
667
350
|
#
|
|
668
|
-
# @param runtime [Runtime::
|
|
351
|
+
# @param runtime [Runtime::Registry] the booted domain to describe
|
|
669
352
|
# @param aggregate [String, Symbol, nil] one aggregate's name, or nil for the
|
|
670
353
|
# whole chapter
|
|
671
|
-
# @return [Hash
|
|
672
|
-
#
|
|
673
|
-
# @raise [Runtime::NotFound] if `aggregate` names no known aggregate
|
|
354
|
+
# @return [Hash] :ok, :domain, :docs; or the refused shape
|
|
355
|
+
# @raise [Runtime::NotFound] if aggregate names no known aggregate
|
|
674
356
|
def describe(runtime:, aggregate: nil)
|
|
675
357
|
bluebook = bluebook_for(runtime)
|
|
676
358
|
options = aggregate ? { aggregate: aggregate_ir!(bluebook, aggregate).hecks_name } : {}
|
|
@@ -680,41 +362,18 @@ module Hecks
|
|
|
680
362
|
refused(e)
|
|
681
363
|
end
|
|
682
364
|
|
|
683
|
-
# Boots a domain and reports whether its wiring
|
|
684
|
-
#
|
|
685
|
-
#
|
|
686
|
-
#
|
|
687
|
-
#
|
|
688
|
-
# verification.rb`) is the one place this repo already answers that
|
|
689
|
-
# question, and `Runtime::Loader.boot` already calls it as the last
|
|
690
|
-
# step of every boot — so this is the one method here that boots for
|
|
691
|
-
# itself rather than taking a `runtime:` already in hand, because a
|
|
692
|
-
# runtime that successfully reached this line already answered the
|
|
693
|
-
# question. Given a domain path, not a booted runtime, deliberately:
|
|
694
|
-
# asking "is this valid" about a domain that failed to boot at all
|
|
695
|
-
# has to be askable without a runtime to hand it.
|
|
365
|
+
# Boots a domain and reports whether its wiring is sound; deep: true also
|
|
366
|
+
# runs Bluebook::ModelCheck past wiring, into logic. Boots for itself,
|
|
367
|
+
# unlike every other tool here, since a domain that failed to boot has
|
|
368
|
+
# no runtime to hand in — any boot failure answers the question, not just
|
|
369
|
+
# WiringError.
|
|
696
370
|
#
|
|
697
|
-
#
|
|
698
|
-
#
|
|
699
|
-
#
|
|
700
|
-
#
|
|
701
|
-
# "this cannot run at all", a model-check finding is "this runs, but
|
|
702
|
-
# part of it can never fire" — two different questions, and the
|
|
703
|
-
# first is far cheaper to ask on every boot.
|
|
704
|
-
#
|
|
705
|
-
# Any boot failure answers the question, not only `WiringError` — a
|
|
706
|
-
# domain path with no `.hecksagon`, a malformed bluebook, is just as
|
|
707
|
-
# much "not valid" as a real wiring mismatch, and this tool exists
|
|
708
|
-
# precisely so none of those ever cross a projection of this bus as
|
|
709
|
-
# a crash.
|
|
710
|
-
#
|
|
711
|
-
# @param domain [String] the domain's directory path, relative to `BOOT_ROOT`
|
|
712
|
-
# @param deep [Boolean] also run `Bluebook::ModelCheck` past wiring, into logic
|
|
713
|
-
# @return [Hash{Symbol => Object}] `{ok: true, domain:, valid: true}`, plus
|
|
714
|
-
# `:findings` (each `{kind:, severity:, subject:, message:}`) when `deep`; or
|
|
715
|
-
# `{ok: false, domain:, valid: false, error:}` if the boot itself failed
|
|
371
|
+
# @param domain [String] the domain's directory path, relative to BOOT_ROOT
|
|
372
|
+
# @param deep [Boolean] also run Bluebook::ModelCheck past wiring, into logic
|
|
373
|
+
# @return [Hash] {ok: true, domain:, valid: true} plus :findings when deep;
|
|
374
|
+
# {ok: false, domain:, valid: false, error:} if the boot itself failed
|
|
716
375
|
def validate(domain:, deep: false)
|
|
717
|
-
runtime = Hecks.boot(confine!(domain, "domain"),
|
|
376
|
+
runtime = Hecks.boot(confine!(domain, "domain"), install_doors: false)
|
|
718
377
|
result = { ok: true, domain: domain, valid: true }
|
|
719
378
|
|
|
720
379
|
if deep
|
|
@@ -728,22 +387,12 @@ module Hecks
|
|
|
728
387
|
{ ok: false, domain: domain, valid: false, error: "#{e.class}: #{e.message}" }
|
|
729
388
|
end
|
|
730
389
|
|
|
731
|
-
#
|
|
732
|
-
|
|
733
|
-
# The full write history, not just the current head — `bin/history`'s
|
|
734
|
-
# own logic, unchanged: every operation that ever touched the
|
|
735
|
-
# aggregate, read off its repository's own `entries`. `Registry#repository`
|
|
736
|
-
# always hands back a `Ports::Persistence::AppendOnly`-wrapped adapter,
|
|
737
|
-
# never the bare adapter, so this never needs to guard against one —
|
|
738
|
-
# what varies by adapter is only how much the wrapped `entries` itself
|
|
739
|
-
# actually holds: a fresh Memory boot honestly has none yet, and a
|
|
740
|
-
# durable adapter (Heki, Postgres, SQLite, D1) has whatever it
|
|
741
|
-
# journaled.
|
|
390
|
+
# The full write history, not just the current head — every operation that
|
|
391
|
+
# ever touched each aggregate, off its repository's own append-only entries.
|
|
742
392
|
#
|
|
743
|
-
# @param runtime [Runtime::
|
|
744
|
-
# @return [Hash
|
|
745
|
-
#
|
|
746
|
-
# `refused` returns
|
|
393
|
+
# @param runtime [Runtime::Registry] the booted domain to read
|
|
394
|
+
# @return [Hash] :ok, :domain, :history (each aggregate's storage name
|
|
395
|
+
# mapped to its journal_entries); or the refused shape
|
|
747
396
|
def history(runtime:)
|
|
748
397
|
bluebook = bluebook_for(runtime)
|
|
749
398
|
entries = bluebook.aggregates.each_with_object({}) do |aggregate, all|
|
|
@@ -756,30 +405,17 @@ module Hecks
|
|
|
756
405
|
refused(e)
|
|
757
406
|
end
|
|
758
407
|
|
|
759
|
-
#
|
|
760
|
-
#
|
|
761
|
-
# @param repository [Ports::Persistence::AppendOnly] the aggregate's repository, as
|
|
762
|
-
# `Registry#repository` always returns it
|
|
763
|
-
# @return [Array<Hash>] each journal entry's `:operation`, `:id` and `:state`
|
|
764
|
-
# (materialized); `[]` when the adapter has journaled nothing yet
|
|
408
|
+
# :nodoc:
|
|
765
409
|
def journal_entries(repository)
|
|
766
|
-
repository.entries.map { |entry| { operation: entry.operation, id: entry.id, state:
|
|
410
|
+
repository.entries.map { |entry| { operation: entry.operation, id: entry.id, state: Doors::JsonDoor.materialize(entry.state) } }
|
|
767
411
|
end
|
|
768
412
|
|
|
769
|
-
#
|
|
770
|
-
# to use a domain, in domain vocabulary (`docs/guides/behaviors.md`),
|
|
771
|
-
# the survey's own "honest-refusal", generated-example-suite items.
|
|
772
|
-
# `Hecks::Behaviors` boots each test fresh through `Hecks.boot_files`
|
|
773
|
-
# itself — `target:` names a `.behaviors` file or a directory to
|
|
774
|
-
# sweep, never a `runtime:`, the one other method here besides
|
|
775
|
-
# `validate` that takes a path instead.
|
|
413
|
+
# Runs a domain's hand-curated .behaviors examples and reports the results.
|
|
776
414
|
#
|
|
777
|
-
# @param target [String] a
|
|
778
|
-
#
|
|
779
|
-
#
|
|
780
|
-
#
|
|
781
|
-
# the shape `refused` returns
|
|
782
|
-
# @raise [Runtime::NotFound] if `target` is nil or names no real file or directory
|
|
415
|
+
# @param target [String] a .behaviors file's path, or a directory to sweep
|
|
416
|
+
# @return [Hash] :ok, :target, :files (each file's behaviors_file shape),
|
|
417
|
+
# :counts; or the refused shape
|
|
418
|
+
# @raise [Runtime::NotFound] if target is nil or names no real file or directory
|
|
783
419
|
def behaviors(target:)
|
|
784
420
|
require_relative "behaviors"
|
|
785
421
|
raise Runtime::NotFound, "no such file or directory: #{target.inspect}" unless target && File.exist?(target)
|
|
@@ -795,31 +431,20 @@ module Hecks
|
|
|
795
431
|
refused(e)
|
|
796
432
|
end
|
|
797
433
|
|
|
798
|
-
#
|
|
799
|
-
#
|
|
800
|
-
# @param result [Behaviors::FileResult] one file's run result
|
|
801
|
-
# @return [Hash{Symbol => Object}] `:path`, `:parse_error`, and `:runs` (each
|
|
802
|
-
# test's `description`/`status`/`message`)
|
|
434
|
+
# :nodoc:
|
|
803
435
|
def behaviors_file(result)
|
|
804
436
|
{ path: result.path,
|
|
805
437
|
parse_error: result.parse_error,
|
|
806
438
|
runs: Array(result.runs).map { |run| { description: run.description, status: run.status, message: run.message } } }
|
|
807
439
|
end
|
|
808
440
|
|
|
809
|
-
#
|
|
810
|
-
#
|
|
811
|
-
#
|
|
812
|
-
# of the survey's `storehouse follow` for that shape: not a
|
|
813
|
-
# subscription, a durable JSONL log every `dispatch`/`query`/`state`/
|
|
814
|
-
# dry-run call appends to (`record!`), tailed back here. Still real,
|
|
815
|
-
# still cross-process — the log outlives any one door's own process —
|
|
816
|
-
# just pull instead of push.
|
|
441
|
+
# Tails this bus's own dispatch/query/state audit log — a pull-based
|
|
442
|
+
# substitute for a push subscription, since a stdio door answers one
|
|
443
|
+
# request at a time with no channel to push through.
|
|
817
444
|
#
|
|
818
|
-
# @param runtime [Runtime::
|
|
819
|
-
# @param limit [Integer, #to_i] how many
|
|
820
|
-
#
|
|
821
|
-
# @return [Hash{Symbol => Object}] `:ok`, `:domain` and `:entries` (each logged
|
|
822
|
-
# call's own JSON entry); or the shape `refused` returns
|
|
445
|
+
# @param runtime [Runtime::Registry] the booted domain to tail
|
|
446
|
+
# @param limit [Integer, #to_i] how many recent log entries to return
|
|
447
|
+
# @return [Hash] :ok, :domain, :entries; or the refused shape
|
|
823
448
|
def follow(runtime:, limit: 20)
|
|
824
449
|
bluebook = bluebook_for(runtime)
|
|
825
450
|
entries = log_lines(bluebook.name).last([limit.to_i, 1].max)
|
|
@@ -829,11 +454,7 @@ module Hecks
|
|
|
829
454
|
refused(e)
|
|
830
455
|
end
|
|
831
456
|
|
|
832
|
-
#
|
|
833
|
-
#
|
|
834
|
-
# @param domain_name [String, Symbol, #to_s] the domain name
|
|
835
|
-
# @return [Array<Hash>] every logged entry, parsed with Symbol keys, in log
|
|
836
|
-
# order; `[]` if the domain has no log file yet
|
|
457
|
+
# :nodoc:
|
|
837
458
|
def log_lines(domain_name)
|
|
838
459
|
path = log_path(domain_name)
|
|
839
460
|
return [] unless File.exist?(path)
|
|
@@ -841,32 +462,18 @@ module Hecks
|
|
|
841
462
|
File.readlines(path).map { |line| JSON.parse(line, symbolize_names: true) }
|
|
842
463
|
end
|
|
843
464
|
|
|
844
|
-
# What actually happened, with payloads
|
|
845
|
-
# (
|
|
846
|
-
#
|
|
847
|
-
#
|
|
848
|
-
# during the very call that populated it, discarded the moment that
|
|
849
|
-
# call returns — there is no cross-call in-memory log to read here.
|
|
850
|
-
# So this reads the same durable audit log `follow` already tails
|
|
851
|
-
# (`record!` now stamps a successful dispatch's own announced
|
|
852
|
-
# events onto its log line), reshaped: `follow` answers "what was
|
|
853
|
-
# called, in order, across every tool"; this answers "what happened
|
|
854
|
-
# to one aggregate/record" — events this bus witnessed, which is
|
|
855
|
-
# every real dispatch ever routed through it, but no more than that.
|
|
856
|
-
# `aggregate:` narrows to one aggregate; `id:` (requires
|
|
857
|
-
# `aggregate:` — an id alone is not unique across aggregates)
|
|
858
|
-
# narrows to one record's own events.
|
|
465
|
+
# What actually happened, with payloads, to one aggregate or record — read
|
|
466
|
+
# off this bus's own audit log (a dispatch's announced events), not a full
|
|
467
|
+
# event-sourcing replay. aggregate: narrows the search; id: (requires
|
|
468
|
+
# aggregate:) narrows further to one record.
|
|
859
469
|
#
|
|
860
|
-
# @param runtime [Runtime::
|
|
861
|
-
# @param aggregate [String, Symbol, nil] narrows to one aggregate
|
|
862
|
-
# @param id [String, Object, nil] narrows to one record
|
|
863
|
-
#
|
|
864
|
-
#
|
|
865
|
-
#
|
|
866
|
-
# @
|
|
867
|
-
# `:name`, `:payload`, `:time`, `:verb` and `:id`); or the shape `refused` returns
|
|
868
|
-
# @raise [Runtime::TypeMismatch] if `id` is given without `aggregate`
|
|
869
|
-
# @raise [Runtime::NotFound] if `aggregate` names no known aggregate
|
|
470
|
+
# @param runtime [Runtime::Registry] the booted domain to search
|
|
471
|
+
# @param aggregate [String, Symbol, nil] narrows the search to one aggregate
|
|
472
|
+
# @param id [String, Object, nil] narrows further to one record; requires aggregate
|
|
473
|
+
# @param limit [Integer, #to_i, nil] how many recent matching events to
|
|
474
|
+
# return; nil returns every one found
|
|
475
|
+
# @return [Hash] :ok, :domain, :events; or the refused shape
|
|
476
|
+
# @raise [Runtime::TypeMismatch] if id is given without aggregate
|
|
870
477
|
def events(runtime:, aggregate: nil, id: nil, limit: nil)
|
|
871
478
|
raise Runtime::TypeMismatch, "id: requires aggregate: too — an id alone is not unique across aggregates" if id && !aggregate
|
|
872
479
|
|
|
@@ -881,20 +488,7 @@ module Hecks
|
|
|
881
488
|
refused(e)
|
|
882
489
|
end
|
|
883
490
|
|
|
884
|
-
#
|
|
885
|
-
# was a successful dispatch carrying events, matching `fqn` (when an
|
|
886
|
-
# aggregate narrowed the search) and `id` (when a record did). A
|
|
887
|
-
# self-contained per-entry check with nothing to share with its
|
|
888
|
-
# neighbors, extracted only to keep `events` itself to the query's
|
|
889
|
-
# own shape: build the filter, apply the limit, wrap the result.
|
|
890
|
-
#
|
|
891
|
-
# @param entry [Hash{Symbol => Object}] one logged audit entry
|
|
892
|
-
# @param fqn [String, nil] narrows to a dispatch whose verb starts with this
|
|
893
|
-
# aggregate FQN; nil matches any aggregate
|
|
894
|
-
# @param id [String, Object, nil] narrows to a dispatch whose settled record has
|
|
895
|
-
# this id; nil matches any record
|
|
896
|
-
# @return [Array<Hash>, nil] `entry`'s own events (each with `:time`, `:verb` and
|
|
897
|
-
# `:id` merged in), or nil if `entry` isn't a matching successful dispatch
|
|
491
|
+
# :nodoc:
|
|
898
492
|
def entry_events(entry, fqn, id)
|
|
899
493
|
return unless entry[:tool] == "dispatch" && entry[:ok] && entry[:events]
|
|
900
494
|
return if fqn && !entry[:verb].to_s.start_with?("#{fqn}.")
|
|
@@ -903,39 +497,18 @@ module Hecks
|
|
|
903
497
|
entry[:events].map { |event| event.merge(time: entry[:time], verb: entry[:verb], id: entry[:id]) }
|
|
904
498
|
end
|
|
905
499
|
|
|
906
|
-
#
|
|
907
|
-
|
|
908
|
-
#
|
|
909
|
-
#
|
|
910
|
-
# rule the caller broke), but because "this domain isn't wired to
|
|
911
|
-
# answer what you're asking" (a `role:`+`actor_id:` caller reaching
|
|
912
|
-
# an authorization port nothing implements, a dry run against a
|
|
913
|
-
# port verb) is exactly the shape this bus promises never crashes
|
|
914
|
-
# through it. `dry_run_outcome` already treats it this way locally;
|
|
915
|
-
# this makes every other caller of `refusal_classes` do the same.
|
|
916
|
-
#
|
|
917
|
-
# @return [Array<Class>] every exception class this bus catches and reshapes
|
|
918
|
-
# into an honest refusal rather than letting crash through
|
|
500
|
+
# Runtime::WiringError is included alongside the true domain refusals: a
|
|
501
|
+
# bus caller sees an honest refusal here too, not a crash, even though a
|
|
502
|
+
# wiring defect isn't a rule the caller broke.
|
|
503
|
+
# :nodoc:
|
|
919
504
|
def refusal_classes = [Runtime::NotFound, Runtime::TypeMismatch, Runtime::WiringError, *Runtime::DOMAIN_REFUSALS]
|
|
920
505
|
|
|
921
|
-
#
|
|
922
|
-
#
|
|
923
|
-
# @param fields [Hash{Symbol => Object}] fields to merge in alongside `ok: true`
|
|
924
|
-
# @return [Hash{Symbol => Object}] `{ok: true}` merged with `fields`
|
|
506
|
+
# :nodoc:
|
|
925
507
|
def ok(**fields) = { ok: true }.merge(fields)
|
|
926
508
|
|
|
927
|
-
#
|
|
928
|
-
#
|
|
929
|
-
#
|
|
930
|
-
# explicit, structured refusal a caller can act on" rather than a
|
|
931
|
-
# stack trace an agent has to parse to find the one line that
|
|
932
|
-
# mattered. The domain's own refusal text travels verbatim
|
|
933
|
-
# (`RefusalWording` already renders every one of these to be read),
|
|
934
|
-
# this only wraps it consistently.
|
|
935
|
-
#
|
|
936
|
-
# @param error [StandardError] the caught exception, a member of `refusal_classes`
|
|
937
|
-
# @param summary [String, nil] the caller's one-line summary
|
|
938
|
-
# @return [Hash{Symbol => Object}] `{ok: false, summary:, error:}`
|
|
509
|
+
# The domain's own refusal text travels verbatim; this only wraps it
|
|
510
|
+
# consistently, rather than letting a stack trace reach the caller.
|
|
511
|
+
# :nodoc:
|
|
939
512
|
def refused(error, summary: nil) = { ok: false, summary: summary, error: error.message }
|
|
940
513
|
end
|
|
941
514
|
end
|