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
|
@@ -6,16 +6,32 @@ module Hecks
|
|
|
6
6
|
# from the IR it is handed, which is why the Postgres mint path can
|
|
7
7
|
# call it too.
|
|
8
8
|
module ShapeDiff
|
|
9
|
+
# Computes an aggregate's structural signature, for comparing two eras of it.
|
|
10
|
+
#
|
|
11
|
+
# @param aggregate [Bluebook::Aggregate] the aggregate to describe
|
|
12
|
+
# @return [Array<Array>] one `[name, signature]` pair per attribute, sorted by name;
|
|
13
|
+
# `name` is a Symbol and `signature` is whatever `attribute_signature` returns
|
|
9
14
|
def shape(aggregate)
|
|
10
15
|
aggregate.attributes.map do |attribute|
|
|
11
16
|
[attribute.name, attribute_signature(aggregate, attribute.type)]
|
|
12
17
|
end.sort_by(&:first)
|
|
13
18
|
end
|
|
14
19
|
|
|
20
|
+
# Expands a declared type into a signature that exposes its members.
|
|
21
|
+
#
|
|
15
22
|
# A plain type name for a primitive; `[type_name, member_signatures]`
|
|
16
23
|
# for a value object or entity, walked recursively — so two
|
|
17
24
|
# attributes with the same declared type name but a different
|
|
18
25
|
# internal shape are never mistaken for unchanged.
|
|
26
|
+
#
|
|
27
|
+
# @param aggregate [Bluebook::Aggregate] the aggregate whose value objects and entities
|
|
28
|
+
# are searched for `type_name`
|
|
29
|
+
# @param type_name [String, Bluebook::Reference] the attribute's declared type
|
|
30
|
+
# @param seen [Array<String, Bluebook::Reference>] types already being expanded, which
|
|
31
|
+
# stops a self-referencing type from recursing forever
|
|
32
|
+
# @return [String, Bluebook::Reference, Array] `type_name` itself for a primitive, a
|
|
33
|
+
# reference, or a type already in `seen`; otherwise `[type_name, members]`, where
|
|
34
|
+
# `members` is an Array of `[member_name, signature]` pairs sorted by member name
|
|
19
35
|
def attribute_signature(aggregate, type_name, seen = [])
|
|
20
36
|
container = nested_type(aggregate, type_name)
|
|
21
37
|
return type_name if container.nil? || seen.include?(type_name)
|
|
@@ -26,13 +42,21 @@ module Hecks
|
|
|
26
42
|
[type_name, members]
|
|
27
43
|
end
|
|
28
44
|
|
|
45
|
+
# Looks a type name up among an aggregate's value objects, then its entities.
|
|
46
|
+
#
|
|
47
|
+
# @param aggregate [Bluebook::Aggregate] the aggregate to search
|
|
48
|
+
# @param type_name [String, Bluebook::Reference] the declared type to find
|
|
49
|
+
# @return [Bluebook::ValueObject, Bluebook::Entity, nil] the matching declaration; nil
|
|
50
|
+
# when the type is a primitive, a reference, or nothing this aggregate declares
|
|
29
51
|
def nested_type(aggregate, type_name)
|
|
30
52
|
aggregate.value_object(type_name) || aggregate.entities.find { |entity| entity.name == type_name }
|
|
31
53
|
end
|
|
32
54
|
|
|
55
|
+
# Lists the new attributes that would leave an existing record with a required gap.
|
|
56
|
+
#
|
|
33
57
|
# Attributes present now that the held shape never had — the
|
|
34
|
-
# addition side of drift, which `uncovered_attributes`
|
|
35
|
-
# looks at, since it only walks the
|
|
58
|
+
# addition side of drift, which `uncovered_attributes` below never
|
|
59
|
+
# looks at, since it only walks the held shape (vanish/retype).
|
|
36
60
|
# Most additions are free (ADR 0025, "Added attributes and
|
|
37
61
|
# absence"): a default:, a list_of (frozen []), or a value object
|
|
38
62
|
# whose fields all default fill an existing record automatically
|
|
@@ -40,11 +64,20 @@ module Hecks
|
|
|
40
64
|
# non-optional attribute with no way to fill itself — can leave an
|
|
41
65
|
# existing record with the field genuinely absent, and that is
|
|
42
66
|
# what this reports: unfilled by a declared translation's own
|
|
43
|
-
# `backfill`,
|
|
67
|
+
# `backfill`, or by a move/convert that lands an old field inside
|
|
44
68
|
# this brand-new attribute (`Lineage#fills?` — the destination-
|
|
45
69
|
# side question, never `explains?`'s source-side one, since a
|
|
46
70
|
# rename from Crate to Bin can introduce a top-level attribute
|
|
47
71
|
# name that never existed to have "vanished").
|
|
72
|
+
#
|
|
73
|
+
# @param aggregate [Bluebook::Aggregate] the aggregate as currently declared
|
|
74
|
+
# @param held_aggregate [Bluebook::Aggregate] the same aggregate as the held era's text
|
|
75
|
+
# declares it
|
|
76
|
+
# @param lineage [Ports::Persistence::Lineage, nil] the edge's rules for this aggregate; nil
|
|
77
|
+
# when the edge declares none, so nothing can fill an addition
|
|
78
|
+
# @return [Array<Symbol>] names of new, non-optional attributes with no default, no
|
|
79
|
+
# list cardinality, no fully-defaulted value object and no rule filling them; `[]`
|
|
80
|
+
# when every addition is safe
|
|
48
81
|
def unsafe_additions(aggregate, held_aggregate, lineage)
|
|
49
82
|
held_names = held_aggregate.attributes.map(&:name)
|
|
50
83
|
|
|
@@ -55,9 +88,17 @@ module Hecks
|
|
|
55
88
|
.map(&:name)
|
|
56
89
|
end
|
|
57
90
|
|
|
58
|
-
#
|
|
91
|
+
# Decides whether a new attribute could be genuinely absent from an existing record.
|
|
92
|
+
#
|
|
93
|
+
# The four-row table, as a predicate over the fourth row only — the
|
|
59
94
|
# other three (default:, list_of, a fully-defaulted value object)
|
|
60
95
|
# all fill an existing record for free and never reach here.
|
|
96
|
+
#
|
|
97
|
+
# @param aggregate [Bluebook::Aggregate] the aggregate declaring the attribute, searched
|
|
98
|
+
# for the value object its type may name
|
|
99
|
+
# @param attribute [Bluebook::Attribute] the newly added attribute
|
|
100
|
+
# @return [Boolean] false when the attribute is optional, has a default, is a list, or
|
|
101
|
+
# is a value object whose every field has a default; true otherwise
|
|
61
102
|
def possibly_absent?(aggregate, attribute)
|
|
62
103
|
return false if attribute.optional?
|
|
63
104
|
return false unless attribute.default.nil?
|
|
@@ -69,14 +110,24 @@ module Hecks
|
|
|
69
110
|
true
|
|
70
111
|
end
|
|
71
112
|
|
|
113
|
+
# Lists the held paths that vanished or changed type with no rule explaining them.
|
|
114
|
+
#
|
|
72
115
|
# Paths the translation needs to explain: attributes that vanished
|
|
73
116
|
# by name, attributes that kept their name but changed type (a
|
|
74
117
|
# `convert` is what lets that be declared at all), and — recursing
|
|
75
|
-
# into a same-named, same-typed value object — its
|
|
118
|
+
# into a same-named, same-typed value object — its own members
|
|
76
119
|
# vanishing or changing type one level down, reported as a dotted
|
|
77
120
|
# path ("price.currency"). A pure addition, at any depth, never
|
|
78
121
|
# needs covering; only vanish-or-retype does, matching the
|
|
79
122
|
# top-level rule at every depth.
|
|
123
|
+
#
|
|
124
|
+
# @param aggregate [Bluebook::Aggregate] the aggregate as currently declared
|
|
125
|
+
# @param held_aggregate [Bluebook::Aggregate] the same aggregate as the held era's text
|
|
126
|
+
# declares it
|
|
127
|
+
# @param lineage [Ports::Persistence::Lineage, nil] the edge's rules for this aggregate; nil
|
|
128
|
+
# when the edge declares none, so every vanished or retyped path is reported
|
|
129
|
+
# @return [Array<String>] bare attribute names and dotted member paths such as
|
|
130
|
+
# `"price.currency"`; `[]` when the whole diff is explained
|
|
80
131
|
def uncovered_attributes(aggregate, held_aggregate, lineage)
|
|
81
132
|
paths = held_aggregate.attributes.flat_map do |held_attribute|
|
|
82
133
|
current_attribute = aggregate.attribute(held_attribute.name)
|
|
@@ -90,20 +141,37 @@ module Hecks
|
|
|
90
141
|
paths.reject { |path| lineage.explains?(path) }
|
|
91
142
|
end
|
|
92
143
|
|
|
93
|
-
#
|
|
94
|
-
#
|
|
144
|
+
# Compares one path's held and current types, recursing into shared members.
|
|
145
|
+
#
|
|
146
|
+
# `held_type`/`current_type` are type names, resolved against each
|
|
147
|
+
# side's own value_object and entity declarations — neither is ever
|
|
95
148
|
# nested in the DSL, only in the type graph, so both are always
|
|
96
149
|
# looked up flat off their respective aggregate. A `list_of` entity
|
|
97
|
-
# is only reached here to
|
|
150
|
+
# is only reached here to detect a member vanish-or-retype; there is
|
|
98
151
|
# no per-element translation machinery yet (`move`/`convert`/`drop`
|
|
99
152
|
# only reach into a single nested hash, not each element of an
|
|
100
153
|
# array) — the only way to satisfy a refusal on an entity path
|
|
101
154
|
# today is a top-level `drop` of the whole list attribute, which
|
|
102
155
|
# `explains?` already recognizes as covering everything nested
|
|
103
156
|
# under it. Blunt, but loud beats silent.
|
|
157
|
+
#
|
|
158
|
+
# @param path [String] the bare or dotted path being compared, reported on a mismatch
|
|
159
|
+
# @param held_type [String, Bluebook::Reference] the type the held era declares there
|
|
160
|
+
# @param current_type [String, Bluebook::Reference] the type declared there now
|
|
161
|
+
# @param held_aggregate [Bluebook::Aggregate] the held aggregate, searched for
|
|
162
|
+
# `held_type`'s declaration
|
|
163
|
+
# @param aggregate [Bluebook::Aggregate] the current aggregate, searched for
|
|
164
|
+
# `current_type`'s declaration
|
|
165
|
+
# @param lineage [Ports::Persistence::Lineage, nil] the edge's rules, asked whether a retype
|
|
166
|
+
# pairs the two type names; nil means no retype is accepted
|
|
167
|
+
# @param seen [Array<String, Bluebook::Reference>] current types already being
|
|
168
|
+
# expanded, which stops a self-referencing type from recursing forever
|
|
169
|
+
# @return [Array<String>] `[path]` when the types differ with no retype declared; the
|
|
170
|
+
# dotted paths of members that vanished or changed type beneath it; `[]` when
|
|
171
|
+
# nothing differs or either type is not a value object or entity
|
|
104
172
|
def diff_type(path, held_type, current_type, held_aggregate, aggregate, lineage, seen = [])
|
|
105
173
|
if held_type != current_type
|
|
106
|
-
# A declared retype says the two
|
|
174
|
+
# A declared retype says the two type names mean the same shape
|
|
107
175
|
# — accept the pair, but still recurse into the members so a
|
|
108
176
|
# member drift hiding beneath the rename is caught by name.
|
|
109
177
|
return [path] unless lineage&.retype?(held_type, current_type)
|
|
@@ -16,24 +16,34 @@ module Hecks
|
|
|
16
16
|
# historical bluebook text under old grammar defaults. Both are real,
|
|
17
17
|
# independent, currently-shipped consumers.
|
|
18
18
|
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
# `PostgresEra`'s own DB-held shapes
|
|
24
|
-
# it
|
|
25
|
-
#
|
|
26
|
-
# real orchestration, not resurrected from here.
|
|
19
|
+
# There is deliberately no top-level `check!`/`check_bluebook!` here
|
|
20
|
+
# walking a registry and reading/writing a held snapshot under
|
|
21
|
+
# `data/eras/*.bluebook`: it would duplicate, on its own, the same
|
|
22
|
+
# per-aggregate walk `CoverageCheck` already performs against
|
|
23
|
+
# `PostgresEra`'s own DB-held shapes, and nothing in production would
|
|
24
|
+
# call it — an unwired driver is not kept, per ADR 0032. If one is
|
|
25
|
+
# wanted, it is built informed by `CoverageCheck`'s real orchestration.
|
|
27
26
|
module EraGuard
|
|
28
27
|
extend ShapeDiff
|
|
29
28
|
|
|
30
29
|
module_function
|
|
31
30
|
|
|
31
|
+
# Refuses the boot when a held aggregate is gone and no translation says where it went.
|
|
32
|
+
#
|
|
32
33
|
# An aggregate that existed in the held text and answers to no
|
|
33
34
|
# current name — renamed silently, with nothing declaring `was:` to
|
|
34
35
|
# explain where its data went — is exactly the disease this guards
|
|
35
36
|
# against, and a plain per-aggregate diff would never see it: the
|
|
36
37
|
# current aggregate simply has no held counterpart to compare to.
|
|
38
|
+
#
|
|
39
|
+
# @param registry [Runtime::Registry] the registry whose declared translations are
|
|
40
|
+
# searched for a `was:` or a `retired` entry naming the held aggregate
|
|
41
|
+
# @param bluebook [Bluebook::Chapter] the bluebook booting now
|
|
42
|
+
# @param held_bluebook [Bluebook::Chapter] the bluebook parsed from the held era's text
|
|
43
|
+
# @return [void]
|
|
44
|
+
# @raise [Runtime::WiringError] if a held aggregate matches no current aggregate by
|
|
45
|
+
# name, no current aggregate's translation declares it as `was:`, and no translation
|
|
46
|
+
# for this domain retires it
|
|
37
47
|
def check_vanished_aggregates!(registry, bluebook, held_bluebook)
|
|
38
48
|
held_bluebook.aggregates.each do |held_aggregate|
|
|
39
49
|
claimed = bluebook.aggregates.any? do |aggregate|
|
|
@@ -54,9 +64,18 @@ module Hecks
|
|
|
54
64
|
end
|
|
55
65
|
end
|
|
56
66
|
|
|
67
|
+
# Raises the refusal naming every changed path no translation rule explains.
|
|
68
|
+
#
|
|
57
69
|
# The Layer-1 coverage refusal — one wording, shared with whoever
|
|
58
70
|
# calls it (today, `PostgresEra::LineageManager::CoverageCheck`'s
|
|
59
71
|
# own mint-time coverage check).
|
|
72
|
+
#
|
|
73
|
+
# @param bluebook [Bluebook::Chapter] the bluebook booting now, named in the message
|
|
74
|
+
# @param aggregate [Bluebook::Aggregate] the aggregate whose shape changed
|
|
75
|
+
# @param uncovered [Array<String>] the unexplained paths, as `uncovered_attributes`
|
|
76
|
+
# returns them; must not be empty, since the first one seeds the suggested rule
|
|
77
|
+
# @return [void] never returns; always raises
|
|
78
|
+
# @raise [Runtime::WiringError] always, carrying the refusal wording
|
|
60
79
|
def refuse_uncovered!(bluebook, aggregate, uncovered)
|
|
61
80
|
raise WiringError,
|
|
62
81
|
"cannot boot #{bluebook.name}::#{aggregate.name}: its shape changed and " \
|
|
@@ -65,9 +84,18 @@ module Hecks
|
|
|
65
84
|
"#{suggestion(uncovered.first)}."
|
|
66
85
|
end
|
|
67
86
|
|
|
87
|
+
# Raises the refusal naming every new required attribute an existing record cannot fill.
|
|
88
|
+
#
|
|
68
89
|
# The addition-side sibling of refuse_uncovered! above — same
|
|
69
90
|
# wording shape, different cause: nothing vanished or changed type,
|
|
70
91
|
# something new arrived that an existing record has no way to hold.
|
|
92
|
+
#
|
|
93
|
+
# @param bluebook [Bluebook::Chapter] the bluebook booting now, named in the message
|
|
94
|
+
# @param aggregate [Bluebook::Aggregate] the aggregate that gained the attributes
|
|
95
|
+
# @param unsafe [Array<Symbol>] the attribute names, as `unsafe_additions` returns them;
|
|
96
|
+
# must not be empty, since the first one seeds the suggested `backfill`
|
|
97
|
+
# @return [void] never returns; always raises
|
|
98
|
+
# @raise [Runtime::WiringError] always, carrying the refusal wording
|
|
71
99
|
def refuse_unsafe_addition!(bluebook, aggregate, unsafe)
|
|
72
100
|
raise WiringError,
|
|
73
101
|
"cannot boot #{bluebook.name}::#{aggregate.name}: #{unsafe.map { |name| ":#{name}" }.join(', ')} " \
|
|
@@ -77,8 +105,18 @@ module Hecks
|
|
|
77
105
|
"`backfill :#{unsafe.first}, default: ...`."
|
|
78
106
|
end
|
|
79
107
|
|
|
108
|
+
# Renders a path the way a translation file spells it.
|
|
109
|
+
#
|
|
110
|
+
# @param path [String] a bare attribute name or a dotted value-object member path
|
|
111
|
+
# @return [String] the path quoted (`"price.currency"`) when dotted, otherwise as a
|
|
112
|
+
# Symbol literal (`:cost`)
|
|
80
113
|
def render_path(path) = path.include?(".") ? path.inspect : ":#{path}"
|
|
81
114
|
|
|
115
|
+
# Proposes the translation rules that would explain one uncovered path.
|
|
116
|
+
#
|
|
117
|
+
# @param path [String] a bare attribute name or a dotted value-object member path
|
|
118
|
+
# @return [String] backticked example rules: `move`/`drop` for a dotted path,
|
|
119
|
+
# `rename`/`drop` for a bare name
|
|
82
120
|
def suggestion(path)
|
|
83
121
|
if path.include?(".")
|
|
84
122
|
"`move #{path.inspect}, to: #{path.inspect}` or `drop #{path.inspect}`"
|
|
@@ -90,60 +128,79 @@ module Hecks
|
|
|
90
128
|
# Parses held source into its own IR, in a scratch registry so a past
|
|
91
129
|
# era's text never touches the one actually booting.
|
|
92
130
|
#
|
|
93
|
-
#
|
|
94
|
-
# unconditionally
|
|
131
|
+
# Normal parse first, shadow only as a fallback — never shadow-parsing
|
|
132
|
+
# unconditionally. A handful of DSL
|
|
95
133
|
# defaults fork on `MetaValidator.shadow_parsing?` for a reason
|
|
96
|
-
# that has
|
|
134
|
+
# that has nothing to do with syntax the live grammar can no longer
|
|
97
135
|
# read at all (`identified_by { }`, `belongs_to`, `has_one`,
|
|
98
136
|
# `has_many` — genuinely removed spellings, exactly what shadow-
|
|
99
137
|
# parsing exists to keep readable): `reference_to`'s own default
|
|
100
138
|
# mint name (`default_reference_name`, attribute_collector.rb)
|
|
101
|
-
#
|
|
102
|
-
# applies even to text using nothing but current, live
|
|
103
|
-
# Held text minted under the
|
|
104
|
-
# this corpus today, since nothing has ever minted a second
|
|
105
|
-
# parses fine normally;
|
|
106
|
-
#
|
|
107
|
-
#
|
|
139
|
+
# is bare under ADR 0025 and `_id`-suffixed under shadow mode, and
|
|
140
|
+
# that fork applies even to text using nothing but current, live
|
|
141
|
+
# syntax. Held text minted under the current grammar — every real
|
|
142
|
+
# era in this corpus today, since nothing has ever minted a second
|
|
143
|
+
# one — parses fine normally; under unconditional shadow mode only
|
|
144
|
+
# the reference-naming default differs, so it silently
|
|
145
|
+
# reconstructs a different shape (and hash) than a fresh parse of
|
|
108
146
|
# the identical text — the same text hashing two different ways
|
|
109
147
|
# depending on which code path read it, breaking `ensure_named!`'s
|
|
110
148
|
# own from/to edge lookup with a spurious "no translation edge
|
|
111
149
|
# covers it" refusal that has nothing to do with any real
|
|
112
150
|
# translation gap.
|
|
113
151
|
#
|
|
114
|
-
# A normal parse can only ever
|
|
152
|
+
# A normal parse can only ever succeed on text the live grammar
|
|
115
153
|
# fully understands — there is no way for it to silently produce a
|
|
116
154
|
# wrong-but-plausible answer for genuinely legacy text, since every
|
|
117
155
|
# removed spelling refuses loudly (`Malformed`) rather than
|
|
118
156
|
# degrading. So: try normal first — if the ordinary grammar reads
|
|
119
|
-
# this text without complaint, that
|
|
157
|
+
# this text without complaint, that is the canonical, unambiguous
|
|
120
158
|
# interpretation, the same one `label_of`/`mint_hash` on the same
|
|
121
159
|
# source text always computes, whoever's asking. Only on a
|
|
122
160
|
# `Malformed` refusal — the one signal that actually means "this
|
|
123
161
|
# spelling doesn't exist anymore" — fall back to the legacy
|
|
124
|
-
# grammar
|
|
162
|
+
# grammar. Any other exception (a
|
|
125
163
|
# genuine syntax error, an unrelated validation refusal) propagates
|
|
126
164
|
# unchanged; swallowing it here to retry under shadow mode would
|
|
127
165
|
# risk masking a real defect in the held text behind a confusing
|
|
128
166
|
# second failure instead of the original, more specific one.
|
|
129
167
|
#
|
|
130
168
|
# `MetaValidator.while_shadow_parsing` (ADR 0025, docs/dsl-work-
|
|
131
|
-
# slices.md's S0a) is what makes the fallback a
|
|
169
|
+
# slices.md's S0a) is what makes the fallback a legacy grammar
|
|
132
170
|
# rather than just a second copy of today's: it stops
|
|
133
171
|
# `BluebookBuilder.build` from judging this text against the
|
|
134
|
-
# grammar as it stands
|
|
135
|
-
# a removed spelling refuse
|
|
172
|
+
# grammar as it stands now, which is the one thing that would make
|
|
173
|
+
# a removed spelling refuse history the day it is removed from
|
|
136
174
|
# live source. The scratch registry is throwaway either way —
|
|
137
175
|
# nothing here is dispatched against or exposed to the real one —
|
|
138
176
|
# so skipping the judge/assemble round-trip changes nothing this
|
|
139
177
|
# method reads: `shape`, `uncovered_attributes`, and friends only
|
|
140
178
|
# ever ask the built IR for its own structure.
|
|
179
|
+
#
|
|
180
|
+
# @param source [String] the held bluebook text to evaluate
|
|
181
|
+
# @param path [String] the file path the text is evaluated as, which the predicate
|
|
182
|
+
# extractor reads from disk; callers pass a tempfile holding the same text
|
|
183
|
+
# @return [Bluebook::Chapter, nil] the first bluebook the text declares; nil when it
|
|
184
|
+
# declares none
|
|
185
|
+
# @raise [Bluebook::DSL::Malformed] if the text parses under neither the live grammar
|
|
186
|
+
# nor the legacy one
|
|
187
|
+
# @raise [SyntaxError] if the text is not valid Ruby
|
|
141
188
|
def shadow_parse(source, path)
|
|
142
189
|
parse_bluebook(source, path, shadow: false)
|
|
143
190
|
rescue Hecks::Bluebook::DSL::Malformed
|
|
144
191
|
parse_bluebook(source, path, shadow: true)
|
|
145
192
|
end
|
|
146
193
|
|
|
194
|
+
# Evaluates bluebook text once, in a throwaway registry, under one chosen grammar.
|
|
195
|
+
#
|
|
196
|
+
# @param source [String] the bluebook text to evaluate
|
|
197
|
+
# @param path [String] the file path reported to `Kernel.eval` as the text's origin
|
|
198
|
+
# @param shadow [Boolean] true evaluates inside `MetaValidator.while_shadow_parsing`
|
|
199
|
+
# (the legacy grammar); false evaluates under the live grammar
|
|
200
|
+
# @return [Bluebook::Chapter, nil] the first bluebook the text registered in the scratch
|
|
201
|
+
# registry; nil when it declares none
|
|
202
|
+
# @raise [Bluebook::DSL::Malformed] if the chosen grammar refuses the text
|
|
203
|
+
# @raise [SyntaxError] if the text is not valid Ruby
|
|
147
204
|
def parse_bluebook(source, path, shadow:)
|
|
148
205
|
scratch = Registry.new
|
|
149
206
|
loading = Ports::Loading.bootstrap
|
|
@@ -6,41 +6,52 @@ require_relative "storage_shape"
|
|
|
6
6
|
module Hecks
|
|
7
7
|
module Runtime
|
|
8
8
|
# The refusal wording for a held era whose digest no longer matches its
|
|
9
|
-
# frozen text — the digest mismatch alone is what
|
|
9
|
+
# frozen text — the digest mismatch alone is what detects tampering (a
|
|
10
10
|
# plain SHA256 comparison over raw bytes, unrelated to any of this);
|
|
11
11
|
# this only supplies the wording once that's already fired.
|
|
12
12
|
#
|
|
13
|
-
#
|
|
13
|
+
# Lives here, not under `Ports::Persistence` — `project` below directly
|
|
14
14
|
# calls `Runtime::EraGuard.shadow_parse`/`Runtime::StorageShape.project`
|
|
15
15
|
# (DSL-execution machinery), the "an adapter/port reaches into the
|
|
16
16
|
# runtime instead of being handed already-computed data" shape that
|
|
17
17
|
# has caused trouble here before. A capability this dependent on the
|
|
18
|
-
# runtime is a runtime-owned one that the Postgres adapter
|
|
18
|
+
# runtime is a runtime-owned one that the Postgres adapter calls, not
|
|
19
19
|
# a ports-level module that happens to reach sideways into it.
|
|
20
20
|
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
# already, not a normal boot path.
|
|
21
|
+
# Every tamper refusal reaches the same generic wording, cosmetic edit
|
|
22
|
+
# or real shape change alike: telling the two apart by re-parsing the
|
|
23
|
+
# edited text is a pure quality-of-message nicety, not a safety
|
|
24
|
+
# property, and an edit that cannot be classified has to fall to the
|
|
25
|
+
# generic wording anyway. An operator judges "did this matter"
|
|
26
|
+
# themselves, reading the still-archived original — an anomalous
|
|
27
|
+
# recovery moment already, not a normal boot path.
|
|
29
28
|
module EraTamper
|
|
30
29
|
module_function
|
|
31
30
|
|
|
31
|
+
# Words the boot refusal for a held era text whose digest no longer matches.
|
|
32
|
+
#
|
|
33
|
+
# @param domain [String] name of the domain whose era was edited
|
|
34
|
+
# @param ordinal [Integer] the edited era's ordinal in `hecks_eras`
|
|
35
|
+
# @return [String] the refusal message, ready to raise as a `Runtime::WiringError`
|
|
32
36
|
def refusal(domain:, ordinal:)
|
|
33
37
|
"cannot boot #{domain}: the held text of era #{ordinal} was edited after it was frozen — " \
|
|
34
38
|
"held era texts are storage facts; restore the original text, or reset the data"
|
|
35
39
|
end
|
|
36
40
|
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
41
|
+
# Parses a bluebook text and returns its storage-shape projection.
|
|
42
|
+
#
|
|
43
|
+
# The projection is JSON-normalized for structural comparison
|
|
44
|
+
# against a stored projection; nil when the text does not load.
|
|
45
|
+
# Always parsed through a fresh tempfile, never the held file's own
|
|
46
|
+
# path: the predicate extractor caches source by path, and a held
|
|
47
|
+
# path whose content has changed (the very situation this module
|
|
48
|
+
# exists for) would hand it stale lines.
|
|
49
|
+
#
|
|
50
|
+
# @param text [String] the bluebook source to parse
|
|
51
|
+
# @param _source_path [String, nil] ignored; the text is always parsed from a tempfile
|
|
52
|
+
# @return [Hash{String => Object}, nil] `StorageShape.project`'s Hash after a JSON
|
|
53
|
+
# round-trip; nil when the text declares no bluebook, or when parsing or projecting
|
|
54
|
+
# raises any `StandardError` or `SyntaxError`
|
|
44
55
|
def project(text, _source_path = nil)
|
|
45
56
|
file = Tempfile.new(["hecks-tamper-", ".bluebook"])
|
|
46
57
|
begin
|