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
|
@@ -10,12 +10,26 @@ module Hecks
|
|
|
10
10
|
module LineageManager
|
|
11
11
|
# What a mint must prove before it may happen: the edge covers
|
|
12
12
|
# the whole diff, no identity path was re-keyed, and the audit's
|
|
13
|
-
# first two layers pass over the
|
|
13
|
+
# first two layers pass over the live compiled chain.
|
|
14
14
|
module CoverageCheck
|
|
15
|
-
#
|
|
15
|
+
# Refuses a mint whose translation edge leaves any part of the shape change
|
|
16
|
+
# unexplained.
|
|
17
|
+
#
|
|
18
|
+
# Layer 1 against this edge specifically: every vanished or
|
|
16
19
|
# retyped path in the held→current diff must be explained, and
|
|
17
20
|
# every held aggregate must still be claimed. The refusal is
|
|
18
21
|
# EraGuard's own, byte for byte.
|
|
22
|
+
#
|
|
23
|
+
# @param registry [Runtime::Registry] the registry whose loaded translations may
|
|
24
|
+
# claim a held aggregate through `was:` or `retired`
|
|
25
|
+
# @param bluebook [Bluebook::Chapter] the domain as currently declared
|
|
26
|
+
# @param held_bluebook [Bluebook::Chapter] the domain as the latest held era's
|
|
27
|
+
# text declares it (a shadow parse)
|
|
28
|
+
# @param edge [Bluebook::Translation] the one edge leaving the latest held era
|
|
29
|
+
# @return [void]
|
|
30
|
+
# @raise [Runtime::WiringError] if an identity path changed without a `rekey`
|
|
31
|
+
# rule, a vanished or retyped path has no rule, a new required attribute has
|
|
32
|
+
# nothing to fill it, or a held aggregate is neither renamed nor retired
|
|
19
33
|
def check_coverage!(registry, bluebook, held_bluebook, edge)
|
|
20
34
|
bluebook.aggregates.each do |aggregate|
|
|
21
35
|
rules = Ports::Persistence::Lineage.from_declared(edge.for_aggregate(aggregate.name), aggregate.name)
|
|
@@ -32,15 +46,28 @@ module Hecks
|
|
|
32
46
|
Runtime::EraGuard.check_vanished_aggregates!(registry, bluebook, held_bluebook)
|
|
33
47
|
end
|
|
34
48
|
|
|
35
|
-
#
|
|
49
|
+
# Refuses a mint that changes an aggregate's identity paths without declaring
|
|
50
|
+
# a `rekey`.
|
|
51
|
+
#
|
|
52
|
+
# An identity-path change is a re-keying, not an ordinary
|
|
36
53
|
# translation — stored ids were fixed at write time under the old
|
|
37
|
-
# key, so this refuses
|
|
54
|
+
# key, so this refuses unless the edge declares a `rekey` for
|
|
38
55
|
# this aggregate covering exactly that. `rules.rekey?` (see
|
|
39
56
|
# `Ports::Persistence::Lineage`'s own comment) is the single
|
|
40
57
|
# source of truth every consumer of this fact asks — this is not
|
|
41
58
|
# a second, independent check of `declared.rekeys`.
|
|
59
|
+
#
|
|
60
|
+
# @param bluebook [Bluebook::Chapter] the domain, named in the refusal
|
|
61
|
+
# @param aggregate [Bluebook::Aggregate] the aggregate as currently declared
|
|
62
|
+
# @param held_aggregate [Bluebook::Aggregate] the same aggregate as the held
|
|
63
|
+
# era's text declares it
|
|
64
|
+
# @param rules [Ports::Persistence::Lineage, nil] the edge's rules for this
|
|
65
|
+
# aggregate; nil when the edge declares none, which cannot excuse a change
|
|
66
|
+
# @return [nil] when the identity paths are unchanged or a `rekey` covers them
|
|
67
|
+
# @raise [Runtime::WiringError] if the identity paths differ and no `rekey` rule
|
|
68
|
+
# is declared
|
|
42
69
|
def check_identity_unchanged!(bluebook, aggregate, held_aggregate, rules)
|
|
43
|
-
# The
|
|
70
|
+
# The full declared path lists, in declaration order — never the
|
|
44
71
|
# single-head shortcut, which is nil for every composite identity
|
|
45
72
|
# and so would let two different composites compare as unchanged.
|
|
46
73
|
return if held_aggregate.identity_paths == aggregate.identity_paths
|
|
@@ -56,10 +83,28 @@ module Hecks
|
|
|
56
83
|
"data explicitly"
|
|
57
84
|
end
|
|
58
85
|
|
|
59
|
-
#
|
|
86
|
+
# Previews every aggregate's translated head through the pending chain and
|
|
87
|
+
# refuses the mint if the audit finds a violation.
|
|
88
|
+
#
|
|
89
|
+
# Layers 1 and 2 of the audit, over the live compiled chain —
|
|
60
90
|
# before anything is minted, so a refusal leaves no half-born
|
|
61
91
|
# era. (A convert meeting an unmapped value raises inside the
|
|
62
92
|
# preview query itself: same rollback-shaped outcome.)
|
|
93
|
+
#
|
|
94
|
+
# @param bluebook [Bluebook::Chapter] the domain as currently declared
|
|
95
|
+
# @param lineage [Adapters::PostgresEra::Lineage] the domain's lineage, on an open
|
|
96
|
+
# connection
|
|
97
|
+
# @param chain [Array<Hash{Symbol => Bluebook::Translation}>] the full edge chain
|
|
98
|
+
# ending in `edge`, as `edge_chain` returns it
|
|
99
|
+
# @param ordinal [Integer] the ordinal of the era about to be minted
|
|
100
|
+
# @param edge [Bluebook::Translation] the pending edge, whose per-aggregate rules
|
|
101
|
+
# the audit checks
|
|
102
|
+
# @return [nil] when no aggregate reports a violation
|
|
103
|
+
# @raise [Runtime::WiringError] if Postgres refuses the translated preview query
|
|
104
|
+
# (a `convert` meeting an unmapped value, for one), or the audit reports any
|
|
105
|
+
# violation; the message lists them all
|
|
106
|
+
# @raise [PG::Error] if Postgres refuses the "before" query; only the translated
|
|
107
|
+
# preview is rescued into a `Runtime::WiringError`
|
|
63
108
|
def audit!(bluebook, lineage, chain, ordinal, edge)
|
|
64
109
|
violations = []
|
|
65
110
|
bluebook.aggregates.each do |aggregate|
|
|
@@ -5,12 +5,15 @@ module Hecks
|
|
|
5
5
|
module Adapters
|
|
6
6
|
class PostgresEra
|
|
7
7
|
module LineageManager
|
|
8
|
-
# The boot-time resolution: which era
|
|
8
|
+
# The boot-time resolution: which era is this checkout? First
|
|
9
9
|
# boot holds era 1; a quiet reboot changes nothing; a
|
|
10
10
|
# held-but-superseded shape boots read-only-toward-the-fence; an
|
|
11
11
|
# unheld shape goes to the minter.
|
|
12
12
|
module EraResolver
|
|
13
13
|
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
14
|
+
# Resolves which era this boot is and records it on the registry, holding era 1
|
|
15
|
+
# on a first boot and minting the next era when the shape is one no era holds.
|
|
16
|
+
#
|
|
14
17
|
# A single order-dependent dispatch over a closed set of four
|
|
15
18
|
# boot scenarios (first boot / quiet reboot / held-but-superseded
|
|
16
19
|
# / unheld-shape-goes-to-minter), each already explained by its
|
|
@@ -18,11 +21,32 @@ module Hecks
|
|
|
18
21
|
# `ensure`. Splitting the branches into separate methods would
|
|
19
22
|
# turn each `return` (which exits `check!`, closing `db`) into a
|
|
20
23
|
# sentinel value threaded back up, obscuring the mutual
|
|
21
|
-
# exclusivity that
|
|
24
|
+
# exclusivity that is the method's whole point.
|
|
25
|
+
#
|
|
26
|
+
# @param registry [Runtime::Registry] the booting registry; `resolved_eras` gets
|
|
27
|
+
# this domain's ordinal on every path but a first boot (where the adapter
|
|
28
|
+
# self-resolves to era 1), and `superseded_eras` gets the newest ordinal when
|
|
29
|
+
# this checkout's shape is held but superseded
|
|
30
|
+
# @param bluebook [Bluebook::Chapter] the domain being booted
|
|
31
|
+
# @param current_text [String] the domain's bluebook source as it stands on disk,
|
|
32
|
+
# held verbatim as the new era's text on a first boot or a mint
|
|
33
|
+
# @param settings [Hash{Symbol, String => Object}] the world's persistence settings
|
|
34
|
+
# for this binding; `database` is required, `schema`, `role` (a Postgres role to
|
|
35
|
+
# grant this era's privileges to) and `allow_superuser` are honored
|
|
36
|
+
# @param directory [String, nil] the domain's bluebook directory, where
|
|
37
|
+
# `HECKS_SCAFFOLD=1` writes a translation edge; nil disables scaffolding
|
|
38
|
+
# @return [Integer, nil] the ordinal of the era this boot minted, or nil when it
|
|
39
|
+
# minted nothing (first boot, quiet reboot, or a held-but-superseded shape)
|
|
40
|
+
# @raise [Runtime::WiringError] if the database cannot be reached, the connection
|
|
41
|
+
# is a superuser or has BYPASSRLS without `allow_superuser`, a declared
|
|
42
|
+
# `formerly_known_as` rename cannot be applied, a held text fails its integrity
|
|
43
|
+
# check, or the shape is unheld and `mint!` refuses
|
|
44
|
+
# @raise [Bluebook::DSL::Malformed] if a held era's text parses under neither the
|
|
45
|
+
# current nor the legacy grammar
|
|
22
46
|
def check!(registry:, bluebook:, current_text:, settings:, directory: nil)
|
|
23
47
|
db = PostgresEra.connect_for(bluebook.name, settings)
|
|
24
48
|
lineage = Lineage.new(db, bluebook.name, formerly_known_as: bluebook.formerly_known_as)
|
|
25
|
-
#
|
|
49
|
+
# First, before ensure_base! provisions or verifies anything —
|
|
26
50
|
# the one point every PostgresEra boot passes through, and the
|
|
27
51
|
# earliest at which the connection's own role is known. See
|
|
28
52
|
# check_fence_applies!'s own header (BUG#24) for why a
|
|
@@ -39,7 +63,7 @@ module Hecks
|
|
|
39
63
|
lineage.hold_first!(current_text, projection: Runtime::StorageShape.project(bluebook))
|
|
40
64
|
bluebook.aggregates.each { |aggregate| lineage.ensure_first_head!(aggregate.storage_name) }
|
|
41
65
|
# hold_first! already established era 1 as current for
|
|
42
|
-
#
|
|
66
|
+
# every role; this one just needs its own privileges.
|
|
43
67
|
lineage.grant_role!(role, aggregates: bluebook.aggregates, era: 1) if role
|
|
44
68
|
return
|
|
45
69
|
end
|
|
@@ -52,7 +76,7 @@ module Hecks
|
|
|
52
76
|
# is permanently the old one — the frozen text is authentic
|
|
53
77
|
# record ("this domain really was called that, at the
|
|
54
78
|
# time") and must never be rewritten. Only the in-memory
|
|
55
|
-
# shape used for
|
|
79
|
+
# shape used for this comparison is normalized, and only
|
|
56
80
|
# where it exactly matches the declared old name, so an
|
|
57
81
|
# unrelated domain that happens to shape-match some other
|
|
58
82
|
# domain's history still shows up as a real mismatch.
|
|
@@ -74,9 +98,9 @@ module Hecks
|
|
|
74
98
|
matched, = shapes.find { |_, shape| shape == current_shape }
|
|
75
99
|
if matched
|
|
76
100
|
# A held-but-superseded era — an old checkout still running.
|
|
77
|
-
# It may keep
|
|
101
|
+
# It may keep booting and reading (PostgresEra is the one
|
|
78
102
|
# adapter that recognizes this rather than refusing), but it
|
|
79
|
-
# may not keep
|
|
103
|
+
# may not keep writing: the shared era fence was already
|
|
80
104
|
# advanced past this ordinal by whichever mint superseded
|
|
81
105
|
# it, and nothing here may roll that back. Granting only
|
|
82
106
|
# this role's privileges, never advance_era!, is what keeps
|
|
@@ -84,11 +108,11 @@ module Hecks
|
|
|
84
108
|
# called with a superseded ordinal.
|
|
85
109
|
lineage.grant_role!(role, aggregates: bluebook.aggregates, era: matched[:ordinal]) if role
|
|
86
110
|
registry.resolved_eras[bluebook.name] = matched[:ordinal]
|
|
87
|
-
# ...and the boot
|
|
111
|
+
# ...and the boot remembers that it is superseded, not only
|
|
88
112
|
# which era it is: `RepositoryFactory.build` hands this to
|
|
89
113
|
# the adapter as `superseded_by:`, and `PostgresEra#append`
|
|
90
114
|
# refuses on it before issuing the INSERT. That is the
|
|
91
|
-
# in-process half of "may not keep
|
|
115
|
+
# in-process half of "may not keep writing" — the half that
|
|
92
116
|
# holds even for a connection the RLS fence cannot bite
|
|
93
117
|
# (a superuser under allow_superuser; BUG#24).
|
|
94
118
|
registry.superseded_eras[bluebook.name] = latest[:ordinal]
|
data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb
CHANGED
|
@@ -10,6 +10,22 @@ module Hecks
|
|
|
10
10
|
# world's post-cut writes into the head by their recorded global
|
|
11
11
|
# ordinals, under the full audit, in one transaction.
|
|
12
12
|
module MergeCoordinator
|
|
13
|
+
# Merges the writes old checkouts made after the last mint into the current
|
|
14
|
+
# era's head, on a connection of its own that it closes afterwards.
|
|
15
|
+
#
|
|
16
|
+
# @param registry [Runtime::Registry] the loaded registry; supplies the
|
|
17
|
+
# translation edges the chain is built from
|
|
18
|
+
# @param bluebook [Bluebook::Chapter] the domain whose tail is merged
|
|
19
|
+
# @param settings [Hash{Symbol, String => Object}] the world's persistence
|
|
20
|
+
# settings for this binding; `database` is required
|
|
21
|
+
# @param winners [Hash{String => String}] record id to `"old"` or `"new"`, naming
|
|
22
|
+
# which world's whole record wins for each id both worlds touched since the cut
|
|
23
|
+
# @return [true] when the merge committed
|
|
24
|
+
# @raise [Runtime::WiringError] if the database cannot be reached, the domain
|
|
25
|
+
# stands at era 1, the edge chain is broken, a record touched by both worlds
|
|
26
|
+
# has no named winner, the post-merge audit reports violations, another mint
|
|
27
|
+
# or merge holds the domain lock for 10 seconds, or Postgres refuses a
|
|
28
|
+
# statement; every refusal rolls the merge back
|
|
13
29
|
def merge!(registry:, bluebook:, settings:, winners: {})
|
|
14
30
|
db = PostgresEra.connect_for(bluebook.name, settings)
|
|
15
31
|
lineage = Lineage.new(db, bluebook.name, formerly_known_as: bluebook.formerly_known_as)
|
|
@@ -12,6 +12,29 @@ module Hecks
|
|
|
12
12
|
# reviewed, check coverage, audit the live chain, and mint — or
|
|
13
13
|
# refuse toward the authoring loop by name.
|
|
14
14
|
module Minter
|
|
15
|
+
# Mints the next era for a drifted shape in one transaction, after proving the
|
|
16
|
+
# edge is present, current, approved where it must be, covering, and audited.
|
|
17
|
+
#
|
|
18
|
+
# @param registry [Runtime::Registry] the booting registry; supplies the loaded
|
|
19
|
+
# translation edges
|
|
20
|
+
# @param bluebook [Bluebook::Chapter] the domain as currently declared
|
|
21
|
+
# @param current_text [String] the domain's bluebook source, held verbatim as the
|
|
22
|
+
# new era's text
|
|
23
|
+
# @param lineage [Adapters::PostgresEra::Lineage] the domain's lineage, on an open
|
|
24
|
+
# connection
|
|
25
|
+
# @param latest [Hash{Symbol => Object}] the newest held era, as `Lineage#eras`
|
|
26
|
+
# returns it (`:ordinal`, `:hash`, `:label`, `:held_text`, `:watermark`)
|
|
27
|
+
# @param role [String, nil] a Postgres role to grant the new era's privileges to;
|
|
28
|
+
# nil grants nothing
|
|
29
|
+
# @param directory [String, nil] the domain's bluebook directory, where
|
|
30
|
+
# `HECKS_SCAFFOLD=1` writes a missing edge; nil disables scaffolding
|
|
31
|
+
# @return [Integer] the ordinal of the era just minted
|
|
32
|
+
# @raise [Runtime::WiringError] if no edge leaves the latest era, more than one
|
|
33
|
+
# does, the edge targets another shape, a compute or rekey edge lacks a current
|
|
34
|
+
# approval, the edge leaves drift uncovered, the chain is broken, the audit
|
|
35
|
+
# reports violations, or the mint transaction itself fails
|
|
36
|
+
# @raise [Bluebook::DSL::Malformed] if the latest held text parses under neither
|
|
37
|
+
# the current nor the legacy grammar
|
|
15
38
|
def mint!(registry, bluebook, current_text, lineage, latest, role: nil, directory: nil)
|
|
16
39
|
ensure_named!(lineage, latest)
|
|
17
40
|
latest = lineage.eras.last
|
|
@@ -34,12 +57,28 @@ module Hecks
|
|
|
34
57
|
ordinal
|
|
35
58
|
end
|
|
36
59
|
|
|
37
|
-
#
|
|
38
|
-
#
|
|
60
|
+
# Finds the one translation edge that leaves the held era, and
|
|
61
|
+
# validates it: exactly one edge must leave (eras fork
|
|
39
62
|
# mechanically — a second edge from the same source is a wiring
|
|
40
63
|
# mistake, not a merge to resolve automatically), and it must
|
|
41
64
|
# target the current shape's label (otherwise the edge is stale
|
|
42
65
|
# and boot must not silently mint past it).
|
|
66
|
+
#
|
|
67
|
+
# @param registry [Runtime::Registry] the registry whose loaded `translations`
|
|
68
|
+
# are searched
|
|
69
|
+
# @param bluebook [Bluebook::Chapter] the domain as currently declared
|
|
70
|
+
# @param lineage [Adapters::PostgresEra::Lineage] the domain's lineage, used only
|
|
71
|
+
# when scaffolding a missing edge
|
|
72
|
+
# @param latest [Hash{Symbol => Object}] the newest held era, already named;
|
|
73
|
+
# `:label` is the edge's required source and `:ordinal` appears in refusals
|
|
74
|
+
# @param label [String] the current shape's label, the edge's required target
|
|
75
|
+
# @param ordinal [Integer] the ordinal of the era about to be minted
|
|
76
|
+
# @param directory [String, nil] where `HECKS_SCAFFOLD=1` writes a missing edge;
|
|
77
|
+
# nil disables scaffolding
|
|
78
|
+
# @return [Bluebook::Translation] the single edge from `latest[:label]` to `label`
|
|
79
|
+
# @raise [Runtime::WiringError] if no edge leaves the latest era (naming the
|
|
80
|
+
# scaffold it wrote, when it wrote one), more than one does, or the one edge
|
|
81
|
+
# targets a label other than `label`
|
|
43
82
|
def resolve_edge!(registry, bluebook, lineage, latest, label, ordinal, directory)
|
|
44
83
|
edges = registry.translations.select { |t| t.domain == bluebook.name && t.from == latest[:label] }
|
|
45
84
|
refuse_toward_the_scaffold!(registry, bluebook, lineage, latest, ordinal, directory) if edges.empty?
|
|
@@ -58,15 +97,29 @@ module Hecks
|
|
|
58
97
|
edge
|
|
59
98
|
end
|
|
60
99
|
|
|
100
|
+
# Refuses a mint whose edge carries a compute or rekey rule without a recorded
|
|
101
|
+
# approval that still matches the edge and the journal.
|
|
102
|
+
#
|
|
61
103
|
# A compute's (and, the same way, a rekey's) only verification
|
|
62
104
|
# is the audit's human-approved sample — mint stays
|
|
63
105
|
# non-interactive by requiring the approval to already exist,
|
|
64
|
-
# recorded
|
|
106
|
+
# recorded in this database by `bin/translation_audit …
|
|
65
107
|
# --approve` and bound to what was actually reviewed: this
|
|
66
108
|
# edge's parsed content, and the journal as it stood when the
|
|
67
109
|
# samples were read. A journal that has advanced past the
|
|
68
110
|
# review invalidates it — the approved samples no longer cover
|
|
69
111
|
# the data.
|
|
112
|
+
#
|
|
113
|
+
# @param bluebook [Bluebook::Chapter] the domain, named in the refusal
|
|
114
|
+
# @param lineage [Adapters::PostgresEra::Lineage] the domain's lineage, which holds
|
|
115
|
+
# the recorded approvals and the journal's last ordinal
|
|
116
|
+
# @param edge [Bluebook::Translation] the edge about to be minted through
|
|
117
|
+
# @param ordinal [Integer] the ordinal of the era about to be minted
|
|
118
|
+
# @return [nil] when the edge has no compute or rekey rule, or its approval is
|
|
119
|
+
# current
|
|
120
|
+
# @raise [Runtime::WiringError] if no approval is recorded for this shape pair, the
|
|
121
|
+
# approval's edge digest differs from this edge's, or the journal has advanced
|
|
122
|
+
# past the reviewed ordinal
|
|
70
123
|
def ensure_compute_rekey_approved!(bluebook, lineage, edge, ordinal)
|
|
71
124
|
return unless edge.aggregates.any? { |declared| !declared.computes.empty? || !declared.rekeys.empty? }
|
|
72
125
|
|
|
@@ -86,10 +139,28 @@ module Hecks
|
|
|
86
139
|
"human approved no longer cover the data; re-run bin/translation_audit with --approve"
|
|
87
140
|
end
|
|
88
141
|
|
|
142
|
+
# Refuses a mint that found no translation edge, naming the authoring tools —
|
|
143
|
+
# or, with scaffolding on, the file it just wrote.
|
|
144
|
+
#
|
|
89
145
|
# No edge yet: the boot refuses toward the authoring loop —
|
|
90
146
|
# naming both tools and the era ordinal. With HECKS_SCAFFOLD=1
|
|
91
|
-
# the boot
|
|
147
|
+
# the boot runs the scaffold first (an explicit flag, never a
|
|
92
148
|
# silent side-effect) and the refusal names the file it wrote.
|
|
149
|
+
#
|
|
150
|
+
# @param registry [Runtime::Registry] the registry, forwarded to `scaffold!` when
|
|
151
|
+
# scaffolding is on
|
|
152
|
+
# @param bluebook [Bluebook::Chapter] the domain, named in the refusal
|
|
153
|
+
# @param lineage [Adapters::PostgresEra::Lineage] the domain's lineage, forwarded
|
|
154
|
+
# to `scaffold!` when scaffolding is on
|
|
155
|
+
# @param latest [Hash{Symbol => Object}] the newest held era, forwarded to
|
|
156
|
+
# `scaffold!` when scaffolding is on
|
|
157
|
+
# @param ordinal [Integer] the ordinal of the era about to be minted, named in
|
|
158
|
+
# the refusal
|
|
159
|
+
# @param directory [String, nil] the domain's bluebook directory; scaffolding
|
|
160
|
+
# requires this to be present as well as `HECKS_SCAFFOLD=1`
|
|
161
|
+
# @return [void] never returns
|
|
162
|
+
# @raise [Runtime::WiringError] always; the message names the scaffolded file
|
|
163
|
+
# when `HECKS_SCAFFOLD=1` and `directory` wrote one
|
|
93
164
|
def refuse_toward_the_scaffold!(registry, bluebook, lineage, latest, ordinal, directory)
|
|
94
165
|
if ENV["HECKS_SCAFFOLD"] == "1" && directory
|
|
95
166
|
path = scaffold!(registry, bluebook, lineage, latest, directory)
|
|
@@ -104,9 +175,19 @@ module Hecks
|
|
|
104
175
|
"check it with bin/translation_audit, then boot again"
|
|
105
176
|
end
|
|
106
177
|
|
|
107
|
-
#
|
|
178
|
+
# Diffs the held era against the current shape and writes the edge
|
|
108
179
|
# file — confident rules inline, ambiguities as parse-refusing
|
|
109
|
-
# `unresolved` lines.
|
|
180
|
+
# `unresolved` lines.
|
|
181
|
+
#
|
|
182
|
+
# @param _registry [Runtime::Registry] unused; kept so the signature matches its
|
|
183
|
+
# caller's own arguments
|
|
184
|
+
# @param bluebook [Bluebook::Chapter] the domain as currently declared
|
|
185
|
+
# @param lineage [Adapters::PostgresEra::Lineage] the domain's lineage; named so
|
|
186
|
+
# `ensure_named!` can mint a name for `latest` first if it has none
|
|
187
|
+
# @param latest [Hash{Symbol => Object}] the newest held era, as `Lineage#eras`
|
|
188
|
+
# returns it
|
|
189
|
+
# @param directory [String] the domain's bluebook directory to write the edge under
|
|
190
|
+
# @return [String] the path `Translation::Scaffold::Writer#write!` wrote
|
|
110
191
|
def scaffold!(_registry, bluebook, lineage, latest, directory)
|
|
111
192
|
ensure_named!(lineage, latest)
|
|
112
193
|
latest = lineage.eras.last
|
|
@@ -126,9 +207,17 @@ module Hecks
|
|
|
126
207
|
Translation::Scaffold.write!(directory, edge)
|
|
127
208
|
end
|
|
128
209
|
|
|
210
|
+
# Mints a name for an era that has none yet, leaving an already-named era untouched.
|
|
211
|
+
#
|
|
129
212
|
# Era names are minted once. An era held before any drift was
|
|
130
213
|
# seen has no name yet; it gets one the moment an edge needs to
|
|
131
214
|
# leave it.
|
|
215
|
+
#
|
|
216
|
+
# @param lineage [Adapters::PostgresEra::Lineage] the domain's lineage, which
|
|
217
|
+
# records the minted name
|
|
218
|
+
# @param era [Hash{Symbol => Object}] the era to name, as `Lineage#eras` returns it;
|
|
219
|
+
# `:hash` present means it is already named
|
|
220
|
+
# @return [void]
|
|
132
221
|
def ensure_named!(lineage, era)
|
|
133
222
|
return if era[:hash]
|
|
134
223
|
|
|
@@ -12,7 +12,7 @@ module Hecks
|
|
|
12
12
|
class PostgresEra
|
|
13
13
|
# The PostgresEra side of the boot-time era gate. Where every other
|
|
14
14
|
# adapter's era check can only hold texts and refuse, this one
|
|
15
|
-
#
|
|
15
|
+
# acts: a drifted shape whose translation edge exists, matches by
|
|
16
16
|
# shape label, covers the whole diff, and passes the audit mints
|
|
17
17
|
# the next era — one transaction — and boots into it. Everything
|
|
18
18
|
# else refuses, as loudly and as specifically as the situation
|
|
@@ -38,10 +38,24 @@ module Hecks
|
|
|
38
38
|
|
|
39
39
|
module_function
|
|
40
40
|
|
|
41
|
-
#
|
|
41
|
+
# Collects the translation edge for every step from era 1 to the current shape.
|
|
42
|
+
#
|
|
43
|
+
# The full chain, one edge per step in original mint order —
|
|
42
44
|
# never flattened. Each step is found by its stored labels;
|
|
43
45
|
# every mint required its own edge, so a break in the chain is a
|
|
44
46
|
# deleted file, and deserves its own refusal.
|
|
47
|
+
#
|
|
48
|
+
# @param registry [Runtime::Registry] the registry whose loaded `translations`
|
|
49
|
+
# are searched
|
|
50
|
+
# @param bluebook [Bluebook::Chapter] the domain; only edges declared for its
|
|
51
|
+
# name are considered
|
|
52
|
+
# @param eras [Array<Hash{Symbol => Object}>] the ancestor eras in ordinal order,
|
|
53
|
+
# as `Lineage#eras` returns them; only `:label` is read
|
|
54
|
+
# @param current_label [String] the shape label the last step must lead to
|
|
55
|
+
# @return [Array<Hash{Symbol => Bluebook::Translation}>] one `{ translation: edge }`
|
|
56
|
+
# per step, in mint order; `[]` when `eras` is empty
|
|
57
|
+
# @raise [Runtime::WiringError] if no loaded translation leads one label to the
|
|
58
|
+
# next
|
|
45
59
|
def edge_chain(registry, bluebook, eras, current_label)
|
|
46
60
|
labels = eras.map { |era| era[:label] } + [current_label]
|
|
47
61
|
(0...(labels.size - 1)).map do |index|
|
|
@@ -57,9 +71,18 @@ module Hecks
|
|
|
57
71
|
end
|
|
58
72
|
end
|
|
59
73
|
|
|
74
|
+
# Parses a held era's bluebook text into a throwaway registry, leaving the live
|
|
75
|
+
# one untouched.
|
|
76
|
+
#
|
|
60
77
|
# Held texts live in rows, not files, but the predicate
|
|
61
78
|
# extractor reads source from disk at the eval path — so a
|
|
62
79
|
# shadow parse writes the text to a scratch file first.
|
|
80
|
+
#
|
|
81
|
+
# @param source [String] bluebook source text, as held in `hecks_eras.held_text`
|
|
82
|
+
# @return [Bluebook::Chapter, nil] the first bluebook the text declares, or nil
|
|
83
|
+
# when it declares none
|
|
84
|
+
# @raise [Bluebook::DSL::Malformed] if the text parses under neither the current
|
|
85
|
+
# grammar nor the legacy shadow-parse fallback
|
|
63
86
|
def shadow(source)
|
|
64
87
|
file = Tempfile.new(["hecks-era-", ".bluebook"])
|
|
65
88
|
file.write(source)
|