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,172 @@
|
|
|
1
|
+
require_relative "../errors"
|
|
2
|
+
require_relative "../refusal_wording"
|
|
3
|
+
require_relative "../../rendering"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module Runtime
|
|
7
|
+
class Value
|
|
8
|
+
# Boundary checks that refuse a mistyped, unknown, missing or malformed field before an
|
|
9
|
+
# invariant reads it; extended into `Value` alongside `Coercion`, which calls them.
|
|
10
|
+
module FieldChecks
|
|
11
|
+
# C3.8 boundary check — refuses a mistyped argument before it breaks a predicate.
|
|
12
|
+
private def check_bare_primitive(owner, attribute, value)
|
|
13
|
+
type = attribute.type.to_s
|
|
14
|
+
expected = NUMERIC[type]
|
|
15
|
+
mistyped = if expected
|
|
16
|
+
!value.is_a?(expected)
|
|
17
|
+
elsif NON_NUMERIC_SCALARS.include?(type)
|
|
18
|
+
COMPOSITE_SHAPES.any? { |shape| value.is_a?(shape) }
|
|
19
|
+
else
|
|
20
|
+
false
|
|
21
|
+
end
|
|
22
|
+
if mistyped
|
|
23
|
+
raise TypeMismatch,
|
|
24
|
+
RefusalWording.render_site("TypeMismatch", "numeric_field",
|
|
25
|
+
type: owner.hecks_name, field: attribute.name,
|
|
26
|
+
expected: type, offered: Rendering.describe(value))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
check_numeric_bounds(owner.hecks_name, attribute.name, value)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# C3.3/C3.4 — value bounds enforced at every boundary: an Integer must fit
|
|
33
|
+
# signed 64 bits, a Float must be finite.
|
|
34
|
+
INT64_RANGE = (-(2**63))..((2**63) - 1)
|
|
35
|
+
|
|
36
|
+
private def check_numeric_bounds(type_name, field_name, given)
|
|
37
|
+
if given.is_a?(Integer) && !INT64_RANGE.cover?(given)
|
|
38
|
+
raise TypeMismatch,
|
|
39
|
+
RefusalWording.render_site("TypeMismatch", "integer_range",
|
|
40
|
+
type: type_name, field: field_name, offered: Rendering.describe(given))
|
|
41
|
+
end
|
|
42
|
+
return unless given.is_a?(Float) && !given.finite?
|
|
43
|
+
|
|
44
|
+
raise TypeMismatch,
|
|
45
|
+
RefusalWording.render_site("TypeMismatch", "non_finite_field",
|
|
46
|
+
type: type_name, field: field_name, offered: Rendering.describe(given))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# A value object refuses an undeclared key the same way a command's own
|
|
50
|
+
# payload does, checked before any other field-content check.
|
|
51
|
+
private def check_unknown_fields(value_object, fields)
|
|
52
|
+
known = value_object.attributes.map { |attribute| attribute.name.to_sym }
|
|
53
|
+
unknown = (fields.keys.map(&:to_sym) - known).sort
|
|
54
|
+
return if unknown.empty?
|
|
55
|
+
|
|
56
|
+
declared = value_object.attributes.map(&:name)
|
|
57
|
+
raise UnknownArgument,
|
|
58
|
+
RefusalWording.render_site("UnknownArgument", "unknown_args",
|
|
59
|
+
command: value_object.hecks_name, unknown: unknown,
|
|
60
|
+
declared: declared)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# C3.7 — every non-optional, non-list field must arrive (or construction
|
|
64
|
+
# refuses); a `default:` has already been filled in by `apply_defaults`.
|
|
65
|
+
private def check_required_fields(value_object, fields)
|
|
66
|
+
value_object.attributes.each do |attribute|
|
|
67
|
+
next if attribute.optional? || attribute.list?
|
|
68
|
+
next unless fields[attribute.name].nil?
|
|
69
|
+
|
|
70
|
+
raise TypeMismatch,
|
|
71
|
+
RefusalWording.render_site("TypeMismatch", "numeric_field",
|
|
72
|
+
type: value_object.hecks_name, field: attribute.name,
|
|
73
|
+
expected: attribute.type, offered: "nil")
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Checked before invariants, because an invariant reading a mistyped field
|
|
78
|
+
# is exactly the thing that would otherwise explode.
|
|
79
|
+
NUMERIC = { "Integer" => Integer, "Float" => Numeric }.freeze
|
|
80
|
+
private def check_numeric_fields(value_object, fields)
|
|
81
|
+
value_object.attributes.each do |attribute|
|
|
82
|
+
expected = NUMERIC[attribute.type.to_s]
|
|
83
|
+
next unless expected
|
|
84
|
+
|
|
85
|
+
given = fields[attribute.name]
|
|
86
|
+
next if given.nil?
|
|
87
|
+
|
|
88
|
+
offered_items(attribute, given).each do |item|
|
|
89
|
+
unless item.is_a?(expected)
|
|
90
|
+
raise TypeMismatch,
|
|
91
|
+
RefusalWording.render_site("TypeMismatch", "numeric_field",
|
|
92
|
+
type: value_object.hecks_name, field: attribute.name,
|
|
93
|
+
expected: attribute.type, offered: Rendering.describe(item))
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# `is_a?(expected)` alone waves NaN/Infinity through — both are real
|
|
97
|
+
# Floats. `-0.0` is deliberately left unchecked: finite and legitimate.
|
|
98
|
+
check_numeric_bounds(value_object.hecks_name, attribute.name, item)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# A `list_of` field holds an Array whatever its element type, so a lone scalar
|
|
104
|
+
# offered for it is refused; nil stays legitimate, as it is for any optional field.
|
|
105
|
+
private def check_list_shapes(value_object, fields)
|
|
106
|
+
value_object.attributes.each do |attribute|
|
|
107
|
+
given = fields[attribute.name]
|
|
108
|
+
next unless attribute.list? && !given.nil? && !given.is_a?(Array)
|
|
109
|
+
|
|
110
|
+
raise TypeMismatch, RefusalWording.render_site(
|
|
111
|
+
"TypeMismatch", "numeric_field", type: value_object.hecks_name, field: attribute.name,
|
|
112
|
+
expected: "list_of(#{attribute.type})", offered: Rendering.describe(given)
|
|
113
|
+
)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# The values a field's element-level checks apply to: each element of a list, or the
|
|
118
|
+
# one value of a scalar field.
|
|
119
|
+
private def offered_items(attribute, given) = attribute.list? && given.is_a?(Array) ? given : [given]
|
|
120
|
+
|
|
121
|
+
# A scalar field (String, or a boolean) must not arrive as a composite
|
|
122
|
+
# (Array/Hash) standing in for a leaf value. A String field additionally
|
|
123
|
+
# must not arrive as any other scalar, except inside `judge_bootstrapping?`.
|
|
124
|
+
COMPOSITE_SHAPES = [Array, ::Hash].freeze
|
|
125
|
+
NON_NUMERIC_SCALARS = %w[String TrueClass FalseClass].freeze
|
|
126
|
+
private def check_scalar_shapes(value_object, fields)
|
|
127
|
+
value_object.attributes.each do |attribute|
|
|
128
|
+
type = attribute.type.to_s
|
|
129
|
+
next unless NON_NUMERIC_SCALARS.include?(type)
|
|
130
|
+
|
|
131
|
+
given = fields[attribute.name]
|
|
132
|
+
next if given.nil?
|
|
133
|
+
|
|
134
|
+
offered_items(attribute, given).each do |item|
|
|
135
|
+
composite = COMPOSITE_SHAPES.any? { |shape| item.is_a?(shape) }
|
|
136
|
+
non_string_scalar = type == "String" && !composite && !item.is_a?(String) && !judge_bootstrapping?
|
|
137
|
+
next unless composite || non_string_scalar
|
|
138
|
+
|
|
139
|
+
raise TypeMismatch,
|
|
140
|
+
RefusalWording.render_site("TypeMismatch", "numeric_field",
|
|
141
|
+
type: value_object.hecks_name, field: attribute.name,
|
|
142
|
+
expected: attribute.type, offered: Rendering.describe(item))
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# A field declared with a pattern must match it, refused as a TypeMismatch
|
|
148
|
+
# rather than surfacing later as a broken predicate. The pattern itself is
|
|
149
|
+
# already vetted by PatternSubset when the bluebook is declared. `^` and `$` match the
|
|
150
|
+
# whole value, as in Rust, so a newline cannot smuggle text past an anchored pattern.
|
|
151
|
+
private def check_patterns(value_object, fields)
|
|
152
|
+
value_object.attributes.each do |attribute|
|
|
153
|
+
pattern = attribute.pattern
|
|
154
|
+
next unless pattern
|
|
155
|
+
|
|
156
|
+
given = fields[attribute.name]
|
|
157
|
+
next if given.nil?
|
|
158
|
+
|
|
159
|
+
offered_items(attribute, given).each do |item|
|
|
160
|
+
next if item.is_a?(String) && Regexp.new(Hecks::Bluebook::PatternSubset.whole_string(pattern)).match?(item)
|
|
161
|
+
|
|
162
|
+
raise TypeMismatch,
|
|
163
|
+
RefusalWording.render_site("TypeMismatch", "pattern_mismatch",
|
|
164
|
+
type: value_object.hecks_name, field: attribute.name,
|
|
165
|
+
pattern: pattern, offered: Rendering.describe(item))
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
data/lib/hecks/runtime/value.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require "json"
|
|
2
2
|
require_relative "../rendering"
|
|
3
3
|
require_relative "value/invariant_violation"
|
|
4
|
+
require_relative "value/field_checks"
|
|
4
5
|
require_relative "value/coercion"
|
|
5
6
|
require_relative "value/entity_list_coercion"
|
|
6
7
|
require_relative "value/admission"
|
|
@@ -8,8 +9,10 @@ require_relative "value/admission"
|
|
|
8
9
|
module Hecks
|
|
9
10
|
module Runtime
|
|
10
11
|
# A typed value object: frozen fields, read by name.
|
|
11
|
-
# Construction lives in value/coercion.rb, entity_list_coercion.rb and
|
|
12
|
+
# Construction lives in value/coercion.rb, field_checks.rb, entity_list_coercion.rb and
|
|
13
|
+
# admission.rb.
|
|
12
14
|
class Value
|
|
15
|
+
extend FieldChecks
|
|
13
16
|
extend Coercion
|
|
14
17
|
extend EntityListCoercion
|
|
15
18
|
extend Admission
|
data/lib/hecks/runtime.rb
CHANGED
|
@@ -42,24 +42,38 @@ module Hecks
|
|
|
42
42
|
#
|
|
43
43
|
# @param path [String] path to a domain directory, or a file inside one
|
|
44
44
|
# @param shared [String, nil] a shared-root override
|
|
45
|
-
# @param
|
|
45
|
+
# @param install_doors [Boolean] whether to install the Ruby facade constants
|
|
46
|
+
# @param install_facade [Boolean, nil] the deprecated spelling of `install_doors`; warns
|
|
46
47
|
# @param environment [String, nil] the environment name for `Adapters::Folder#load_domain`
|
|
47
48
|
# @return [Runtime::Dispatcher, Runtime::RemoteDispatcher] the dispatcher bound
|
|
48
49
|
# to the booted domain
|
|
49
|
-
def boot(path, shared: nil,
|
|
50
|
-
Loader.boot(path, shared: shared,
|
|
50
|
+
def boot(path, shared: nil, install_doors: true, install_facade: nil, environment: Runtime::Loader::FROM_ENV)
|
|
51
|
+
Loader.boot(path, shared: shared, install_doors: install_doors, environment: environment,
|
|
52
|
+
install_facade: install_facade)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Loads a domain's declarations without binding any adapter. See Loader.describe.
|
|
56
|
+
#
|
|
57
|
+
# @param path [String] path to a domain directory, or a file inside one
|
|
58
|
+
# @param shared [String, nil] a shared-root override
|
|
59
|
+
# @param environment [String, nil] the environment overlay; defaults to `HECKS_ENVIRONMENT`
|
|
60
|
+
# @return [Runtime::Loader::Described] the loaded registry, answering `registry`
|
|
61
|
+
def describe(path, shared: nil, environment: Runtime::Loader::FROM_ENV)
|
|
62
|
+
Loader.describe(path, shared: shared, environment: environment)
|
|
51
63
|
end
|
|
52
64
|
|
|
53
65
|
# Loads only the given files of a domain; otherwise like `boot`. See Loader.boot_files.
|
|
54
66
|
#
|
|
55
67
|
# @param paths [String, Array<String>] one or more file paths within the domain
|
|
56
68
|
# @param shared [String, nil] a shared-root override
|
|
57
|
-
# @param
|
|
69
|
+
# @param install_doors [Boolean] whether to install the Ruby facade constants
|
|
70
|
+
# @param install_facade [Boolean, nil] the deprecated spelling of `install_doors`; warns
|
|
58
71
|
# @param environment [String, nil] the environment name for the selected-file loader
|
|
59
72
|
# @return [Runtime::Dispatcher, Runtime::RemoteDispatcher] the dispatcher bound
|
|
60
73
|
# to the booted domain
|
|
61
|
-
def boot_files(paths, shared: nil,
|
|
62
|
-
Loader.boot_files(paths, shared: shared,
|
|
74
|
+
def boot_files(paths, shared: nil, install_doors: true, install_facade: nil, environment: Runtime::Loader::FROM_ENV)
|
|
75
|
+
Loader.boot_files(paths, shared: shared, install_doors: install_doors, environment: environment,
|
|
76
|
+
install_facade: install_facade)
|
|
63
77
|
end
|
|
64
78
|
|
|
65
79
|
# Bind the ambient registry for the duration of the block, restoring
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "open3"
|
|
5
|
+
require_relative "../rust_build"
|
|
6
|
+
require_relative "native_build"
|
|
7
|
+
|
|
8
|
+
module Hecks
|
|
9
|
+
module RustBuild
|
|
10
|
+
# The differential harness (ADR 0010): replays a corpus script through the Ruby interpreter
|
|
11
|
+
# (`Fuzzing::Replay`) and prints or diffs the JSON surface a Rust artifact must reproduce.
|
|
12
|
+
#
|
|
13
|
+
# With no artifact it prints Ruby's result as JSON, which saved to a file becomes a pinned
|
|
14
|
+
# expectation. The artifact is another artifact's JSON file, `native` (the workspace's
|
|
15
|
+
# `target/{release,debug}/rust`), a `.wasm` path (run under `wasmtime`), an executable path,
|
|
16
|
+
# or `build` (the domain's own Cargo feature, built and run); a live one gets the script on
|
|
17
|
+
# stdin. `HECKS_RUST_DIR` names the workspace and `CARGO_TARGET_DIR` where its output went.
|
|
18
|
+
module Conformance
|
|
19
|
+
USAGE = "usage: hecks check_conformance <domain> <script.json> " \
|
|
20
|
+
"[rust_output.json | native | build | path/to/binary | path/to/module.wasm]"
|
|
21
|
+
|
|
22
|
+
# The refusal kinds are not compared for ad-hoc filter steps: C8.3
|
|
23
|
+
# (docs/semantics/bluebook-semantics.md) is open (RuntimeError in Ruby, TypeMismatch in
|
|
24
|
+
# Rust), and only the message is byte-exact.
|
|
25
|
+
FILTER_VERB = "filter "
|
|
26
|
+
|
|
27
|
+
module_function
|
|
28
|
+
|
|
29
|
+
# @param argv [Array<String>] the domain, the script and optionally the artifact
|
|
30
|
+
# @return [Integer] 0 when the result is printed or matches, 1 on a mismatch
|
|
31
|
+
# @raise [Failure] when the artifact cannot be run
|
|
32
|
+
def call(argv)
|
|
33
|
+
require_relative "../../hecks"
|
|
34
|
+
require_relative "../fuzzing"
|
|
35
|
+
domain, script, other = argv
|
|
36
|
+
raise Failure, USAGE unless domain && script
|
|
37
|
+
|
|
38
|
+
ours = comparable(Hecks::Fuzzing::Replay.call(domain, JSON.parse(File.read(script)).fetch("steps")))
|
|
39
|
+
return print_result(ours) unless other
|
|
40
|
+
|
|
41
|
+
theirs = normalize(JSON.parse(artifact_output(domain, script, other)))
|
|
42
|
+
report(domain, script, ours, theirs)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def print_result(result)
|
|
46
|
+
puts JSON.pretty_generate(result)
|
|
47
|
+
0
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @param result [Hash] `Fuzzing::Replay`'s answer
|
|
51
|
+
# @return [Hash{String => Object}] the surface a Rust artifact must reproduce
|
|
52
|
+
def comparable(result)
|
|
53
|
+
surface = {
|
|
54
|
+
"instances" => result[:instances].transform_values { |state| JSON.parse(JSON.generate(state)) },
|
|
55
|
+
"events" => result[:events].map { |event| event_surface(event) },
|
|
56
|
+
"refusals" => result[:refusals].map do |refusal|
|
|
57
|
+
{ "verb" => refusal[:verb], "error" => refusal[:error], "kind" => refusal[:kind]&.split("::")&.last }
|
|
58
|
+
end
|
|
59
|
+
}
|
|
60
|
+
# `verb` and `ok` only, as `Hecks::Fuzzing::Differential` compares; omitted when none.
|
|
61
|
+
dry_runs = result[:dry_runs].map { |dry| { "verb" => dry[:verb].to_s, "ok" => dry[:ok] } }
|
|
62
|
+
surface["dry_runs"] = dry_runs unless dry_runs.empty?
|
|
63
|
+
surface
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def event_surface(event)
|
|
67
|
+
{ "name" => event[:name], "aggregate" => event[:aggregate], "id" => event[:id].to_s,
|
|
68
|
+
"payload" => JSON.parse(JSON.generate(event[:payload])) }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# The artifact's JSON for the script: live from a binary, or read from a file.
|
|
72
|
+
def artifact_output(domain, script, other)
|
|
73
|
+
case other
|
|
74
|
+
when "native" then run_binary(native_binary || missing_native!, script)
|
|
75
|
+
when "build" then run_binary(built_binary(domain), script)
|
|
76
|
+
when /\.wasm\z/
|
|
77
|
+
raise Failure, "#{other}: no such file — run hecks build_wasm #{domain} first" unless File.exist?(other)
|
|
78
|
+
|
|
79
|
+
run_process(["wasmtime", "run", other], File.read(script))
|
|
80
|
+
when ->(path) { !path.end_with?(".json") && File.file?(path) && File.executable?(path) }
|
|
81
|
+
# A pinned per-domain binary, as named by a sweep's `replay:` line.
|
|
82
|
+
run_binary(other, script)
|
|
83
|
+
else File.read(other)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def run_binary(binary, script) = run_process([binary], File.read(script))
|
|
88
|
+
|
|
89
|
+
def run_process(command, stdin_data)
|
|
90
|
+
stdout, status = Open3.capture2(*command, stdin_data: stdin_data)
|
|
91
|
+
raise Failure, "#{command.join(' ')} exited #{status.exitstatus}:\n#{stdout}" unless status.success?
|
|
92
|
+
|
|
93
|
+
stdout
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def target_dir = ENV.fetch("CARGO_TARGET_DIR", File.join(RustBuild.rust_dir, "target"))
|
|
97
|
+
|
|
98
|
+
def native_binary
|
|
99
|
+
%w[release debug].map { |profile| File.join(target_dir, profile, "rust") }.find { |path| File.executable?(path) }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def missing_native!
|
|
103
|
+
raise Failure, "no native rust binary found under rust/target/{release,debug}/rust — run " \
|
|
104
|
+
"`cd rust && cargo build` first"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Built on demand because `native` runs whichever domain the crate's default feature names.
|
|
108
|
+
def built_binary(domain)
|
|
109
|
+
feature = File.basename(domain.chomp("/"))
|
|
110
|
+
NativeBuild.build_rust_for(feature, RustBuild.rust_dir) or
|
|
111
|
+
raise Failure, "no `#{feature}` feature in rust/Cargo.toml — run hecks project_rust #{domain} first"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Strips what a Rust run carries that Ruby has no analog for: `emitted_*` snapshot flags
|
|
115
|
+
# (ADR 0048) and the wall-clock `occurred_at` stamp two runs can never match.
|
|
116
|
+
def normalize(theirs)
|
|
117
|
+
if theirs["instances"].is_a?(Hash)
|
|
118
|
+
theirs["instances"].each_value do |record|
|
|
119
|
+
record.reject! { |key, _| key.start_with?("emitted_") } if record.is_a?(Hash)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
theirs["events"].each { |event| event.delete("occurred_at") if event.is_a?(Hash) } if theirs["events"].is_a?(Array)
|
|
123
|
+
theirs
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def report(domain, script, ours, theirs)
|
|
127
|
+
[ours, theirs].each { |side| drop_filter_kinds(side) }
|
|
128
|
+
mismatches = mismatches(ours, theirs)
|
|
129
|
+
if mismatches.empty?
|
|
130
|
+
puts "#{domain} / #{script}: matches."
|
|
131
|
+
return 0
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
warn "#{domain} / #{script}: #{mismatches.size} mismatch(es)\n\n#{mismatches.join("\n\n")}"
|
|
135
|
+
1
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def drop_filter_kinds(side)
|
|
139
|
+
return unless side["refusals"].is_a?(Array)
|
|
140
|
+
|
|
141
|
+
side["refusals"].each { |r| r.delete("kind") if r.is_a?(Hash) && r["verb"].to_s.start_with?(FILTER_VERB) }
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def mismatches(ours, theirs)
|
|
145
|
+
found = %w[instances events refusals].filter_map do |key|
|
|
146
|
+
next if ours[key] == theirs[key]
|
|
147
|
+
|
|
148
|
+
"#{key}:\n ruby: #{ours[key].inspect}\n other: #{theirs[key].inspect}"
|
|
149
|
+
end
|
|
150
|
+
their_dry_runs = Array(theirs["dry_runs"]).map { |dry| dry.slice("verb", "ok") }
|
|
151
|
+
if (ours["dry_runs"] || []) != their_dry_runs
|
|
152
|
+
found << "dry_runs:\n ruby: #{ours['dry_runs'].inspect}\n other: #{their_dry_runs.inspect}"
|
|
153
|
+
end
|
|
154
|
+
found
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "json"
|
|
5
|
+
require "tmpdir"
|
|
6
|
+
require_relative "../cache_dir"
|
|
7
|
+
require_relative "../rust_build"
|
|
8
|
+
require_relative "conformance"
|
|
9
|
+
|
|
10
|
+
module Hecks
|
|
11
|
+
module RustBuild
|
|
12
|
+
# Runs generated fuzz sequences through `Conformance` against a native or `.wasm` artifact
|
|
13
|
+
# (ADR 0039), stopping at the first diverging seed with a reproduction command.
|
|
14
|
+
#
|
|
15
|
+
# ConformanceFuzz.call([domain, "native" | "path/to/module.wasm", seeds, steps])
|
|
16
|
+
#
|
|
17
|
+
# `seeds` and `steps` default to `SEEDS` and `STEPS` in the environment, then to 10 and 25.
|
|
18
|
+
module ConformanceFuzz
|
|
19
|
+
USAGE = "usage: hecks fuzz_conformance <domain> <native|path/to/module.wasm> [seeds] [steps]"
|
|
20
|
+
|
|
21
|
+
# What a domain directory's basename may be: a plain identifier, never `.` or `..`.
|
|
22
|
+
DOMAIN_NAME = /\A[A-Za-z0-9][A-Za-z0-9_-]*\z/
|
|
23
|
+
|
|
24
|
+
module_function
|
|
25
|
+
|
|
26
|
+
# @param argv [Array<String>] the domain, the artifact, and optionally seeds and steps
|
|
27
|
+
# @return [Integer] 0 when every sequence matched
|
|
28
|
+
# @raise [Failure] at the first seed that diverges, naming how to reproduce it
|
|
29
|
+
def call(argv)
|
|
30
|
+
require_relative "../../hecks"
|
|
31
|
+
require_relative "../fuzzing"
|
|
32
|
+
domain, artifact, seeds, steps = argv
|
|
33
|
+
raise Failure, USAGE unless domain && artifact
|
|
34
|
+
|
|
35
|
+
seeds = Integer(seeds || ENV["SEEDS"] || 10)
|
|
36
|
+
steps = Integer(steps || ENV["STEPS"] || 25)
|
|
37
|
+
scratch = scratch_dir(domain)
|
|
38
|
+
(1..seeds).each { |seed| replay(domain, artifact, seed, seeds, steps, scratch) }
|
|
39
|
+
FileUtils.rm_rf(scratch)
|
|
40
|
+
puts "#{domain}: #{seeds} generated sequence(s) x #{steps} step(s) each, all matched #{artifact}."
|
|
41
|
+
0
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# A fresh directory per run, under the cache root outside the gem, so concurrent runs never
|
|
45
|
+
# share one; it is kept when a seed diverges, so the failing script outlives the run.
|
|
46
|
+
#
|
|
47
|
+
# @param domain [String] the domain's directory; its basename names the scratch directory
|
|
48
|
+
# @return [String] the new directory
|
|
49
|
+
# @raise [Failure] when the basename cannot safely name a directory
|
|
50
|
+
def scratch_dir(domain)
|
|
51
|
+
name = File.basename(domain.to_s.chomp("/"))
|
|
52
|
+
raise Failure, "#{USAGE}\n`#{domain}` is not a domain directory name" unless name.match?(DOMAIN_NAME)
|
|
53
|
+
|
|
54
|
+
parent = Hecks::CacheDir.path("rust_conformance_fuzz")
|
|
55
|
+
FileUtils.mkdir_p(parent)
|
|
56
|
+
Dir.mktmpdir("#{name}-", parent)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def replay(domain, artifact, seed, seeds, steps, scratch)
|
|
60
|
+
sequence = Hecks::Fuzzing::SequenceGenerator.generate(domain, seed: seed, steps: steps)
|
|
61
|
+
path = File.join(scratch, "seed-#{seed}.json")
|
|
62
|
+
File.write(path, JSON.generate({ "steps" => sequence }))
|
|
63
|
+
return if Conformance.call([domain, path, artifact]).zero?
|
|
64
|
+
|
|
65
|
+
raise Failure, "hecks fuzz_conformance: seed #{seed}/#{seeds} diverged against #{artifact} " \
|
|
66
|
+
"(see the conformance output above) — " \
|
|
67
|
+
"reproduce with: hecks check_conformance #{domain} #{path} #{artifact}"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|