hecks 1.3.0 → 1.4.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/lib/hecks/adapters/driven/claude_code.rb +7 -7
- data/lib/hecks/adapters/driven/d1.rb +187 -23
- data/lib/hecks/adapters/driven/folder.rb +10 -10
- data/lib/hecks/adapters/driven/google_authentication.rb +8 -8
- data/lib/hecks/adapters/driven/governance_authorization.rb +31 -13
- data/lib/hecks/adapters/driven/heki/journal.rb +5 -4
- data/lib/hecks/adapters/driven/heki/saga_store.rb +5 -5
- data/lib/hecks/adapters/driven/heki.rb +13 -7
- data/lib/hecks/adapters/driven/identity_registry.rb +2 -2
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +3 -3
- data/lib/hecks/adapters/driven/lambda/client.rb +34 -9
- data/lib/hecks/adapters/driven/lambda.rb +39 -33
- data/lib/hecks/adapters/driven/local_storage.rb +17 -10
- data/lib/hecks/adapters/driven/memory.rb +205 -9
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +1 -1
- data/lib/hecks/adapters/driven/postgres/codec.rb +27 -11
- data/lib/hecks/adapters/driven/postgres/outbox.rb +40 -2
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +23 -7
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +14 -14
- data/lib/hecks/adapters/driven/postgres.rb +175 -28
- data/lib/hecks/adapters/driven/prism.rb +4 -4
- data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
- data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
- data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
- data/lib/hecks/adapters/driven/sqlite.rb +181 -21
- data/lib/hecks/adapters/driven.rb +4 -4
- data/lib/hecks/adapters/driving/github_webhook.rb +18 -18
- data/lib/hecks/behaviors/dsl.rb +2 -2
- data/lib/hecks/behaviors/expectations.rb +23 -23
- data/lib/hecks/behaviors/ir.rb +1 -1
- data/lib/hecks/behaviors/rspec.rb +1 -1
- data/lib/hecks/behaviors/runner.rb +2 -2
- data/lib/hecks/behaviors.rb +1 -1
- data/lib/hecks/bluebook/aggregate.rb +13 -13
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +10 -10
- data/lib/hecks/bluebook/assembly/build.rb +1 -1
- data/lib/hecks/bluebook/assembly/contract.rb +39 -16
- data/lib/hecks/bluebook/assembly/contracts.rb +40 -38
- data/lib/hecks/bluebook/assembly/marks.rb +18 -18
- data/lib/hecks/bluebook/assembly/specializer.rb +19 -21
- data/lib/hecks/bluebook/assembly.rb +9 -9
- data/lib/hecks/bluebook/attribute.rb +9 -9
- data/lib/hecks/bluebook/behaviour/aggregate.rb +11 -11
- data/lib/hecks/bluebook/behaviour/attribute.rb +5 -5
- data/lib/hecks/bluebook/behaviour/chapter.rb +23 -5
- data/lib/hecks/bluebook/behaviour/command.rb +23 -23
- data/lib/hecks/bluebook/behaviour/domain_port.rb +7 -7
- data/lib/hecks/bluebook/behaviour/entity.rb +8 -8
- data/lib/hecks/bluebook/behaviour/hexagon.rb +4 -4
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +5 -5
- data/lib/hecks/bluebook/behaviour/policy.rb +12 -12
- data/lib/hecks/bluebook/behaviour/process_manager.rb +7 -7
- data/lib/hecks/bluebook/behaviour/query.rb +1 -1
- data/lib/hecks/bluebook/behaviour/read_model.rb +8 -8
- data/lib/hecks/bluebook/behaviour/traits.rb +12 -12
- data/lib/hecks/bluebook/behaviour/value_object.rb +6 -6
- data/lib/hecks/bluebook/capabilities.rb +27 -0
- data/lib/hecks/bluebook/chapter.rb +28 -9
- data/lib/hecks/bluebook/command.rb +12 -12
- data/lib/hecks/bluebook/domain_port.rb +9 -9
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +96 -96
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +41 -41
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +22 -2
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +111 -74
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +48 -30
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +103 -103
- data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
- data/lib/hecks/bluebook/dsl/entity_builder.rb +56 -56
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +89 -30
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +17 -17
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
- data/lib/hecks/bluebook/dsl/policy_builder.rb +30 -21
- data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +35 -35
- data/lib/hecks/bluebook/dsl/query_builder.rb +5 -5
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +34 -34
- data/lib/hecks/bluebook/dsl/rule_reference.rb +41 -39
- data/lib/hecks/bluebook/dsl/translation_builder.rb +9 -9
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +16 -16
- data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
- data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
- data/lib/hecks/bluebook/entity.rb +11 -11
- data/lib/hecks/bluebook/expression/ast_json.rb +20 -20
- data/lib/hecks/bluebook/expression/ast_reader.rb +3 -3
- data/lib/hecks/bluebook/expression/canonical_form.rb +9 -9
- data/lib/hecks/bluebook/expression/evaluator.rb +18 -18
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +18 -18
- data/lib/hecks/bluebook/expression/resolver.rb +60 -62
- data/lib/hecks/bluebook/hexagon.rb +1 -1
- data/lib/hecks/bluebook/lifecycle.rb +1 -1
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -1
- data/lib/hecks/bluebook/meta_validator/judge.rb +101 -87
- data/lib/hecks/bluebook/meta_validator/plan.rb +39 -39
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -2
- data/lib/hecks/bluebook/meta_validator/readings.rb +43 -43
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +44 -37
- data/lib/hecks/bluebook/meta_validator/shapes.rb +25 -21
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +145 -31
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +6 -6
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +5 -5
- data/lib/hecks/bluebook/meta_validator.rb +70 -70
- data/lib/hecks/bluebook/model_check.rb +170 -84
- data/lib/hecks/bluebook/pattern_subset.rb +9 -9
- data/lib/hecks/bluebook/policy.rb +15 -13
- data/lib/hecks/bluebook/process_manager.rb +14 -14
- data/lib/hecks/bluebook/project_register.rb +6 -6
- data/lib/hecks/bluebook/query.rb +4 -4
- data/lib/hecks/bluebook/read_model.rb +14 -14
- data/lib/hecks/bluebook/reference.rb +8 -8
- data/lib/hecks/bluebook/smoke_test.rb +19 -19
- data/lib/hecks/bluebook/synthesizer.rb +12 -12
- data/lib/hecks/bluebook/translation.rb +4 -4
- data/lib/hecks/bluebook/value_object.rb +6 -6
- data/lib/hecks/bluebook.rb +2 -2
- data/lib/hecks/codemod/legacy_dispatch_args.rb +299 -0
- data/lib/hecks/codemod/legacy_dispatch_recorder.rb +186 -0
- data/lib/hecks/codemod.rb +36 -35
- data/lib/hecks/construct.rb +6 -6
- data/lib/hecks/corpus.rb +317 -0
- data/lib/hecks/deprecation.rb +95 -0
- data/lib/hecks/doc/reference.rb +19 -19
- data/lib/hecks/embryonaut_bluebook.rb +11 -11
- data/lib/hecks/facade/cli_door.rb +69 -10
- data/lib/hecks/facade/cli_runner.rb +105 -24
- data/lib/hecks/facade/command_request.rb +23 -0
- data/lib/hecks/facade/handle.rb +79 -32
- data/lib/hecks/facade/json_door.rb +106 -25
- data/lib/hecks/facade/surface/aggregate_door.rb +42 -27
- data/lib/hecks/facade/surface/chapter.rb +26 -17
- data/lib/hecks/facade/surface.rb +16 -3
- data/lib/hecks/facade.rb +15 -4
- data/lib/hecks/forms/app.rb +46 -30
- data/lib/hecks/forms/command_form_renderer.rb +70 -9
- data/lib/hecks/forms/field_renderer.rb +142 -6
- data/lib/hecks/forms/field_shape.rb +180 -19
- data/lib/hecks/forms/html.rb +51 -7
- data/lib/hecks/forms/index_renderer.rb +14 -2
- data/lib/hecks/forms/params.rb +120 -23
- data/lib/hecks/forms/port_argument.rb +2 -2
- data/lib/hecks/forms/query_form_renderer.rb +2 -2
- data/lib/hecks/forms/record_renderer.rb +2 -2
- data/lib/hecks/forms/record_table.rb +1 -1
- data/lib/hecks/forms/value_object_shape.rb +3 -3
- data/lib/hecks/forms.rb +24 -4
- data/lib/hecks/fqn.rb +1 -1
- data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
- data/lib/hecks/framework.rb +48 -17
- data/lib/hecks/freezer.rb +11 -11
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +90 -90
- data/lib/hecks/fuzzing/combination_miner.rb +119 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +140 -45
- data/lib/hecks/fuzzing/coverage_campaign.rb +118 -0
- data/lib/hecks/fuzzing/differential.rb +158 -0
- data/lib/hecks/fuzzing/domain_generator.rb +694 -0
- data/lib/hecks/fuzzing/era_boundary.rb +37 -18
- data/lib/hecks/fuzzing/form_census.rb +35 -20
- data/lib/hecks/fuzzing/generated_domain_check.rb +95 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +6 -6
- data/lib/hecks/fuzzing/isolated_boot.rb +62 -62
- data/lib/hecks/fuzzing/nondeterministic.rb +67 -0
- data/lib/hecks/fuzzing/persistence_parity.rb +19 -21
- data/lib/hecks/fuzzing/properties/corrections.rb +9 -9
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +154 -49
- data/lib/hecks/fuzzing/properties/guards.rb +42 -42
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +32 -32
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +22 -35
- data/lib/hecks/fuzzing/properties/outbox.rb +24 -24
- data/lib/hecks/fuzzing/properties/querying.rb +17 -17
- data/lib/hecks/fuzzing/properties.rb +64 -40
- data/lib/hecks/fuzzing/qa_settings.rb +152 -0
- data/lib/hecks/fuzzing/replay.rb +114 -114
- data/lib/hecks/fuzzing/rotation_priority.rb +10 -10
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +113 -0
- data/lib/hecks/fuzzing/self_consistency.rb +141 -112
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +35 -35
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +18 -11
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +13 -12
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +21 -12
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +58 -31
- data/lib/hecks/fuzzing/sequence_generator.rb +111 -23
- data/lib/hecks/fuzzing/shrinker.rb +197 -0
- data/lib/hecks/fuzzing/structural_skips.rb +23 -130
- data/lib/hecks/fuzzing/sweep_depth.rb +4 -4
- data/lib/hecks/fuzzing/target_capabilities.rb +49 -18
- data/lib/hecks/fuzzing/value_generator.rb +14 -14
- data/lib/hecks/fuzzing.rb +5 -0
- data/lib/hecks/grammar/evolve.rb +10 -10
- data/lib/hecks/grammar.rb +7 -7
- data/lib/hecks/ir.rb +13 -13
- data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
- data/lib/hecks/language/bluebook/policy.bluebook +11 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +348 -13
- data/lib/hecks/language/oidc.json +5 -0
- data/lib/hecks/literal.rb +9 -9
- data/lib/hecks/naming.rb +25 -25
- data/lib/hecks/ports/access_control.rb +58 -2
- data/lib/hecks/ports/agent/answers.rb +83 -6
- data/lib/hecks/ports/agent.rb +119 -35
- data/lib/hecks/ports/authentication.rb +44 -4
- data/lib/hecks/ports/authorization.rb +53 -11
- data/lib/hecks/ports/clock.rb +42 -23
- data/lib/hecks/ports/extraction.rb +16 -0
- data/lib/hecks/ports/identity_assignment.rb +24 -2
- data/lib/hecks/ports/identity_generation.rb +17 -3
- data/lib/hecks/ports/identity_resolution.rb +18 -1
- data/lib/hecks/ports/loading.rb +4 -0
- data/lib/hecks/ports/persistence/append_only.rb +172 -8
- data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
- data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
- data/lib/hecks/ports/persistence/execution.rb +4 -0
- data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
- data/lib/hecks/ports/persistence/plugin.rb +42 -4
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +118 -66
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +28 -5
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +33 -9
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +57 -4
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +314 -90
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +42 -8
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -19
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +5 -5
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
- data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
- data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
- data/lib/hecks/ports/persistence/repository_factory.rb +23 -3
- data/lib/hecks/ports/persistence/state_codec.rb +319 -0
- data/lib/hecks/ports/persistence.rb +36 -1
- data/lib/hecks/ports/projection.rb +61 -7
- data/lib/hecks/ports/query/in_memory.rb +3 -3
- data/lib/hecks/ports/query/ordering.rb +6 -6
- data/lib/hecks/ports/query.rb +35 -0
- data/lib/hecks/projections/bootstrap_table.rb +112 -0
- data/lib/hecks/projections/diagrams.rb +75 -75
- data/lib/hecks/projections/glossary/html.rb +3 -3
- data/lib/hecks/projections/glossary/markdown.rb +3 -3
- data/lib/hecks/projections/glossary/mermaid.rb +1 -1
- data/lib/hecks/projections/glossary/sections.rb +1 -1
- data/lib/hecks/projections/glossary/sentences.rb +5 -5
- data/lib/hecks/projections/glossary.rb +15 -15
- data/lib/hecks/projections/ir.rb +1 -1
- data/lib/hecks/projections/model/deviations.rb +18 -17
- data/lib/hecks/projections/model.rb +25 -21
- data/lib/hecks/projections/oidc.rb +7 -7
- data/lib/hecks/projections/parser_table.rb +5 -5
- data/lib/hecks/projections/reference.rb +3 -3
- data/lib/hecks/projections/rust_vocabulary.rb +443 -0
- data/lib/hecks/projections/shape.rb +2 -2
- data/lib/hecks/projections/statements.rb +11 -11
- data/lib/hecks/projections/vocabulary.rb +9 -9
- data/lib/hecks/projections.rb +5 -3
- data/lib/hecks/projector/cli_projector.rb +29 -29
- data/lib/hecks/projector/docs_projector.rb +13 -13
- data/lib/hecks/projector/exporter.rb +42 -21
- data/lib/hecks/projector/ir_projector.rb +1 -1
- data/lib/hecks/projector/narrate_projector.rb +11 -11
- data/lib/hecks/projector/target.rb +13 -13
- data/lib/hecks/projector.rb +15 -15
- data/lib/hecks/query_ir.rb +47 -47
- data/lib/hecks/query_specification/common/comparators.rb +19 -3
- data/lib/hecks/query_specification/common/comparison.rb +109 -27
- data/lib/hecks/query_specification/common/dsl.rb +65 -9
- data/lib/hecks/query_specification/common/null_policy.rb +57 -13
- data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
- data/lib/hecks/query_specification/common/options.rb +25 -0
- data/lib/hecks/query_specification/field_path.rb +69 -15
- data/lib/hecks/query_specification/hop_path.rb +57 -20
- data/lib/hecks/query_specification/read_model/specification.rb +4 -0
- data/lib/hecks/rendering.rb +3 -3
- data/lib/hecks/router/namespace_installer.rb +3 -3
- data/lib/hecks/router.rb +1 -1
- data/lib/hecks/runtime/aggregate_lock.rb +11 -11
- data/lib/hecks/runtime/boot_gates.rb +3 -3
- data/lib/hecks/runtime/caller.rb +8 -8
- data/lib/hecks/runtime/capability_graph.rb +2 -2
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +22 -25
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +42 -63
- data/lib/hecks/runtime/command_interpreter.rb +159 -102
- data/lib/hecks/runtime/command_rules/admissibility.rb +69 -69
- data/lib/hecks/runtime/command_rules/arithmetic.rb +137 -91
- data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
- data/lib/hecks/runtime/command_rules/emission.rb +18 -1
- data/lib/hecks/runtime/command_rules/references.rb +35 -35
- data/lib/hecks/runtime/command_rules.rb +3 -0
- data/lib/hecks/runtime/dependency_planning.rb +11 -11
- data/lib/hecks/runtime/dispatcher.rb +283 -90
- data/lib/hecks/runtime/entity_element.rb +151 -67
- data/lib/hecks/runtime/entity_interpreter.rb +108 -82
- data/lib/hecks/runtime/errors.rb +19 -19
- data/lib/hecks/runtime/event.rb +6 -6
- data/lib/hecks/runtime/identity.rb +22 -22
- data/lib/hecks/runtime/instance.rb +39 -14
- data/lib/hecks/runtime/interpreting.rb +12 -12
- data/lib/hecks/runtime/invocation.rb +276 -0
- data/lib/hecks/runtime/loader.rb +14 -14
- data/lib/hecks/runtime/outbox.rb +23 -23
- data/lib/hecks/runtime/policy_interpreter.rb +54 -54
- data/lib/hecks/runtime/port_operation_interpreter.rb +22 -19
- data/lib/hecks/runtime/query_interpreter.rb +55 -64
- data/lib/hecks/runtime/reaction_invocation.rb +20 -20
- data/lib/hecks/runtime/read_model_interpreter.rb +40 -40
- data/lib/hecks/runtime/rebuild_sweep.rb +4 -4
- data/lib/hecks/runtime/reference_hop.rb +6 -6
- data/lib/hecks/runtime/refusal_wording.rb +92 -115
- data/lib/hecks/runtime/registry/saga_persistence.rb +21 -21
- data/lib/hecks/runtime/registry/verification.rb +36 -26
- data/lib/hecks/runtime/registry.rb +45 -28
- data/lib/hecks/runtime/remote_dispatcher.rb +38 -23
- data/lib/hecks/runtime/routing.rb +10 -153
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +17 -17
- data/lib/hecks/runtime/saga_interpreter.rb +64 -64
- data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
- data/lib/hecks/runtime/tenant_check.rb +9 -9
- data/lib/hecks/runtime/tenant_scope.rb +5 -5
- data/lib/hecks/runtime/value/admission.rb +75 -30
- data/lib/hecks/runtime/value/coercion.rb +318 -127
- data/lib/hecks/runtime/value/entity_list_coercion.rb +34 -34
- data/lib/hecks/runtime/value.rb +21 -21
- data/lib/hecks/runtime.rb +7 -7
- data/lib/hecks/storehouse.rb +64 -64
- data/lib/hecks/version.rb +3 -3
- data/lib/hecks/vocabulary.rb +205 -4
- data/lib/hecks.rb +13 -11
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +11 -11
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +14 -12
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +11 -11
- metadata +22 -2
data/lib/hecks/storehouse.rb
CHANGED
|
@@ -8,18 +8,18 @@ require_relative "projector"
|
|
|
8
8
|
require_relative "runtime/errors"
|
|
9
9
|
|
|
10
10
|
module Hecks
|
|
11
|
-
#
|
|
11
|
+
# **The bus, not a door** — `docs/hecks-survey-what-we-wish-we-had.md` and
|
|
12
12
|
# `docs/future-features.md` both name the sibling project's own
|
|
13
13
|
# "Storehouse" the single highest-priority gap this repo had: "no
|
|
14
|
-
# per-command tool... the bluebook
|
|
15
|
-
# projects it." This module
|
|
14
|
+
# per-command tool... the bluebook is the contract, the [door] just
|
|
15
|
+
# projects it." This module is that bus, borrowing its name too — the
|
|
16
16
|
# dispatch/query/state/... surface below is a pure function of a
|
|
17
17
|
# `Runtime::Dispatcher` plus plain Ruby arguments, no IO, no protocol
|
|
18
|
-
# awareness. `bin/hecks_mcp_door` is
|
|
18
|
+
# awareness. `bin/hecks_mcp_door` is one projection of it — MCP over
|
|
19
19
|
# stdio — not the whole thing; a plain CLI door, an HTTP door, a second
|
|
20
20
|
# transport of any shape, would sit beside it on the exact same bus,
|
|
21
21
|
# sharing the same audit log and caller-identity handling, without
|
|
22
|
-
# ever needing to speak MCP. (This module used to
|
|
22
|
+
# ever needing to speak MCP. (This module used to be named `McpDoor`
|
|
23
23
|
# and live under `Facade` — that conflated the bus with its one built
|
|
24
24
|
# transport; this file is the rename, not a rewrite.)
|
|
25
25
|
#
|
|
@@ -30,21 +30,21 @@ module Hecks
|
|
|
30
30
|
# dispatch — issue a command (dry_run: preview, steps: batch)
|
|
31
31
|
# query — ask a question
|
|
32
32
|
# state — read what is actually stored, no verb involved
|
|
33
|
-
# events — what
|
|
34
|
-
# current state; events
|
|
33
|
+
# events — what happened, with payloads, to one record — not just its
|
|
34
|
+
# current state; events this bus witnessed, sourced from
|
|
35
35
|
# its own audit log, not a full event-sourcing replay
|
|
36
36
|
# history — the full append-only journal, not just current state
|
|
37
37
|
# behaviors — run a domain's hand-curated `.behaviors` examples
|
|
38
38
|
# follow — tail this bus's own dispatch/query/state audit log
|
|
39
39
|
#
|
|
40
|
-
# `dispatch`/`query`/`state`
|
|
40
|
+
# `dispatch`/`query`/`state` each take a `summary` — the survey's "every
|
|
41
41
|
# audit row carries human intent for free" — and `dispatch`/`query`
|
|
42
42
|
# additionally take an optional `source:` (`SOURCE_TAGS`, the survey's
|
|
43
|
-
# `SourceTag`: who is calling)
|
|
43
|
+
# `SourceTag`: who is calling) and an optional `role:`/`actor_id:` —
|
|
44
44
|
# a real caller identity, bound for the call's duration via `Hecks.
|
|
45
45
|
# as_caller`, checked against a `role`-gated command's own declared
|
|
46
46
|
# role (`CommandRules::Authorization`) rather than merely documented by
|
|
47
|
-
# `describe`. `dispatch`
|
|
47
|
+
# `describe`. `dispatch` requires it for any command that declares a
|
|
48
48
|
# role — `require_caller_for_role_gated!` refuses an unbound dispatch
|
|
49
49
|
# against one rather than silently running it unchecked; `query`'s own
|
|
50
50
|
# authorization runs on a separate mechanism (`Runtime::TenantScope`)
|
|
@@ -54,21 +54,21 @@ module Hecks
|
|
|
54
54
|
# header for what that does and does not guarantee. Every call through
|
|
55
55
|
# those three, plus a dry run, is appended to a per-domain JSONL audit
|
|
56
56
|
# log (`record!`) `follow` tails back — a record of what the caller
|
|
57
|
-
#
|
|
57
|
+
# said it was, not independently verified identity. `catalog`/
|
|
58
58
|
# `describe`/`validate`/`domains`/`history`/`behaviors`/`events` need
|
|
59
59
|
# neither — they change nothing and commit nothing to any log.
|
|
60
60
|
#
|
|
61
|
-
# A
|
|
61
|
+
# A caller hands in an already-booted `runtime`, the same division of
|
|
62
62
|
# labor `Facade::CliRunner` already keeps against `bin/run`: booting a
|
|
63
63
|
# domain from a path is IO the calling `bin/` script owns, this stays a
|
|
64
64
|
# pure function of a `Runtime::Dispatcher` plus plain Ruby arguments —
|
|
65
65
|
# no different from `CliRunner.call(runtime:, argv:)` itself.
|
|
66
66
|
# `validate` and `domains` are the two exceptions: `validate`'s whole
|
|
67
67
|
# job is to attempt the boot and report whether it survived, so it
|
|
68
|
-
# takes the domain
|
|
68
|
+
# takes the domain path instead and boots it; `domains` has no
|
|
69
69
|
# domain to be handed one of yet, that's what it's answering.
|
|
70
70
|
#
|
|
71
|
-
#
|
|
71
|
+
# No new vocabulary otherwise. Every method here composes doors that
|
|
72
72
|
# already exist — `Projector.call(:cli, ...)` for verb/question alias
|
|
73
73
|
# resolution (the identical table `CliRunner` itself resolves against),
|
|
74
74
|
# `Projector.call(:docs, ...)` for `describe`, `Facade::JsonDoor` for
|
|
@@ -82,21 +82,21 @@ module Hecks
|
|
|
82
82
|
module Storehouse
|
|
83
83
|
module_function
|
|
84
84
|
|
|
85
|
-
#
|
|
86
|
-
# `SourceTag` names —
|
|
85
|
+
# The same closed set `docs/hecks-survey-what-we-wish-we-had.md`'s
|
|
86
|
+
# `SourceTag` names — who dispatched, not what. Optional: a caller
|
|
87
87
|
# that omits it gets `source: nil` recorded, honestly, rather than a
|
|
88
88
|
# guessed default.
|
|
89
89
|
SOURCE_TAGS = %w[process-manager operator hook sidequest-agent cascade daemon].freeze
|
|
90
90
|
|
|
91
|
-
#
|
|
91
|
+
# **The bus's own audit trail** — a JSONL file per domain, one line per
|
|
92
92
|
# `dispatch`/`query`/`state`/dry-run call, `follow` tails it back.
|
|
93
|
-
# `tmp/`, not the domain's own directory: this is the
|
|
93
|
+
# `tmp/`, not the domain's own directory: this is the bus's record
|
|
94
94
|
# of what was asked of it, not part of the domain's own persisted
|
|
95
95
|
# state, and `tmp/` is already gitignored for exactly this kind of
|
|
96
96
|
# local, disposable-but-useful-while-it-lasts file.
|
|
97
97
|
LOG_ROOT = File.expand_path("../../tmp/storehouse", __dir__)
|
|
98
98
|
|
|
99
|
-
#
|
|
99
|
+
# The root every `domain:`/`under:` must resolve under — the project
|
|
100
100
|
# directory by default, `HECKS_STOREHOUSE_ROOT` to widen or move it.
|
|
101
101
|
# `Hecks.boot` `Kernel.load`s the `.hecksagon`/`.bluebook`/`.world`
|
|
102
102
|
# files a domain path resolves to, and those are Ruby, not a data
|
|
@@ -108,7 +108,7 @@ module Hecks
|
|
|
108
108
|
# project tree.
|
|
109
109
|
BOOT_ROOT = File.expand_path(ENV["HECKS_STOREHOUSE_ROOT"] || File.expand_path("../..", __dir__))
|
|
110
110
|
|
|
111
|
-
#
|
|
111
|
+
# **Refused, not silently clamped** — a path outside `BOOT_ROOT` is either
|
|
112
112
|
# an honest mistake (a relative path typed against the wrong cwd) or
|
|
113
113
|
# the exact thing this check exists to catch, and both deserve the
|
|
114
114
|
# same clear refusal rather than a silent rewrite to something the
|
|
@@ -124,7 +124,7 @@ module Hecks
|
|
|
124
124
|
|
|
125
125
|
# ── shared resolution helpers ────────────────────────────────────
|
|
126
126
|
|
|
127
|
-
#
|
|
127
|
+
# The one bluebook a domain directory boots. Every `bin/*` script that
|
|
128
128
|
# projects a whole-domain CLI or doc set makes this same assumption
|
|
129
129
|
# (`Facade::CliRunner#call`'s own `bluebook = runtime.registry.
|
|
130
130
|
# bluebooks.values.first`) — one `.hecksagon` names one chapter.
|
|
@@ -139,7 +139,7 @@ module Hecks
|
|
|
139
139
|
"known: #{bluebook.aggregates.map(&:hecks_name).sort.join(', ')}"
|
|
140
140
|
end
|
|
141
141
|
|
|
142
|
-
#
|
|
142
|
+
# The same alias table `CliRunner` resolves a typed word against — a
|
|
143
143
|
# short name when it's unambiguous, the qualified `Aggregate.Verb`
|
|
144
144
|
# form always. Shared here so `dispatch` and `query` (and their error
|
|
145
145
|
# messages) never drift from what a human typing `bin/run` sees.
|
|
@@ -166,7 +166,7 @@ module Hecks
|
|
|
166
166
|
raise Runtime::TypeMismatch, "source: #{source.inspect} is not one of #{SOURCE_TAGS.join(', ')}"
|
|
167
167
|
end
|
|
168
168
|
|
|
169
|
-
# `actor_id`
|
|
169
|
+
# `actor_id` names who, `role` names what they hold — `Hecks.
|
|
170
170
|
# as_caller` requires the latter always, the former is additive
|
|
171
171
|
# (`Runtime::Caller::Current`'s own shape). An `actor_id` with no
|
|
172
172
|
# `role` would silently do nothing rather than bind a real caller,
|
|
@@ -178,18 +178,18 @@ module Hecks
|
|
|
178
178
|
raise Runtime::TypeMismatch, "actor_id: requires role: too — a caller names WHO through WHICH role they hold"
|
|
179
179
|
end
|
|
180
180
|
|
|
181
|
-
#
|
|
181
|
+
# Bound for the duration of one call, then gone — `Hecks.as_caller`
|
|
182
182
|
# is itself a `Thread.current`-scoped `ensure`-guarded block, so
|
|
183
183
|
# nothing here needs its own cleanup. `role: nil` yields unbound —
|
|
184
184
|
# for `query`, exactly as before: `CommandRules::Authorization#
|
|
185
|
-
# refuse_role_mismatch` is
|
|
185
|
+
# refuse_role_mismatch` is opt-in on the domain side (`return unless
|
|
186
186
|
# caller`), and query authorization runs on a wholly separate
|
|
187
187
|
# mechanism (`authorize policy, tenant: :field`, checked against an
|
|
188
188
|
# explicit `tenant:` argument — see `Runtime::TenantScope`), so
|
|
189
|
-
# binding a caller around a query has no effect on it
|
|
189
|
+
# binding a caller around a query has no effect on it today; it is
|
|
190
190
|
# still accepted here, for symmetry and for the audit log, against
|
|
191
191
|
# the day a read model does check `Caller.current`. For `dispatch`,
|
|
192
|
-
# `require_caller_for_role_gated!` (below) now refuses
|
|
192
|
+
# `require_caller_for_role_gated!` (below) now refuses before this
|
|
193
193
|
# is ever reached when the command declares a role and no caller is
|
|
194
194
|
# bound — so an unbound `dispatch` here means either the command
|
|
195
195
|
# declares no role at all, or a caller-side check let it through.
|
|
@@ -199,14 +199,14 @@ module Hecks
|
|
|
199
199
|
Hecks.as_caller(role: role, actor_id: actor_id, &block)
|
|
200
200
|
end
|
|
201
201
|
|
|
202
|
-
#
|
|
203
|
-
# Governance RBAC work fixed
|
|
202
|
+
# The fail-open half `with_caller` itself cannot close — ADR 0025's
|
|
203
|
+
# Governance RBAC work fixed what a *bound* role is checked against
|
|
204
204
|
# (a live `Governance::RoleAssignment` lookup instead of a bare
|
|
205
205
|
# string match), but changed nothing about a caller who binds no
|
|
206
206
|
# role at all: `refuse_role_mismatch` `return`s immediately when
|
|
207
207
|
# `Caller.current` is nil, so a bus caller who simply omits `role:`
|
|
208
208
|
# sails past a role-gated command unchecked, not denied. That is a
|
|
209
|
-
# property of
|
|
209
|
+
# property of this bus choosing to dispatch unbound, not of the
|
|
210
210
|
# domain rule — `bin/run`, the human CLI, has no such gap because a
|
|
211
211
|
# human always dispatches through a real `Hecks.as_caller` binding
|
|
212
212
|
# upstream of it. Refusing here, before `with_caller`/`dispatch` are
|
|
@@ -220,17 +220,17 @@ module Hecks
|
|
|
220
220
|
"(role:/actor_id:) is bound; dispatching it unbound is refused, not silently unchecked"
|
|
221
221
|
end
|
|
222
222
|
|
|
223
|
-
# `dry_run?` (Runtime::Dispatcher) understands only the
|
|
223
|
+
# `dry_run?` (Runtime::Dispatcher) understands only the old flat
|
|
224
224
|
# legacy args shape — no to:/with: envelope, `route:` never passed
|
|
225
225
|
# (its own header explains why: built directly against
|
|
226
226
|
# CommandInterpreter/EntityInterpreter's pre-envelope contract,
|
|
227
227
|
# never updated because nothing else needed it to be — a real
|
|
228
228
|
# record of history, not a defect this bus should paper over
|
|
229
229
|
# silently). `Facade::CommandRequest`/`spec[:legacy_receiver]`
|
|
230
|
-
# already know how to
|
|
230
|
+
# already know how to name that same flat shape for every receiver
|
|
231
231
|
# kind (a bare id under one string key for :aggregate, a
|
|
232
232
|
# {aggregate:, entity:} pair of keys for :entity) — this is the one
|
|
233
|
-
# door back
|
|
233
|
+
# door back into it.
|
|
234
234
|
def flatten_legacy(envelope, receiver, legacy_receiver)
|
|
235
235
|
facts = envelope[:with] || {}
|
|
236
236
|
return facts unless envelope.key?(:to)
|
|
@@ -251,8 +251,8 @@ module Hecks
|
|
|
251
251
|
File.join(LOG_ROOT, "#{domain_name.to_s.gsub(/[^A-Za-z0-9_-]/, '_')}.jsonl")
|
|
252
252
|
end
|
|
253
253
|
|
|
254
|
-
#
|
|
255
|
-
#
|
|
254
|
+
# Never fails a real call because its own audit log couldn't be
|
|
255
|
+
# written — a full disk or a permissions problem is a `follow`
|
|
256
256
|
# feature going dark, not a reason to refuse the dispatch/query/
|
|
257
257
|
# state call that was actually asked for.
|
|
258
258
|
def record!(domain_name, tool:, summary:, source:, outcome:, verb: nil, role: nil, actor_id: nil)
|
|
@@ -269,9 +269,9 @@ module Hecks
|
|
|
269
269
|
|
|
270
270
|
# ── the three that drive it ────────────────────────────────────────
|
|
271
271
|
|
|
272
|
-
# `dry_run: true`
|
|
272
|
+
# `dry_run: true` answers a different question than a real dispatch
|
|
273
273
|
# does — "would this succeed", not "here is what happened" — so a
|
|
274
|
-
# domain refusal is the legitimate, complete
|
|
274
|
+
# domain refusal is the legitimate, complete answer (`ok: true,
|
|
275
275
|
# would_succeed: false`), not a failed call. A malformed request
|
|
276
276
|
# (unknown command, a bad args shape) is still a failed call
|
|
277
277
|
# (`ok: false`) either way — it never reached the domain to be asked.
|
|
@@ -308,7 +308,7 @@ module Hecks
|
|
|
308
308
|
end
|
|
309
309
|
|
|
310
310
|
def real_dispatch(runtime, spec, envelope, summary)
|
|
311
|
-
result = runtime.
|
|
311
|
+
result = runtime.dispatch_flat(spec[:verb], envelope)
|
|
312
312
|
ok(summary: summary,
|
|
313
313
|
id: result.id,
|
|
314
314
|
state: result.state.nil? ? nil : Facade::JsonDoor.materialize(result.state),
|
|
@@ -323,8 +323,8 @@ module Hecks
|
|
|
323
323
|
ok(summary: summary, would_succeed: false, error: e.message)
|
|
324
324
|
end
|
|
325
325
|
|
|
326
|
-
#
|
|
327
|
-
# shape, so an agent issuing a known
|
|
326
|
+
# **One call, many steps** — the survey's own `bin/run <domain> script`
|
|
327
|
+
# shape, so an agent issuing a known sequence of commands (open an
|
|
328
328
|
# account, then fund it) pays one round trip instead of N. Every step
|
|
329
329
|
# goes through `dispatch` itself — same resolution, same audit log
|
|
330
330
|
# line per step, same summary/source stamped on all of them since
|
|
@@ -370,11 +370,11 @@ module Hecks
|
|
|
370
370
|
ok(summary: summary, rows: rows.map { |row| Facade::JsonDoor.materialize(row) }).merge(verb: spec[:verb])
|
|
371
371
|
end
|
|
372
372
|
|
|
373
|
-
#
|
|
373
|
+
# **What is actually stored** — no verb, no interpretation, the repository
|
|
374
374
|
# itself. `id:` given answers one record (`NotFound` when it names
|
|
375
375
|
# nothing); omitted answers every record the aggregate currently
|
|
376
376
|
# holds. This is the difference `query` can't cover: a query answers a
|
|
377
|
-
#
|
|
377
|
+
# declared question, and an aggregate that never declared "list
|
|
378
378
|
# everything" has no query this could reuse.
|
|
379
379
|
def state(runtime:, aggregate:, summary:, id: nil, source: nil)
|
|
380
380
|
bluebook = bluebook_for(runtime)
|
|
@@ -405,7 +405,7 @@ module Hecks
|
|
|
405
405
|
|
|
406
406
|
# ── the four zoom levels ─────────────────────────────────────────
|
|
407
407
|
|
|
408
|
-
#
|
|
408
|
+
# **Zoom level zero** — every domain directory a root actually holds,
|
|
409
409
|
# discovered rather than typed from memory. Every other tool takes
|
|
410
410
|
# `domain:` as a directory it assumes the caller already knows; this
|
|
411
411
|
# is how a caller who doesn't finds out. `Adapters::Folder#domain?`
|
|
@@ -422,7 +422,7 @@ module Hecks
|
|
|
422
422
|
ok(under: under, domains: found.map { |name| File.join(under, name) })
|
|
423
423
|
end
|
|
424
424
|
|
|
425
|
-
#
|
|
425
|
+
# **Zoom level one** — every aggregate this domain declares, and every
|
|
426
426
|
# command/query name each answers to, snake_cased exactly as
|
|
427
427
|
# `dispatch`/`query` want it. Enough to pick a target; `describe` is
|
|
428
428
|
# the next level down for what one of them actually takes.
|
|
@@ -439,7 +439,7 @@ module Hecks
|
|
|
439
439
|
refused(e)
|
|
440
440
|
end
|
|
441
441
|
|
|
442
|
-
#
|
|
442
|
+
# **Zoom level two** — the exact same usage document a human gets from
|
|
443
443
|
# `bin/docs <domain> [aggregate]` (`Projector::DocsProjector`, the
|
|
444
444
|
# identical projection `Surface::AggregateDoor#docs` calls one door
|
|
445
445
|
# over): every command's arguments, the states it may be issued
|
|
@@ -454,19 +454,19 @@ module Hecks
|
|
|
454
454
|
refused(e)
|
|
455
455
|
end
|
|
456
456
|
|
|
457
|
-
#
|
|
457
|
+
# **Zoom level three** — is the wiring sound at all: every bind names a
|
|
458
458
|
# declared aggregate, every adapter satisfies the port it claims, the
|
|
459
459
|
# default adapter is usable. `Registry#verify!` (`runtime/registry/
|
|
460
460
|
# verification.rb`) is the one place this repo already answers that
|
|
461
461
|
# question, and `Runtime::Loader.boot` already calls it as the last
|
|
462
|
-
# step of every boot — so
|
|
462
|
+
# step of every boot — so this is the one method here that boots for
|
|
463
463
|
# itself rather than taking a `runtime:` already in hand, because a
|
|
464
464
|
# runtime that successfully reached this line already answered the
|
|
465
|
-
# question. Given a domain
|
|
465
|
+
# question. Given a domain path, not a booted runtime, deliberately:
|
|
466
466
|
# asking "is this valid" about a domain that failed to boot at all
|
|
467
467
|
# has to be askable without a runtime to hand it.
|
|
468
468
|
#
|
|
469
|
-
# `deep: true`
|
|
469
|
+
# `deep: true` goes past wiring into logic — `Bluebook::ModelCheck`,
|
|
470
470
|
# the lightweight-formal-methods leg (dead lifecycle transitions, a
|
|
471
471
|
# saga state no handler chain reaches, a dispatch to nowhere). Opt-in
|
|
472
472
|
# and separate from the base check on purpose: a wiring defect is
|
|
@@ -474,7 +474,7 @@ module Hecks
|
|
|
474
474
|
# part of it can never fire" — two different questions, and the
|
|
475
475
|
# first is far cheaper to ask on every boot.
|
|
476
476
|
#
|
|
477
|
-
#
|
|
477
|
+
# Any boot failure answers the question, not only `WiringError` — a
|
|
478
478
|
# domain path with no `.hecksagon`, a malformed bluebook, is just as
|
|
479
479
|
# much "not valid" as a real wiring mismatch, and this tool exists
|
|
480
480
|
# precisely so none of those ever cross a projection of this bus as
|
|
@@ -496,7 +496,7 @@ module Hecks
|
|
|
496
496
|
|
|
497
497
|
# ── beyond the zoom levels ──────────────────────────────────────────
|
|
498
498
|
|
|
499
|
-
#
|
|
499
|
+
# The full write history, not just the current head — `bin/history`'s
|
|
500
500
|
# own logic, unchanged: an append-only-backed aggregate's `entries`,
|
|
501
501
|
# every operation that ever touched it. An aggregate bound to a
|
|
502
502
|
# non-append-only adapter (Memory, Postgres proper) answers an empty
|
|
@@ -519,11 +519,11 @@ module Hecks
|
|
|
519
519
|
repository.entries.map { |entry| { operation: entry.operation, id: entry.id, state: Facade::JsonDoor.materialize(entry.state) } }
|
|
520
520
|
end
|
|
521
521
|
|
|
522
|
-
# `.behaviors`
|
|
522
|
+
# `.behaviors` files, run and reported — hand-curated examples of how
|
|
523
523
|
# to use a domain, in domain vocabulary (`docs/guides/behaviors.md`),
|
|
524
524
|
# the survey's own "honest-refusal", generated-example-suite items.
|
|
525
525
|
# `Hecks::Behaviors` boots each test fresh through `Hecks.boot_files`
|
|
526
|
-
# itself — `target:` names a `.behaviors` file
|
|
526
|
+
# itself — `target:` names a `.behaviors` file or a directory to
|
|
527
527
|
# sweep, never a `runtime:`, the one other method here besides
|
|
528
528
|
# `validate` that takes a path instead.
|
|
529
529
|
def behaviors(target:)
|
|
@@ -547,7 +547,7 @@ module Hecks
|
|
|
547
547
|
runs: Array(result.runs).map { |run| { description: run.description, status: run.status, message: run.message } } }
|
|
548
548
|
end
|
|
549
549
|
|
|
550
|
-
# A
|
|
550
|
+
# A live tail without a live process — `bin/hecks_mcp_door` (its
|
|
551
551
|
# transport of MCP-over-stdio) answers one request at a time, no push
|
|
552
552
|
# channel to a client that only ever asks. This is the honest version
|
|
553
553
|
# of the survey's `storehouse follow` for that shape: not a
|
|
@@ -571,17 +571,17 @@ module Hecks
|
|
|
571
571
|
File.readlines(path).map { |line| JSON.parse(line, symbolize_names: true) }
|
|
572
572
|
end
|
|
573
573
|
|
|
574
|
-
#
|
|
575
|
-
# (what's stored
|
|
576
|
-
#
|
|
574
|
+
# What actually happened, with payloads — distinct from `state`
|
|
575
|
+
# (what's stored now) and `history` (append-only operation
|
|
576
|
+
# snapshots, no payload). Not a domain-wide event-sourcing replay:
|
|
577
577
|
# "one boot per call" means `runtime.events` is always empty except
|
|
578
578
|
# during the very call that populated it, discarded the moment that
|
|
579
579
|
# call returns — there is no cross-call in-memory log to read here.
|
|
580
|
-
# So this reads the
|
|
580
|
+
# So this reads the same durable audit log `follow` already tails
|
|
581
581
|
# (`record!` now stamps a successful dispatch's own announced
|
|
582
582
|
# events onto its log line), reshaped: `follow` answers "what was
|
|
583
|
-
#
|
|
584
|
-
# to one aggregate/record" — events
|
|
583
|
+
# called, in order, across every tool"; this answers "what happened
|
|
584
|
+
# to one aggregate/record" — events this bus witnessed, which is
|
|
585
585
|
# every real dispatch ever routed through it, but no more than that.
|
|
586
586
|
# `aggregate:` narrows to one aggregate; `id:` (requires
|
|
587
587
|
# `aggregate:` — an id alone is not unique across aggregates)
|
|
@@ -616,19 +616,19 @@ module Hecks
|
|
|
616
616
|
|
|
617
617
|
# ── shared shape ────────────────────────────────────────────────────
|
|
618
618
|
|
|
619
|
-
# `Runtime::WiringError`
|
|
620
|
-
# refusals — not because it
|
|
619
|
+
# `Runtime::WiringError` belongs here too, alongside the true domain
|
|
620
|
+
# refusals — not because it is one (it's a structural defect, not a
|
|
621
621
|
# rule the caller broke), but because "this domain isn't wired to
|
|
622
622
|
# answer what you're asking" (a `role:`+`actor_id:` caller reaching
|
|
623
623
|
# an authorization port nothing implements, a dry run against a
|
|
624
624
|
# port verb) is exactly the shape this bus promises never crashes
|
|
625
625
|
# through it. `dry_run_outcome` already treats it this way locally;
|
|
626
|
-
# this makes every
|
|
626
|
+
# this makes every other caller of `refusal_classes` do the same.
|
|
627
627
|
def refusal_classes = [Runtime::NotFound, Runtime::TypeMismatch, Runtime::WiringError, *Runtime::DOMAIN_REFUSALS]
|
|
628
628
|
|
|
629
629
|
def ok(**fields) = { ok: true }.merge(fields)
|
|
630
630
|
|
|
631
|
-
#
|
|
631
|
+
# **An honest refusal, not a crash** — the survey's own item #9: "an
|
|
632
632
|
# explicit, structured refusal a caller can act on" rather than a
|
|
633
633
|
# stack trace an agent has to parse to find the one line that
|
|
634
634
|
# mattered. The domain's own refusal text travels verbatim
|
data/lib/hecks/version.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Hecks
|
|
2
|
-
#
|
|
2
|
+
# **Isolated, on purpose** — the gemspec reads this file directly rather
|
|
3
3
|
# than `require_relative "lib/hecks"` (the whole framework),
|
|
4
|
-
# specifically so evaluating the gemspec never triggers
|
|
4
|
+
# specifically so evaluating the gemspec never triggers any of this
|
|
5
5
|
# gem's own dependencies (prism among them) before Bundler has even
|
|
6
6
|
# resolved what to install. A real, live chicken-and-egg bug caught
|
|
7
7
|
# deploying to a Ruby version that doesn't bundle prism for free
|
|
@@ -12,5 +12,5 @@ module Hecks
|
|
|
12
12
|
# it there, or even in the consuming Gemfile, never closed the gap,
|
|
13
13
|
# because gemspec evaluation happens before anything Bundler
|
|
14
14
|
# resolves is actually loadable yet.
|
|
15
|
-
VERSION = "1.
|
|
15
|
+
VERSION = "1.4.0".freeze
|
|
16
16
|
end
|