hecks 2.9.0 → 3.0.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/exe/hecks +51 -6
- data/lib/hecks/adapters/driven/heki/journal.rb +1 -1
- data/lib/hecks/adapters/driven/lambda/client.rb +1 -1
- data/lib/hecks/adapters/driven/lambda.rb +1 -1
- data/lib/hecks/adapters/driven/memory.rb +16 -3
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +6 -2
- data/lib/hecks/adapters/driven/postgres/shared_connection.rb +180 -0
- data/lib/hecks/adapters/driven/postgres.rb +4 -2
- data/lib/hecks/adapters/driven/prism.rb +41 -8
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +73 -16
- data/lib/hecks/behaviors/expectations.rb +1 -1
- data/lib/hecks/behaviors.rb +1 -1
- data/lib/hecks/bench/cli.rb +62 -0
- data/lib/hecks/bench/environment.rb +71 -0
- data/lib/hecks/bench/postgres_probe.rb +30 -0
- data/lib/hecks/bench/report.rb +76 -0
- data/lib/hecks/bench/ruby_runner.rb +93 -0
- data/lib/hecks/bench/run.rb +55 -0
- data/lib/hecks/bench/rust_runner.rb +101 -0
- data/lib/hecks/bench/stats.rb +46 -0
- data/lib/hecks/bench/suite.rb +98 -0
- data/lib/hecks/bench/workload.rb +89 -0
- data/lib/hecks/bench.rb +17 -0
- data/lib/hecks/bluebook/assembly/contracts.rb +2 -0
- data/lib/hecks/bluebook/attribute.rb +1 -1
- data/lib/hecks/bluebook/behaviour/aggregate.rb +30 -1
- data/lib/hecks/bluebook/behaviour/domain_port.rb +7 -0
- data/lib/hecks/bluebook/chapter.rb +6 -2
- data/lib/hecks/bluebook/domain_port.rb +36 -5
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +10 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +8 -0
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -1
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +31 -3
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +122 -3
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +1 -1
- data/lib/hecks/bluebook/dsl/query_builder.rb +27 -1
- data/lib/hecks/bluebook/dsl/rule_reference.rb +1 -1
- data/lib/hecks/bluebook/dsl/world_builder.rb +29 -2
- data/lib/hecks/bluebook/expression/canonical_form.rb +1 -1
- data/lib/hecks/bluebook/expression/resolver.rb +3 -2
- data/lib/hecks/bluebook/hexagon.rb +13 -5
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +1 -0
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +4 -1
- data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +246 -0
- data/lib/hecks/bluebook/meta_validator.rb +9 -4
- data/lib/hecks/bluebook/model_check.rb +23 -0
- data/lib/hecks/bluebook/pattern_subset.rb +38 -0
- data/lib/hecks/bluebook/query.rb +20 -5
- data/lib/hecks/bluebook/smoke_test.rb +1 -1
- data/lib/hecks/canonical_json.rb +30 -0
- data/lib/hecks/chapters.rb +102 -0
- data/lib/hecks/cli/behaviors.rb +61 -0
- data/lib/hecks/cli/canonicalise.rb +25 -0
- data/lib/hecks/cli/check_engine_agreement.rb +32 -0
- data/lib/hecks/cli/check_era.rb +88 -0
- data/lib/hecks/cli/console.rb +66 -0
- data/lib/hecks/cli/deploy_template_diff.rb +40 -0
- data/lib/hecks/cli/doc_coverage.rb +32 -0
- data/lib/hecks/cli/document.rb +2 -2
- data/lib/hecks/cli/expression_projection.rb +41 -0
- data/lib/hecks/cli/follow.rb +88 -0
- data/lib/hecks/cli/history.rb +44 -0
- data/lib/hecks/cli/ir.rb +1 -1
- data/lib/hecks/cli/mcp.rb +3 -3
- data/lib/hecks/cli/model_check.rb +7 -4
- data/lib/hecks/cli/pattern_cases.rb +47 -0
- data/lib/hecks/cli/present.rb +39 -0
- data/lib/hecks/cli/project_cli.rb +224 -32
- data/lib/hecks/cli/project_diagrams.rb +4 -3
- data/lib/hecks/cli/project_glossary.rb +44 -0
- data/lib/hecks/cli/reference.rb +46 -0
- data/lib/hecks/cli/refresh_projections.rb +44 -0
- data/lib/hecks/cli/refresh_rspec_runtime_baseline.rb +150 -0
- data/lib/hecks/cli/release.rb +70 -0
- data/lib/hecks/cli/release_gem.rb +59 -0
- data/lib/hecks/cli/rspec_io_parallel_files.rb +137 -0
- data/lib/hecks/cli/rspec_shard_files.rb +66 -0
- data/lib/hecks/cli/run.rb +3 -3
- data/lib/hecks/cli/seed_semantics_corpus.rb +71 -0
- data/lib/hecks/cli/serve_query_ir_mcp.rb +30 -0
- data/lib/hecks/cli/shape.rb +70 -0
- data/lib/hecks/cli/smoke_http.rb +230 -0
- data/lib/hecks/cli/smoke_test.rb +5 -3
- data/lib/hecks/cli/spec_example.rb +27 -0
- data/lib/hecks/cli/statements.rb +32 -0
- data/lib/hecks/cli/stores.rb +4 -5
- data/lib/hecks/cli/stress_concurrency_specs.rb +177 -0
- data/lib/hecks/cli.rb +201 -52
- data/lib/hecks/codemod.rb +324 -0
- data/lib/hecks/corpus.rb +423 -0
- data/lib/hecks/deploy/bluebook/deploy.bluebook +591 -23
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +41 -4
- data/lib/hecks/deploy/oidc.json +82 -2
- data/lib/hecks/doc/reference.rb +351 -0
- data/lib/hecks/doors/cli_door.rb +196 -0
- data/lib/hecks/doors/cli_runner.rb +297 -0
- data/lib/hecks/{facade → doors}/command_request.rb +1 -1
- data/lib/hecks/{facade → doors}/handle.rb +2 -2
- data/lib/hecks/{facade → doors}/json_door.rb +4 -4
- data/lib/hecks/doors/launcher_options.rb +129 -0
- data/lib/hecks/{cli → doors}/mcp_door.rb +7 -7
- data/lib/hecks/doors/mcp_door_scope.rb +97 -0
- data/lib/hecks/{facade/surface → doors/ruby_door}/aggregate_door.rb +8 -3
- data/lib/hecks/{facade/surface → doors/ruby_door}/chapter.rb +3 -3
- data/lib/hecks/{facade/surface.rb → doors/ruby_door.rb} +36 -5
- data/lib/hecks/doors.rb +61 -0
- data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +1 -1
- data/lib/hecks/embryonaut_bluebook.rb +1 -1
- data/lib/hecks/engine_agreement.rb +156 -0
- data/lib/hecks/facade.rb +24 -37
- data/lib/hecks/forms/app.rb +6 -6
- data/lib/hecks/forms/banking_presentation.rb +50 -0
- data/lib/hecks/forms/field_shape.rb +2 -1
- data/lib/hecks/forms/port_argument.rb +1 -1
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +393 -0
- data/lib/hecks/fuzzing/combination_miner.rb +86 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +214 -0
- data/lib/hecks/fuzzing/coverage_campaign.rb +143 -0
- data/lib/hecks/fuzzing/differential.rb +156 -0
- data/lib/hecks/fuzzing/domain_generator.rb +615 -0
- data/lib/hecks/fuzzing/era_boundary.rb +71 -0
- data/lib/hecks/fuzzing/form_census.rb +136 -0
- data/lib/hecks/fuzzing/generated_domain_check.rb +93 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +64 -0
- data/lib/hecks/fuzzing/isolated_boot.rb +301 -0
- data/lib/hecks/fuzzing/nondeterministic.rb +51 -0
- data/lib/hecks/fuzzing/outside_world.rb +45 -0
- data/lib/hecks/fuzzing/persistence_parity.rb +102 -0
- data/lib/hecks/fuzzing/properties/corrections.rb +88 -0
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +314 -0
- data/lib/hecks/fuzzing/properties/guards.rb +190 -0
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +300 -0
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +93 -0
- data/lib/hecks/fuzzing/properties/outbox.rb +89 -0
- data/lib/hecks/fuzzing/properties/querying.rb +175 -0
- data/lib/hecks/fuzzing/properties.rb +162 -0
- data/lib/hecks/fuzzing/qa_settings.rb +123 -0
- data/lib/hecks/fuzzing/replay.rb +505 -0
- data/lib/hecks/fuzzing/rotation_priority.rb +48 -0
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +108 -0
- data/lib/hecks/fuzzing/self_consistency.rb +339 -0
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +385 -0
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +132 -0
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +95 -0
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +77 -0
- data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +109 -0
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +237 -0
- data/lib/hecks/fuzzing/sequence_generator.rb +189 -0
- data/lib/hecks/fuzzing/shrinker.rb +166 -0
- data/lib/hecks/fuzzing/structural_skips.rb +33 -0
- data/lib/hecks/fuzzing/sweep_depth.rb +43 -0
- data/lib/hecks/fuzzing/target_capabilities.rb +163 -0
- data/lib/hecks/fuzzing/value_generator.rb +216 -0
- data/lib/hecks/fuzzing.rb +23 -0
- data/lib/hecks/grammar/evolve.rb +336 -0
- data/lib/hecks/hecks/adapters/codebase/codemods.rb +45 -0
- data/lib/hecks/hecks/adapters/codebase/conformance.rb +71 -0
- data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +202 -0
- data/lib/hecks/hecks/adapters/codebase/gem_registry.rb +80 -0
- data/lib/hecks/hecks/adapters/codebase/kernel_tables.rb +54 -0
- data/lib/hecks/hecks/adapters/codebase/language.rb +240 -0
- data/lib/hecks/hecks/adapters/codebase/npm_registry.rb +102 -0
- data/lib/hecks/hecks/adapters/codebase/publishing.rb +142 -0
- data/lib/hecks/hecks/adapters/codebase/regeneration.rb +67 -0
- data/lib/hecks/hecks/adapters/codebase/release_facts.rb +90 -0
- data/lib/hecks/hecks/adapters/codebase/ruby_child.rb +83 -0
- data/lib/hecks/hecks/adapters/codebase/secret_vault.rb +36 -0
- data/lib/hecks/hecks/adapters/codebase/source_tree.adapter +7 -0
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +189 -0
- data/lib/hecks/hecks/adapters/codebase/sqlite_fixture.rb +71 -0
- data/lib/hecks/hecks/adapters/codebase/style.rb +171 -0
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +55 -0
- data/lib/hecks/hecks/adapters/codebase/test_suite.rb +165 -0
- data/lib/hecks/hecks/adapters/codebase/tree.rb +111 -0
- data/lib/hecks/hecks/adapters/console_capture.rb +79 -0
- data/lib/hecks/hecks/adapters/deploy_toolchain.adapter +7 -0
- data/lib/hecks/hecks/adapters/deploy_toolchain.rb +108 -0
- data/lib/hecks/hecks/adapters/git.adapter +7 -0
- data/lib/hecks/hecks/adapters/git.rb +102 -0
- data/lib/hecks/hecks/adapters/host_http.adapter +7 -0
- data/lib/hecks/hecks/adapters/host_http.rb +65 -0
- data/lib/hecks/hecks/adapters/in_process_boot.adapter +7 -0
- data/lib/hecks/hecks/adapters/in_process_boot.rb +197 -0
- data/lib/hecks/hecks/adapters/in_process_operations.rb +202 -0
- data/lib/hecks/hecks/adapters/journal_store/changes.rb +143 -0
- data/lib/hecks/hecks/adapters/journal_store/compaction.rb +145 -0
- data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +132 -0
- data/lib/hecks/hecks/adapters/journal_store/examination.rb +135 -0
- data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +163 -0
- data/lib/hecks/hecks/adapters/journal_store/readings.rb +187 -0
- data/lib/hecks/hecks/adapters/journal_store.adapter +7 -0
- data/lib/hecks/hecks/adapters/journal_store.rb +64 -0
- data/lib/hecks/hecks/adapters/local_files.adapter +7 -0
- data/lib/hecks/hecks/adapters/local_files.rb +46 -0
- data/lib/hecks/hecks/adapters/pg_admin.adapter +7 -0
- data/lib/hecks/hecks/adapters/pg_admin.rb +245 -0
- data/lib/hecks/hecks/adapters/process_pool.adapter +7 -0
- data/lib/hecks/hecks/adapters/process_pool.rb +119 -0
- data/lib/hecks/hecks/adapters/rust_toolchain.adapter +7 -0
- data/lib/hecks/hecks/adapters/rust_toolchain.rb +179 -0
- data/lib/hecks/hecks/adapters/rust_workspace.rb +150 -0
- data/lib/hecks/hecks/adapters/shell.adapter +7 -0
- data/lib/hecks/hecks/adapters/shell.rb +49 -0
- data/lib/hecks/hecks/adapters/terminal.adapter +7 -0
- data/lib/hecks/hecks/adapters/terminal.rb +74 -0
- data/lib/hecks/hecks/codebase.behaviors +172 -0
- data/lib/hecks/hecks/codebase.bluebook +2453 -0
- data/lib/hecks/hecks/context_map.hecksagon +13 -0
- data/lib/hecks/hecks/custodian.behaviors +179 -0
- data/lib/hecks/hecks/custodian.bluebook +1847 -0
- data/lib/hecks/hecks/deploy.behaviors +74 -0
- data/lib/hecks/hecks/deploy.world +12 -0
- data/lib/hecks/hecks/environments/memory.world +10 -0
- data/lib/hecks/hecks/governance.world +7 -0
- data/lib/hecks/hecks/hecks.behaviors +62 -0
- data/lib/hecks/hecks/hecks.bluebook +124 -0
- data/lib/hecks/hecks/hecks.hecksagon +390 -0
- data/lib/hecks/hecks/hecks.world +42 -0
- data/lib/hecks/kernel_capabilities.rb +194 -0
- data/lib/hecks/language/bluebook/bluebook.bluebook +11 -0
- data/lib/hecks/language/bluebook/query.bluebook +7 -0
- data/lib/hecks/language/bluebook/vocabulary.bluebook +4 -4
- data/lib/hecks/language/hecksagon/domain_port.bluebook +10 -0
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +20 -0
- data/lib/hecks/mcp_stdio_guard.rb +2 -2
- data/lib/hecks/persistence_legacy_fixture/regenerate.rb +161 -0
- data/lib/hecks/persistence_legacy_fixture.rb +113 -0
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +8 -5
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +4 -1
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +15 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +32 -15
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +6 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +227 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +43 -24
- data/lib/hecks/ports/persistence/plugins/era/translation.rb +1 -0
- data/lib/hecks/ports/persistence/state_codec.rb +24 -5
- data/lib/hecks/projection_files.rb +285 -0
- data/lib/hecks/projections/bootstrap_table.rb +1 -1
- data/lib/hecks/projections/deploy/fargate.rb +5 -5
- data/lib/hecks/projections/deploy/lambda.rb +43 -32
- data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
- data/lib/hecks/projections/deploy/preview/template.rb +1 -1
- data/lib/hecks/projections/deploy/scripts/deploy-service.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/scripts/expected-era.header +1 -1
- data/lib/hecks/projections/deploy/scripts/hosting.mk.tmpl +3 -3
- data/lib/hecks/projections/deploy/scripts/smoke-after-deploy.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/shared.rb +1 -1
- data/lib/hecks/projections/diagrams.rb +3 -3
- data/lib/hecks/projections/model/deviations.rb +1 -1
- data/lib/hecks/projections/parser_table.rb +4 -4
- data/lib/hecks/projections/reference.rb +2 -3
- data/lib/hecks/projections/rust_vocabulary.rb +1 -1
- data/lib/hecks/projector/cli_projector.rb +106 -37
- data/lib/hecks/projector.rb +1 -1
- data/lib/hecks/quality_control/adapters/agent.adapter +7 -0
- data/lib/hecks/quality_control/adapters/agent.rb +58 -0
- data/lib/hecks/quality_control/adapters/angle_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/angle_tools.rb +15 -0
- data/lib/hecks/quality_control/adapters/clearance_recorder.rb +36 -0
- data/lib/hecks/quality_control/adapters/clearance_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/clearance_tools.rb +17 -0
- data/lib/hecks/quality_control/adapters/git_pr.adapter +7 -0
- data/lib/hecks/quality_control/adapters/git_pr.rb +226 -0
- data/lib/hecks/quality_control/adapters/github_checks.adapter +9 -0
- data/lib/hecks/quality_control/adapters/github_checks.rb +83 -0
- data/lib/hecks/quality_control/adapters/github_ci_webhook.rb +56 -0
- data/lib/hecks/quality_control/adapters/github_issues.adapter +7 -0
- data/lib/hecks/quality_control/adapters/github_issues.rb +49 -0
- data/lib/hecks/quality_control/adapters/qa_tool.rb +59 -0
- data/lib/hecks/quality_control/adapters/sweep_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/sweep_tools.rb +51 -0
- data/lib/hecks/quality_control/adapters/target_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/target_tools.rb +43 -0
- data/lib/hecks/quality_control/cli/child.rb +45 -0
- data/lib/hecks/quality_control/cli/qa_concurrency_racer.rb +45 -0
- data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +248 -0
- data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +163 -0
- data/lib/hecks/quality_control/cli/qa_generated_domains.rb +450 -0
- data/lib/hecks/quality_control/cli/qa_log_bug.rb +195 -0
- data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +220 -0
- data/lib/hecks/quality_control/cli/qa_open_pr.rb +305 -0
- data/lib/hecks/quality_control/cli/qa_postgres_migrate.rb +154 -0
- data/lib/hecks/quality_control/cli/qa_postgres_role.rb +159 -0
- data/lib/hecks/quality_control/cli/qa_pr_check.rb +232 -0
- data/lib/hecks/quality_control/cli/qa_seed_angles.rb +142 -0
- data/lib/hecks/quality_control/cli/qa_seed_targets.rb +69 -0
- data/lib/hecks/quality_control/cli/qa_sweep/all_mode.rb +280 -0
- data/lib/hecks/quality_control/cli/qa_sweep/checks.rb +329 -0
- data/lib/hecks/quality_control/cli/qa_sweep/dials.rb +92 -0
- data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +117 -0
- data/lib/hecks/quality_control/cli/qa_sweep/parsing.rb +175 -0
- data/lib/hecks/quality_control/cli/qa_sweep/release_mode.rb +60 -0
- data/lib/hecks/quality_control/cli/qa_sweep.rb +611 -0
- data/lib/hecks/quality_control/cli/qa_tick.rb +274 -0
- data/lib/hecks/quality_control/quality_control.bluebook +2224 -0
- data/lib/hecks/quality_control/quality_control.hecksagon +13 -0
- data/lib/hecks/quality_control/quality_control.ports.hecksagon +54 -0
- data/lib/hecks/query_ir.rb +346 -0
- data/lib/hecks/query_ir_mcp.rb +198 -0
- data/lib/hecks/release/runner/ci_publisher.rb +2 -2
- data/lib/hecks/release/runner/clean_tree.rb +42 -0
- data/lib/hecks/release/runner/console.rb +6 -0
- data/lib/hecks/release/runner/gem_publisher.rb +14 -7
- data/lib/hecks/release/runner/npm_publisher.rb +6 -36
- data/lib/hecks/release/runner/preflight.rb +26 -2
- data/lib/hecks/release/runner/published.rb +8 -26
- data/lib/hecks/release/runner/tagger.rb +30 -15
- data/lib/hecks/release/runner.rb +57 -6
- data/lib/hecks/router/namespace_installer.rb +3 -3
- data/lib/hecks/runtime/adapter_lookup.rb +124 -0
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +4 -0
- data/lib/hecks/runtime/dependency_planning.rb +20 -2
- data/lib/hecks/runtime/dispatcher.rb +53 -3
- data/lib/hecks/runtime/interpreting.rb +3 -0
- data/lib/hecks/runtime/loader.rb +65 -12
- data/lib/hecks/runtime/outbox.rb +1 -1
- data/lib/hecks/runtime/policy_interpreter.rb +5 -2
- data/lib/hecks/runtime/port_operation_interpreter.rb +2 -10
- data/lib/hecks/runtime/query_interpreter.rb +77 -0
- data/lib/hecks/runtime/reaction_outcome.rb +89 -0
- data/lib/hecks/runtime/refusal_wording.rb +23 -1
- data/lib/hecks/runtime/registry/verification.rb +163 -4
- data/lib/hecks/runtime/registry.rb +57 -3
- data/lib/hecks/runtime/remote_dispatcher.rb +31 -2
- data/lib/hecks/runtime/value/coercion.rb +23 -135
- data/lib/hecks/runtime/value/field_checks.rb +172 -0
- data/lib/hecks/runtime/value.rb +4 -1
- data/lib/hecks/runtime.rb +20 -6
- data/lib/hecks/rust_build/conformance.rb +158 -0
- data/lib/hecks/rust_build/conformance_fuzz.rb +71 -0
- data/lib/hecks/rust_build/coverage.rb +218 -0
- data/lib/hecks/rust_build/native_build.rb +124 -0
- data/lib/hecks/rust_build/project_rust/root_mod.rb +120 -0
- data/lib/hecks/rust_build/project_rust.rb +332 -0
- data/lib/hecks/rust_build/wasm.rb +105 -0
- data/lib/hecks/rust_build/wasm_browser.rb +113 -0
- data/lib/hecks/rust_build.rb +144 -0
- data/lib/hecks/storehouse.rb +16 -16
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +2 -2
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +1 -1
- data/lib/hecks/three_zero/forms.yml +127 -0
- data/lib/hecks/three_zero.rb +12 -0
- data/lib/hecks/tools/argument_gate_matrix.rb +316 -0
- data/lib/hecks/tools/comment_style.rb +1070 -0
- data/lib/hecks/tools/corpus_report.rb +87 -0
- data/lib/hecks/tools/deploy_recipe.rb +255 -0
- data/lib/hecks/tools/deploy_recipe_lint.rb +282 -0
- data/lib/hecks/tools/drop_implicit_append_fields.rb +95 -0
- data/lib/hecks/tools/era_projection_backfill.rb +94 -0
- data/lib/hecks/tools/era_reattest.rb +146 -0
- data/lib/hecks/tools/evolve_run.rb +189 -0
- data/lib/hecks/tools/fuzz_sweep.rb +373 -0
- data/lib/hecks/tools/heki_compaction.rb +100 -0
- data/lib/hecks/tools/hoist_local_givens.rb +246 -0
- data/lib/hecks/tools/journal_compaction.rb +89 -0
- data/lib/hecks/tools/oidc_manifests.rb +70 -0
- data/lib/hecks/tools/query_ir_run.rb +81 -0
- data/lib/hecks/tools/regeneration_run.rb +166 -0
- data/lib/hecks/tools/rust_comment_style.rb +642 -0
- data/lib/hecks/tools/sequence_script.rb +62 -0
- data/lib/hecks/tools/tail_merge.rb +117 -0
- data/lib/hecks/tools/tenant_provisioning.rb +142 -0
- data/lib/hecks/tools/translation_audit.rb +208 -0
- data/lib/hecks/tools/translation_scaffold.rb +131 -0
- data/lib/hecks/tools.rb +86 -0
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks.rb +32 -15
- data/qa/settings.yml +66 -0
- data/rust/.cargo/config.toml +6 -0
- data/rust/.gitignore +2 -0
- data/rust/Cargo.lock +54 -0
- data/rust/Cargo.toml +61 -0
- data/rust/build/.cargo/config.toml +3 -0
- data/rust/build/.gitignore +1 -0
- data/rust/build/Cargo.lock +7 -0
- data/rust/build/Cargo.toml +19 -0
- data/rust/build/src/build_artifact.rs +47 -0
- data/rust/build/src/cargo_sync.rs +248 -0
- data/rust/build/src/json.rs +421 -0
- data/rust/build/src/lineage_pass.rs +285 -0
- data/rust/build/src/main.rs +51 -0
- data/rust/build/src/optional_pass.rs +150 -0
- data/rust/build/src/pipeline.rs +264 -0
- data/rust/build/src/reserved_names.rs +76 -0
- data/rust/build/src/resolve.rs +124 -0
- data/rust/build/src/root.rs +25 -0
- data/rust/build/src/sidecars.rs +73 -0
- data/rust/build/src/subprocess.rs +51 -0
- data/rust/build/src/tmp.rs +31 -0
- data/rust/codegen/.cargo/config.toml +3 -0
- data/rust/codegen/.gitignore +1 -0
- data/rust/codegen/Cargo.lock +7 -0
- data/rust/codegen/Cargo.toml +20 -0
- data/rust/codegen/src/attr.rs +32 -0
- data/rust/codegen/src/bridging.rs +481 -0
- data/rust/codegen/src/commands.rs +1226 -0
- data/rust/codegen/src/constraints.rs +109 -0
- data/rust/codegen/src/dependency_planning.rs +209 -0
- data/rust/codegen/src/domain_generator.rs +1288 -0
- data/rust/codegen/src/exemplar.rs +230 -0
- data/rust/codegen/src/expr_emitter.rs +87 -0
- data/rust/codegen/src/fielded.rs +192 -0
- data/rust/codegen/src/hecks_naming.rs +33 -0
- data/rust/codegen/src/json.rs +335 -0
- data/rust/codegen/src/json_codec.rs +697 -0
- data/rust/codegen/src/literal.rs +207 -0
- data/rust/codegen/src/main.rs +290 -0
- data/rust/codegen/src/manifest.rs +227 -0
- data/rust/codegen/src/mutations.rs +784 -0
- data/rust/codegen/src/naming.rs +286 -0
- data/rust/codegen/src/ports.rs +208 -0
- data/rust/codegen/src/prelude.rs +116 -0
- data/rust/codegen/src/queries.rs +786 -0
- data/rust/codegen/src/reactions.rs +532 -0
- data/rust/codegen/src/read_models.rs +697 -0
- data/rust/codegen/src/reference_specs.rs +40 -0
- data/rust/codegen/src/registry.rs +966 -0
- data/rust/codegen/src/reserved_names.rs +76 -0
- data/rust/codegen/src/shared.rs +93 -0
- data/rust/codegen/src/skip_reason.rs +25 -0
- data/rust/codegen/src/types.rs +362 -0
- data/rust/host/.cargo/config.toml +3 -0
- data/rust/host/.gitignore +1 -0
- data/rust/host/Cargo.lock +4297 -0
- data/rust/host/Cargo.toml +61 -0
- data/rust/host/HECKS_RELEASE +1 -0
- data/rust/host/rds-ca-bundle.pem +2736 -0
- data/rust/host/src/api.rs +1596 -0
- data/rust/host/src/approval.rs +652 -0
- data/rust/host/src/auth.rs +1287 -0
- data/rust/host/src/bin/expr_harness.rs +46 -0
- data/rust/host/src/bin/lineage_harness.rs +119 -0
- data/rust/host/src/bin/mint_harness.rs +135 -0
- data/rust/host/src/checkout.rs +265 -0
- data/rust/host/src/dispatch.rs +1443 -0
- data/rust/host/src/expr_json.rs +1117 -0
- data/rust/host/src/field_hints.rs +52 -0
- data/rust/host/src/ir.rs +733 -0
- data/rust/host/src/journal.rs +874 -0
- data/rust/host/src/lambda_client.rs +378 -0
- data/rust/host/src/log.rs +147 -0
- data/rust/host/src/main.rs +474 -0
- data/rust/host/src/mint.rs +1865 -0
- data/rust/host/src/payments/keystore.rs +330 -0
- data/rust/host/src/payments/tests.rs +1400 -0
- data/rust/host/src/payments.rs +767 -0
- data/rust/host/src/presentation.rs +698 -0
- data/rust/host/src/presentation_write.rs +1431 -0
- data/rust/host/src/query_step.rs +149 -0
- data/rust/host/src/rate_limit.rs +904 -0
- data/rust/host/src/reference_transform.rs +241 -0
- data/rust/host/src/reference_validate.rs +338 -0
- data/rust/host/src/resend.rs +296 -0
- data/rust/host/src/secrets.rs +106 -0
- data/rust/host/src/server.rs +663 -0
- data/rust/host/src/storage_shape.rs +175 -0
- data/rust/host/src/test_pg.rs +40 -0
- data/rust/host/src/ui_schema.rs +1351 -0
- data/rust/host/src/wasm_runner.rs +191 -0
- data/rust/host/src/web/newsletter/cooldown.rs +126 -0
- data/rust/host/src/web/newsletter/tests.rs +104 -0
- data/rust/host/src/web/newsletter.rs +310 -0
- data/rust/host/src/web/newsletter_send.rs +357 -0
- data/rust/host/src/web/registrations.rs +1505 -0
- data/rust/host/src/web.rs +2937 -0
- data/rust/lsp/.gitignore +1 -0
- data/rust/lsp/Cargo.lock +7 -0
- data/rust/lsp/Cargo.toml +16 -0
- data/rust/lsp/README.md +133 -0
- data/rust/lsp/src/diagnostics.rs +165 -0
- data/rust/lsp/src/json.rs +385 -0
- data/rust/lsp/src/main.rs +399 -0
- data/rust/lsp/src/outline.rs +177 -0
- data/rust/lsp/src/rpc.rs +67 -0
- data/rust/parser/.cargo/config.toml +5 -0
- data/rust/parser/.gitignore +1 -0
- data/rust/parser/Cargo.lock +7 -0
- data/rust/parser/Cargo.toml +21 -0
- data/rust/parser/src/build/closed_sets.rs +27 -0
- data/rust/parser/src/build/identity.rs +276 -0
- data/rust/parser/src/build/mod.rs +12 -0
- data/rust/parser/src/build/naming.rs +141 -0
- data/rust/parser/src/build/pattern_subset.rs +161 -0
- data/rust/parser/src/build/query_derive.rs +72 -0
- data/rust/parser/src/build/query_inference.rs +269 -0
- data/rust/parser/src/build/query_options.rs +100 -0
- data/rust/parser/src/build/read_model.rs +94 -0
- data/rust/parser/src/build/references.rs +32 -0
- data/rust/parser/src/canonical.rs +114 -0
- data/rust/parser/src/diag.rs +55 -0
- data/rust/parser/src/emit.rs +953 -0
- data/rust/parser/src/expr/ast_json.rs +147 -0
- data/rust/parser/src/expr/evaluator.rs +81 -0
- data/rust/parser/src/expr/mod.rs +84 -0
- data/rust/parser/src/expr/resolver.rs +497 -0
- data/rust/parser/src/ir.rs +387 -0
- data/rust/parser/src/keywords.rs +585 -0
- data/rust/parser/src/lex.rs +662 -0
- data/rust/parser/src/main.rs +252 -0
- data/rust/parser/src/parse/aggregate.rs +612 -0
- data/rust/parser/src/parse/chapter.rs +517 -0
- data/rust/parser/src/parse/command.rs +637 -0
- data/rust/parser/src/parse/domain_port.rs +127 -0
- data/rust/parser/src/parse/entity.rs +527 -0
- data/rust/parser/src/parse/file.rs +67 -0
- data/rust/parser/src/parse/hecksagon.rs +231 -0
- data/rust/parser/src/parse/lifecycle.rs +162 -0
- data/rust/parser/src/parse/mod.rs +1262 -0
- data/rust/parser/src/parse/policy.rs +87 -0
- data/rust/parser/src/parse/process_manager.rs +305 -0
- data/rust/parser/src/parse/query.rs +100 -0
- data/rust/parser/src/parse/read_model.rs +214 -0
- data/rust/parser/src/parse/value_object.rs +250 -0
- data/rust/parser/src/ruby_value.rs +422 -0
- data/rust/project/bridging.rb +257 -0
- data/rust/project/commands.rb +877 -0
- data/rust/project/constraints.rb +86 -0
- data/rust/project/dependency_planning.rb +178 -0
- data/rust/project/domain_generator.rb +980 -0
- data/rust/project/exemplar.rb +148 -0
- data/rust/project/expr_emitter.rb +72 -0
- data/rust/project/fielded.rb +141 -0
- data/rust/project/json_codec.rb +641 -0
- data/rust/project/mutations.rb +721 -0
- data/rust/project/naming.rb +205 -0
- data/rust/project/ports.rb +65 -0
- data/rust/project/queries.rb +493 -0
- data/rust/project/reactions.rb +317 -0
- data/rust/project/read_models.rb +690 -0
- data/rust/project/reference_specs.rb +27 -0
- data/rust/project/registry.rb +396 -0
- data/rust/project/skip_reason.rb +29 -0
- data/rust/project/types.rb +225 -0
- data/rust/project/write_if_changed.rb +83 -0
- data/rust/project.rb +30 -0
- data/rust/project_rust_pipeline.rb +383 -0
- data/rust/src/exemplar/commands.rs +279 -0
- data/rust/src/exemplar/constraints.rs +18 -0
- data/rust/src/exemplar/fielded.rs +280 -0
- data/rust/src/exemplar/json.rs +277 -0
- data/rust/src/exemplar/mod.rs +17 -0
- data/rust/src/exemplar/mutations.rs +80 -0
- data/rust/src/exemplar/queries.rs +33 -0
- data/rust/src/exemplar/reactions.rs +88 -0
- data/rust/src/exemplar/read_models.rs +48 -0
- data/rust/src/exemplar/registry.rs +166 -0
- data/rust/src/exemplar/types.rs +44 -0
- data/rust/src/kernel/attribute_shapes/composite.rs +88 -0
- data/rust/src/kernel/attribute_shapes/list.rs +18 -0
- data/rust/src/kernel/attribute_shapes/mod.rs +44 -0
- data/rust/src/kernel/attribute_shapes/optional.rs +9 -0
- data/rust/src/kernel/attribute_shapes/scalar.rs +39 -0
- data/rust/src/kernel/cli.rs +585 -0
- data/rust/src/kernel/dispatch.rs +1090 -0
- data/rust/src/kernel/expr.rs +458 -0
- data/rust/src/kernel/expression_operators/arithmetic.rs +128 -0
- data/rust/src/kernel/expression_operators/comparison.rs +48 -0
- data/rust/src/kernel/expression_operators/enumeration.rs +232 -0
- data/rust/src/kernel/expression_operators/logical.rs +13 -0
- data/rust/src/kernel/expression_operators/membership.rs +18 -0
- data/rust/src/kernel/expression_operators/mod.rs +68 -0
- data/rust/src/kernel/expression_operators/pattern_match.rs +73 -0
- data/rust/src/kernel/expression_operators/positional.rs +126 -0
- data/rust/src/kernel/expression_operators/presence.rs +101 -0
- data/rust/src/kernel/expression_operators/sign_test.rs +16 -0
- data/rust/src/kernel/expression_operators/sized.rs +35 -0
- data/rust/src/kernel/expression_operators/text.rs +79 -0
- data/rust/src/kernel/expression_operators/to_string.rs +18 -0
- data/rust/src/kernel/json.rs +779 -0
- data/rust/src/kernel/mod.rs +133 -0
- data/rust/src/kernel/named_query.rs +219 -0
- data/rust/src/kernel/orchestrate.rs +1810 -0
- data/rust/src/kernel/pattern.rs +307 -0
- data/rust/src/kernel/query_comparators.rs +206 -0
- data/rust/src/kernel/query_ordering.rs +103 -0
- data/rust/src/kernel/read_model.rs +914 -0
- data/rust/src/kernel/reference_lookup.rs +149 -0
- data/rust/src/kernel/repository.rs +468 -0
- data/rust/src/kernel/routing.rs +612 -0
- data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +118 -0
- data/rust/src/kernel/vocab/entity_dispatch_order.rs +114 -0
- data/rust/src/kernel/vocab/field_hint.rs +70 -0
- data/rust/src/kernel/vocab/mod.rs +20 -0
- data/rust/src/kernel/vocab/query_comparator.rs +65 -0
- data/rust/src/kernel/vocab/refusal_template.rs +1592 -0
- data/rust/src/lib.rs +8 -0
- data/rust/src/main.rs +16 -0
- data/rust/web/.gitignore +1 -0
- data/rust/web/Cargo.lock +119 -0
- data/rust/web/Cargo.toml +18 -0
- data/rust/web/src/lib.rs +8 -0
- metadata +477 -12
- data/lib/hecks/facade/cli_door.rb +0 -96
- data/lib/hecks/facade/cli_runner.rb +0 -127
- data/lib/hecks/mcp_door_scope.rb +0 -95
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "hecks"
|
|
5
|
+
require "fileutils"
|
|
6
|
+
|
|
7
|
+
module Hecks
|
|
8
|
+
# The seed records of `spec/fixtures/persistence_legacy/`, over `examples/banking`, and the
|
|
9
|
+
# regeneration of those fixtures through the real adapters.
|
|
10
|
+
#
|
|
11
|
+
# Banking's IR only: no hecksagon, no boot; adapters are built straight from `aggregate:`.
|
|
12
|
+
module PersistenceLegacyFixture
|
|
13
|
+
# The gem's root directory.
|
|
14
|
+
ROOT = File.expand_path("../..", __dir__)
|
|
15
|
+
# Where the fixtures stand in a checkout.
|
|
16
|
+
DIR = File.join(ROOT, "spec/fixtures/persistence_legacy").freeze
|
|
17
|
+
|
|
18
|
+
# Each seed: aggregate name, id and field values.
|
|
19
|
+
SEEDS = [
|
|
20
|
+
[
|
|
21
|
+
"Account", "ACC-1",
|
|
22
|
+
{
|
|
23
|
+
number: { value: "ACC-1" },
|
|
24
|
+
customer: "CUST-1",
|
|
25
|
+
balance: { cents: 1250, currency: "USD" },
|
|
26
|
+
kind: { name: "current" },
|
|
27
|
+
daily_limit: { cents: 500 },
|
|
28
|
+
ledger: [
|
|
29
|
+
{ sequence: { value: 1 }, amount: { cents: 1000, currency: "USD" }, narrative: { text: "opening" },
|
|
30
|
+
direction: { value: "credit" }, state: "posted" },
|
|
31
|
+
{ sequence: { value: 2 }, amount: { cents: 250, currency: "USD" }, narrative: { text: "top up" },
|
|
32
|
+
direction: { value: "credit" }, state: "reversed" }
|
|
33
|
+
],
|
|
34
|
+
status: "open",
|
|
35
|
+
customer_status: "active"
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
[
|
|
39
|
+
"CardPayment", "AUTH-1",
|
|
40
|
+
{
|
|
41
|
+
authorisation: { value: "AUTH-1" },
|
|
42
|
+
account: "ACC-1",
|
|
43
|
+
amount: { cents: 300 },
|
|
44
|
+
merchant: { value: "Cafe" },
|
|
45
|
+
tags: [{ value: "food" }, { value: "travel" }],
|
|
46
|
+
status: "authorized",
|
|
47
|
+
account_status: "open"
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
].freeze
|
|
51
|
+
|
|
52
|
+
module_function
|
|
53
|
+
|
|
54
|
+
# @return [Hecks::Bluebook::Structure::Domain] banking's bluebook, loaded once
|
|
55
|
+
def bluebook
|
|
56
|
+
@bluebook ||= begin
|
|
57
|
+
registry = Hecks::Runtime::Registry.new
|
|
58
|
+
Hecks.with_registry(registry) do
|
|
59
|
+
Kernel.load(File.join(ROOT, "lib/hecks/ports/persistence.port"))
|
|
60
|
+
Kernel.load(File.join(ROOT, "lib/hecks/ports/extraction.port"))
|
|
61
|
+
Kernel.load(File.join(ROOT, "lib/hecks/adapters/driven/prism.adapter"))
|
|
62
|
+
folder = Hecks::Adapters::Folder.new
|
|
63
|
+
folder.load_bluebooks(folder.bluebook_directory(File.join(ROOT, "examples/banking/bluebook")))
|
|
64
|
+
end
|
|
65
|
+
registry.bluebook("Banking")
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# @param name [String] an aggregate of banking
|
|
70
|
+
# @return [Object] its definition
|
|
71
|
+
def aggregate(name) = bluebook.aggregate(name)
|
|
72
|
+
|
|
73
|
+
# @return [Array<Hecks::Runtime::Instance>] one instance for each seed
|
|
74
|
+
def instances
|
|
75
|
+
SEEDS.map do |name, id, fields|
|
|
76
|
+
aggregate = aggregate(name)
|
|
77
|
+
instance = Hecks::Runtime::Instance.new(aggregate: aggregate, id: id)
|
|
78
|
+
fields.each { |field, value| instance[field] = Hecks::Runtime::Value.for(aggregate, field, value) }
|
|
79
|
+
instance
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# In-memory SQLite answering `Connection`'s four methods, as `d1_spec.rb` does.
|
|
84
|
+
#
|
|
85
|
+
# @return [Object] the connection
|
|
86
|
+
def fake_d1_connection
|
|
87
|
+
require "sqlite3"
|
|
88
|
+
db = SQLite3::Database.new(":memory:")
|
|
89
|
+
db.results_as_hash = true
|
|
90
|
+
Class.new do
|
|
91
|
+
def initialize(db) = @db = db
|
|
92
|
+
def execute(sql, binds = []) = @db.execute(sql, binds)
|
|
93
|
+
def get_first_row(sql, binds = []) = execute(sql, binds).first
|
|
94
|
+
def get_first_value(sql, binds = []) = get_first_row(sql, binds)&.values&.first
|
|
95
|
+
end.new(db)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Makes `D1::Connection.new` answer `connection` for the length of the block.
|
|
99
|
+
#
|
|
100
|
+
# @param connection [Object] the connection to answer
|
|
101
|
+
# @yield the work that builds a D1 adapter
|
|
102
|
+
# @return [Object] the block's value
|
|
103
|
+
def with_d1_connection(connection)
|
|
104
|
+
klass = Hecks::Adapters::D1::Connection
|
|
105
|
+
klass.define_singleton_method(:new) { |**| connection }
|
|
106
|
+
yield
|
|
107
|
+
ensure
|
|
108
|
+
klass.singleton_class.send(:remove_method, :new) if klass.singleton_class.method_defined?(:new, false)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
require_relative "persistence_legacy_fixture/regenerate"
|
|
@@ -3,6 +3,7 @@ require_relative "../../../../ports/persistence/binding_policy"
|
|
|
3
3
|
require_relative "lineage"
|
|
4
4
|
require_relative "../../../../naming"
|
|
5
5
|
require_relative "../../../../framework"
|
|
6
|
+
require_relative "../../../../chapters"
|
|
6
7
|
require_relative "../../../../runtime/registry"
|
|
7
8
|
require_relative "expected_era"
|
|
8
9
|
|
|
@@ -115,21 +116,23 @@ module Hecks
|
|
|
115
116
|
|
|
116
117
|
# Picks the files to read for a bluebook no file in the domain directory declares.
|
|
117
118
|
#
|
|
118
|
-
# Tries, in order:
|
|
119
|
-
# vendored embryonaut bluebook.
|
|
119
|
+
# Tries, in order: a chapter the gem carries (the QA ledger's QualityControl), the domain's
|
|
120
|
+
# own single remaining file, a framework member, then a vendored embryonaut bluebook.
|
|
120
121
|
#
|
|
121
122
|
# @param bluebook [Bluebook::Chapter] the bluebook whose source is wanted
|
|
122
123
|
# @param directory [String] path of the domain's own bluebook directory
|
|
123
124
|
# @param domain_files [Array<String>] paths of every `.bluebook` file in `directory`
|
|
124
125
|
# @param registry [Runtime::Registry, nil] the registry asked for a vendored name; nil
|
|
125
126
|
# skips the vendored lookup
|
|
126
|
-
# @return [Array<String>] file paths to read, in load order; `[]` when
|
|
127
|
-
# comes from none of the three sources
|
|
127
|
+
# @return [Array<String>] file paths to read, in load order; `[]` when none applies
|
|
128
128
|
def fallback_source_files(bluebook, directory, domain_files, registry)
|
|
129
129
|
vendored_name = registry && vendored_bluebook_name_for(registry, bluebook.name)
|
|
130
130
|
framework_path = Framework.members[bluebook.name]
|
|
131
|
+
attached = Chapters.index.fetch(bluebook.name, [])
|
|
131
132
|
|
|
132
|
-
if
|
|
133
|
+
if attached.any? && !framework_path && !vendored_name
|
|
134
|
+
attached
|
|
135
|
+
elsif domain_files.size == 1 && !framework_path && !vendored_name
|
|
133
136
|
domain_files
|
|
134
137
|
elsif framework_path
|
|
135
138
|
[framework_path]
|
|
@@ -139,7 +139,10 @@ module Hecks
|
|
|
139
139
|
run = lambda do
|
|
140
140
|
Hecks.with_registry(scratch) do
|
|
141
141
|
loading.load_library
|
|
142
|
-
|
|
142
|
+
# Held text of a chapter spread over files is several blocks in one string, each
|
|
143
|
+
# opening the same chapter: judge once after all of them, as a directory load does.
|
|
144
|
+
Hecks::Bluebook::MetaValidator.defer { Kernel.eval(source, TOPLEVEL_BINDING, path, 1) }
|
|
145
|
+
Hecks::Bluebook::MetaValidator.judge_deferred!(scratch)
|
|
143
146
|
end
|
|
144
147
|
end
|
|
145
148
|
shadow ? Hecks::Bluebook::MetaValidator.while_shadow_parsing(&run) : run.call
|
|
@@ -59,13 +59,26 @@ module Hecks
|
|
|
59
59
|
# @raise [Unreachable] if the request fails or the status is not `200`
|
|
60
60
|
# @raise [BadResponse] if the body is not JSON or its `era` is not a non-empty string
|
|
61
61
|
def fetch_era(url, timeout: 10)
|
|
62
|
+
fetch_version(url, timeout: timeout).fetch("era")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Asks a host for its `/version` document.
|
|
66
|
+
#
|
|
67
|
+
# @param url [String] the host's base URL, or its `/version` URL
|
|
68
|
+
# @param timeout [Numeric] seconds allowed for connecting and for reading
|
|
69
|
+
# @return [Hash{String => String}] `"era"` (never empty) and `"version"` (empty when the
|
|
70
|
+
# host reports none)
|
|
71
|
+
# @raise [Unreachable] if the request fails or the status is not `200`
|
|
72
|
+
# @raise [BadResponse] if the body is not JSON or its `era` is not a non-empty string
|
|
73
|
+
def fetch_version(url, timeout: 10)
|
|
62
74
|
response = get(version_url(url), timeout)
|
|
63
75
|
raise Unreachable, "#{version_url(url)} answered #{response.code}" unless response.code == "200"
|
|
64
76
|
|
|
65
|
-
|
|
77
|
+
document = JSON.parse(response.body)
|
|
78
|
+
era = document["era"]
|
|
66
79
|
raise BadResponse, "#{version_url(url)} carries no era" unless era.is_a?(String) && !era.empty?
|
|
67
80
|
|
|
68
|
-
era
|
|
81
|
+
{ "era" => era, "version" => document["version"].to_s }
|
|
69
82
|
rescue JSON::ParserError, TypeError
|
|
70
83
|
raise BadResponse, "#{version_url(url)} did not answer a JSON version document"
|
|
71
84
|
end
|
|
@@ -22,7 +22,7 @@ module Hecks
|
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
# Skips the integrity check `eras` runs, so
|
|
25
|
+
# Skips the integrity check `eras` runs, so hecks reattest can show the raw row first.
|
|
26
26
|
def raw_era(ordinal)
|
|
27
27
|
rows = @db.exec_params(
|
|
28
28
|
"SELECT held_text, held_digest, hash, held_projection::text FROM hecks_eras WHERE domain = $1 AND ordinal = $2",
|
|
@@ -53,7 +53,7 @@ module Hecks
|
|
|
53
53
|
raise Runtime::WiringError,
|
|
54
54
|
"cannot merge the tail of #{@domain}: touched by both worlds since the cut — " \
|
|
55
55
|
"#{unresolved.map { |storage, id| "#{storage}##{id}" }.sort.join(', ')}. " \
|
|
56
|
-
"Name each winner (--winner <id>=old or --winner <id>=new), then run
|
|
56
|
+
"Name each winner (--winner <id>=old or --winner <id>=new), then run hecks merge_tail again. " \
|
|
57
57
|
"A winner takes the WHOLE record — the aggregate is the consistency boundary, so the " \
|
|
58
58
|
"loser's edits are discarded even where they touched different attributes"
|
|
59
59
|
end
|
data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb
CHANGED
|
@@ -6,7 +6,7 @@ module Hecks
|
|
|
6
6
|
module Adapters
|
|
7
7
|
class PostgresEra
|
|
8
8
|
module LineageManager
|
|
9
|
-
# Tail-merge (
|
|
9
|
+
# Tail-merge (hecks merge_tail): interleaves the stale world's post-cut writes into the
|
|
10
10
|
# head by global ordinal, audited, in one transaction.
|
|
11
11
|
module MergeCoordinator
|
|
12
12
|
# Merges writes old checkouts made after the last mint into the current era's head.
|
|
@@ -2,6 +2,7 @@ require_relative "../../../../../../runtime/registry"
|
|
|
2
2
|
require_relative "../../storage_shape"
|
|
3
3
|
require_relative "../../translation/audit"
|
|
4
4
|
require_relative "../../translation/scaffold"
|
|
5
|
+
require_relative "../../translation/approval_file"
|
|
5
6
|
|
|
6
7
|
module Hecks
|
|
7
8
|
module Adapters
|
|
@@ -20,7 +21,7 @@ module Hecks
|
|
|
20
21
|
ordinal = latest[:ordinal] + 1
|
|
21
22
|
|
|
22
23
|
edge = resolve_edge!(registry, bluebook, lineage, latest, label, ordinal, directory)
|
|
23
|
-
ensure_compute_rekey_approved!(bluebook, lineage, edge, ordinal)
|
|
24
|
+
ensure_compute_rekey_approved!(bluebook, lineage, edge, ordinal, directory: directory)
|
|
24
25
|
check_coverage!(registry, bluebook, shadow(latest[:held_text]), edge)
|
|
25
26
|
|
|
26
27
|
chain = edge_chain(registry, bluebook, lineage.eras, label)
|
|
@@ -49,32 +50,48 @@ module Hecks
|
|
|
49
50
|
unless edge.to == label
|
|
50
51
|
raise Runtime::WiringError,
|
|
51
52
|
"cannot boot #{bluebook.name}: the translation edge from #{latest[:label]} targets " \
|
|
52
|
-
"#{edge.to}, but the current shape is #{label} — the edge is stale; re-run
|
|
53
|
+
"#{edge.to}, but the current shape is #{label} — the edge is stale; re-run hecks scaffold_translation"
|
|
53
54
|
end
|
|
54
55
|
edge
|
|
55
56
|
end
|
|
56
57
|
|
|
57
|
-
# Refuses a mint whose edge carries a compute or rekey rule without
|
|
58
|
-
# edge-matching, journal-current approval.
|
|
58
|
+
# Refuses a mint whose edge carries a compute or rekey rule without an approval.
|
|
59
59
|
#
|
|
60
|
-
# A compute or rekey's only verification is a human-approved audit sample.
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
# A compute or rekey's only verification is a human-approved audit sample. Two approvals
|
|
61
|
+
# satisfy it: one recorded in the journal that matches the edge and the journal's current
|
|
62
|
+
# tip, or a committed `translations/<edge>.approval` that matches the edge's digest and
|
|
63
|
+
# records a passed rehearsal on a compatible host release. A committed approval that
|
|
64
|
+
# applies is written into the journal, so the journal stays the single history.
|
|
65
|
+
def ensure_compute_rekey_approved!(bluebook, lineage, edge, ordinal, directory: nil)
|
|
66
|
+
return unless Translation::ApprovalFile.needs_rehearsal?(edge)
|
|
63
67
|
|
|
64
68
|
approval = lineage.approval_for(from: edge.from, to: edge.to)
|
|
65
|
-
|
|
69
|
+
digest = Translation::Audit.edge_digest(edge)
|
|
70
|
+
tip = lineage.last_ordinal
|
|
71
|
+
return if approval && approval[:edge_digest] == digest && approval[:reviewed_ordinal] == tip
|
|
72
|
+
|
|
73
|
+
if Translation::ApprovalFile.applicable(directory, edge)
|
|
74
|
+
lineage.record_approval!(from: edge.from, to: edge.to, edge_digest: digest)
|
|
75
|
+
return
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
unless approval && approval[:edge_digest] == digest
|
|
79
|
+
if (mismatch = Translation::ApprovalFile.host_mismatch(directory, edge))
|
|
80
|
+
raise Runtime::WiringError,
|
|
81
|
+
"cannot mint era #{ordinal} of #{bluebook.name}: the committed approval does not " \
|
|
82
|
+
"apply — #{mismatch}"
|
|
83
|
+
end
|
|
84
|
+
|
|
66
85
|
raise Runtime::WiringError,
|
|
67
86
|
"cannot mint era #{ordinal} of #{bluebook.name}: this edge carries a compute or rekey " \
|
|
68
87
|
"rule, and the audit's human-approved sample is its only verification — run " \
|
|
69
|
-
"
|
|
88
|
+
"hecks audit_translation with --approve, then boot again"
|
|
70
89
|
end
|
|
71
|
-
tip = lineage.last_ordinal
|
|
72
|
-
return unless approval[:reviewed_ordinal] != tip
|
|
73
90
|
|
|
74
91
|
raise Runtime::WiringError,
|
|
75
92
|
"cannot mint era #{ordinal} of #{bluebook.name}: the journal advanced past the approved " \
|
|
76
93
|
"review (ordinal #{approval[:reviewed_ordinal]} reviewed, #{tip} now) — the samples a " \
|
|
77
|
-
"human approved no longer cover the data; re-run
|
|
94
|
+
"human approved no longer cover the data; re-run hecks audit_translation with --approve"
|
|
78
95
|
end
|
|
79
96
|
|
|
80
97
|
# Refuses toward the authoring loop, or, under HECKS_SCAFFOLD=1, scaffolds the
|
|
@@ -84,13 +101,13 @@ module Hecks
|
|
|
84
101
|
path = scaffold!(registry, bluebook, lineage, latest, directory)
|
|
85
102
|
raise Runtime::WiringError,
|
|
86
103
|
"cannot boot #{bluebook.name}: the shape changed (era #{ordinal}) — wrote #{path}; " \
|
|
87
|
-
"review it (resolve every unresolved), check it with
|
|
104
|
+
"review it (resolve every unresolved), check it with hecks audit_translation, then boot again"
|
|
88
105
|
end
|
|
89
106
|
|
|
90
107
|
raise Runtime::WiringError,
|
|
91
108
|
"cannot boot #{bluebook.name}: the shape changed (era #{ordinal}) and no translation edge " \
|
|
92
|
-
"covers it — run
|
|
93
|
-
"check it with
|
|
109
|
+
"covers it — run hecks scaffold_translation to write the edge, " \
|
|
110
|
+
"check it with hecks audit_translation, then boot again"
|
|
94
111
|
end
|
|
95
112
|
|
|
96
113
|
# Diffs the held era against the current shape and writes the edge file —
|
|
@@ -3,6 +3,7 @@ require "json"
|
|
|
3
3
|
require_relative "../../../../adapters/driven/sql_query_builder"
|
|
4
4
|
require_relative "../../../../adapters/driven/postgres/outbox"
|
|
5
5
|
require_relative "../../../../adapters/driven/postgres/reconnect"
|
|
6
|
+
require_relative "../../../../adapters/driven/postgres/shared_connection"
|
|
6
7
|
require_relative "postgres_era/lineage"
|
|
7
8
|
require_relative "postgres_era/lineage_manager"
|
|
8
9
|
require_relative "postgres_era/events"
|
|
@@ -118,8 +119,10 @@ module Hecks
|
|
|
118
119
|
"cannot bind PostgresEra at #{declared} for #{name}: #{e.message.strip}"
|
|
119
120
|
end
|
|
120
121
|
|
|
121
|
-
#
|
|
122
|
-
#
|
|
122
|
+
# Joins the process's shared connection for the declared database and schema, so every
|
|
123
|
+
# aggregate of a domain runs on one connection, then provisions the journal, this
|
|
124
|
+
# aggregate's head and field caches, and the event, saga and outbox tables. Every step is
|
|
125
|
+
# idempotent.
|
|
123
126
|
#
|
|
124
127
|
# @param settings [Hash{Symbol, String => Object}] world settings plus what
|
|
125
128
|
# `RepositoryFactory.build` merges in: `domain`, `era` and `superseded_by`
|
|
@@ -127,7 +130,7 @@ module Hecks
|
|
|
127
130
|
def initialize(aggregate:, settings: {}, root: nil)
|
|
128
131
|
@aggregate = aggregate
|
|
129
132
|
@settings = settings
|
|
130
|
-
@db =
|
|
133
|
+
@db = PostgresSharedConnection.for(aggregate.name, settings, connector: self.class)
|
|
131
134
|
# Journal name: the owning bluebook's declared name, matching the key rust/host derives its
|
|
132
135
|
# advisory lock from (ADR 0036). A bare aggregate with no owner falls back to its own name.
|
|
133
136
|
@domain = self.class.setting(
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require "date"
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require_relative "audit/approval_digest"
|
|
5
|
+
require_relative "../../../../../version"
|
|
6
|
+
|
|
7
|
+
module Hecks
|
|
8
|
+
module Translation
|
|
9
|
+
# The committed form of an edge's approval: `translations/<edge>.approval`, a JSON file beside
|
|
10
|
+
# the edge that a reviewer sees in the same diff.
|
|
11
|
+
#
|
|
12
|
+
# It binds to the edge's content digest and not to the journal's tip, so a production journal
|
|
13
|
+
# that keeps writing between the commit and the deploy does not void it. An edge with a compute
|
|
14
|
+
# or rekey rule also carries the rehearsal a person ran against real data, since the audit's
|
|
15
|
+
# samples are that edge's only other verification.
|
|
16
|
+
module ApprovalFile
|
|
17
|
+
EXTENSION = ".approval".freeze
|
|
18
|
+
|
|
19
|
+
# The keys of a rehearsal block; every one is required when the block is required.
|
|
20
|
+
#
|
|
21
|
+
# `host_version` is the Hecks release the rehearsal ran on, and the gate checks it: a
|
|
22
|
+
# rehearsal counts only on a host whose release shares its `major.minor` (see
|
|
23
|
+
# `host_compatible?`).
|
|
24
|
+
REHEARSAL_KEYS = %w[snapshot host_version result at].freeze
|
|
25
|
+
|
|
26
|
+
# How a `host_version` reads: `major.minor`, an optional patch, an optional pre-release or
|
|
27
|
+
# build suffix. Only the first two numbers take part in the comparison.
|
|
28
|
+
HOST_LINE = /\A(\d+)\.(\d+)(?:\.\d+)?(?:[-+][0-9A-Za-z.+-]+)?\z/
|
|
29
|
+
|
|
30
|
+
# The release the running host reports: the Hecks release this code was built for. The Rust
|
|
31
|
+
# host embeds the same string from `rust/host/HECKS_RELEASE`, and a spec fails when the two
|
|
32
|
+
# drift.
|
|
33
|
+
HOST_RELEASE = Hecks::VERSION
|
|
34
|
+
|
|
35
|
+
# An ISO 8601 time: `2026-09-28T12:30:00Z`, with optional fraction and `+HH:MM` offset. The
|
|
36
|
+
# host's approval.rs accepts the same shape.
|
|
37
|
+
TIMESTAMP = /\A(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})\z/
|
|
38
|
+
|
|
39
|
+
extend Audit::ApprovalDigest
|
|
40
|
+
|
|
41
|
+
module_function
|
|
42
|
+
|
|
43
|
+
# Names an edge by the labels it joins; labels are hex, so the dash cannot be part of one.
|
|
44
|
+
#
|
|
45
|
+
# @param edge [Bluebook::Translation] the parsed translation edge
|
|
46
|
+
# @return [String] `<from>-<to>`
|
|
47
|
+
def edge_name(edge) = "#{edge.from}-#{edge.to}"
|
|
48
|
+
|
|
49
|
+
# Where an edge's approval lives.
|
|
50
|
+
#
|
|
51
|
+
# @param directory [String] the domain's bluebook directory
|
|
52
|
+
# @param edge [Bluebook::Translation] the parsed translation edge
|
|
53
|
+
# @return [String] `<directory>/translations/<edge>.approval`
|
|
54
|
+
def path_for(directory, edge) = File.join(directory, "translations", "#{edge_name(edge)}#{EXTENSION}")
|
|
55
|
+
|
|
56
|
+
# Whether the edge carries a rule whose only verification is a human's review of samples.
|
|
57
|
+
#
|
|
58
|
+
# @param edge [Bluebook::Translation] the parsed translation edge
|
|
59
|
+
# @return [Boolean] true when any aggregate declares a compute or a rekey
|
|
60
|
+
def needs_rehearsal?(edge)
|
|
61
|
+
edge.aggregates.any? { |declared| !declared.computes.empty? || !declared.rekeys.empty? }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Whether a rehearsal block records a run that passed, with every field named.
|
|
65
|
+
#
|
|
66
|
+
# @param rehearsal [Hash, nil] the block, string-keyed as it reads from JSON
|
|
67
|
+
# @return [Boolean] true when all of `REHEARSAL_KEYS` are non-empty Strings and `result` is
|
|
68
|
+
# `pass`
|
|
69
|
+
def rehearsed?(rehearsal)
|
|
70
|
+
return false unless rehearsal.is_a?(Hash)
|
|
71
|
+
|
|
72
|
+
REHEARSAL_KEYS.all? { |key| named?(rehearsal[key]) } && rehearsal["result"] == "pass"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# The `major.minor` a version names.
|
|
76
|
+
#
|
|
77
|
+
# @param version [Object] a `host_version` as it reads from JSON
|
|
78
|
+
# @return [Array<Integer>, nil] `[major, minor]`, or nil when it is not a version
|
|
79
|
+
def release_line(version)
|
|
80
|
+
match = HOST_LINE.match(version) if version.is_a?(String)
|
|
81
|
+
match && [match[1].to_i, match[2].to_i]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Whether a rehearsal ran on a host compatible with the running one.
|
|
85
|
+
#
|
|
86
|
+
# The rule is the smallest sound one: equal `major.minor`. A patch release does not change
|
|
87
|
+
# what a mint does, so a rehearsal survives it; a minor or major release may, so a rehearsal
|
|
88
|
+
# from an older host (or a newer one, which proves nothing about this one) does not.
|
|
89
|
+
#
|
|
90
|
+
# @param rehearsal [Hash] a rehearsal block, string-keyed
|
|
91
|
+
# @param host_version [String] the running host's release
|
|
92
|
+
# @return [Boolean] true when both versions parse and share `major.minor`
|
|
93
|
+
def host_compatible?(rehearsal, host_version = HOST_RELEASE)
|
|
94
|
+
line = release_line(rehearsal["host_version"])
|
|
95
|
+
!line.nil? && line == release_line(host_version)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# The refusal for a rehearsal that ran on another host line; rust/host words it the same.
|
|
99
|
+
#
|
|
100
|
+
# @param rehearsal [Hash] a rehearsal block, string-keyed
|
|
101
|
+
# @param host_version [String] the running host's release
|
|
102
|
+
# @return [String] names both versions
|
|
103
|
+
def host_refusal(rehearsal, host_version = HOST_RELEASE)
|
|
104
|
+
line = release_line(host_version)&.join(".")
|
|
105
|
+
"the rehearsal ran on Hecks #{rehearsal['host_version']}, but this host is Hecks #{host_version}; " \
|
|
106
|
+
"a rehearsal counts only on a host of the same major.minor (#{line}.x) — " \
|
|
107
|
+
"re-run the rehearsal on this host and approve again"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Whether a value is a String with something in it; a number or nil names nothing.
|
|
111
|
+
#
|
|
112
|
+
# @param value [Object] a field as it reads from JSON
|
|
113
|
+
# @return [Boolean] true for a String that is not blank
|
|
114
|
+
def named?(value) = value.is_a?(String) && !value.strip.empty?
|
|
115
|
+
|
|
116
|
+
# Whether a value is an ISO 8601 time that exists on the calendar.
|
|
117
|
+
#
|
|
118
|
+
# @param value [Object] a field as it reads from JSON
|
|
119
|
+
# @return [Boolean] true for a String of the `TIMESTAMP` shape with a real date and clock time
|
|
120
|
+
def timestamp?(value)
|
|
121
|
+
return false unless value.is_a?(String) && (match = TIMESTAMP.match(value))
|
|
122
|
+
|
|
123
|
+
year, month, day, hour, minute, second = match.captures.map(&:to_i)
|
|
124
|
+
Date.valid_date?(year, month, day) && hour < 24 && minute < 60 && second < 60
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Whether an approval names who approved it and when, the two things a reviewer signs.
|
|
128
|
+
#
|
|
129
|
+
# @param approval [Hash] the file, string-keyed as it reads from JSON
|
|
130
|
+
# @return [Boolean] true when `approved_by` is a non-empty String and `approved_at` a time
|
|
131
|
+
def attributed?(approval)
|
|
132
|
+
named?(approval["approved_by"]) && timestamp?(approval["approved_at"])
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Builds the JSON document of one approval.
|
|
136
|
+
#
|
|
137
|
+
# @param edge [Bluebook::Translation] the parsed translation edge
|
|
138
|
+
# @param approved_by [String] who approved it, from their git identity
|
|
139
|
+
# @param approved_at [String] when, as an ISO 8601 UTC time
|
|
140
|
+
# @param rehearsal [Hash{String => String}, nil] the block; required for a compute or rekey
|
|
141
|
+
# @return [Hash{String => Object}] `edge`, `edge_digest`, `approved_by`, `approved_at`, and
|
|
142
|
+
# `rehearsal` when one was given
|
|
143
|
+
# @raise [ArgumentError] if `approved_by` is blank or `approved_at` is not a time, or if the
|
|
144
|
+
# edge needs a rehearsal and none that passed was given
|
|
145
|
+
def build(edge:, approved_by:, approved_at:, rehearsal: nil)
|
|
146
|
+
unless attributed?("approved_by" => approved_by, "approved_at" => approved_at)
|
|
147
|
+
raise ArgumentError, "an approval names who approved it (approved_by, your git identity) " \
|
|
148
|
+
"and when (approved_at, an ISO 8601 time)"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
if needs_rehearsal?(edge) && !rehearsed?(rehearsal)
|
|
152
|
+
raise ArgumentError, "an edge with a compute or rekey rule is approved on a rehearsal that passed: " \
|
|
153
|
+
"name the snapshot, the host version, the result (pass) and when it ran"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
document = { "edge" => edge_name(edge), "edge_digest" => edge_digest(edge),
|
|
157
|
+
"approved_by" => approved_by, "approved_at" => approved_at }
|
|
158
|
+
document["rehearsal"] = rehearsal.slice(*REHEARSAL_KEYS) if rehearsal
|
|
159
|
+
document
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Writes an edge's approval file, replacing an earlier approval of the same edge.
|
|
163
|
+
#
|
|
164
|
+
# @param directory [String] the domain's bluebook directory
|
|
165
|
+
# @param edge [Bluebook::Translation] the parsed translation edge
|
|
166
|
+
# @param document [Hash{String => Object}] what `build` answered
|
|
167
|
+
# @return [String] the path written
|
|
168
|
+
def write!(directory, edge, document)
|
|
169
|
+
path = path_for(directory, edge)
|
|
170
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
171
|
+
File.write(path, "#{JSON.pretty_generate(document)}\n")
|
|
172
|
+
path
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Reads every committed approval of a domain.
|
|
176
|
+
#
|
|
177
|
+
# @param directory [String] the domain's bluebook directory
|
|
178
|
+
# @return [Array<Hash{String => Object}>] each parsed file; one that is not JSON is skipped,
|
|
179
|
+
# since a file that cannot be read approves nothing
|
|
180
|
+
def read_all(directory)
|
|
181
|
+
Dir[File.join(directory, "translations", "*#{EXTENSION}")].filter_map do |path|
|
|
182
|
+
parsed = JSON.parse(File.read(path))
|
|
183
|
+
parsed if parsed.is_a?(Hash)
|
|
184
|
+
rescue JSON::ParserError
|
|
185
|
+
nil
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# The committed approval that lets an edge mint, if there is one.
|
|
190
|
+
#
|
|
191
|
+
# @param directory [String, nil] the domain's bluebook directory
|
|
192
|
+
# @param edge [Bluebook::Translation] the parsed translation edge
|
|
193
|
+
# @param host_version [String] the running host's release
|
|
194
|
+
# @return [Hash{String => Object}, nil] an approval whose digest is the edge's, that names
|
|
195
|
+
# who approved it and when, and whose rehearsal passed on a compatible host when the edge
|
|
196
|
+
# needs one; nil when none applies
|
|
197
|
+
def applicable(directory, edge, host_version: HOST_RELEASE)
|
|
198
|
+
return unless directory
|
|
199
|
+
|
|
200
|
+
digest = edge_digest(edge)
|
|
201
|
+
read_all(directory).find do |approval|
|
|
202
|
+
approval["edge_digest"] == digest && attributed?(approval) &&
|
|
203
|
+
(!needs_rehearsal?(edge) ||
|
|
204
|
+
(rehearsed?(approval["rehearsal"]) && host_compatible?(approval["rehearsal"], host_version)))
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Why a committed approval that covers the edge was still not applied, when the only thing
|
|
209
|
+
# wrong is the host it was rehearsed on.
|
|
210
|
+
#
|
|
211
|
+
# @param directory [String, nil] the domain's bluebook directory
|
|
212
|
+
# @param edge [Bluebook::Translation] the parsed translation edge
|
|
213
|
+
# @param host_version [String] the running host's release
|
|
214
|
+
# @return [String, nil] `host_refusal`'s text, or nil when no such approval exists
|
|
215
|
+
def host_mismatch(directory, edge, host_version: HOST_RELEASE)
|
|
216
|
+
return unless directory && needs_rehearsal?(edge)
|
|
217
|
+
|
|
218
|
+
digest = edge_digest(edge)
|
|
219
|
+
stale = read_all(directory).find do |approval|
|
|
220
|
+
approval["edge_digest"] == digest && attributed?(approval) && rehearsed?(approval["rehearsal"]) &&
|
|
221
|
+
!host_compatible?(approval["rehearsal"], host_version)
|
|
222
|
+
end
|
|
223
|
+
host_refusal(stale["rehearsal"], host_version) if stale
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
@@ -4,7 +4,7 @@ require_relative "../../../../../../projector/exporter"
|
|
|
4
4
|
module Hecks
|
|
5
5
|
module Translation
|
|
6
6
|
module Audit
|
|
7
|
-
# The human gate: a compute edge cannot mint until `
|
|
7
|
+
# The human gate: a compute edge cannot mint until `hecks audit_translation --approve`
|
|
8
8
|
# binds an approval to the edge's content digest and the journal's ordinal at review.
|
|
9
9
|
module ApprovalDigest
|
|
10
10
|
# Fingerprints a parsed translation edge, so an approval lapses when the edge's
|