hecks 1.3.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 +72 -7
- data/lib/hecks/adapters/driven/d1.rb +187 -23
- data/lib/hecks/adapters/driven/folder.rb +83 -10
- data/lib/hecks/adapters/driven/google_authentication.rb +33 -12
- data/lib/hecks/adapters/driven/governance_authorization.rb +87 -18
- data/lib/hecks/adapters/driven/heki/journal.rb +13 -4
- data/lib/hecks/adapters/driven/heki/saga_store.rb +56 -10
- data/lib/hecks/adapters/driven/heki/snapshot.rb +1 -1
- data/lib/hecks/adapters/driven/heki.rb +106 -9
- data/lib/hecks/adapters/driven/identity_registry.rb +12 -2
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +25 -3
- 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 +67 -14
- data/lib/hecks/adapters/driven/lambda.rb +82 -35
- data/lib/hecks/adapters/driven/local_storage.rb +83 -10
- data/lib/hecks/adapters/driven/memory.rb +205 -9
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +21 -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 +50 -11
- data/lib/hecks/adapters/driven/secure_random_identity.rb +3 -0
- 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/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 +6 -4
- data/lib/hecks/adapters/driving/github_webhook.rb +31 -18
- data/lib/hecks/behaviors/dsl.rb +60 -2
- data/lib/hecks/behaviors/expectations.rb +190 -29
- data/lib/hecks/behaviors/ir.rb +12 -1
- data/lib/hecks/behaviors/rspec.rb +9 -1
- data/lib/hecks/behaviors/runner.rb +21 -2
- data/lib/hecks/behaviors.rb +9 -1
- data/lib/hecks/bluebook/aggregate.rb +43 -13
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +17 -10
- data/lib/hecks/bluebook/assembly/build.rb +26 -5
- data/lib/hecks/bluebook/assembly/contract.rb +98 -23
- data/lib/hecks/bluebook/assembly/contracts.rb +59 -52
- data/lib/hecks/bluebook/assembly/marks.rb +159 -30
- data/lib/hecks/bluebook/assembly/specializer.rb +38 -21
- data/lib/hecks/bluebook/assembly.rb +32 -14
- data/lib/hecks/bluebook/attribute.rb +26 -12
- data/lib/hecks/bluebook/behaviour/aggregate.rb +43 -11
- data/lib/hecks/bluebook/behaviour/attribute.rb +18 -5
- data/lib/hecks/bluebook/behaviour/chapter.rb +76 -5
- data/lib/hecks/bluebook/behaviour/command.rb +55 -25
- data/lib/hecks/bluebook/behaviour/domain_port.rb +27 -7
- data/lib/hecks/bluebook/behaviour/entity.rb +20 -8
- data/lib/hecks/bluebook/behaviour/hexagon.rb +30 -4
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +27 -6
- data/lib/hecks/bluebook/behaviour/policy.rb +42 -17
- data/lib/hecks/bluebook/behaviour/process_manager.rb +39 -8
- data/lib/hecks/bluebook/behaviour/query.rb +6 -1
- data/lib/hecks/bluebook/behaviour/read_model.rb +29 -8
- data/lib/hecks/bluebook/behaviour/traits.rb +48 -12
- data/lib/hecks/bluebook/behaviour/value_object.rb +21 -9
- data/lib/hecks/bluebook/capabilities.rb +27 -0
- data/lib/hecks/bluebook/chapter.rb +51 -9
- data/lib/hecks/bluebook/command.rb +62 -17
- data/lib/hecks/bluebook/domain_port.rb +34 -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 +282 -123
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +112 -75
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +81 -2
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +486 -117
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +179 -47
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +284 -122
- 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 +191 -61
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +130 -30
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +38 -21
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
- data/lib/hecks/bluebook/dsl/policy_builder.rb +86 -36
- 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 +111 -47
- data/lib/hecks/bluebook/dsl/query_builder.rb +37 -8
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +127 -52
- data/lib/hecks/bluebook/dsl/rule_reference.rb +97 -43
- data/lib/hecks/bluebook/dsl/translation_builder.rb +150 -44
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +68 -20
- 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 +40 -11
- data/lib/hecks/bluebook/expression/ast_json.rb +128 -36
- data/lib/hecks/bluebook/expression/ast_reader.rb +32 -3
- data/lib/hecks/bluebook/expression/canonical_form.rb +55 -16
- data/lib/hecks/bluebook/expression/evaluator.rb +221 -43
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +54 -18
- data/lib/hecks/bluebook/expression/resolver.rb +369 -128
- data/lib/hecks/bluebook/hexagon.rb +35 -1
- data/lib/hecks/bluebook/lifecycle.rb +12 -1
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +2 -1
- data/lib/hecks/bluebook/meta_validator/judge.rb +126 -108
- data/lib/hecks/bluebook/meta_validator/plan.rb +81 -46
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +3 -2
- data/lib/hecks/bluebook/meta_validator/readings.rb +200 -50
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +68 -41
- data/lib/hecks/bluebook/meta_validator/shapes.rb +166 -21
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +286 -46
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +11 -10
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -5
- data/lib/hecks/bluebook/meta_validator.rb +235 -139
- data/lib/hecks/bluebook/model_check.rb +434 -104
- data/lib/hecks/bluebook/pattern_subset.rb +32 -10
- data/lib/hecks/bluebook/policy.rb +15 -13
- data/lib/hecks/bluebook/process_manager.rb +27 -14
- 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 +50 -6
- data/lib/hecks/bluebook/query.rb +31 -4
- data/lib/hecks/bluebook/read_model.rb +35 -15
- data/lib/hecks/bluebook/reference.rb +26 -13
- data/lib/hecks/bluebook/smoke_test.rb +46 -23
- data/lib/hecks/bluebook/synthesizer.rb +46 -12
- data/lib/hecks/bluebook/translation.rb +34 -5
- data/lib/hecks/bluebook/value_object.rb +29 -11
- data/lib/hecks/bluebook.rb +5 -6
- data/lib/hecks/codemod.rb +138 -50
- data/lib/hecks/construct.rb +21 -7
- data/lib/hecks/corpus.rb +438 -0
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
- data/lib/hecks/doc/reference.rb +200 -31
- data/lib/hecks/embryonaut_bluebook.rb +38 -15
- 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 +155 -35
- data/lib/hecks/facade/json_door.rb +106 -25
- data/lib/hecks/facade/surface/aggregate_door.rb +50 -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 +183 -19
- data/lib/hecks/forms/html.rb +51 -7
- data/lib/hecks/forms/index_renderer.rb +14 -2
- data/lib/hecks/forms/page.rb +14 -0
- data/lib/hecks/forms/params.rb +120 -23
- data/lib/hecks/forms/port_argument.rb +14 -2
- data/lib/hecks/forms/query_form_renderer.rb +65 -2
- data/lib/hecks/forms/record_renderer.rb +60 -2
- data/lib/hecks/forms/record_table.rb +28 -1
- data/lib/hecks/forms/reference_options.rb +24 -0
- data/lib/hecks/forms/value_object_shape.rb +13 -3
- data/lib/hecks/forms.rb +24 -4
- data/lib/hecks/fqn.rb +59 -1
- data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
- data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
- data/lib/hecks/framework/oidc.json +15 -0
- data/lib/hecks/framework.rb +79 -25
- data/lib/hecks/freezer.rb +27 -11
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +241 -106
- data/lib/hecks/fuzzing/combination_miner.rb +178 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +241 -45
- data/lib/hecks/fuzzing/coverage_campaign.rb +161 -0
- data/lib/hecks/fuzzing/differential.rb +192 -0
- data/lib/hecks/fuzzing/domain_generator.rb +871 -0
- data/lib/hecks/fuzzing/era_boundary.rb +68 -19
- data/lib/hecks/fuzzing/form_census.rb +121 -20
- data/lib/hecks/fuzzing/generated_domain_check.rb +171 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +45 -6
- data/lib/hecks/fuzzing/isolated_boot.rb +137 -80
- data/lib/hecks/fuzzing/nondeterministic.rb +79 -0
- data/lib/hecks/fuzzing/persistence_parity.rb +111 -21
- data/lib/hecks/fuzzing/properties/corrections.rb +34 -9
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +300 -51
- data/lib/hecks/fuzzing/properties/guards.rb +86 -42
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +80 -32
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +40 -35
- data/lib/hecks/fuzzing/properties/outbox.rb +70 -32
- data/lib/hecks/fuzzing/properties/querying.rb +82 -28
- data/lib/hecks/fuzzing/properties.rb +84 -51
- data/lib/hecks/fuzzing/qa_settings.rb +164 -0
- data/lib/hecks/fuzzing/replay.rb +241 -133
- data/lib/hecks/fuzzing/rotation_priority.rb +48 -28
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +139 -0
- data/lib/hecks/fuzzing/self_consistency.rb +315 -137
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +46 -40
- 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 +59 -32
- data/lib/hecks/fuzzing/sequence_generator.rb +158 -23
- data/lib/hecks/fuzzing/shrinker.rb +309 -0
- data/lib/hecks/fuzzing/structural_skips.rb +37 -130
- data/lib/hecks/fuzzing/sweep_depth.rb +12 -4
- data/lib/hecks/fuzzing/target_capabilities.rb +107 -29
- data/lib/hecks/fuzzing/value_generator.rb +110 -22
- data/lib/hecks/fuzzing.rb +5 -0
- data/lib/hecks/grammar/evolve.rb +188 -12
- data/lib/hecks/grammar.rb +53 -7
- data/lib/hecks/ir.rb +51 -20
- 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/hecksagon/hecksagon.bluebook +11 -0
- data/lib/hecks/language/oidc.json +5 -0
- data/lib/hecks/literal.rb +41 -9
- data/lib/hecks/naming.rb +112 -31
- data/lib/hecks/ports/access_control.rb +53 -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 +50 -11
- data/lib/hecks/ports/clock.rb +42 -23
- data/lib/hecks/ports/extraction.rb +16 -0
- data/lib/hecks/ports/identity_assignment.rb +23 -2
- data/lib/hecks/ports/identity_generation.rb +17 -3
- data/lib/hecks/ports/identity_resolution.rb +17 -1
- data/lib/hecks/ports/key_vault.port +6 -0
- data/lib/hecks/ports/key_vault.rb +58 -0
- 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 +34 -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 +95 -6
- 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 +48 -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 +58 -19
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +96 -5
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +9 -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 +41 -8
- data/lib/hecks/ports/query/ordering.rb +21 -6
- data/lib/hecks/ports/query.rb +35 -0
- data/lib/hecks/ports.rb +1 -0
- data/lib/hecks/projections/bootstrap_table.rb +147 -0
- data/lib/hecks/projections/diagrams.rb +315 -79
- data/lib/hecks/projections/glossary/html.rb +91 -3
- data/lib/hecks/projections/glossary/markdown.rb +50 -3
- data/lib/hecks/projections/glossary/mermaid.rb +49 -1
- data/lib/hecks/projections/glossary/sections.rb +1 -1
- data/lib/hecks/projections/glossary/sentences.rb +110 -5
- data/lib/hecks/projections/glossary.rb +173 -31
- data/lib/hecks/projections/ir.rb +1 -1
- data/lib/hecks/projections/model/deviations.rb +62 -17
- data/lib/hecks/projections/model.rb +75 -21
- data/lib/hecks/projections/oidc.rb +45 -8
- data/lib/hecks/projections/parser_table.rb +53 -12
- data/lib/hecks/projections/reference.rb +15 -3
- data/lib/hecks/projections/rust_vocabulary.rb +646 -0
- data/lib/hecks/projections/shape.rb +10 -2
- data/lib/hecks/projections/statements.rb +69 -22
- data/lib/hecks/projections/vocabulary.rb +26 -9
- data/lib/hecks/projections.rb +5 -3
- data/lib/hecks/projector/cli_projector.rb +245 -37
- data/lib/hecks/projector/docs_projector.rb +154 -28
- data/lib/hecks/projector/exporter.rb +104 -29
- data/lib/hecks/projector/ir_projector.rb +7 -1
- data/lib/hecks/projector/narrate_projector.rb +144 -23
- data/lib/hecks/projector/target.rb +42 -18
- data/lib/hecks/projector.rb +86 -18
- data/lib/hecks/query_ir.rb +94 -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 +61 -15
- 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 +9 -3
- data/lib/hecks/router/namespace_installer.rb +16 -3
- data/lib/hecks/router.rb +56 -1
- data/lib/hecks/runtime/aggregate_lock.rb +20 -11
- data/lib/hecks/runtime/boot_gates.rb +21 -3
- data/lib/hecks/runtime/caller.rb +40 -8
- data/lib/hecks/runtime/capability_graph.rb +13 -2
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +40 -41
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +45 -67
- data/lib/hecks/runtime/command_interpreter.rb +195 -113
- data/lib/hecks/runtime/command_rules/admissibility.rb +231 -80
- data/lib/hecks/runtime/command_rules/arithmetic.rb +154 -96
- 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 +145 -55
- data/lib/hecks/runtime/command_rules.rb +3 -0
- data/lib/hecks/runtime/dependency_planning.rb +56 -11
- data/lib/hecks/runtime/dispatcher.rb +263 -92
- data/lib/hecks/runtime/entity_element.rb +304 -67
- data/lib/hecks/runtime/entity_interpreter.rb +149 -88
- data/lib/hecks/runtime/errors.rb +37 -23
- data/lib/hecks/runtime/event.rb +15 -10
- data/lib/hecks/runtime/identity.rb +91 -23
- data/lib/hecks/runtime/instance.rb +103 -18
- data/lib/hecks/runtime/interpreting.rb +24 -16
- data/lib/hecks/runtime/invocation.rb +358 -0
- data/lib/hecks/runtime/loader.rb +105 -19
- data/lib/hecks/runtime/outbox.rb +164 -26
- data/lib/hecks/runtime/policy_interpreter.rb +73 -60
- data/lib/hecks/runtime/port_operation_interpreter.rb +42 -19
- data/lib/hecks/runtime/query_interpreter.rb +93 -74
- data/lib/hecks/runtime/reaction_invocation.rb +73 -28
- data/lib/hecks/runtime/read_model_interpreter.rb +60 -44
- data/lib/hecks/runtime/rebuild_sweep.rb +32 -4
- data/lib/hecks/runtime/reference_hop.rb +48 -6
- data/lib/hecks/runtime/refusal_wording.rb +142 -115
- data/lib/hecks/runtime/registry/saga_persistence.rb +32 -21
- data/lib/hecks/runtime/registry/verification.rb +153 -28
- data/lib/hecks/runtime/registry.rb +202 -32
- data/lib/hecks/runtime/remote_dispatcher.rb +125 -24
- data/lib/hecks/runtime/routing.rb +36 -154
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +25 -27
- data/lib/hecks/runtime/saga_interpreter.rb +90 -76
- data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
- data/lib/hecks/runtime/tenant_check.rb +33 -13
- data/lib/hecks/runtime/tenant_scope.rb +23 -5
- data/lib/hecks/runtime/value/admission.rb +75 -30
- data/lib/hecks/runtime/value/coercion.rb +555 -142
- data/lib/hecks/runtime/value/entity_list_coercion.rb +132 -60
- data/lib/hecks/runtime/value.rb +71 -21
- data/lib/hecks/runtime.rb +39 -7
- data/lib/hecks/storehouse.rb +368 -72
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
- data/lib/hecks/version.rb +3 -3
- data/lib/hecks/vocabulary.rb +205 -4
- data/lib/hecks.rb +91 -11
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +19 -11
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +24 -12
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +38 -14
- metadata +28 -2
data/lib/hecks.rb
CHANGED
|
@@ -22,16 +22,17 @@ require_relative "hecks/router"
|
|
|
22
22
|
require_relative "hecks/runtime"
|
|
23
23
|
require_relative "hecks/adapters"
|
|
24
24
|
require_relative "hecks/projector"
|
|
25
|
-
#
|
|
26
|
-
# every target registers itself as it loads, so this require
|
|
25
|
+
# After the projector registry and its `Target` mixin are both real —
|
|
26
|
+
# every target registers itself as it loads, so this require is the
|
|
27
27
|
# installation of them.
|
|
28
28
|
require_relative "hecks/projections"
|
|
29
|
-
#
|
|
29
|
+
# After `Projector` (dispatches against the `:cli` projection) and
|
|
30
30
|
# `Ports::Clock` (fills a staleness rule's `now` at the door) both exist.
|
|
31
31
|
require_relative "hecks/facade/cli_door"
|
|
32
32
|
require_relative "hecks/facade/cli_runner"
|
|
33
33
|
require_relative "hecks/storehouse"
|
|
34
34
|
require_relative "hecks/framework"
|
|
35
|
+
require_relative "hecks/corpus"
|
|
35
36
|
require_relative "hecks/embryonaut_bluebook"
|
|
36
37
|
|
|
37
38
|
# The root namespace and public facade of the whole DSL/runtime: `Hecks.boot`/
|
|
@@ -44,14 +45,14 @@ module Hecks
|
|
|
44
45
|
class LoadOutsideBoot < StandardError; end
|
|
45
46
|
|
|
46
47
|
class << self
|
|
47
|
-
# The loading words below collect into the registry the
|
|
48
|
+
# The loading words below collect into the registry the runtime is
|
|
48
49
|
# holding open ; booting and that ambient state belong to the runtime
|
|
49
50
|
# layer, so this module is their facade and Hecks::Runtime is where
|
|
50
51
|
# they live.
|
|
51
52
|
# `install_facade:` — see Runtime::Loader.boot. Defaults on; a caller
|
|
52
53
|
# that only dispatches by FQN string can skip the global sugar.
|
|
53
54
|
#
|
|
54
|
-
# `environment:` —
|
|
55
|
+
# `environment:` — recovered, not new: this parameter (and the
|
|
55
56
|
# `environments/<name>.hecksagon` / `.world` overlay it loads —
|
|
56
57
|
# see Adapters::Folder#load_domain) existed on a prior commit of
|
|
57
58
|
# this repo (933d1dd), was vendored out to a real consumer
|
|
@@ -65,6 +66,16 @@ module Hecks
|
|
|
65
66
|
# resolves its own env var name and passes the resulting string
|
|
66
67
|
# straight through, e.g. `Hecks.boot(path, environment:
|
|
67
68
|
# ENV.fetch("MYAPP_ENV", "development"))`.
|
|
69
|
+
#
|
|
70
|
+
# @param path [String] path to a domain directory, or a file inside one
|
|
71
|
+
# @param shared [String, nil] a shared-root override; see `Runtime::Loader.boot`
|
|
72
|
+
# @param install_facade [Boolean] whether to install the `Widget::Item.Add`-style
|
|
73
|
+
# Ruby facade constants for this boot
|
|
74
|
+
# @param environment [String, nil] the environment name whose
|
|
75
|
+
# `environments/<name>.hecksagon`/`.world` overlay, if present, loads after the
|
|
76
|
+
# domain's own
|
|
77
|
+
# @return [Runtime::Dispatcher, Runtime::RemoteDispatcher] the dispatcher bound
|
|
78
|
+
# to the booted domain
|
|
68
79
|
def boot(path, shared: nil, install_facade: true, environment: nil)
|
|
69
80
|
Runtime.boot(path, shared: shared, install_facade: install_facade, environment: environment)
|
|
70
81
|
end
|
|
@@ -73,12 +84,30 @@ module Hecks
|
|
|
73
84
|
# `.hecksagon`, optionally a `.world`), loaded in place from wherever
|
|
74
85
|
# they actually live — see Runtime::Loader.boot_files's own header for
|
|
75
86
|
# why this exists beside `boot` rather than as a special case of it.
|
|
87
|
+
#
|
|
88
|
+
# @param paths [String, Array<String>] one or more file paths within the domain
|
|
89
|
+
# to load, instead of the whole directory
|
|
90
|
+
# @param shared [String, nil] a shared-root override; see `Runtime::Loader.boot_files`
|
|
91
|
+
# @param install_facade [Boolean] whether to install the `Widget::Item.Add`-style
|
|
92
|
+
# Ruby facade constants for this boot
|
|
93
|
+
# @param environment [String, nil] the environment name passed through to the
|
|
94
|
+
# selected-file loader
|
|
95
|
+
# @return [Runtime::Dispatcher, Runtime::RemoteDispatcher] the dispatcher bound
|
|
96
|
+
# to the booted domain
|
|
76
97
|
def boot_files(paths, shared: nil, install_facade: true, environment: nil)
|
|
77
98
|
Runtime.boot_files(paths, shared: shared, install_facade: install_facade, environment: environment)
|
|
78
99
|
end
|
|
79
100
|
|
|
101
|
+
# Binds the ambient registry for the duration of the block.
|
|
102
|
+
#
|
|
103
|
+
# @param registry [Runtime::Registry] the registry to make current for the block
|
|
104
|
+
# @yield the code that should see `registry` as `current_registry`
|
|
105
|
+
# @return [Object] the block's result
|
|
80
106
|
def with_registry(registry, &) = Runtime.with_registry(registry, &)
|
|
81
107
|
|
|
108
|
+
# Reads the ambient registry a declaration is currently landing in.
|
|
109
|
+
#
|
|
110
|
+
# @return [Runtime::Registry, nil] the current registry, or nil outside a boot
|
|
82
111
|
def current_registry = Runtime.current_registry
|
|
83
112
|
|
|
84
113
|
# Bind who is dispatching for the duration of the block — checked
|
|
@@ -86,25 +115,50 @@ module Hecks
|
|
|
86
115
|
# default), a command's role stays exactly what it is without this:
|
|
87
116
|
# decoration.
|
|
88
117
|
#
|
|
89
|
-
# `actor_id` is
|
|
118
|
+
# `actor_id` is optional — a caller naming only a role is checked by
|
|
90
119
|
# string equality against the command's own `role`, exactly as
|
|
91
|
-
# before. A caller that also names
|
|
120
|
+
# before. A caller that also names who it is lets the check run
|
|
92
121
|
# against a real Governance `RoleAssignment` instead, once the
|
|
93
122
|
# command's domain has Governance attached — see
|
|
94
123
|
# `CommandRules::Authorization`'s own header.
|
|
95
124
|
#
|
|
96
|
-
# `as_of` and `scope` are
|
|
125
|
+
# `as_of` and `scope` are optional too, same shape — see
|
|
97
126
|
# `Runtime.as_caller`'s own header for what each does.
|
|
127
|
+
#
|
|
128
|
+
# @param role [String, Symbol] the role to check the caller against
|
|
129
|
+
# @param actor_id [String, nil] who is calling, checked against a real Governance
|
|
130
|
+
# `RoleAssignment` when given; string-equality only against `role` when nil
|
|
131
|
+
# @param as_of [Integer, nil] Unix epoch seconds to check a matching `RoleAssignment`'s
|
|
132
|
+
# own `starts_at` against; unchecked when nil
|
|
133
|
+
# @param scope [String, nil] the scope to check a matching `RoleAssignment`'s own
|
|
134
|
+
# `scope` against; unchecked when nil
|
|
135
|
+
# @yield the code to run with this caller bound
|
|
136
|
+
# @return [Object] the block's result
|
|
98
137
|
def as_caller(role:, actor_id: nil, as_of: nil, scope: nil, &)
|
|
99
138
|
Runtime.as_caller(role: role, actor_id: actor_id, as_of: as_of, scope: scope, &)
|
|
100
139
|
end
|
|
101
140
|
|
|
141
|
+
# Declares a chapter — a `.bluebook` file's top-level word.
|
|
142
|
+
#
|
|
143
|
+
# @param name [String] the chapter's declared name
|
|
144
|
+
# @param version [String, nil] the chapter's pinned version, or nil for unversioned
|
|
145
|
+
# @yield the chapter's body, evaluated against a `Bluebook::DSL::BluebookBuilder`
|
|
146
|
+
# @return [Bluebook::Chapter] the built, judged chapter
|
|
147
|
+
# @raise [LoadOutsideBoot] if called outside `Hecks.boot`/`.boot_files`
|
|
148
|
+
# @raise [Bluebook::DSL::Malformed] if the chapter fails the language's own judgment
|
|
102
149
|
def bluebook(name, version: nil, &)
|
|
103
150
|
collect(:add_bluebook, Bluebook::DSL::BluebookBuilder.build(name, version: version, &))
|
|
104
151
|
end
|
|
105
152
|
|
|
153
|
+
# Declares a hecksagon — a `.hecksagon` file's top-level word, binding a
|
|
154
|
+
# domain's aggregates to adapters.
|
|
155
|
+
#
|
|
156
|
+
# @param name [String, Symbol] the domain's name the hecksagon binds
|
|
157
|
+
# @yield the hecksagon's body, evaluated against a `Bluebook::DSL::HecksagonBuilder`
|
|
158
|
+
# @return [Bluebook::Hecksagon] the built hecksagon
|
|
159
|
+
# @raise [LoadOutsideBoot] if called outside `Hecks.boot`/`.boot_files`
|
|
106
160
|
def hecksagon(name, &) = collect(:add_hecksagon, Bluebook::DSL::HecksagonBuilder.build(name, &))
|
|
107
|
-
#
|
|
161
|
+
# Repointed to DomainPortBuilder — the migration DomainPort's own
|
|
108
162
|
# class comment names as its goal, now landed for the top-level
|
|
109
163
|
# `.port` file callers too (the aggregate-scoped `Thing.port(...)`,
|
|
110
164
|
# binding_proxy.rb, already went through this builder). Every real
|
|
@@ -115,19 +169,45 @@ module Hecks
|
|
|
115
169
|
# identity check) — a pure repoint, no behavior change for any
|
|
116
170
|
# existing caller reading `.verb`/`.signal` off what comes back.
|
|
117
171
|
#
|
|
118
|
-
# `legacy_bare_port: true` — the
|
|
172
|
+
# `legacy_bare_port: true` — the one real semantic gap this repoint
|
|
119
173
|
# would otherwise open: `PortBuilder#build` never refused a
|
|
120
174
|
# completely empty build (no verb, no signal even), a real shape
|
|
121
175
|
# dsl_spec.rb's own "a port" tests exercise (`signal`-only, no
|
|
122
176
|
# `verb`). `DomainPortBuilder`'s own "declares no verb and no
|
|
123
|
-
# operations" refusal is real and correct for its
|
|
177
|
+
# operations" refusal is real and correct for its other two callers
|
|
124
178
|
# (`BindingProxy#port`, `HecksagonBuilder#port_impl`) — only this,
|
|
125
179
|
# the literal top-level `.port` file entry point, keeps the older,
|
|
126
180
|
# looser rule (see `DomainPortBuilder#initialize`'s own comment).
|
|
127
181
|
def port(name, &) = collect(:add_port, Bluebook::DSL::DomainPortBuilder.build(name, legacy_bare_port: true, &))
|
|
182
|
+
|
|
183
|
+
# Declares an adapter — an `.adapter` file's top-level word.
|
|
184
|
+
#
|
|
185
|
+
# @param name [String] the adapter's name
|
|
186
|
+
# @yield the adapter's body, evaluated against a `Bluebook::DSL::AdapterBuilder`
|
|
187
|
+
# @return [Bluebook::Adapter] the built, judged adapter
|
|
188
|
+
# @raise [LoadOutsideBoot] if called outside `Hecks.boot`/`.boot_files`
|
|
128
189
|
def adapter(name, &) = collect(:add_adapter, Bluebook::DSL::AdapterBuilder.build(name, &))
|
|
190
|
+
|
|
191
|
+
# Declares a world — a `.world` file's top-level word, naming a domain's
|
|
192
|
+
# deployment realm.
|
|
193
|
+
#
|
|
194
|
+
# @param name [String, Symbol] the domain's name the world describes
|
|
195
|
+
# @yield the world's body, evaluated against a `Bluebook::DSL::WorldBuilder`
|
|
196
|
+
# @return [Bluebook::World] the built, judged world
|
|
197
|
+
# @raise [LoadOutsideBoot] if called outside `Hecks.boot`/`.boot_files`
|
|
129
198
|
def world(name, &) = collect(:add_world, Bluebook::DSL::WorldBuilder.build(name, &))
|
|
130
199
|
|
|
200
|
+
# Declares a data translation — a `.translation` file's top-level word,
|
|
201
|
+
# migrating one domain's stored shape from one era to the next.
|
|
202
|
+
#
|
|
203
|
+
# @param name [String, Symbol] the domain this translation carries forward
|
|
204
|
+
# @param from [String, Symbol] the origin era
|
|
205
|
+
# @param to [String, Symbol] the destination era
|
|
206
|
+
# @yield the translation's body, evaluated against a
|
|
207
|
+
# `Bluebook::DSL::TranslationBuilder`
|
|
208
|
+
# @return [Bluebook::Translation] the built, judged translation
|
|
209
|
+
# @raise [LoadOutsideBoot] if called outside `Hecks.boot`/`.boot_files`
|
|
210
|
+
# @raise [Bluebook::DSL::Malformed] if `name`, `from`, or `to` is empty
|
|
131
211
|
def data_translation(name, from:, to:, &)
|
|
132
212
|
collect(:add_translation, Bluebook::DSL::TranslationBuilder.build(name, from: from, to: to, &))
|
|
133
213
|
end
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
module RuboCop
|
|
2
2
|
module Cop
|
|
3
3
|
module Hecks
|
|
4
|
-
#
|
|
4
|
+
# Flags the exact shape behind 8+ real bugs already fixed one at a
|
|
5
5
|
# time across this codebase (Tiers 1-5): `holder[a] || holder[b]` —
|
|
6
|
-
# the
|
|
6
|
+
# the same receiver looked up by two different keys, falling back to
|
|
7
7
|
# the second lookup whenever the first is falsy. `||` cannot tell a
|
|
8
|
-
# genuinely
|
|
8
|
+
# genuinely stored `false` from a missing key — both are falsy in
|
|
9
9
|
# Ruby — so a real `false` sitting at `holder[a]` is silently
|
|
10
10
|
# discarded and `holder[b]` (usually absent, so `nil`) is returned
|
|
11
11
|
# instead. Every one of those 8+ instances was the identical
|
|
@@ -13,26 +13,30 @@ module RuboCop
|
|
|
13
13
|
# `h[k.to_sym] || h[k]`, reading a value that could arrive keyed
|
|
14
14
|
# either by symbol or by string off the wire).
|
|
15
15
|
#
|
|
16
|
-
#
|
|
16
|
+
# ## The fix
|
|
17
|
+
#
|
|
18
|
+
# The fix this codebase already converged on — see
|
|
17
19
|
# `lib/hecks/query_specification/field_path.rb#read`, the shared
|
|
18
20
|
# digger this whole bug class got consolidated behind: check
|
|
19
|
-
# `key?`
|
|
21
|
+
# `key?` first, never fall back through `||`.
|
|
20
22
|
#
|
|
21
23
|
# sym = segment.to_sym
|
|
22
24
|
# return current.key?(sym) ? current[sym] : current[segment]
|
|
23
25
|
#
|
|
24
26
|
# That method's own comment says it plainly: "`key?` first, never
|
|
25
27
|
# `||`, because `||` falls through a genuinely-stored `false` to
|
|
26
|
-
# the
|
|
27
|
-
# This cop exists so the
|
|
28
|
+
# the other spelling (usually absent) and returns `nil` instead."
|
|
29
|
+
# This cop exists so the next `holder[a] || holder[b]` gets caught
|
|
28
30
|
# mechanically, before it becomes bug #9, rather than found by
|
|
29
31
|
# hand again in a future audit.
|
|
30
32
|
#
|
|
31
|
-
#
|
|
33
|
+
# ## Scope
|
|
34
|
+
#
|
|
35
|
+
# Scoped to the `[]`/`[]` shape only, deliberately — a receiver
|
|
32
36
|
# method-call is compared by AST structure (`==`, which ignores
|
|
33
37
|
# source location), so `hash[a] || hash[b]` is flagged whether
|
|
34
38
|
# `hash` is a local variable, a method call, or a constant, but
|
|
35
|
-
# `a[k] || b[k]` (
|
|
39
|
+
# `a[k] || b[k]` (different receivers) and `value || default`
|
|
36
40
|
# (an ordinary default, not a second lookup at all) are both left
|
|
37
41
|
# alone — neither one can silently drop a stored `false` the way
|
|
38
42
|
# a same-receiver double-lookup can.
|
|
@@ -58,6 +62,10 @@ module RuboCop
|
|
|
58
62
|
# @!method bracket_lookup(node)
|
|
59
63
|
def_node_matcher :bracket_lookup, "(send $_receiver :[] $_key)"
|
|
60
64
|
|
|
65
|
+
# Flags an `a[k] || a[k2]` double bracket-lookup on the same receiver.
|
|
66
|
+
#
|
|
67
|
+
# @param node [RuboCop::AST::OrNode] the `||` node being visited
|
|
68
|
+
# @return [void]
|
|
61
69
|
def on_or(node)
|
|
62
70
|
lhs_receiver, lhs_key = bracket_lookup(node.lhs)
|
|
63
71
|
return unless lhs_receiver
|
|
@@ -65,11 +73,11 @@ module RuboCop
|
|
|
65
73
|
rhs_receiver, rhs_key = bracket_lookup(node.rhs)
|
|
66
74
|
return unless rhs_receiver
|
|
67
75
|
|
|
68
|
-
#
|
|
76
|
+
# **The structural equality check** — `==` on an AST node (from the
|
|
69
77
|
# `ast` gem `Node` this compiles down to) compares `type` and
|
|
70
78
|
# `children` recursively and ignores source location, so
|
|
71
79
|
# `hash[a] || hash[b]` matches even though the two `hash`
|
|
72
|
-
# sub-nodes are two distinct node
|
|
80
|
+
# sub-nodes are two distinct node objects parsed from two
|
|
73
81
|
# different source ranges. A different receiver on each side
|
|
74
82
|
# (`a[k] || b[k]`) fails this check and is correctly left alone.
|
|
75
83
|
return unless lhs_receiver == rhs_receiver
|
|
@@ -1,47 +1,54 @@
|
|
|
1
1
|
module RuboCop
|
|
2
2
|
module Cop
|
|
3
3
|
module Hecks
|
|
4
|
-
#
|
|
5
|
-
# both
|
|
4
|
+
# Flags `hash[new] = hash.delete(old)` inside a loop — a method that
|
|
5
|
+
# both reads and writes the same hash across a loop's iterations,
|
|
6
6
|
# in the specific rename/permutation shape this codebase has
|
|
7
7
|
# already lost real data to once.
|
|
8
8
|
#
|
|
9
|
-
#
|
|
9
|
+
# ## The bug this follows up on
|
|
10
|
+
#
|
|
11
|
+
# The bug this is the mechanical follow-up to (M27,
|
|
10
12
|
# docs/audits/2026-08-10-main-bug-audit.md,
|
|
11
13
|
# docs/audits/2026-08-11-bug-triage.md; fixed in
|
|
12
14
|
# `Hecks::Ports::Persistence::Lineage#apply_renames`,
|
|
13
15
|
# lib/hecks/ports/persistence/plugins/era/lineage.rb): a rename
|
|
14
16
|
# rule set applied one rule at a time, `state[new_name] =
|
|
15
|
-
# state.delete(old_name)` per rule, against the
|
|
17
|
+
# state.delete(old_name)` per rule, against the same hash it was
|
|
16
18
|
# reading from — so a swap (`rename :a, to: :b` alongside `rename
|
|
17
19
|
# :b, to: :a`) on `{a: 1, b: 2}` collapsed to `{a: 1}`. The first
|
|
18
20
|
# rule wrote `b: 1` (clobbering the real `b: 2`) before the second
|
|
19
21
|
# rule ever got a chance to read the value that was supposed to
|
|
20
22
|
# move to `:a` — sequential edits stepping on each other, not the
|
|
21
23
|
# simultaneous permutation a rename set actually means. The same
|
|
22
|
-
# shape loses data identically for
|
|
24
|
+
# shape loses data identically for any chain longer than one link
|
|
23
25
|
# (`a->b->c->a`), not just a two-element swap.
|
|
24
26
|
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
+
# ## The fix this cop points at
|
|
28
|
+
#
|
|
29
|
+
# Snapshot every old key's value
|
|
30
|
+
# first, delete all old keys, then write all new keys, so no
|
|
27
31
|
# rename in the pass ever reads a key this same pass has already
|
|
28
32
|
# written:
|
|
29
33
|
#
|
|
30
|
-
# snapshot = renames.filter_map { |old_name, new_name| [old_name, new_name, state[old_name]]
|
|
34
|
+
# snapshot = renames.filter_map { |old_name, new_name| [old_name, new_name, state[old_name]]
|
|
35
|
+
# if state.key?(old_name) }
|
|
31
36
|
# snapshot.each { |old_name, _new_name, _value| state.delete(old_name) }
|
|
32
37
|
# snapshot.each { |_old_name, new_name, value| state[new_name] = value }
|
|
33
38
|
#
|
|
34
|
-
#
|
|
39
|
+
# ## What this cop does not attempt
|
|
40
|
+
#
|
|
41
|
+
# The general checklist item is
|
|
35
42
|
# broader than any AST pattern can safely automate ("any method
|
|
36
43
|
# that both reads and writes the same collection across a loop"
|
|
37
44
|
# covers plenty of correct code too — accumulating into a result
|
|
38
45
|
# hash, memoizing into a cache). This cop stays narrow and
|
|
39
|
-
# precise: only the
|
|
46
|
+
# precise: only the exact `recv[x] = recv.delete(y)` shape, same
|
|
40
47
|
# receiver both sides, inside something that iterates. A single
|
|
41
48
|
# rename outside any loop is not this bug (nothing else can have
|
|
42
49
|
# run first); `hash.store(...)` instead of `hash[...] =`, or
|
|
43
50
|
# `hash.delete(...)` whose result is not immediately written back
|
|
44
|
-
# into the
|
|
51
|
+
# into the same hash, are not this shape either and are left to
|
|
45
52
|
# the broader code-review checklist item, not this cop.
|
|
46
53
|
#
|
|
47
54
|
# @example
|
|
@@ -51,7 +58,8 @@ module RuboCop
|
|
|
51
58
|
# end
|
|
52
59
|
#
|
|
53
60
|
# # good — snapshot first, then delete, then write: one permutation
|
|
54
|
-
# snapshot = renames.filter_map { |old_name, new_name| [old_name, new_name, state[old_name]]
|
|
61
|
+
# snapshot = renames.filter_map { |old_name, new_name| [old_name, new_name, state[old_name]]
|
|
62
|
+
# if state.key?(old_name) }
|
|
55
63
|
# snapshot.each { |old_name, _new_name, _value| state.delete(old_name) }
|
|
56
64
|
# snapshot.each { |_old_name, new_name, value| state[new_name] = value }
|
|
57
65
|
class SequentialHashRenameInLoop < Base
|
|
@@ -82,6 +90,10 @@ module RuboCop
|
|
|
82
90
|
(send $_recv :[]= _new_key (send $_recv2 :delete _old_key))
|
|
83
91
|
PATTERN
|
|
84
92
|
|
|
93
|
+
# Flags a `recv[new] = recv.delete(old)` write inside a loop, same receiver both sides.
|
|
94
|
+
#
|
|
95
|
+
# @param node [RuboCop::AST::SendNode] the `[]=` send node being visited
|
|
96
|
+
# @return [void]
|
|
85
97
|
def on_send(node)
|
|
86
98
|
rename_write?(node) do |recv, recv2|
|
|
87
99
|
next unless same_receiver?(recv, recv2)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module RuboCop
|
|
2
2
|
module Cop
|
|
3
3
|
module Hecks
|
|
4
|
-
#
|
|
4
|
+
# Flags plain `@ivar` mutation inside a class this codebase already
|
|
5
5
|
# knows is a thread-shared singleton — one `Dispatcher`/`Registry`
|
|
6
6
|
# instance lives for the life of a boot and is dispatched through
|
|
7
7
|
# from every thread of a Puma worker pool (or any other multi-
|
|
@@ -9,29 +9,35 @@ module RuboCop
|
|
|
9
9
|
# shared, mutable state with no per-thread or mutex-guarded
|
|
10
10
|
# isolation at all.
|
|
11
11
|
#
|
|
12
|
-
#
|
|
12
|
+
# ## The bug this follows up on
|
|
13
|
+
#
|
|
14
|
+
# This is the mechanical follow-up to a real bug already fixed here
|
|
13
15
|
# (see `dispatcher.rb`'s own `#reenter` comment, and
|
|
14
|
-
# `spec/runtime/dispatcher_spec.rb`): `@reaction_depth`
|
|
15
|
-
#
|
|
16
|
-
# dispatches
|
|
16
|
+
# `spec/runtime/dispatcher_spec.rb`): a plain `@reaction_depth` ivar on
|
|
17
|
+
# `Dispatcher` let two threads' concurrent top-level
|
|
18
|
+
# dispatches corrupt each other's view of "how deep into a
|
|
17
19
|
# reaction cascade am I". The fix moved that one ivar to
|
|
18
20
|
# `Thread.current[:hecks_reaction_depth]`. This cop exists so the
|
|
19
|
-
#
|
|
21
|
+
# next plain ivar someone adds to either class gets flagged before
|
|
20
22
|
# it becomes the next instance of the same bug, rather than after.
|
|
21
23
|
#
|
|
22
|
-
#
|
|
24
|
+
# ## Scoped narrowly on purpose
|
|
25
|
+
#
|
|
26
|
+
# By class name
|
|
23
27
|
# (`Hecks::Runtime::Dispatcher`/`Hecks::Runtime::Registry`), not by
|
|
24
28
|
# blanket-flagging every ivar mutation in the codebase. Most classes
|
|
25
|
-
# in this codebase are
|
|
29
|
+
# in this codebase are not shared across threads (a fresh value
|
|
26
30
|
# object per call, a builder used once at boot) and ivar mutation
|
|
27
31
|
# there is completely ordinary Ruby with no hazard behind it at
|
|
28
32
|
# all — flagging it everywhere would be noise nobody trusts, the
|
|
29
33
|
# same reasoning `.rubocop.yml`'s own header gives for every other
|
|
30
34
|
# cop in this repo.
|
|
31
35
|
#
|
|
32
|
-
#
|
|
36
|
+
# ## What counts as "plain"
|
|
37
|
+
#
|
|
38
|
+
# `@ivar = ...`, `@ivar ||= ...`, `@ivar +=
|
|
33
39
|
# ...`, `@ivar << ...`, `@ivar[k] = v`. `initialize` is exempt — an
|
|
34
|
-
# ivar being
|
|
40
|
+
# ivar being set up for the first time, before any other thread can
|
|
35
41
|
# possibly hold a reference to this object, is not the hazard (see
|
|
36
42
|
# `Registry#initialize`'s own `@saga_mutex = Mutex.new`, which this
|
|
37
43
|
# cop must not flag). `Thread.current[...]`-backed state and a
|
|
@@ -67,9 +73,13 @@ module RuboCop
|
|
|
67
73
|
|
|
68
74
|
RESTRICT_ON_SEND = [:<<, :[]=].freeze
|
|
69
75
|
|
|
76
|
+
# Flags a plain `@ivar = ...` assignment inside a thread-shared class.
|
|
77
|
+
#
|
|
78
|
+
# @param node [RuboCop::AST::IvasgnNode] the ivar-assignment node being visited
|
|
79
|
+
# @return [void]
|
|
70
80
|
def on_ivasgn(node)
|
|
71
|
-
# A
|
|
72
|
-
# children. The commissioner also visits the
|
|
81
|
+
# A plain `@x = 1` parses as `(ivasgn :@x (int 1))` — two
|
|
82
|
+
# children. The commissioner also visits the bare `(ivasgn :@x)`
|
|
73
83
|
# node nested one level inside an `op_asgn`/`or_asgn` (`@x += 1`,
|
|
74
84
|
# `@x ||= 1`) as its own `ivasgn` node with only one child — that
|
|
75
85
|
# one is handled by `on_op_asgn`/`on_or_asgn` below instead, so
|
|
@@ -79,9 +89,14 @@ module RuboCop
|
|
|
79
89
|
check(node, node.children.first)
|
|
80
90
|
end
|
|
81
91
|
|
|
92
|
+
# Flags a plain `@ivar += ...` (or similar) compound assignment inside a
|
|
93
|
+
# thread-shared class.
|
|
94
|
+
#
|
|
95
|
+
# @param node [RuboCop::AST::OpAsgnNode] the compound-assignment node being visited
|
|
96
|
+
# @return [void]
|
|
82
97
|
def on_op_asgn(node)
|
|
83
98
|
# `@x += 1` parses as `(op_asgn (ivasgn :@x) :+ (int 1))` — the
|
|
84
|
-
# target is an `ivasgn` node carrying
|
|
99
|
+
# target is an `ivasgn` node carrying just the name (no value
|
|
85
100
|
# child, unlike a plain `@x = 1`), not an `ivar` node.
|
|
86
101
|
ivar_node = node.children.first
|
|
87
102
|
return unless ivar_node.is_a?(RuboCop::AST::Node) && ivar_node.ivasgn_type?
|
|
@@ -89,17 +104,26 @@ module RuboCop
|
|
|
89
104
|
check(node, ivar_node.children.first)
|
|
90
105
|
end
|
|
91
106
|
|
|
107
|
+
# Flags a plain `@ivar ||= ...` assignment inside a thread-shared class.
|
|
108
|
+
#
|
|
109
|
+
# @param node [RuboCop::AST::OrAsgnNode] the `||=` assignment node being visited
|
|
110
|
+
# @return [void]
|
|
92
111
|
def on_or_asgn(node)
|
|
93
112
|
on_op_asgn(node)
|
|
94
113
|
end
|
|
95
114
|
|
|
115
|
+
# Flags a plain `@ivar << ...` or `@ivar[k] = v` mutation inside a thread-shared
|
|
116
|
+
# class.
|
|
117
|
+
#
|
|
118
|
+
# @param node [RuboCop::AST::SendNode] the `<<` or `[]=` send node being visited
|
|
119
|
+
# @return [void]
|
|
96
120
|
def on_send(node)
|
|
97
121
|
return unless RESTRICT_ON_SEND.include?(node.method_name)
|
|
98
122
|
|
|
99
123
|
receiver = node.receiver
|
|
100
124
|
return unless receiver
|
|
101
125
|
|
|
102
|
-
# `@ivar << x` — the receiver
|
|
126
|
+
# `@ivar << x` — the receiver is the ivar.
|
|
103
127
|
# `@ivar[k] = v` — the receiver is a `send(@ivar, :[], k)`, whose
|
|
104
128
|
# own receiver is the ivar (`node.receiver.receiver`).
|
|
105
129
|
ivar_node = if receiver.ivar_type?
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hecks
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chris Young
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: prism
|
|
@@ -55,6 +55,8 @@ files:
|
|
|
55
55
|
- lib/hecks/adapters/driven/identity_registry.adapter
|
|
56
56
|
- lib/hecks/adapters/driven/identity_registry.rb
|
|
57
57
|
- lib/hecks/adapters/driven/in_memory_ordering.rb
|
|
58
|
+
- lib/hecks/adapters/driven/in_process_key_vault.adapter
|
|
59
|
+
- lib/hecks/adapters/driven/in_process_key_vault.rb
|
|
58
60
|
- lib/hecks/adapters/driven/lambda.adapter
|
|
59
61
|
- lib/hecks/adapters/driven/lambda.rb
|
|
60
62
|
- lib/hecks/adapters/driven/lambda/client.rb
|
|
@@ -83,6 +85,8 @@ files:
|
|
|
83
85
|
- lib/hecks/adapters/driven/sqlite/schema_builder.rb
|
|
84
86
|
- lib/hecks/adapters/driven/system_clock.adapter
|
|
85
87
|
- lib/hecks/adapters/driven/system_clock.rb
|
|
88
|
+
- lib/hecks/adapters/driven/tenant_provisioner.adapter
|
|
89
|
+
- lib/hecks/adapters/driven/tenant_provisioner.rb
|
|
86
90
|
- lib/hecks/adapters/driving/github_webhook.rb
|
|
87
91
|
- lib/hecks/behaviors.rb
|
|
88
92
|
- lib/hecks/behaviors/dsl.rb
|
|
@@ -114,6 +118,7 @@ files:
|
|
|
114
118
|
- lib/hecks/bluebook/behaviour/read_model.rb
|
|
115
119
|
- lib/hecks/bluebook/behaviour/traits.rb
|
|
116
120
|
- lib/hecks/bluebook/behaviour/value_object.rb
|
|
121
|
+
- lib/hecks/bluebook/capabilities.rb
|
|
117
122
|
- lib/hecks/bluebook/chapter.rb
|
|
118
123
|
- lib/hecks/bluebook/command.rb
|
|
119
124
|
- lib/hecks/bluebook/domain_port.rb
|
|
@@ -125,6 +130,7 @@ files:
|
|
|
125
130
|
- lib/hecks/bluebook/dsl/binding_proxy.rb
|
|
126
131
|
- lib/hecks/bluebook/dsl/bluebook_builder.rb
|
|
127
132
|
- lib/hecks/bluebook/dsl/bluebook_builder/validation.rb
|
|
133
|
+
- lib/hecks/bluebook/dsl/bootstrap_table.rb
|
|
128
134
|
- lib/hecks/bluebook/dsl/command_builder.rb
|
|
129
135
|
- lib/hecks/bluebook/dsl/const_shim.rb
|
|
130
136
|
- lib/hecks/bluebook/dsl/domain_port_builder.rb
|
|
@@ -183,6 +189,7 @@ files:
|
|
|
183
189
|
- lib/hecks/bluebook/value_object.rb
|
|
184
190
|
- lib/hecks/codemod.rb
|
|
185
191
|
- lib/hecks/construct.rb
|
|
192
|
+
- lib/hecks/corpus.rb
|
|
186
193
|
- lib/hecks/deploy/bluebook/deploy.bluebook
|
|
187
194
|
- lib/hecks/deploy/bluebook/deploy.hecksagon
|
|
188
195
|
- lib/hecks/deploy/oidc.json
|
|
@@ -220,15 +227,22 @@ files:
|
|
|
220
227
|
- lib/hecks/framework/bluebook/framework.hecksagon
|
|
221
228
|
- lib/hecks/framework/bluebook/governance.bluebook
|
|
222
229
|
- lib/hecks/framework/bluebook/identity.bluebook
|
|
230
|
+
- lib/hecks/framework/bluebook/privacy.bluebook
|
|
223
231
|
- lib/hecks/framework/oidc.json
|
|
224
232
|
- lib/hecks/freezer.rb
|
|
225
233
|
- lib/hecks/fuzzing.rb
|
|
226
234
|
- lib/hecks/fuzzing/bounded_exhaustive_expressions.rb
|
|
235
|
+
- lib/hecks/fuzzing/combination_miner.rb
|
|
227
236
|
- lib/hecks/fuzzing/concurrent_dispatch.rb
|
|
237
|
+
- lib/hecks/fuzzing/coverage_campaign.rb
|
|
238
|
+
- lib/hecks/fuzzing/differential.rb
|
|
239
|
+
- lib/hecks/fuzzing/domain_generator.rb
|
|
228
240
|
- lib/hecks/fuzzing/era_boundary.rb
|
|
229
241
|
- lib/hecks/fuzzing/form_census.rb
|
|
242
|
+
- lib/hecks/fuzzing/generated_domain_check.rb
|
|
230
243
|
- lib/hecks/fuzzing/invalid_value_generator.rb
|
|
231
244
|
- lib/hecks/fuzzing/isolated_boot.rb
|
|
245
|
+
- lib/hecks/fuzzing/nondeterministic.rb
|
|
232
246
|
- lib/hecks/fuzzing/persistence_parity.rb
|
|
233
247
|
- lib/hecks/fuzzing/properties.rb
|
|
234
248
|
- lib/hecks/fuzzing/properties/corrections.rb
|
|
@@ -238,8 +252,10 @@ files:
|
|
|
238
252
|
- lib/hecks/fuzzing/properties/lifecycle_and_replay.rb
|
|
239
253
|
- lib/hecks/fuzzing/properties/outbox.rb
|
|
240
254
|
- lib/hecks/fuzzing/properties/querying.rb
|
|
255
|
+
- lib/hecks/fuzzing/qa_settings.rb
|
|
241
256
|
- lib/hecks/fuzzing/replay.rb
|
|
242
257
|
- lib/hecks/fuzzing/rotation_priority.rb
|
|
258
|
+
- lib/hecks/fuzzing/rust_gap_manifest.rb
|
|
243
259
|
- lib/hecks/fuzzing/self_consistency.rb
|
|
244
260
|
- lib/hecks/fuzzing/sequence_generator.rb
|
|
245
261
|
- lib/hecks/fuzzing/sequence_generator/adversary.rb
|
|
@@ -247,6 +263,7 @@ files:
|
|
|
247
263
|
- lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb
|
|
248
264
|
- lib/hecks/fuzzing/sequence_generator/picker.rb
|
|
249
265
|
- lib/hecks/fuzzing/sequence_generator/step_builder.rb
|
|
266
|
+
- lib/hecks/fuzzing/shrinker.rb
|
|
250
267
|
- lib/hecks/fuzzing/structural_skips.rb
|
|
251
268
|
- lib/hecks/fuzzing/sweep_depth.rb
|
|
252
269
|
- lib/hecks/fuzzing/target_capabilities.rb
|
|
@@ -305,12 +322,15 @@ files:
|
|
|
305
322
|
- lib/hecks/ports/identity_generation.rb
|
|
306
323
|
- lib/hecks/ports/identity_resolution.port
|
|
307
324
|
- lib/hecks/ports/identity_resolution.rb
|
|
325
|
+
- lib/hecks/ports/key_vault.port
|
|
326
|
+
- lib/hecks/ports/key_vault.rb
|
|
308
327
|
- lib/hecks/ports/loading.port
|
|
309
328
|
- lib/hecks/ports/loading.rb
|
|
310
329
|
- lib/hecks/ports/persistence.port
|
|
311
330
|
- lib/hecks/ports/persistence.rb
|
|
312
331
|
- lib/hecks/ports/persistence/append_only.rb
|
|
313
332
|
- lib/hecks/ports/persistence/binding_policy.rb
|
|
333
|
+
- lib/hecks/ports/persistence/codec_boundary.rb
|
|
314
334
|
- lib/hecks/ports/persistence/execution.rb
|
|
315
335
|
- lib/hecks/ports/persistence/null_saga_store.rb
|
|
316
336
|
- lib/hecks/ports/persistence/plugin.rb
|
|
@@ -350,12 +370,14 @@ files:
|
|
|
350
370
|
- lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb
|
|
351
371
|
- lib/hecks/ports/persistence/remote_runtime.rb
|
|
352
372
|
- lib/hecks/ports/persistence/repository_factory.rb
|
|
373
|
+
- lib/hecks/ports/persistence/state_codec.rb
|
|
353
374
|
- lib/hecks/ports/projection.port
|
|
354
375
|
- lib/hecks/ports/projection.rb
|
|
355
376
|
- lib/hecks/ports/query.rb
|
|
356
377
|
- lib/hecks/ports/query/in_memory.rb
|
|
357
378
|
- lib/hecks/ports/query/ordering.rb
|
|
358
379
|
- lib/hecks/projections.rb
|
|
380
|
+
- lib/hecks/projections/bootstrap_table.rb
|
|
359
381
|
- lib/hecks/projections/diagrams.rb
|
|
360
382
|
- lib/hecks/projections/glossary.rb
|
|
361
383
|
- lib/hecks/projections/glossary/html.rb
|
|
@@ -371,6 +393,7 @@ files:
|
|
|
371
393
|
- lib/hecks/projections/oidc.rb
|
|
372
394
|
- lib/hecks/projections/parser_table.rb
|
|
373
395
|
- lib/hecks/projections/reference.rb
|
|
396
|
+
- lib/hecks/projections/rust_vocabulary.rb
|
|
374
397
|
- lib/hecks/projections/shape.rb
|
|
375
398
|
- lib/hecks/projections/statements.rb
|
|
376
399
|
- lib/hecks/projections/vocabulary.rb
|
|
@@ -426,6 +449,7 @@ files:
|
|
|
426
449
|
- lib/hecks/runtime/identity.rb
|
|
427
450
|
- lib/hecks/runtime/instance.rb
|
|
428
451
|
- lib/hecks/runtime/interpreting.rb
|
|
452
|
+
- lib/hecks/runtime/invocation.rb
|
|
429
453
|
- lib/hecks/runtime/loader.rb
|
|
430
454
|
- lib/hecks/runtime/outbox.rb
|
|
431
455
|
- lib/hecks/runtime/policy_interpreter.rb
|
|
@@ -452,6 +476,8 @@ files:
|
|
|
452
476
|
- lib/hecks/runtime/value/entity_list_coercion.rb
|
|
453
477
|
- lib/hecks/runtime/value/invariant_violation.rb
|
|
454
478
|
- lib/hecks/storehouse.rb
|
|
479
|
+
- lib/hecks/tenancy/bluebook/tenancy.bluebook
|
|
480
|
+
- lib/hecks/tenancy/bluebook/tenancy.hecksagon
|
|
455
481
|
- lib/hecks/version.rb
|
|
456
482
|
- lib/hecks/vocabulary.rb
|
|
457
483
|
- lib/rubocop/cop/hecks/fallback_hash_lookup.rb
|