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
|
@@ -5,12 +5,12 @@ require_relative "../../runtime/value"
|
|
|
5
5
|
module Hecks
|
|
6
6
|
module Fuzzing
|
|
7
7
|
class SequenceGenerator
|
|
8
|
-
#
|
|
9
|
-
#
|
|
8
|
+
# The argument shapes the Ruby/Rust divergences were actually found
|
|
9
|
+
# through, injected on purpose, on every domain.
|
|
10
10
|
#
|
|
11
11
|
# Ten bugs in one QA session (BUG#7–#16, `QualityControl`'s own
|
|
12
12
|
# ledger) clustered in a handful of mechanisms — and several were
|
|
13
|
-
# only ever reachable because
|
|
13
|
+
# only ever reachable because one domain happened to declare the
|
|
14
14
|
# argument that tripped them: `Roster::Roster.Mark`'s own `to:`
|
|
15
15
|
# collided with the dispatcher's routing `to:` (BUG#7), a present-
|
|
16
16
|
# but-null `to:` was misread as a routing envelope (BUG#16), a blank
|
|
@@ -20,28 +20,28 @@ module Hecks
|
|
|
20
20
|
# in the rotation could ever have found any of those, because
|
|
21
21
|
# nothing generated ever produced the shape. This module produces
|
|
22
22
|
# them — for a configurable fraction of command steps, chosen and
|
|
23
|
-
# parameterised from the
|
|
23
|
+
# parameterised from the same seeded RNG the rest of the generator
|
|
24
24
|
# already draws from, so `generate(domain, seed:, steps:,
|
|
25
25
|
# adversarial:)` stays exactly as reproducible per seed as it was.
|
|
26
26
|
#
|
|
27
|
-
#
|
|
27
|
+
# **Opt-in, by construction**. `adversarial: 0.0` (the default) returns
|
|
28
28
|
# before drawing a single random number, so every pinned seed in
|
|
29
29
|
# spec/fuzzing, spec/rust_conformance_fuzz_spec.rb, bin/fuzz and
|
|
30
30
|
# bin/generate produces byte-for-byte what it produced before this
|
|
31
31
|
# module existed. `bin/qa_sweep` turns it on (reading
|
|
32
32
|
# `QualityControlDials::ADVERSARIAL_FRACTION`) — that script is the
|
|
33
|
-
# one place a mutated step's own divergence is a
|
|
33
|
+
# one place a mutated step's own divergence is a finding rather than
|
|
34
34
|
# a red CI gate.
|
|
35
35
|
#
|
|
36
|
-
#
|
|
37
|
-
# after the arguments and identity are built and
|
|
36
|
+
# One mutation per step, applied in `StepBuilder#build_command_step`
|
|
37
|
+
# after the arguments and identity are built and before the step's
|
|
38
38
|
# own inline dispatch — so the generator's own `known_ids` tracking
|
|
39
|
-
# sees what really happened, and so the returned step's `args`
|
|
39
|
+
# sees what really happened, and so the returned step's `args` are
|
|
40
40
|
# the mutated bytes both `Fuzzing::Replay` (Ruby) and the compiled
|
|
41
41
|
# conformance binary (Rust) later receive. The step carries an
|
|
42
42
|
# `"adversarial"` key naming what was done (`mutation`, the bug
|
|
43
43
|
# class it exercises, and the exact sub-shape), which `bin/qa_sweep`
|
|
44
|
-
# prints on a
|
|
44
|
+
# prints on a found something report so the agent logging the Bug
|
|
45
45
|
# can name the mechanism, not guess it. Both replay paths ignore
|
|
46
46
|
# the key: `Replay.call` reads only verb/query/dry_run/args/role,
|
|
47
47
|
# and `kernel/cli.rs` reads step keys by name.
|
|
@@ -72,19 +72,19 @@ module Hecks
|
|
|
72
72
|
VALUE_OBJECT_SHAPES = %w[null empty_object].freeze
|
|
73
73
|
|
|
74
74
|
# BUG#7/#8/#14's class — an unknown key, a type-mismatched declared
|
|
75
|
-
# key and an absent required key in the
|
|
75
|
+
# key and an absent required key in the same step, so both engines
|
|
76
76
|
# have to pick which refusal wins; and each pair, so the ordering
|
|
77
77
|
# of any two is observable on a command too small for all three.
|
|
78
78
|
#
|
|
79
|
-
#
|
|
79
|
+
# The second row reaches further down `DISPATCH_ORDER`
|
|
80
80
|
# (lib/hecks/vocabulary.rb): `nonexistent` addresses an id nothing
|
|
81
81
|
# holds (a `hydrate`-stage NotFound), `lifecycle` aims a
|
|
82
82
|
# transition-guarded command at a real record (an
|
|
83
|
-
# `admissible_transition`-stage refusal,
|
|
83
|
+
# `admissible_transition`-stage refusal, if the record's state
|
|
84
84
|
# refuses it), `role` binds a caller whose role the command does
|
|
85
85
|
# not name (the `refuse_role_mismatch` stage no generated step
|
|
86
86
|
# had ever reached). Paired with an argument-stage fault each,
|
|
87
|
-
# so the ordering of an
|
|
87
|
+
# so the ordering of an early stage against a late one is
|
|
88
88
|
# observable — BUG#13 (ledger_ordering's own NOTES.md) was
|
|
89
89
|
# exactly an argument-invariant-vs-entity-existence ordering
|
|
90
90
|
# split, and nothing generated had ever asked the question on
|
|
@@ -94,8 +94,8 @@ module Hecks
|
|
|
94
94
|
nonexistent+mismatch nonexistent+unknown lifecycle+mismatch role+absent role+nonexistent
|
|
95
95
|
].freeze
|
|
96
96
|
|
|
97
|
-
#
|
|
98
|
-
#
|
|
97
|
+
# Item 2 of the detection plan (angle-5) — a drawn caller on a
|
|
98
|
+
# role-gated command. `refuse_role_mismatch` is a `DISPATCH_ORDER`
|
|
99
99
|
# step both engines implement (`command_rules/authorization.rb`,
|
|
100
100
|
# `rust/src/kernel/repository.rs check_role`) and both replay doors
|
|
101
101
|
# already read (`Fuzzing::Replay` binds `Hecks.as_caller` from a
|
|
@@ -109,24 +109,24 @@ module Hecks
|
|
|
109
109
|
# absent_on_gated no caller at all on a gated command — the
|
|
110
110
|
# unchecked default, recorded so the step reads
|
|
111
111
|
# as a deliberate control, not an omission
|
|
112
|
-
# actor_known the role
|
|
113
|
-
# already granted it to (
|
|
114
|
-
#
|
|
112
|
+
# actor_known the role plus an actor this same sequence
|
|
113
|
+
# already granted it to (the authorization
|
|
114
|
+
# provider's declared `grant:` verb succeeded
|
|
115
|
+
# earlier — `catalog[:grant_verbs]`) —
|
|
115
116
|
# the real `holds_role?` lookup, both sides
|
|
116
117
|
# actor_unknown the role plus an actor nothing granted —
|
|
117
118
|
# `holds_role?` must refuse on both
|
|
118
|
-
#
|
|
119
|
+
# a separate layer from `KINDS`, with its own probability
|
|
119
120
|
# (`role_draw:` — `QualityControlDials::ROLE_DRAW_PROBABILITY`):
|
|
120
121
|
# a caller composes with any argument mutation above rather than
|
|
121
122
|
# competing with it for the one-mutation-per-step slot, and draws
|
|
122
123
|
# nothing from the RNG when off, so every pinned seed is
|
|
123
124
|
# byte-identical to before it existed.
|
|
124
125
|
CALLER_SHAPES = %w[matching mismatched absent_on_gated actor_known actor_unknown].freeze
|
|
125
|
-
GRANT_VERB = "Governance::RoleAssignment.Assign".freeze
|
|
126
126
|
UNKNOWN_ROLE = "Nobody the domain names".freeze
|
|
127
127
|
|
|
128
128
|
# BUG#11 — an entity command two or more hops deep. Not a mutation
|
|
129
|
-
# of arguments but a
|
|
129
|
+
# of arguments but a preference (picker.rb weights these up when
|
|
130
130
|
# adversarial) plus an addressing coin: flat one-head-per-hop args
|
|
131
131
|
# (what every other generated entity step uses) or the routed
|
|
132
132
|
# `to: { aggregate:, entities: [...] }` envelope BUG#11's own fix
|
|
@@ -136,7 +136,7 @@ module Hecks
|
|
|
136
136
|
DEEP_ENTITY_WEIGHT = 4
|
|
137
137
|
|
|
138
138
|
# BUG#13's mutation is only ever applicable on a step whose
|
|
139
|
-
# parent
|
|
139
|
+
# parent already holds an element this same sequence appended —
|
|
140
140
|
# a rare moment (an append has to have succeeded first, and most
|
|
141
141
|
# mutated appends are refused), so when it is on offer it is
|
|
142
142
|
# weighted up the same way the picker weights an unexercised
|
|
@@ -147,10 +147,10 @@ module Hecks
|
|
|
147
147
|
|
|
148
148
|
def adversarial? = @adversarial.positive?
|
|
149
149
|
|
|
150
|
-
# `[]` — and
|
|
150
|
+
# `[]` — and no RNG draw — when adversarial mode is off. Otherwise
|
|
151
151
|
# the deep-entity addressing note (every deep step, when
|
|
152
152
|
# adversarial), then with probability `@adversarial` exactly one
|
|
153
|
-
# mutation among those applicable to
|
|
153
|
+
# mutation among those applicable to this step's own command.
|
|
154
154
|
def adversarial_mutations!(args, entry, catalog)
|
|
155
155
|
return [] unless adversarial?
|
|
156
156
|
|
|
@@ -176,7 +176,7 @@ module Hecks
|
|
|
176
176
|
|
|
177
177
|
heads = [entry[:aggregate], *entry[:chain]].map { |construct| (construct.identified_by || :id).to_s }
|
|
178
178
|
scalars = heads.map { |head| ValueGenerator.scalar_of(args[head]) }
|
|
179
|
-
# The heads leave the flat args — this is the
|
|
179
|
+
# The heads leave the flat args — this is the clean routed
|
|
180
180
|
# caller BUG#11's own spec pins (`to: {...}, note: {...}`) —
|
|
181
181
|
# unless the command itself declares an attribute of that name
|
|
182
182
|
# (chess's `Piece.Move` declares `id` as a fact too), which
|
|
@@ -236,7 +236,7 @@ module Hecks
|
|
|
236
236
|
|
|
237
237
|
def blank_identity_applicable?(args, entry, catalog) = blank_identity_targets(args, entry, catalog).any?
|
|
238
238
|
|
|
239
|
-
# The identity heads
|
|
239
|
+
# The identity heads this step supplies itself: a creating
|
|
240
240
|
# aggregate command's own (every part of a composite), and an
|
|
241
241
|
# append's caller-supplied entity identity arguments (an entity is
|
|
242
242
|
# "created" by its append, and BUG#15's blank-identity question
|
|
@@ -344,8 +344,8 @@ module Hecks
|
|
|
344
344
|
precedence_shapes_for(args, entry).any?
|
|
345
345
|
end
|
|
346
346
|
|
|
347
|
-
#
|
|
348
|
-
# and `lifecycle` need a command that
|
|
347
|
+
# Every shape whose every part this step can carry. `nonexistent`
|
|
348
|
+
# and `lifecycle` need a command that acts on a record (a creating
|
|
349
349
|
# step has no addressed id to point elsewhere, and no state to be
|
|
350
350
|
# in) and flat addressing (`deep_entity_addressing!`'s routed `to:`
|
|
351
351
|
# envelope owns the ids then); `lifecycle` additionally needs a
|
|
@@ -401,14 +401,14 @@ module Hecks
|
|
|
401
401
|
applied << "unknown"
|
|
402
402
|
end
|
|
403
403
|
apply_late_stage_parts!(wanted, args, entry, detail, applied, catalog)
|
|
404
|
-
# Reported as what was
|
|
404
|
+
# Reported as what was actually done — a command with a single
|
|
405
405
|
# attribute cannot carry both a dropped and a corrupted one.
|
|
406
406
|
detail.merge("shape" => applied.sort.join("+"))
|
|
407
407
|
end
|
|
408
408
|
|
|
409
|
-
# The three parts that reach
|
|
409
|
+
# The three parts that reach past the argument gate — see
|
|
410
410
|
# `PRECEDENCE_SHAPES`' own second row. `nonexistent` re-addresses
|
|
411
|
-
# the step's
|
|
411
|
+
# the step's last hop (the entity element for an entity command,
|
|
412
412
|
# the aggregate itself otherwise) to an id nothing holds;
|
|
413
413
|
# `lifecycle` mutates nothing (the record's own state is what
|
|
414
414
|
# refuses, or doesn't) and is recorded so the pairing is visible;
|
|
@@ -433,7 +433,7 @@ module Hecks
|
|
|
433
433
|
applied << "role"
|
|
434
434
|
end
|
|
435
435
|
|
|
436
|
-
# ──
|
|
436
|
+
# ── Angle-5: a drawn caller on a role-gated command ──────────────
|
|
437
437
|
|
|
438
438
|
def role_draw? = @role_draw.positive?
|
|
439
439
|
|
|
@@ -491,7 +491,7 @@ module Hecks
|
|
|
491
491
|
end
|
|
492
492
|
end
|
|
493
493
|
|
|
494
|
-
# A
|
|
494
|
+
# A grant aimed at a role some command actually declares. Left to
|
|
495
495
|
# `ValueGenerator`, `Assign`'s `role_name` is random text
|
|
496
496
|
# ("hotel"), which no command is gated on — so a granted actor
|
|
497
497
|
# could never satisfy `holds_role?` for anything, and the
|
|
@@ -500,7 +500,7 @@ module Hecks
|
|
|
500
500
|
# domain's own declared roles instead; with it off, nothing here
|
|
501
501
|
# runs (no RNG draw, no change to the args).
|
|
502
502
|
def steer_grant!(args, entry, catalog)
|
|
503
|
-
return unless role_draw? && entry[:verb]
|
|
503
|
+
return unless role_draw? && catalog[:grant_verbs].include?(entry[:verb]) && catalog[:roles].any?
|
|
504
504
|
return unless args.key?("role_name")
|
|
505
505
|
|
|
506
506
|
args["role_name"] = { "value" => catalog[:roles].sample(random: @random) }
|
|
@@ -29,8 +29,8 @@ module Hecks
|
|
|
29
29
|
catalog_entities(domain_name, aggregate, entity_commands, entity_queries)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
#
|
|
33
|
-
#
|
|
32
|
+
# **The bare domain form** — "Domain.report_name", no "::" — the
|
|
33
|
+
# same shape `Dispatcher#query` itself branches on to route a
|
|
34
34
|
# read-model ask apart from an aggregate query. A report was
|
|
35
35
|
# never in this catalog at all before: `aggregation_matches_
|
|
36
36
|
# recompute` (count/median) has only ever been exercised by
|
|
@@ -52,24 +52,31 @@ module Hecks
|
|
|
52
52
|
# from (adversary.rb `other_role`).
|
|
53
53
|
roles: (creating + instance + entity_commands).filter_map { |e| e[:command].role }
|
|
54
54
|
.map(&:to_s).reject(&:empty?).uniq.sort,
|
|
55
|
+
# The grant verb every loaded authorization provider declares
|
|
56
|
+
# (`provides "authorization", grant: ...`) — what the caller
|
|
57
|
+
# draw steers at a declared role and records as a real grant.
|
|
58
|
+
# Read off the declaration, never the literal Governance name.
|
|
59
|
+
grant_verbs: runtime.registry.authorization_providers
|
|
60
|
+
.filter_map { |chapter| chapter.provided_verb(Bluebook::Capabilities::AUTHORIZATION, :grant) }
|
|
61
|
+
.sort,
|
|
55
62
|
# Which aggregates this corpus can actually make one of — the ones
|
|
56
63
|
# `satisfiable?` is entitled to wait for.
|
|
57
64
|
creatable: creating.to_set { |entry| entry[:aggregate].hecks_name } }
|
|
58
65
|
end
|
|
59
66
|
|
|
60
|
-
#
|
|
67
|
+
# Every entity, at every depth — `Card` nested inside `Board`
|
|
61
68
|
# inside `Workspace` (qa/stress_domains/nested_pieces) walks in
|
|
62
69
|
# as `chain: [Board, Card]`, the exact hop list
|
|
63
|
-
# `EntityInterpreter
|
|
70
|
+
# `EntityInterpreter::Resolution.of` resolves the dotted verb
|
|
64
71
|
# back into. Before this walk existed the catalog only ever read
|
|
65
72
|
# `aggregate.entities` one level down, so a two-hop entity
|
|
66
73
|
# command (BUG#11's whole class) could never be generated at all
|
|
67
74
|
# — the one shape the differential harness most needed to reach
|
|
68
75
|
# was structurally absent from every sequence it ever produced.
|
|
69
|
-
# `entity:` stays the
|
|
76
|
+
# `entity:` stays the last hop (what every existing reader means
|
|
70
77
|
# by "the entity"); `chain:` is the whole path.
|
|
71
78
|
#
|
|
72
|
-
# Entity
|
|
79
|
+
# Entity queries stay one hop deep, exactly as before — a nested
|
|
73
80
|
# entity's query has no established wire spelling this generator
|
|
74
81
|
# can vouch for, and nothing in the corpus declares one.
|
|
75
82
|
def catalog_entities(domain_name, aggregate, entity_commands, entity_queries)
|
|
@@ -90,7 +97,7 @@ module Hecks
|
|
|
90
97
|
end
|
|
91
98
|
|
|
92
99
|
# Depth-first, parents before children, in declaration order — so
|
|
93
|
-
# the depth-1 entries land in `entity_commands` in
|
|
100
|
+
# the depth-1 entries land in `entity_commands` in exactly the
|
|
94
101
|
# order they always did (a pinned seed's picker pool is the same
|
|
95
102
|
# pool it was), and a nested entity's own entries follow its
|
|
96
103
|
# parent's.
|
|
@@ -108,16 +115,16 @@ module Hecks
|
|
|
108
115
|
# (CommandInterpreter#entity_element) when the append's own field
|
|
109
116
|
# mapping doesn't already assign it — the common case, predicted here.
|
|
110
117
|
# A domain whose append explicitly assigns identity through a mapped
|
|
111
|
-
# argument is covered too, without guessing: whatever value
|
|
112
|
-
# generator supplied for that argument at dispatch time
|
|
118
|
+
# argument is covered too, without guessing: whatever value this
|
|
119
|
+
# generator supplied for that argument at dispatch time is the
|
|
113
120
|
# identity, and gets recorded directly (see `record_outcome`).
|
|
114
121
|
#
|
|
115
122
|
# `owner_chain:` — `[]` for an aggregate-level append (`Board.AddList`,
|
|
116
|
-
# `Folder.AddSlip`), the entity path for an
|
|
123
|
+
# `Folder.AddSlip`), the entity path for an entity-level one
|
|
117
124
|
# (`Board.AddCard` appending into `Board.cards`, owner_chain
|
|
118
125
|
# `[Board]`) — so `record_outcome` can key the appended element
|
|
119
126
|
# under the exact parent-plus-hops it landed beneath.
|
|
120
|
-
# `identity_arguments:` —
|
|
127
|
+
# `identity_arguments:` — every identity head the mapping sources
|
|
121
128
|
# from a command argument (a composite entity identity has several),
|
|
122
129
|
# what the adversarial duplicate-identity mutation replays;
|
|
123
130
|
# `identity_argument:` stays the single-head reading the existing
|
|
@@ -15,7 +15,7 @@ module Hecks
|
|
|
15
15
|
parent_scalar = identity_scalar_of(aggregate, args)
|
|
16
16
|
|
|
17
17
|
@known_ids[aggregate.hecks_name] << parent_scalar if entry[:entity].nil? && entry[:command].creates?
|
|
18
|
-
record_grant(args) if entry[:verb]
|
|
18
|
+
record_grant(args) if catalog[:grant_verbs].include?(entry[:verb])
|
|
19
19
|
|
|
20
20
|
populator = populator_for_entry(catalog, entry)
|
|
21
21
|
return unless populator
|
|
@@ -35,24 +35,25 @@ module Hecks
|
|
|
35
35
|
end
|
|
36
36
|
@entity_known_ids[key] << new_id
|
|
37
37
|
|
|
38
|
-
#
|
|
38
|
+
# The caller-supplied identity tuple, kept whole — every mapped
|
|
39
39
|
# identity argument and the exact value this step offered for it
|
|
40
40
|
# — so the adversarial duplicate-identity mutation (BUG#13's own
|
|
41
41
|
# shape, `Folder.AddSlip` twice under the same `reference`) can
|
|
42
|
-
# offer it
|
|
43
|
-
# identities included (BUG#13's fix explicitly did
|
|
42
|
+
# offer it again later against the same parent, composite
|
|
43
|
+
# identities included (BUG#13's fix explicitly did not cover
|
|
44
44
|
# those; this is how a sequence gets to ask).
|
|
45
45
|
return if populator[:identity_arguments].empty?
|
|
46
46
|
|
|
47
47
|
@appended_identities[key] << populator[:identity_arguments].to_h { |name| [name.to_s, args[name.to_s]] }
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
# A
|
|
51
|
-
#
|
|
50
|
+
# A grant this sequence made for real — the authorization provider's
|
|
51
|
+
# declared `grant:` verb (`Governance::RoleAssignment.Assign` in
|
|
52
|
+
# every boot today) succeeded with these exact args, so `actor_id` now holds
|
|
52
53
|
# `role_name` in this boot's own store, on both replay sides. The
|
|
53
54
|
# `actor_known` caller shape (adversary.rb) replays that identity
|
|
54
55
|
# against a command gated on the same role: the one way a
|
|
55
|
-
# generated step reaches `holds_role?`'s
|
|
56
|
+
# generated step reaches `holds_role?`'s authorized branch rather
|
|
56
57
|
# than only its refusing one.
|
|
57
58
|
def record_grant(args)
|
|
58
59
|
role = ValueGenerator.scalar_of(args["role_name"]).to_s
|
|
@@ -60,7 +61,7 @@ module Hecks
|
|
|
60
61
|
@granted[role] << actor unless role.empty? || actor.empty?
|
|
61
62
|
end
|
|
62
63
|
|
|
63
|
-
#
|
|
64
|
+
# The pool an appended element lands in — the aggregate's own
|
|
64
65
|
# identity, then one scalar per owning hop (an aggregate-level
|
|
65
66
|
# append has none; `Board.AddCard` has `Board`'s own `number`,
|
|
66
67
|
# read straight back off the args this step addressed it by).
|
|
@@ -81,18 +82,18 @@ module Hecks
|
|
|
81
82
|
"#{aggregate_name}.#{chain_names.join('.')}##{scalars.join('/')}"
|
|
82
83
|
end
|
|
83
84
|
|
|
84
|
-
#
|
|
85
|
+
# The scalar this step's own aggregate identity resolves to, from the
|
|
85
86
|
# args a creating (or entity-populating) command actually
|
|
86
|
-
# dispatched — a
|
|
87
|
+
# dispatched — a single declared head (or the untyped default
|
|
87
88
|
# `:id`) reads straight off its own top-level arg the way this
|
|
88
89
|
# always has (`ValueGenerator.scalar_of`, opening the identity value
|
|
89
|
-
# object). A genuinely
|
|
90
|
+
# object). A genuinely composite identity (`composite_identity?`,
|
|
90
91
|
# shared with StepBuilder#add_identity! — see its own comment) has
|
|
91
92
|
# no such top-level arg at all: `add_identity!` deliberately leaves
|
|
92
93
|
# a composite creating command's own individually-declared fields
|
|
93
94
|
# alone rather than forcing a synthetic `id` neither the bluebook
|
|
94
95
|
# nor the command ever declared, so this joins those fields itself,
|
|
95
|
-
# in the
|
|
96
|
+
# in the same declaration order `Runtime::Identity.of` joins them
|
|
96
97
|
# at dispatch (`Naming.identity`, `Naming::IDENTITY_JOIN`) — which
|
|
97
98
|
# is exactly what makes the result usable later as the bare `id:`
|
|
98
99
|
# a composite Revoke/act-again command expects (`Identity.from`'s
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Fuzzing
|
|
3
3
|
class SequenceGenerator
|
|
4
|
-
# Which step to try next: eligibility (what is
|
|
5
|
-
# state so far) and weighting (what is
|
|
4
|
+
# Which step to try next: eligibility (what is possible from the
|
|
5
|
+
# state so far) and weighting (what is likely to reach somewhere
|
|
6
6
|
# new).
|
|
7
7
|
module Picker
|
|
8
8
|
private
|
|
@@ -23,7 +23,7 @@ module Hecks
|
|
|
23
23
|
|
|
24
24
|
# BUG#11's own preference (adversary.rb) — an entity command two
|
|
25
25
|
# or more hops deep is weighted up the same way an unexercised
|
|
26
|
-
# verb is,
|
|
26
|
+
# verb is, only in adversarial mode: the eligibility rules above
|
|
27
27
|
# still decide what is possible, and a default-mode pool is
|
|
28
28
|
# exactly the pool it always was.
|
|
29
29
|
def deep_entity_bias(rest)
|
|
@@ -31,11 +31,11 @@ module Hecks
|
|
|
31
31
|
deep * Adversary::DEEP_ENTITY_WEIGHT
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
#
|
|
34
|
+
# While there is nothing to find, making something is the only useful move.
|
|
35
35
|
#
|
|
36
36
|
# A flat weight is right once the domain has records in it, and badly
|
|
37
37
|
# wrong before: banking declares ten queries, and from an empty store
|
|
38
|
-
# exactly
|
|
38
|
+
# exactly one creating command is satisfiable — Customer.Register, the
|
|
39
39
|
# verb the whole cascade waits on. Two entries against ten left runs
|
|
40
40
|
# spending their entire budget querying a store nothing had been written
|
|
41
41
|
# to ; one seed answered ten empty queries in a row and emitted nothing
|
|
@@ -54,9 +54,9 @@ module Hecks
|
|
|
54
54
|
@known_ids[entry[:aggregate].hecks_name].any? && satisfiable?(catalog, entry)
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
# A
|
|
57
|
+
# A rootless report (no `reference_to` at all) has nothing to wait
|
|
58
58
|
# for — it reads whole tables, the same "always eligible" position
|
|
59
|
-
# `catalog[:queries]` itself takes. A
|
|
59
|
+
# `catalog[:queries]` itself takes. A rooted one needs a real
|
|
60
60
|
# instance of its own `reference_target` to ask about first, same
|
|
61
61
|
# rule `actionable?` already gives an instance command — asking
|
|
62
62
|
# `Banking.disputed_payment_count` before any Account exists would
|
|
@@ -66,7 +66,7 @@ module Hecks
|
|
|
66
66
|
entry[:model].reference_target.nil? || @known_ids[entry[:model].reference_target].any?
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
-
# A
|
|
69
|
+
# A command that references nothing that exists cannot succeed, so it is
|
|
70
70
|
# not offered until something does.
|
|
71
71
|
#
|
|
72
72
|
# `ValueGenerator.reference_value` has no real id to hand over when its
|
|
@@ -78,7 +78,7 @@ module Hecks
|
|
|
78
78
|
# spent its whole budget being refused.
|
|
79
79
|
#
|
|
80
80
|
# This is the rule instance commands already follow — `known_ids.any?` —
|
|
81
|
-
# applied to what a command
|
|
81
|
+
# applied to what a command references rather than to what it acts on.
|
|
82
82
|
# A target no creating command in this corpus can make (a cross-domain
|
|
83
83
|
# reference, which `CommandRules#resolve_references` skips anyway) is
|
|
84
84
|
# exempt, or the whole domain would starve waiting for it.
|
|
@@ -92,14 +92,23 @@ module Hecks
|
|
|
92
92
|
# Coverage-guided rather than uniformly random : a verb this sequence has
|
|
93
93
|
# not dispatched yet is weighted up, so a run spends its budget on the
|
|
94
94
|
# commands it has not reached instead of re-rolling the ones it has. The
|
|
95
|
-
# eligibility rules above still decide what is
|
|
95
|
+
# eligibility rules above still decide what is possible — this only decides
|
|
96
96
|
# what is likely, so a verb gated behind state it does not have yet stays
|
|
97
97
|
# out of the pool entirely rather than being preferred forever.
|
|
98
98
|
def steer(pool)
|
|
99
99
|
fresh = pool.reject { |entry| @exercised.include?(entry[:verb]) }
|
|
100
|
-
|
|
100
|
+
steered = fresh.empty? ? pool : pool + (fresh * UNEXERCISED_WEIGHT)
|
|
101
|
+
favor(steered)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# `favor:` (CoverageCampaign's rare verbs) — weighted up when
|
|
105
|
+
# eligible, never made eligible. An empty favor list returns the
|
|
106
|
+
# pool untouched, so an unguided seed draws exactly what it did.
|
|
107
|
+
def favor(pool)
|
|
108
|
+
return pool if @favor.empty?
|
|
101
109
|
|
|
102
|
-
pool
|
|
110
|
+
favored = pool.uniq.select { |entry| @favor.include?(entry[:verb]) }
|
|
111
|
+
pool + (favored * SequenceGenerator::FAVOR_WEIGHT)
|
|
103
112
|
end
|
|
104
113
|
end
|
|
105
114
|
end
|