hecks 1.4.0 → 1.5.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 +65 -0
- data/lib/hecks/adapters/driven/folder.rb +73 -0
- data/lib/hecks/adapters/driven/google_authentication.rb +25 -4
- data/lib/hecks/adapters/driven/governance_authorization.rb +57 -6
- data/lib/hecks/adapters/driven/heki/journal.rb +8 -0
- data/lib/hecks/adapters/driven/heki/saga_store.rb +53 -7
- data/lib/hecks/adapters/driven/heki/snapshot.rb +1 -1
- data/lib/hecks/adapters/driven/heki.rb +93 -2
- data/lib/hecks/adapters/driven/identity_registry.rb +10 -0
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +22 -0
- data/lib/hecks/adapters/driven/in_process_key_vault.adapter +3 -0
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +53 -0
- data/lib/hecks/adapters/driven/lambda/client.rb +35 -7
- data/lib/hecks/adapters/driven/lambda.rb +43 -2
- data/lib/hecks/adapters/driven/local_storage.rb +67 -1
- data/lib/hecks/adapters/driven/memory.rb +13 -13
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +20 -0
- data/lib/hecks/adapters/driven/prism.rb +46 -7
- data/lib/hecks/adapters/driven/secure_random_identity.rb +3 -0
- data/lib/hecks/adapters/driven/system_clock.rb +3 -0
- data/lib/hecks/adapters/driven/tenant_provisioner.adapter +3 -0
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +66 -0
- data/lib/hecks/adapters/driven.rb +2 -0
- data/lib/hecks/adapters/driving/github_webhook.rb +14 -1
- data/lib/hecks/behaviors/dsl.rb +58 -0
- data/lib/hecks/behaviors/expectations.rb +168 -7
- data/lib/hecks/behaviors/ir.rb +11 -0
- data/lib/hecks/behaviors/rspec.rb +8 -0
- data/lib/hecks/behaviors/runner.rb +19 -0
- data/lib/hecks/behaviors.rb +8 -0
- data/lib/hecks/bluebook/aggregate.rb +30 -0
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +7 -0
- data/lib/hecks/bluebook/assembly/build.rb +25 -4
- data/lib/hecks/bluebook/assembly/contract.rb +66 -14
- data/lib/hecks/bluebook/assembly/contracts.rb +24 -19
- data/lib/hecks/bluebook/assembly/marks.rb +143 -14
- data/lib/hecks/bluebook/assembly/specializer.rb +19 -0
- data/lib/hecks/bluebook/assembly.rb +25 -7
- data/lib/hecks/bluebook/attribute.rb +17 -3
- data/lib/hecks/bluebook/behaviour/aggregate.rb +32 -0
- data/lib/hecks/bluebook/behaviour/attribute.rb +13 -0
- data/lib/hecks/bluebook/behaviour/chapter.rb +55 -2
- data/lib/hecks/bluebook/behaviour/command.rb +33 -3
- data/lib/hecks/bluebook/behaviour/domain_port.rb +20 -0
- data/lib/hecks/bluebook/behaviour/entity.rb +12 -0
- data/lib/hecks/bluebook/behaviour/hexagon.rb +26 -0
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +23 -2
- data/lib/hecks/bluebook/behaviour/policy.rb +34 -9
- data/lib/hecks/bluebook/behaviour/process_manager.rb +32 -1
- data/lib/hecks/bluebook/behaviour/query.rb +5 -0
- data/lib/hecks/bluebook/behaviour/read_model.rb +21 -0
- data/lib/hecks/bluebook/behaviour/traits.rb +36 -0
- data/lib/hecks/bluebook/behaviour/value_object.rb +17 -5
- data/lib/hecks/bluebook/chapter.rb +23 -0
- data/lib/hecks/bluebook/command.rb +53 -8
- data/lib/hecks/bluebook/domain_port.rb +25 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +196 -37
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +79 -42
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +59 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +385 -53
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +135 -21
- data/lib/hecks/bluebook/dsl/command_builder.rb +189 -27
- data/lib/hecks/bluebook/dsl/entity_builder.rb +139 -9
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +41 -0
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +23 -6
- data/lib/hecks/bluebook/dsl/policy_builder.rb +60 -19
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +79 -15
- data/lib/hecks/bluebook/dsl/query_builder.rb +33 -4
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +102 -27
- data/lib/hecks/bluebook/dsl/rule_reference.rb +60 -8
- data/lib/hecks/bluebook/dsl/translation_builder.rb +146 -40
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +55 -7
- data/lib/hecks/bluebook/entity.rb +29 -0
- data/lib/hecks/bluebook/expression/ast_json.rb +115 -23
- data/lib/hecks/bluebook/expression/ast_reader.rb +29 -0
- data/lib/hecks/bluebook/expression/canonical_form.rb +48 -9
- data/lib/hecks/bluebook/expression/evaluator.rb +207 -29
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +36 -0
- data/lib/hecks/bluebook/expression/resolver.rb +320 -77
- data/lib/hecks/bluebook/hexagon.rb +34 -0
- data/lib/hecks/bluebook/lifecycle.rb +11 -0
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -0
- data/lib/hecks/bluebook/meta_validator/judge.rb +30 -26
- data/lib/hecks/bluebook/meta_validator/plan.rb +43 -8
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +1 -0
- data/lib/hecks/bluebook/meta_validator/readings.rb +157 -7
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -4
- data/lib/hecks/bluebook/meta_validator/shapes.rb +141 -0
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +149 -23
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +5 -4
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +1 -0
- data/lib/hecks/bluebook/meta_validator.rb +180 -84
- data/lib/hecks/bluebook/model_check.rb +268 -24
- data/lib/hecks/bluebook/pattern_subset.rb +23 -1
- data/lib/hecks/bluebook/process_manager.rb +13 -0
- data/lib/hecks/bluebook/project_discovery.rb +5 -0
- data/lib/hecks/bluebook/project_loader.rb +40 -0
- data/lib/hecks/bluebook/project_register.rb +44 -0
- data/lib/hecks/bluebook/query.rb +27 -0
- data/lib/hecks/bluebook/read_model.rb +21 -1
- data/lib/hecks/bluebook/reference.rb +21 -8
- data/lib/hecks/bluebook/smoke_test.rb +29 -6
- data/lib/hecks/bluebook/synthesizer.rb +34 -0
- data/lib/hecks/bluebook/translation.rb +30 -1
- data/lib/hecks/bluebook/value_object.rb +23 -5
- data/lib/hecks/bluebook.rb +3 -4
- data/lib/hecks/codemod.rb +107 -20
- data/lib/hecks/construct.rb +15 -1
- data/lib/hecks/corpus.rb +146 -25
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
- data/lib/hecks/doc/reference.rb +185 -16
- data/lib/hecks/embryonaut_bluebook.rb +32 -9
- data/lib/hecks/facade/handle.rb +76 -3
- data/lib/hecks/facade/surface/aggregate_door.rb +8 -0
- data/lib/hecks/forms/field_shape.rb +3 -0
- data/lib/hecks/forms/page.rb +14 -0
- data/lib/hecks/forms/port_argument.rb +12 -0
- data/lib/hecks/forms/query_form_renderer.rb +63 -0
- data/lib/hecks/forms/record_renderer.rb +58 -0
- data/lib/hecks/forms/record_table.rb +27 -0
- data/lib/hecks/forms/reference_options.rb +24 -0
- data/lib/hecks/forms/value_object_shape.rb +10 -0
- data/lib/hecks/fqn.rb +58 -0
- data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
- data/lib/hecks/framework/oidc.json +15 -0
- data/lib/hecks/framework.rb +43 -20
- data/lib/hecks/freezer.rb +17 -1
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +159 -24
- data/lib/hecks/fuzzing/combination_miner.rb +59 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +109 -8
- data/lib/hecks/fuzzing/coverage_campaign.rb +56 -13
- data/lib/hecks/fuzzing/differential.rb +34 -0
- data/lib/hecks/fuzzing/domain_generator.rb +188 -11
- data/lib/hecks/fuzzing/era_boundary.rb +45 -15
- data/lib/hecks/fuzzing/form_census.rb +86 -0
- data/lib/hecks/fuzzing/generated_domain_check.rb +76 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +39 -0
- data/lib/hecks/fuzzing/isolated_boot.rb +79 -22
- data/lib/hecks/fuzzing/nondeterministic.rb +13 -1
- data/lib/hecks/fuzzing/persistence_parity.rb +95 -3
- data/lib/hecks/fuzzing/properties/corrections.rb +25 -0
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +158 -14
- data/lib/hecks/fuzzing/properties/guards.rb +44 -0
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +48 -0
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +18 -0
- data/lib/hecks/fuzzing/properties/outbox.rb +49 -11
- data/lib/hecks/fuzzing/properties/querying.rb +68 -14
- data/lib/hecks/fuzzing/properties.rb +24 -15
- data/lib/hecks/fuzzing/qa_settings.rb +12 -0
- data/lib/hecks/fuzzing/replay.rb +137 -29
- data/lib/hecks/fuzzing/rotation_priority.rb +41 -21
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +46 -20
- data/lib/hecks/fuzzing/self_consistency.rb +189 -40
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +12 -6
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +1 -1
- data/lib/hecks/fuzzing/sequence_generator.rb +47 -0
- data/lib/hecks/fuzzing/shrinker.rb +114 -2
- data/lib/hecks/fuzzing/structural_skips.rb +18 -4
- data/lib/hecks/fuzzing/sweep_depth.rb +8 -0
- data/lib/hecks/fuzzing/target_capabilities.rb +61 -14
- data/lib/hecks/fuzzing/value_generator.rb +98 -10
- data/lib/hecks/grammar/evolve.rb +178 -2
- data/lib/hecks/grammar.rb +46 -0
- data/lib/hecks/ir.rb +38 -7
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +11 -0
- data/lib/hecks/literal.rb +32 -0
- data/lib/hecks/naming.rb +88 -7
- data/lib/hecks/ports/access_control.rb +5 -10
- data/lib/hecks/ports/authorization.rb +3 -6
- data/lib/hecks/ports/identity_assignment.rb +1 -2
- data/lib/hecks/ports/identity_resolution.rb +1 -2
- data/lib/hecks/ports/key_vault.port +6 -0
- data/lib/hecks/ports/key_vault.rb +58 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +6 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +38 -2
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +40 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +92 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +8 -0
- data/lib/hecks/ports/query/in_memory.rb +39 -6
- data/lib/hecks/ports/query/ordering.rb +15 -0
- data/lib/hecks/ports.rb +1 -0
- data/lib/hecks/projections/bootstrap_table.rb +43 -8
- data/lib/hecks/projections/diagrams.rb +243 -7
- data/lib/hecks/projections/glossary/html.rb +88 -0
- data/lib/hecks/projections/glossary/markdown.rb +47 -0
- data/lib/hecks/projections/glossary/mermaid.rb +48 -0
- data/lib/hecks/projections/glossary/sentences.rb +105 -0
- data/lib/hecks/projections/glossary.rb +161 -19
- data/lib/hecks/projections/model/deviations.rb +44 -0
- data/lib/hecks/projections/model.rb +51 -1
- data/lib/hecks/projections/oidc.rb +40 -3
- data/lib/hecks/projections/parser_table.rb +49 -8
- data/lib/hecks/projections/reference.rb +12 -0
- data/lib/hecks/projections/rust_vocabulary.rb +219 -16
- data/lib/hecks/projections/shape.rb +8 -0
- data/lib/hecks/projections/statements.rb +63 -16
- data/lib/hecks/projections/vocabulary.rb +17 -0
- data/lib/hecks/projector/cli_projector.rb +218 -10
- data/lib/hecks/projector/docs_projector.rb +145 -19
- data/lib/hecks/projector/exporter.rb +65 -11
- data/lib/hecks/projector/ir_projector.rb +6 -0
- data/lib/hecks/projector/narrate_projector.rb +136 -15
- data/lib/hecks/projector/target.rb +29 -5
- data/lib/hecks/projector.rb +74 -6
- data/lib/hecks/query_ir.rb +47 -0
- data/lib/hecks/query_specification/common/null_policy.rb +5 -3
- data/lib/hecks/rendering.rb +6 -0
- data/lib/hecks/router/namespace_installer.rb +13 -0
- data/lib/hecks/router.rb +55 -0
- data/lib/hecks/runtime/aggregate_lock.rb +9 -0
- data/lib/hecks/runtime/boot_gates.rb +18 -0
- data/lib/hecks/runtime/caller.rb +32 -0
- data/lib/hecks/runtime/capability_graph.rb +11 -0
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +23 -21
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +14 -15
- data/lib/hecks/runtime/command_interpreter.rb +42 -17
- data/lib/hecks/runtime/command_rules/admissibility.rb +165 -14
- data/lib/hecks/runtime/command_rules/arithmetic.rb +17 -5
- data/lib/hecks/runtime/command_rules/references.rb +118 -28
- data/lib/hecks/runtime/dependency_planning.rb +45 -0
- data/lib/hecks/runtime/dispatcher.rb +28 -50
- data/lib/hecks/runtime/entity_element.rb +161 -8
- data/lib/hecks/runtime/entity_interpreter.rb +44 -9
- data/lib/hecks/runtime/errors.rb +18 -4
- data/lib/hecks/runtime/event.rb +10 -5
- data/lib/hecks/runtime/identity.rb +71 -3
- data/lib/hecks/runtime/instance.rb +67 -7
- data/lib/hecks/runtime/interpreting.rb +13 -5
- data/lib/hecks/runtime/invocation.rb +118 -36
- data/lib/hecks/runtime/loader.rb +94 -8
- data/lib/hecks/runtime/outbox.rb +145 -7
- data/lib/hecks/runtime/policy_interpreter.rb +22 -9
- data/lib/hecks/runtime/port_operation_interpreter.rb +20 -0
- data/lib/hecks/runtime/query_interpreter.rb +40 -12
- data/lib/hecks/runtime/reaction_invocation.rb +53 -8
- data/lib/hecks/runtime/read_model_interpreter.rb +23 -7
- data/lib/hecks/runtime/rebuild_sweep.rb +28 -0
- data/lib/hecks/runtime/reference_hop.rb +42 -0
- data/lib/hecks/runtime/refusal_wording.rb +50 -0
- data/lib/hecks/runtime/registry/saga_persistence.rb +11 -0
- data/lib/hecks/runtime/registry/verification.rb +119 -4
- data/lib/hecks/runtime/registry.rb +157 -4
- data/lib/hecks/runtime/remote_dispatcher.rb +92 -6
- data/lib/hecks/runtime/routing.rb +27 -2
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +10 -12
- data/lib/hecks/runtime/saga_interpreter.rb +27 -13
- data/lib/hecks/runtime/tenant_check.rb +26 -6
- data/lib/hecks/runtime/tenant_scope.rb +18 -0
- data/lib/hecks/runtime/value/coercion.rb +255 -33
- data/lib/hecks/runtime/value/entity_list_coercion.rb +102 -30
- data/lib/hecks/runtime/value.rb +50 -0
- data/lib/hecks/runtime.rb +32 -0
- data/lib/hecks/storehouse.rb +305 -9
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks.rb +79 -1
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -0
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +12 -2
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +29 -5
- metadata +11 -5
- data/lib/hecks/codemod/legacy_dispatch_args.rb +0 -299
- data/lib/hecks/codemod/legacy_dispatch_recorder.rb +0 -186
- data/lib/hecks/deprecation.rb +0 -95
data/lib/hecks/fuzzing/replay.rb
CHANGED
|
@@ -61,10 +61,10 @@ module Hecks
|
|
|
61
61
|
# One tightly ordered loop over steps, with several "oracle" snapshots
|
|
62
62
|
# (reaction_mark, fan_out_snapshot, guard_check, mutation_trace) that
|
|
63
63
|
# must be taken at very specific points relative to dispatch — see
|
|
64
|
-
# fan_out_snapshot's own comment above for the real
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
#
|
|
64
|
+
# fan_out_snapshot's own comment above for the real bug this exact
|
|
65
|
+
# before/after ordering fixes. Splitting this into smaller methods
|
|
66
|
+
# would mean threading five-plus oracle-state locals across method
|
|
67
|
+
# boundaries as parameters/return values, and
|
|
68
68
|
# would let a future editor silently reorder a snapshot relative to
|
|
69
69
|
# `dispatch` without any single method looking wrong — the ordering
|
|
70
70
|
# invariant is only visible with the whole sequence in one place.
|
|
@@ -91,6 +91,22 @@ module Hecks
|
|
|
91
91
|
# identity rather than a shared default the way `:postgres` does.
|
|
92
92
|
# Forwarded straight through, unchanged, exactly like `adapter:`
|
|
93
93
|
# itself already was.
|
|
94
|
+
#
|
|
95
|
+
# @param domain_path [String] filesystem path to the domain directory to replay
|
|
96
|
+
# @param steps [Array<Hash>] the step list to dispatch, any-keyed (normalized to
|
|
97
|
+
# String keys internally)
|
|
98
|
+
# @param adapter [Symbol] persistence adapter to boot the copy with
|
|
99
|
+
# @param database [String, nil] PostgresEra database name; required, only meaningful
|
|
100
|
+
# when `adapter: :postgres_era`
|
|
101
|
+
# @param schema [String, nil] PostgresEra schema name; required, only meaningful
|
|
102
|
+
# when `adapter: :postgres_era`
|
|
103
|
+
# @param self_consistency [Boolean] whether to run `SelfConsistency.check` against
|
|
104
|
+
# this replay's own runtime before returning
|
|
105
|
+
# @return [Hash] the replay history: `:instances`, `:events`, `:refusals`,
|
|
106
|
+
# `:reactions`, `:sagas`, `:saga_instances`, `:queries`, `:dry_runs`,
|
|
107
|
+
# `:dry_run_traces`, `:fan_outs`, `:guard_checks`, `:mutation_traces`,
|
|
108
|
+
# `:outbox_traces`, `:saga_dispatches`, `:policy_dispatches`, `:bluebook`,
|
|
109
|
+
# `:bluebooks`, and (when `self_consistency:` is true) `:self_consistency`
|
|
94
110
|
def call(domain_path, steps, adapter: :memory, database: nil, schema: nil, self_consistency: false)
|
|
95
111
|
# See isolated_boot.rb's own header: resets data/ and rebinds
|
|
96
112
|
# persistence to the chosen adapter (Memory by default), since a
|
|
@@ -457,9 +473,16 @@ module Hecks
|
|
|
457
473
|
end
|
|
458
474
|
end
|
|
459
475
|
|
|
476
|
+
# Runs the block with this step's `role:`/`actor_id:` bound as the
|
|
477
|
+
# ambient caller, if it declares one.
|
|
478
|
+
#
|
|
460
479
|
# A step with no `role:` dispatches exactly as every corpus step
|
|
461
480
|
# always has — bare, no caller bound at all (`Caller.current` nil,
|
|
462
481
|
# so `refuse_role_mismatch` returns before checking anything).
|
|
482
|
+
#
|
|
483
|
+
# @param step [Hash] a String-keyed step hash, read for `"role"`/`"actor_id"`
|
|
484
|
+
# @yield the step's own dispatch or dry-run call
|
|
485
|
+
# @return [Object] whatever the block returns
|
|
463
486
|
def as_step_caller(step, &)
|
|
464
487
|
return yield unless step["role"]
|
|
465
488
|
|
|
@@ -491,15 +514,14 @@ module Hecks
|
|
|
491
514
|
# reference) become the step's own real dispatch outcome — this
|
|
492
515
|
# is a separate, best-effort read, not part of the step's own
|
|
493
516
|
# control flow.
|
|
494
|
-
#
|
|
495
|
-
#
|
|
496
|
-
#
|
|
497
|
-
#
|
|
498
|
-
#
|
|
499
|
-
#
|
|
500
|
-
#
|
|
501
|
-
#
|
|
502
|
-
# the ones that existed when the query actually ran. Found live:
|
|
517
|
+
# Every persisted record, keyed the way `query_eligible_rows`/
|
|
518
|
+
# `#eligible_rows` (properties.rb) already expect. Called once per
|
|
519
|
+
# query step (see `call`, above), not only once at the very end,
|
|
520
|
+
# because a query asked at step 1 of a script whose later steps go on
|
|
521
|
+
# to create more records needs to be checked, by every property that
|
|
522
|
+
# independently recomputes "the eligible rows," against the state as
|
|
523
|
+
# of that query — not the final snapshot after the whole replay.
|
|
524
|
+
# Found live:
|
|
503
525
|
# `Banking.accounts_by_kind`, asked as literally the first step of a
|
|
504
526
|
# 3-step script, correctly answered against zero accounts (none
|
|
505
527
|
# existed yet) while `group_by_matches_recompute`'s own independent
|
|
@@ -508,6 +530,11 @@ module Hecks
|
|
|
508
530
|
# its own `instances_at:` snapshot, taken at the moment it ran, so
|
|
509
531
|
# every property that recomputes against "the eligible rows" reads
|
|
510
532
|
# the state as that query actually saw it, not a shared final one.
|
|
533
|
+
#
|
|
534
|
+
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted runtime
|
|
535
|
+
# to read every persisted record from
|
|
536
|
+
# @return [Hash{String => Hash}] every persisted record's state, keyed by
|
|
537
|
+
# `"Domain::Aggregate#id"`
|
|
511
538
|
def snapshot_instances(runtime)
|
|
512
539
|
instances = {}
|
|
513
540
|
runtime.registry.bluebooks.each do |domain_name, bluebook|
|
|
@@ -530,6 +557,15 @@ module Hecks
|
|
|
530
557
|
# rubocop:disable-next Metrics/AbcSize
|
|
531
558
|
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
532
559
|
# rubocop:disable-next Metrics/PerceivedComplexity
|
|
560
|
+
#
|
|
561
|
+
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted runtime
|
|
562
|
+
# to resolve the step's own verb and record against
|
|
563
|
+
# @param verb [String] the step's own command verb
|
|
564
|
+
# @param args [Hash{Symbol => Object}] the step's own args, symbol-keyed
|
|
565
|
+
# @return [Hash, nil] `{verb:, domain:, aggregate:, command:, id:, recomputed_refused:,
|
|
566
|
+
# recomputed_kind:}` if there was a record and a guard to check; nil for anything
|
|
567
|
+
# out of scope (an entity/port verb, a creating command, an unresolvable id, or a
|
|
568
|
+
# command with nothing to check)
|
|
533
569
|
def build_guard_check(runtime, verb, args)
|
|
534
570
|
domain_name, aggregate_name, command_name = Naming.split_verb(verb)
|
|
535
571
|
return nil unless command_name && !command_name.include?(".")
|
|
@@ -618,6 +654,14 @@ module Hecks
|
|
|
618
654
|
# rubocop:disable-next Metrics/AbcSize
|
|
619
655
|
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
620
656
|
# rubocop:disable-next Metrics/PerceivedComplexity
|
|
657
|
+
#
|
|
658
|
+
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted runtime
|
|
659
|
+
# to resolve the step's own verb and record against
|
|
660
|
+
# @param verb [String] the step's own command verb
|
|
661
|
+
# @param args [Hash{Symbol => Object}] the step's own args, symbol-keyed
|
|
662
|
+
# @return [Hash, nil] `{verb:, domain:, aggregate:, command:, parent_id:, list_attr:,
|
|
663
|
+
# element_wants:, before:, args:}` if this is an entity-dispatched command with
|
|
664
|
+
# mutations acting on a resolvable element; nil for anything out of scope
|
|
621
665
|
def build_mutation_trace(runtime, verb, args)
|
|
622
666
|
domain_name, aggregate_name, command_name = Naming.split_verb(verb)
|
|
623
667
|
return nil unless command_name&.include?(".")
|
|
@@ -660,12 +704,22 @@ module Hecks
|
|
|
660
704
|
nil
|
|
661
705
|
end
|
|
662
706
|
|
|
707
|
+
# Re-reads the same entity element, after dispatch, for comparison against
|
|
708
|
+
# `trace[:before]`.
|
|
709
|
+
#
|
|
663
710
|
# The same element, re-located, after dispatch — by identity, not
|
|
664
711
|
# position (an append could have changed the array's own length
|
|
665
712
|
# or order relative to it). `nil` if it somehow vanished (not
|
|
666
713
|
# expected for any op this fixture declares — none of them
|
|
667
714
|
# remove the acted-on element itself — but a property comparing
|
|
668
715
|
# against `nil` fails loudly rather than crashing this replay).
|
|
716
|
+
#
|
|
717
|
+
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted runtime
|
|
718
|
+
# dispatch just ran against
|
|
719
|
+
# @param trace [Hash] the mutation trace `build_mutation_trace` built for this step,
|
|
720
|
+
# read for `:domain`, `:aggregate`, `:parent_id`, `:list_attr`, `:element_wants`
|
|
721
|
+
# @return [Hash, nil] the element's materialized state after dispatch, or nil if
|
|
722
|
+
# the parent record, its aggregate, or the element itself can no longer be found
|
|
669
723
|
def read_mutation_after(runtime, trace)
|
|
670
724
|
aggregate = runtime.registry.bluebook(trace[:domain])&.aggregate(trace[:aggregate])
|
|
671
725
|
return nil unless aggregate
|
|
@@ -696,6 +750,15 @@ module Hecks
|
|
|
696
750
|
# apart. Recomputed once per event, not once per policy-and-event,
|
|
697
751
|
# because a `Chapter` de-duplicates on nothing this loop cannot
|
|
698
752
|
# cheaply repeat.
|
|
753
|
+
#
|
|
754
|
+
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted runtime
|
|
755
|
+
# @param snapshot [Hash{Array(String, String) => Hash}] the pre-dispatch `for_each`
|
|
756
|
+
# query snapshot, keyed by `[query_domain, aggregate_name]`
|
|
757
|
+
# @param announced [Array<Runtime::Event>] the events this step's own dispatch produced
|
|
758
|
+
# @param reactions_since [Array<Hash>] the reaction-log rows this step's own
|
|
759
|
+
# dispatch appended
|
|
760
|
+
# @return [Array<Hash>] one `{policy:, on:, expected_row_ids:, actual_row_ids:}`
|
|
761
|
+
# finding per (event, fanning-out policy) pair
|
|
699
762
|
def fan_out_findings(runtime, snapshot, announced, reactions_since)
|
|
700
763
|
announced.each_with_object([]) do |event, findings|
|
|
701
764
|
# `event.aggregate` is domain-qualified ("Banking::Account" —
|
|
@@ -718,6 +781,19 @@ module Hecks
|
|
|
718
781
|
end
|
|
719
782
|
end
|
|
720
783
|
|
|
784
|
+
# Builds one fan-out finding, comparing one `for_each` policy's independently
|
|
785
|
+
# recomputed expected rows against what actually reacted for one event.
|
|
786
|
+
#
|
|
787
|
+
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted runtime
|
|
788
|
+
# @param snapshot [Hash{Array(String, String) => Hash}] the pre-dispatch `for_each`
|
|
789
|
+
# query snapshot, keyed by `[query_domain, aggregate_name]`
|
|
790
|
+
# @param policy [Bluebook::Policy] the fanning-out policy this event could trigger
|
|
791
|
+
# @param event [Runtime::Event] the announced event being checked
|
|
792
|
+
# @param domain [String] the emitting event's own domain name
|
|
793
|
+
# @param reactions_since [Array<Hash>] the reaction-log rows this step's own
|
|
794
|
+
# dispatch appended
|
|
795
|
+
# @return [Hash{Symbol => Object}] `{policy: String, on: String,
|
|
796
|
+
# expected_row_ids: Array<String>, nil, actual_row_ids: Array}`
|
|
721
797
|
def fan_out_finding(runtime, snapshot, policy, event, domain, reactions_since)
|
|
722
798
|
payload = event.payload.transform_keys(&:to_sym)
|
|
723
799
|
held = policy.where.to_s.empty? ||
|
|
@@ -743,6 +819,19 @@ module Hecks
|
|
|
743
819
|
# the triggering event's own payload (the same binding
|
|
744
820
|
# `OpenForCustomer`'s `customer_id: :customer_id` relies on); a
|
|
745
821
|
# literal is compared as declared.
|
|
822
|
+
#
|
|
823
|
+
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted runtime
|
|
824
|
+
# to resolve the target query against
|
|
825
|
+
# @param snapshot [Hash{Array(String, String) => Hash}] the pre-dispatch `for_each`
|
|
826
|
+
# query snapshot, keyed by `[query_domain, aggregate_name]`
|
|
827
|
+
# @param policy [Bluebook::Policy] the fanning-out policy whose `for_each` route
|
|
828
|
+
# names the target query
|
|
829
|
+
# @param domain [String] the emitting event's own domain name, resolved through
|
|
830
|
+
# `policy.for_each_route`
|
|
831
|
+
# @param payload [Hash] the triggering event's own payload, symbol-keyed; binds any
|
|
832
|
+
# `Symbol` where-value
|
|
833
|
+
# @return [Array<String>] matching row ids, as strings, sorted; `[]` if the target
|
|
834
|
+
# query cannot be resolved
|
|
746
835
|
def expected_fan_out_rows(runtime, snapshot, policy, domain, payload)
|
|
747
836
|
query_domain, aggregate_name, query_name = policy.for_each_route(domain)
|
|
748
837
|
aggregate = runtime.registry.bluebook(query_domain)&.aggregate(aggregate_name)
|
|
@@ -760,6 +849,21 @@ module Hecks
|
|
|
760
849
|
matched.keys.map(&:to_s).sort
|
|
761
850
|
end
|
|
762
851
|
|
|
852
|
+
# Names the outcome class a recorded refusal row should carry.
|
|
853
|
+
#
|
|
854
|
+
# The outcome class a recorded refusal row names (C8.2/C8.3,
|
|
855
|
+
# docs/semantics/bluebook-semantics.md): a domain refusal is its own
|
|
856
|
+
# class; an evaluation fault — the language refusing to interpret a
|
|
857
|
+
# broken rule or input — is `"Fault"`, the same word the Rust kernel
|
|
858
|
+
# emits (`Refusal::Fault`), never a refusal class and never a raw
|
|
859
|
+
# Ruby exception name.
|
|
860
|
+
#
|
|
861
|
+
# @param error [StandardError] the raised error a step's refusal was rescued as
|
|
862
|
+
# @return [String] `"Fault"` for an evaluation fault, otherwise `error`'s own class name
|
|
863
|
+
def refusal_kind(error)
|
|
864
|
+
error.is_a?(Bluebook::Expression::EvaluationError) ? "Fault" : error.class.name
|
|
865
|
+
end
|
|
866
|
+
|
|
763
867
|
# Answers one ad hoc filter step for real — the mirror image of
|
|
764
868
|
# kernel/cli.rs's own `run_filter`, deliberately calling the exact
|
|
765
869
|
# same production module that method's Rust port stands in for
|
|
@@ -775,16 +879,15 @@ module Hecks
|
|
|
775
879
|
# Sorted by id ascending regardless — `Ports::Query::Ordering`'s own
|
|
776
880
|
# header explains why an ask with no declared order still needs
|
|
777
881
|
# this tier ("the identity tier is what makes an ask total").
|
|
778
|
-
#
|
|
779
|
-
#
|
|
780
|
-
#
|
|
781
|
-
#
|
|
782
|
-
#
|
|
783
|
-
#
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
882
|
+
#
|
|
883
|
+
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted runtime
|
|
884
|
+
# to filter against
|
|
885
|
+
# @param filter [Hash] the ad hoc filter step's own Hash value: String-keyed
|
|
886
|
+
# `"aggregate"`, `"field"`, `"op"`, `"value"`
|
|
887
|
+
# @return [Array<Hash>] matching records, sorted by id ascending, each `{id:}`
|
|
888
|
+
# merged with the record's own state
|
|
889
|
+
# @raise [Bluebook::Expression::EvaluationError] if `op` names no known comparator,
|
|
890
|
+
# or `"aggregate"` names no loaded aggregate
|
|
788
891
|
def run_filter(runtime, filter)
|
|
789
892
|
aggregate_ref = filter["aggregate"].to_s
|
|
790
893
|
field = filter["field"].to_s
|
|
@@ -810,12 +913,17 @@ module Hecks
|
|
|
810
913
|
matched.sort_by { |record| record.id.to_s }.map { |record| { id: record.id }.merge(record.state) }
|
|
811
914
|
end
|
|
812
915
|
|
|
813
|
-
#
|
|
814
|
-
#
|
|
815
|
-
#
|
|
816
|
-
#
|
|
817
|
-
# of any of them being
|
|
818
|
-
# the same way that Rust
|
|
916
|
+
# Builds the `refusals` entry's own "verb" column for a refused ad hoc filter.
|
|
917
|
+
#
|
|
918
|
+
# There is no real verb to report (a filter step carries none), so this
|
|
919
|
+
# builds the same descriptive label kernel/cli.rs's own `filter_label`
|
|
920
|
+
# builds from the same three raw fields, tolerant of any of them being
|
|
921
|
+
# missing (Ruby's own nil-to-"" interpolation) the same way that Rust
|
|
922
|
+
# port is.
|
|
923
|
+
#
|
|
924
|
+
# @param filter [Hash] the ad hoc filter step's own Hash value, read for
|
|
925
|
+
# `"aggregate"`, `"field"`, `"op"`
|
|
926
|
+
# @return [String] a descriptive label such as `"filter Banking::Account.status eq"`
|
|
819
927
|
def filter_label(filter) = "filter #{filter['aggregate']}.#{filter['field']} #{filter['op']}"
|
|
820
928
|
end
|
|
821
929
|
end
|
|
@@ -10,28 +10,30 @@ module Hecks
|
|
|
10
10
|
# view; this module is where the blend actually happens, against the
|
|
11
11
|
# rows that query already returns.
|
|
12
12
|
#
|
|
13
|
-
#
|
|
14
|
-
# arguments only — no clock read, no query dispatched, nothing
|
|
15
|
-
# random — so this is unit-testable with plain hashes and no ledger
|
|
16
|
-
# boot at all, and reruns identically given the same rotation, the
|
|
17
|
-
# same `now` and the same dials. `now` arrives as an argument for
|
|
18
|
-
# the exact reason `Target.Claim`'s own comment already gives: a
|
|
19
|
-
# pure function cannot ask the time, so whoever calls this supplies
|
|
20
|
-
# it, exactly as it supplies an id.
|
|
13
|
+
# ## Pure, deliberately
|
|
21
14
|
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
15
|
+
# Every method here is a function of its own arguments only — no clock
|
|
16
|
+
# read, no query dispatched, nothing random — so this is unit-testable
|
|
17
|
+
# with plain hashes and no ledger boot at all, and reruns identically
|
|
18
|
+
# given the same rotation, the same `now` and the same dials. `now`
|
|
19
|
+
# arrives as an argument for the exact reason `Target.Claim`'s own
|
|
20
|
+
# comment already gives: a pure function cannot ask the time, so
|
|
21
|
+
# whoever calls this supplies it, exactly as it supplies an id.
|
|
22
|
+
#
|
|
23
|
+
# ## Where this actually matters, and where it does not
|
|
24
|
+
#
|
|
25
|
+
# Checked against `bin/qa_sweep`'s own code, not assumed. `bin/qa_sweep
|
|
26
|
+
# --all` sweeps every currently-waiting target in one pass, regardless
|
|
27
|
+
# of order (`run_all_mode`'s own `waiting.map { spawn_sweep_child }` —
|
|
28
|
+
# every element the query returned, never `.first`), so nothing about
|
|
29
|
+
# this module's own ordering changes what `--all` does; it already
|
|
30
|
+
# sweeps everything the ledger has to offer. What it changes is
|
|
31
|
+
# `bin/qa_sweep` invoked with no target argument, which today picks
|
|
32
|
+
# exactly one target via `Target.Rotation.first` — wiring `.pick` in
|
|
33
|
+
# there instead is this module's one real caller, and (per the same
|
|
34
|
+
# investigation) its only one: there is no separate scheduling or
|
|
35
|
+
# concurrency-limit mechanism elsewhere in this repository for it to
|
|
36
|
+
# feed instead.
|
|
35
37
|
module RotationPriority
|
|
36
38
|
module_function
|
|
37
39
|
|
|
@@ -46,6 +48,14 @@ module Hecks
|
|
|
46
48
|
# that does not care still gets the practice's own current answer
|
|
47
49
|
# to "how much of this is 'recent'" rather than a second, silently
|
|
48
50
|
# drifting copy of the same number.
|
|
51
|
+
#
|
|
52
|
+
# @param old_score [Integer] the target's `Target.yield_score` before this period
|
|
53
|
+
# @param surprises_this_period [Integer] how many Surprised checks this period found
|
|
54
|
+
# @param decay_percent [Integer] percent of `old_score` that survives into the new
|
|
55
|
+
# score; defaults to `QualityControlDials::YIELD_DECAY_PERCENT`
|
|
56
|
+
# @return [Integer] the next `Target.yield_score` to store
|
|
57
|
+
# @raise [ArgumentError] if `old_score` is negative
|
|
58
|
+
# @raise [ArgumentError] if `surprises_this_period` is negative
|
|
49
59
|
def next_yield_score(old_score:, surprises_this_period:,
|
|
50
60
|
decay_percent: QualityControlDials::YIELD_DECAY_PERCENT)
|
|
51
61
|
raise ArgumentError, "old_score must not be negative" if old_score.negative?
|
|
@@ -77,6 +87,16 @@ module Hecks
|
|
|
77
87
|
# "how many seconds of extra staleness one point of yield is
|
|
78
88
|
# worth" rather than an opaque multiplier nobody could sanity
|
|
79
89
|
# check by eye.
|
|
90
|
+
#
|
|
91
|
+
# @param rows [Array<Hash>] rows from `Target.Rotation`, each carrying at least
|
|
92
|
+
# `:last_swept` and `:yield_score` as `{ value: Integer }`
|
|
93
|
+
# @param now [Integer] the current time, Unix epoch seconds
|
|
94
|
+
# @param weight_seconds [Integer] seconds of extra staleness one point of yield is
|
|
95
|
+
# worth; defaults to `QualityControlDials::YIELD_WEIGHT_SECONDS`
|
|
96
|
+
# @param floor_seconds [Integer] staleness, in seconds, past which a row is picked
|
|
97
|
+
# ahead of every row not yet that stale; defaults to
|
|
98
|
+
# `QualityControlDials::ROTATION_STALE_FLOOR_SECONDS`
|
|
99
|
+
# @return [Hash, nil] the picked row, or `nil` if `rows` is empty
|
|
80
100
|
def pick(rows, now:, weight_seconds: QualityControlDials::YIELD_WEIGHT_SECONDS,
|
|
81
101
|
floor_seconds: QualityControlDials::ROTATION_STALE_FLOOR_SECONDS)
|
|
82
102
|
return nil if rows.empty?
|
|
@@ -7,28 +7,32 @@ module Hecks
|
|
|
7
7
|
# `manifest.json` files `rust/project/domain_generator.rb` writes beside
|
|
8
8
|
# every generated module, never inferred from Rust's refusal wording.
|
|
9
9
|
#
|
|
10
|
-
# The differential fuzzer (`Differential.diff`)
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# does not account for stays in the
|
|
10
|
+
# The differential fuzzer (`Differential.diff`) drops a query or
|
|
11
|
+
# read-model verb from the comparison if and only if the generator's own
|
|
12
|
+
# manifest recorded it as `generated: false`, with the `gap_class` and
|
|
13
|
+
# `construct` that explain why — never by pattern-matching Rust's
|
|
14
|
+
# refusal wording ("is not generated for this domain"), which would
|
|
15
|
+
# tolerate whatever Rust happened to say, including a codegen
|
|
16
|
+
# regression that silently started refusing a construct it once
|
|
17
|
+
# generated. A refusal the manifest does not account for stays in the
|
|
18
|
+
# comparison and fails it.
|
|
18
19
|
#
|
|
19
|
-
#
|
|
20
|
-
# rust_conformance_helpers.rb) and `bin/qa_generated_domains` both pin a
|
|
21
|
-
# feature's binary at `<rust_dir>/target/debug/rust-<feature>`. That
|
|
22
|
-
# binary compiles `src/generated/<feature>/` plus every shared framework
|
|
23
|
-
# chapter (a generated directory with no `merged.rs` of its own —
|
|
24
|
-
# `governance`, `identity`; see the generated `mod.rs` header). Ids in
|
|
25
|
-
# those manifests are domain-qualified, so a chapter a domain never
|
|
26
|
-
# attaches contributes entries no sequence for that domain can name.
|
|
20
|
+
# ## Which manifests describe a binary
|
|
27
21
|
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
22
|
+
# `build_and_pin` (spec/support/rust_conformance_helpers.rb) and
|
|
23
|
+
# `bin/qa_generated_domains` both pin a feature's binary at
|
|
24
|
+
# `<rust_dir>/target/debug/rust-<feature>`. That binary compiles
|
|
25
|
+
# `src/generated/<feature>/` plus every shared framework chapter (a
|
|
26
|
+
# generated directory with no `merged.rs` of its own — `governance`,
|
|
27
|
+
# `identity`; see the generated `mod.rs` header). Ids in those manifests
|
|
28
|
+
# are domain-qualified, so a chapter a domain never attaches contributes
|
|
29
|
+
# entries no sequence for that domain can name.
|
|
30
|
+
#
|
|
31
|
+
# ## A missing manifest tolerates nothing
|
|
32
|
+
#
|
|
33
|
+
# A hand-written fixture crate, or a tree generated before manifests
|
|
34
|
+
# existed, has no declaration to honour, so every refusal it produces
|
|
35
|
+
# is compared as-is. That is the fail-closed direction.
|
|
32
36
|
class RustGapManifest
|
|
33
37
|
# The only kinds the kernel answers as a query step. A not-generated
|
|
34
38
|
# command changes state, so dropping its refusal would not make the
|
|
@@ -38,6 +42,11 @@ module Hecks
|
|
|
38
42
|
|
|
39
43
|
attr_reader :rust_dir, :feature, :entries
|
|
40
44
|
|
|
45
|
+
# Builds the manifest reader for the pinned conformance binary at `binary`.
|
|
46
|
+
#
|
|
47
|
+
# @param binary [String] path to a pinned binary, `<rust_dir>/target/debug/rust-<feature>`
|
|
48
|
+
# @return [Fuzzing::RustGapManifest] the manifest reader for that binary's feature
|
|
49
|
+
# @raise [ArgumentError] if `binary` does not match the pinned-binary path shape
|
|
41
50
|
def self.for_binary(binary)
|
|
42
51
|
match = PINNED_BINARY.match(File.expand_path(binary.to_s))
|
|
43
52
|
unless match
|
|
@@ -51,6 +60,10 @@ module Hecks
|
|
|
51
60
|
# Every committed manifest entry under `rust_dir`, each tagged with the
|
|
52
61
|
# generated module it came from — what the boundary ratchet and
|
|
53
62
|
# bin/rust_coverage's allowlist staleness check read.
|
|
63
|
+
#
|
|
64
|
+
# @param rust_dir [String] path to the Rust project root (holds `src/generated/*/`)
|
|
65
|
+
# @return [Array<Hash>] every `src/generated/*/manifest.json` entry, each a
|
|
66
|
+
# String-keyed manifest Hash plus `"module" => String` naming the directory it came from
|
|
54
67
|
def self.all_entries(rust_dir)
|
|
55
68
|
Dir.glob(File.join(rust_dir, "src/generated/*/manifest.json")).flat_map do |path|
|
|
56
69
|
module_name = File.basename(File.dirname(path))
|
|
@@ -58,6 +71,8 @@ module Hecks
|
|
|
58
71
|
end
|
|
59
72
|
end
|
|
60
73
|
|
|
74
|
+
# @param rust_dir [String] path to the Rust project root (holds `src/generated/*/`)
|
|
75
|
+
# @param feature [String] the domain feature this manifest reader describes
|
|
61
76
|
def initialize(rust_dir:, feature:)
|
|
62
77
|
@rust_dir = rust_dir
|
|
63
78
|
@feature = feature
|
|
@@ -68,14 +83,25 @@ module Hecks
|
|
|
68
83
|
# The manifest entry that declares `verb` not generated, or nil. `verb`
|
|
69
84
|
# is the wire spelling a sequence step uses; an ad hoc filter (a Hash)
|
|
70
85
|
# is never declared and answers nil.
|
|
86
|
+
#
|
|
87
|
+
# @param verb [String, Object] the wire-spelled query or read-model verb to look up
|
|
88
|
+
# @return [Hash, nil] the String-keyed manifest entry declaring `verb` not generated,
|
|
89
|
+
# or nil if `verb` is not a String or no entry declares it
|
|
71
90
|
def not_generated(verb)
|
|
72
91
|
return nil unless verb.is_a?(String)
|
|
73
92
|
|
|
74
93
|
@not_generated[verb]
|
|
75
94
|
end
|
|
76
95
|
|
|
96
|
+
# Answers whether `verb` is declared not generated by this manifest.
|
|
97
|
+
#
|
|
98
|
+
# @param verb [String, Object] the wire-spelled query or read-model verb to look up
|
|
99
|
+
# @return [Boolean] whether the manifest declares `verb` not generated
|
|
77
100
|
def not_generated?(verb) = !not_generated(verb).nil?
|
|
78
101
|
|
|
102
|
+
# Lists every verb this manifest declares not generated.
|
|
103
|
+
#
|
|
104
|
+
# @return [Set<String>] every verb the manifest declares not generated
|
|
79
105
|
def not_generated_verbs = @not_generated.keys.to_set
|
|
80
106
|
|
|
81
107
|
private
|