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,336 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Grammar
|
|
3
|
+
# File surgery under `hecks word_status` and its siblings: reads and rewrites the
|
|
4
|
+
# aggregate-local KeywordSeed/ArgumentSeed rows as text, preserving the table's own formatting.
|
|
5
|
+
module Evolve
|
|
6
|
+
class Refusal < StandardError; end
|
|
7
|
+
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
# Only consumes the next argv element as the value when it isn't itself
|
|
11
|
+
# a flag, so `--foo --bar` doesn't swallow `--bar` as `--foo`'s value.
|
|
12
|
+
def option(argv, name, default = nil)
|
|
13
|
+
index = argv.index("--#{name}")
|
|
14
|
+
return default unless index
|
|
15
|
+
|
|
16
|
+
value = argv[index + 1]
|
|
17
|
+
value.nil? || value.start_with?("-") ? default : value
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Runs the block against an in-memory copy of the tables: every edit it makes is held, none
|
|
21
|
+
# is written, and later reads see the earlier edits.
|
|
22
|
+
#
|
|
23
|
+
# @yield the edits to rehearse
|
|
24
|
+
# @return [Hash{String => String}] each file the edits changed, with the text it would hold
|
|
25
|
+
def rehearse
|
|
26
|
+
@overlay = {}
|
|
27
|
+
yield
|
|
28
|
+
@overlay.dup
|
|
29
|
+
ensure
|
|
30
|
+
@overlay = nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @param path [String] a table file
|
|
34
|
+
# @return [String] its text: a rehearsed edit if one was made, else what the file holds
|
|
35
|
+
def read_source(path)
|
|
36
|
+
@overlay&.key?(path) ? @overlay[path] : File.read(path)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @param path [String] a table file
|
|
40
|
+
# @param text [String] what it holds afterwards; held, not written, while rehearsing
|
|
41
|
+
# @return [Object] the text
|
|
42
|
+
def write_source(path, text)
|
|
43
|
+
@overlay ? (@overlay[path] = text) : File.write(path, text)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Snapshots `paths` before running the block and restores every file
|
|
47
|
+
# if it raises, even partway through a multi-file write.
|
|
48
|
+
def restore_on_raise(paths)
|
|
49
|
+
snapshots = paths.to_h { |path| [path, File.read(path)] }
|
|
50
|
+
yield
|
|
51
|
+
rescue StandardError
|
|
52
|
+
snapshots.each { |path, content| File.write(path, content) }
|
|
53
|
+
raise
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Every syntax-table file declaring a KeywordSeed or ArgumentSeed value object.
|
|
57
|
+
def syntax_paths
|
|
58
|
+
Dir.glob(File.expand_path("../language/**/*.bluebook", __dir__)).select do |path|
|
|
59
|
+
source = read_source(path)
|
|
60
|
+
source.include?('value_object "KeywordSeed"') || source.include?('value_object "ArgumentSeed"')
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Narrow compatibility door for single-file callers; syntax_paths is normal.
|
|
65
|
+
def syntax_path = syntax_paths.first
|
|
66
|
+
|
|
67
|
+
# Resolves the file(s) a call should search or write.
|
|
68
|
+
def paths_for(path) = path ? Array(path) : syntax_paths
|
|
69
|
+
|
|
70
|
+
# Parses the Keyword one_of's member rows leniently off the text.
|
|
71
|
+
def keyword_rows(path = nil)
|
|
72
|
+
paths_for(path).flat_map do |candidate|
|
|
73
|
+
blocks = seed_blocks(read_source(candidate), "KeywordSeed")
|
|
74
|
+
raise Refusal, "source declares no KeywordSeed value object" if path && blocks.empty?
|
|
75
|
+
|
|
76
|
+
blocks.flat_map do |block|
|
|
77
|
+
block.scan(/^\s*member (.+)$/).map do |(cells)|
|
|
78
|
+
row = cells.scan(/(\w+): "((?:[^"\\]|\\.)*)"/).to_h
|
|
79
|
+
{ word: row["word"], context: row["context"],
|
|
80
|
+
status: row.fetch("status", "admitted"), was: row["was"] }
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Declares a new, proposed keyword row in the syntax table that owns
|
|
87
|
+
# `context` (or the aggregate `opens` names, for a File-context word).
|
|
88
|
+
def propose(word:, context:, body: "none", inner: "", opens: "", fills: "", path: nil)
|
|
89
|
+
if keyword_rows(path).any? do |row|
|
|
90
|
+
row[:word] == word && row[:context] == context
|
|
91
|
+
end
|
|
92
|
+
raise Refusal,
|
|
93
|
+
"#{context}.#{word} is already declared — one row per (word, context, form)"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
path = owner_path(context: context, word: word, opens: opens, paths: paths_for(path))
|
|
97
|
+
source = read_source(path)
|
|
98
|
+
block = keyword_blocks(source).find { |candidate| candidate.include?(%(context: "#{context}")) } || keyword_block(source)
|
|
99
|
+
indent = block[/^(\s*)member /, 1] || " "
|
|
100
|
+
row = %(#{indent}member word: "#{word}", context: "#{context}", body: "#{body}", ) +
|
|
101
|
+
%(inner: "#{inner}", opens: "#{opens}", fills: "#{fills}", status: "proposed"\n)
|
|
102
|
+
|
|
103
|
+
# Appended at the end; whoever admits it may move it to its group.
|
|
104
|
+
closing = block.rindex(/^\s*end\s*$/)
|
|
105
|
+
updated = block[0...closing] + row + block[closing..]
|
|
106
|
+
write_source(path, source.sub(block, updated))
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Rewrites a declared keyword row's status: cell in place.
|
|
110
|
+
def set_status(word:, context:, to:, path: nil)
|
|
111
|
+
raise Refusal, "#{to.inspect} is not a station a word's life admits" unless %w[proposed admitted deprecated
|
|
112
|
+
retired].include?(to)
|
|
113
|
+
|
|
114
|
+
path = path_holding_keyword(word, context, paths_for(path))
|
|
115
|
+
source = read_source(path)
|
|
116
|
+
block = keyword_blocks(source).find { |candidate| candidate.lines.any? { |line| member_row?(line, word, context) } }
|
|
117
|
+
rows = block.lines.select { |line| member_row?(line, word, context) }
|
|
118
|
+
raise Refusal, "#{context}.#{word} is not declared" if rows.empty?
|
|
119
|
+
|
|
120
|
+
updated = block.lines.map do |line|
|
|
121
|
+
next line unless member_row?(line, word, context)
|
|
122
|
+
|
|
123
|
+
stripped = line.sub(/,\s*status: "[^"]*"/, "")
|
|
124
|
+
# Admitted is the default and stays unspelled in the row.
|
|
125
|
+
to == "admitted" ? stripped : stripped.sub(/\n\z/, %(, status: "#{to}"\n))
|
|
126
|
+
end.join
|
|
127
|
+
|
|
128
|
+
write_source(path, source.sub(block, updated))
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Respells a keyword row (was: holds the old spelling) — one hop only;
|
|
132
|
+
# its Argument rows follow, row-aware (see cascade_argument_rename).
|
|
133
|
+
def rename(word:, context:, to:, path: nil)
|
|
134
|
+
row = keyword_rows(path).find { |r| r[:word] == word && r[:context] == context }
|
|
135
|
+
raise Refusal, "#{context}.#{word} is not declared" unless row
|
|
136
|
+
raise Refusal, "#{context}.#{word} was already #{row[:was]} — one rename hop, then eras" if row[:was]
|
|
137
|
+
if keyword_rows(path).any? do |r|
|
|
138
|
+
r[:word] == to && r[:context] == context
|
|
139
|
+
end
|
|
140
|
+
raise Refusal,
|
|
141
|
+
"#{context}.#{to} is already declared — a rename cannot land on a living word"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
paths = paths_for(path)
|
|
145
|
+
path = path_holding_keyword(word, context, paths)
|
|
146
|
+
source = read_source(path)
|
|
147
|
+
block = keyword_blocks(source).find { |candidate| candidate.lines.any? { |line| member_row?(line, word, context) } }
|
|
148
|
+
updated = block.lines.map do |line|
|
|
149
|
+
next line unless member_row?(line, word, context)
|
|
150
|
+
|
|
151
|
+
line.sub(%(word: "#{word}"), %(word: "#{to}"))
|
|
152
|
+
.sub(/\n\z/, %(, was: "#{word}"\n))
|
|
153
|
+
end.join
|
|
154
|
+
source = source.sub(block, updated)
|
|
155
|
+
write_source(path, source)
|
|
156
|
+
|
|
157
|
+
cascade_argument_rename(keyword: word, context: context, to: to, path: paths)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Tells whether `line` is a KeywordSeed member row for (word, context).
|
|
161
|
+
def member_row?(line, word, context)
|
|
162
|
+
line =~ /^\s*member / && line.include?(%(word: "#{word}")) && line.include?(%(context: "#{context}"))
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Finds which syntax-table path declares a keyword row.
|
|
166
|
+
def path_holding_keyword(word, context, paths = syntax_paths)
|
|
167
|
+
paths.find do |candidate|
|
|
168
|
+
keyword_blocks(read_source(candidate)).any? { |block| block.lines.any? { |line| member_row?(line, word, context) } }
|
|
169
|
+
end || raise(Refusal, "#{context}.#{word} is not declared")
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Finds which syntax-table path declares an argument row.
|
|
173
|
+
def path_holding_argument(keyword, context, at, named, paths = syntax_paths)
|
|
174
|
+
paths.find do |candidate|
|
|
175
|
+
argument_blocks(read_source(candidate)).any? do |block|
|
|
176
|
+
block.lines.any? do |line|
|
|
177
|
+
argument_row?(line, keyword, context, at, named)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end || raise(Refusal, "#{context}.#{keyword}'s argument at #{at.inspect}/named #{named.inspect} is not declared")
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Picks the owning file for a new row; opens names the aggregate for a File-context word.
|
|
184
|
+
def owner_path(context:, word:, opens: "", paths: syntax_paths)
|
|
185
|
+
if context == "File" && !opens.to_s.empty?
|
|
186
|
+
aggregate_path = paths.find { |candidate| read_source(candidate).match?(/^\s*aggregate "#{Regexp.escape(opens)}" do$/) }
|
|
187
|
+
return aggregate_path if aggregate_path
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
paths.find do |candidate|
|
|
191
|
+
source = read_source(candidate)
|
|
192
|
+
%w[KeywordSeed ArgumentSeed].any? do |seed|
|
|
193
|
+
seed_blocks(source, seed).any? { |block| block.include?(%(context: "#{context}")) }
|
|
194
|
+
end
|
|
195
|
+
end || raise(Refusal, "no aggregate-local syntax table owns context #{context.inspect} for #{word}")
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Every value_object "<name>" block's full source text, opener to closing end.
|
|
199
|
+
def seed_blocks(source, name)
|
|
200
|
+
opener = /^([ \t]*)value_object "#{Regexp.escape(name)}" do$/
|
|
201
|
+
source.to_enum(:scan, opener).map do
|
|
202
|
+
match = Regexp.last_match
|
|
203
|
+
start = match.begin(0)
|
|
204
|
+
indent = match[1]
|
|
205
|
+
closing = source.index(/^#{Regexp.escape(indent)}end\s*$/, match.end(0))
|
|
206
|
+
closing = source.index(/\n/, closing) + 1
|
|
207
|
+
source[start...closing]
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# The first value_object "<name>" block's source text.
|
|
212
|
+
def seed_block(source, name, required: false)
|
|
213
|
+
block = seed_blocks(source, name).first
|
|
214
|
+
unless block
|
|
215
|
+
raise Refusal, "source declares no #{name} value object" if required
|
|
216
|
+
|
|
217
|
+
return
|
|
218
|
+
end
|
|
219
|
+
block
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# First `end` after the opener is the block's own — member rows sit bare (ADR 0025).
|
|
223
|
+
def keyword_blocks(source) = seed_blocks(source, "KeywordSeed")
|
|
224
|
+
|
|
225
|
+
# The first KeywordSeed block's source text.
|
|
226
|
+
def keyword_block(source) = seed_block(source, "KeywordSeed", required: true)
|
|
227
|
+
|
|
228
|
+
# Parses the ArgumentSeed's member rows leniently off the text.
|
|
229
|
+
def argument_rows(path = nil)
|
|
230
|
+
paths_for(path).flat_map do |candidate|
|
|
231
|
+
blocks = seed_blocks(read_source(candidate), "ArgumentSeed")
|
|
232
|
+
raise Refusal, "source declares no ArgumentSeed value object" if path && blocks.empty?
|
|
233
|
+
|
|
234
|
+
blocks.flat_map do |block|
|
|
235
|
+
block.scan(/^\s*member (.+)$/).map do |(cells)|
|
|
236
|
+
row = cells.scan(/(\w+): "((?:[^"\\]|\\.)*)"/).to_h
|
|
237
|
+
{ keyword: row["keyword"], context: row["context"], at: row["at"].to_s,
|
|
238
|
+
named: row["named"].to_s, kind: row["kind"], required: row["required"],
|
|
239
|
+
fills: row["fills"].to_s, status: row.fetch("status", "admitted") }
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# Declares a new, proposed argument row; pairs_shape marks a `pairs`
|
|
246
|
+
# argument that fills a whole key/value list, not a single field.
|
|
247
|
+
def propose_argument(keyword:, context:, kind:, required: "false", at: "", named: "", fills: "",
|
|
248
|
+
pairs_shape: nil, path: nil)
|
|
249
|
+
if argument_rows(path).any? { |r| argument_identity(r) == [keyword, context, at, named] }
|
|
250
|
+
raise Refusal, "#{context}.#{keyword}'s argument at #{at.inspect}/named #{named.inspect} is " \
|
|
251
|
+
"already declared — one row per (keyword, context, at, named)"
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
path = owner_path(context: context, word: keyword, paths: paths_for(path))
|
|
255
|
+
source = read_source(path)
|
|
256
|
+
block = argument_blocks(source).find do |candidate|
|
|
257
|
+
candidate.include?(%(context: "#{context}"))
|
|
258
|
+
end || argument_block(source)
|
|
259
|
+
indent = block[/^(\s*)member /, 1] || " "
|
|
260
|
+
shape = pairs_shape.to_s.empty? ? "" : %(pairs_shape: "#{pairs_shape}", )
|
|
261
|
+
row = %(#{indent}member keyword: "#{keyword}", context: "#{context}", at: "#{at}", ) +
|
|
262
|
+
%(named: "#{named}", kind: "#{kind}", required: "#{required}", fills: "#{fills}", ) +
|
|
263
|
+
shape + %(status: "proposed"\n)
|
|
264
|
+
|
|
265
|
+
closing = block.rindex(/^\s*end\s*$/)
|
|
266
|
+
updated = block[0...closing] + row + block[closing..]
|
|
267
|
+
write_source(path, source.sub(block, updated))
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Rewrites a declared argument row's status: cell in place.
|
|
271
|
+
def set_argument_status(keyword:, context:, to:, at: "", named: "", path: nil)
|
|
272
|
+
raise Refusal, "#{to.inspect} is not a station an argument's life admits" unless %w[proposed admitted deprecated
|
|
273
|
+
retired].include?(to)
|
|
274
|
+
|
|
275
|
+
path = path_holding_argument(keyword, context, at, named, paths_for(path))
|
|
276
|
+
source = read_source(path)
|
|
277
|
+
block = argument_blocks(source).find do |candidate|
|
|
278
|
+
candidate.lines.any? do |line|
|
|
279
|
+
argument_row?(line, keyword, context, at, named)
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
rows = block.lines.select { |line| argument_row?(line, keyword, context, at, named) }
|
|
283
|
+
if rows.empty?
|
|
284
|
+
raise Refusal, "#{context}.#{keyword}'s argument at #{at.inspect}/named #{named.inspect} is not " \
|
|
285
|
+
"declared"
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
updated = block.lines.map do |line|
|
|
289
|
+
next line unless argument_row?(line, keyword, context, at, named)
|
|
290
|
+
|
|
291
|
+
stripped = line.sub(/,\s*status: "[^"]*"/, "")
|
|
292
|
+
to == "admitted" ? stripped : stripped.sub(/\n\z/, %(, status: "#{to}"\n))
|
|
293
|
+
end.join
|
|
294
|
+
|
|
295
|
+
write_source(path, source.sub(block, updated))
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# Tells whether `line` is an ArgumentSeed row for this (keyword, context, at, named) tuple.
|
|
299
|
+
def argument_row?(line, keyword, context, at, named)
|
|
300
|
+
line =~ /^\s*member / &&
|
|
301
|
+
line.include?(%(keyword: "#{keyword}")) && line.include?(%(context: "#{context}")) &&
|
|
302
|
+
line.include?(%(at: "#{at}")) && line.include?(%(named: "#{named}"))
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# The (keyword, context, at, named) identity tuple an argument row is keyed
|
|
306
|
+
# by — wider than a Keyword row's two-field key.
|
|
307
|
+
def argument_identity(row) = [row[:keyword], row[:context], row[:at], row[:named]]
|
|
308
|
+
|
|
309
|
+
# Row-aware rename cascade — updates only the argument rows that belong
|
|
310
|
+
# to the renamed keyword, not a blind gsub across the file.
|
|
311
|
+
def cascade_argument_rename(keyword:, context:, to:, path: nil)
|
|
312
|
+
paths_for(path).each do |candidate|
|
|
313
|
+
source = read_source(candidate)
|
|
314
|
+
original = source
|
|
315
|
+
argument_blocks(source).each do |block|
|
|
316
|
+
updated = block.lines.map do |line|
|
|
317
|
+
next line unless line =~ /^\s*member / &&
|
|
318
|
+
line.include?(%(keyword: "#{keyword}")) && line.include?(%(context: "#{context}"))
|
|
319
|
+
|
|
320
|
+
line.sub(%(keyword: "#{keyword}"), %(keyword: "#{to}"))
|
|
321
|
+
end.join
|
|
322
|
+
source = source.sub(block, updated) if updated != block
|
|
323
|
+
end
|
|
324
|
+
write_source(candidate, source) if source != original
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# ArgumentSeed blocks; see keyword_blocks for why the first bare end
|
|
329
|
+
# after the opener is already the block's own.
|
|
330
|
+
def argument_blocks(source) = seed_blocks(source, "ArgumentSeed")
|
|
331
|
+
|
|
332
|
+
# The first ArgumentSeed block's source text.
|
|
333
|
+
def argument_block(source) = seed_block(source, "ArgumentSeed", required: true)
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "tree"
|
|
4
|
+
require_relative "ruby_child"
|
|
5
|
+
|
|
6
|
+
module Hecks
|
|
7
|
+
module Adapters
|
|
8
|
+
module Codebase
|
|
9
|
+
# What Codebase's `CodemodRun` asks of the working tree: the mechanical rewrites of the
|
|
10
|
+
# example bluebooks.
|
|
11
|
+
#
|
|
12
|
+
# Each codemod is a script built on `Hecks::Codemod` (`lib/hecks/codemod.rb`), which reboots
|
|
13
|
+
# every bluebook it edits and puts the file back when the rewrite changes what the bluebook
|
|
14
|
+
# means. The script runs in a child process from the checkout's root. Unconfirmed it runs
|
|
15
|
+
# with `--dry-run`, which restores every file it touched, so tracked files stay changed only
|
|
16
|
+
# when the request is confirmed.
|
|
17
|
+
module Codemods
|
|
18
|
+
# Every operation this family carries out.
|
|
19
|
+
OPERATIONS = %w[hoist_local_givens drop_implicit_append_fields].freeze
|
|
20
|
+
|
|
21
|
+
# The script each operation runs.
|
|
22
|
+
SCRIPTS = { "hoist_local_givens" => "codemod_hoist_local_givens",
|
|
23
|
+
"drop_implicit_append_fields" => "codemod_implicit_append_fields" }.freeze
|
|
24
|
+
|
|
25
|
+
module_function
|
|
26
|
+
|
|
27
|
+
# Carries out one codemod.
|
|
28
|
+
#
|
|
29
|
+
# @param operation [String] one of `OPERATIONS`
|
|
30
|
+
# @param held [Hash] the `CodemodRun` record's fields: `confirm`
|
|
31
|
+
# @param tree [Tree] the working tree, already known to be a hecks checkout
|
|
32
|
+
# @param shell [#capture, nil] unused: the codemod runs in this process
|
|
33
|
+
# @return [String] what the script found for each example, and whether it rewrote it
|
|
34
|
+
# @raise [ConsoleCapture::Failure] when the script ends badly
|
|
35
|
+
def call(operation, held, tree, shell: nil)
|
|
36
|
+
confirm = held[:confirm].is_a?(Hash) ? held[:confirm][:value] : held[:confirm]
|
|
37
|
+
child = RubyChild.new(tree)
|
|
38
|
+
return child.answer(SCRIPTS.fetch(operation)) if confirm == true
|
|
39
|
+
|
|
40
|
+
"dry run, nothing kept (add --confirm to rewrite):\n#{child.answer(SCRIPTS.fetch(operation), '--dry-run')}"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "tree"
|
|
4
|
+
require_relative "ruby_child"
|
|
5
|
+
require "hecks/engine_agreement"
|
|
6
|
+
require "hecks/doc/reference"
|
|
7
|
+
|
|
8
|
+
module Hecks
|
|
9
|
+
module Adapters
|
|
10
|
+
module Codebase
|
|
11
|
+
# What Codebase's `ConformanceRun` asks of the working tree: the checks on the language
|
|
12
|
+
# itself.
|
|
13
|
+
#
|
|
14
|
+
# The query-engine check and the reference-docs check read the tree in this process, with the
|
|
15
|
+
# code the verbs run elsewhere, and write nothing. The argument-gate matrix boots several
|
|
16
|
+
# throwaway domains and replays them, so it runs in a child process; without `confirm` the
|
|
17
|
+
# script only reports, and with it the script rewrites the committed matrix and its fixtures.
|
|
18
|
+
module Conformance
|
|
19
|
+
# Every operation this family carries out.
|
|
20
|
+
OPERATIONS = %w[check_engine_agreement measure_doc_coverage argument_gate_matrix].freeze
|
|
21
|
+
|
|
22
|
+
module_function
|
|
23
|
+
|
|
24
|
+
# Carries out one operation.
|
|
25
|
+
#
|
|
26
|
+
# @param operation [String] one of `OPERATIONS`
|
|
27
|
+
# @param held [Hash] the `ConformanceRun` record's fields
|
|
28
|
+
# @param tree [Tree] the working tree, already known to be a hecks checkout
|
|
29
|
+
# @return [String] what the check found
|
|
30
|
+
# @raise [ConsoleCapture::Failure] when the check finds a disagreement
|
|
31
|
+
def call(operation, held, tree, shell: nil)
|
|
32
|
+
case operation
|
|
33
|
+
when "check_engine_agreement" then engine_agreement(tree)
|
|
34
|
+
when "measure_doc_coverage" then doc_coverage(tree)
|
|
35
|
+
else gate_matrix(held, tree)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @param tree [Tree] the checkout
|
|
40
|
+
# @return [String] the clean verdict
|
|
41
|
+
# @raise [ConsoleCapture::Failure] with every problem, when the engines disagree
|
|
42
|
+
def engine_agreement(tree)
|
|
43
|
+
finding = EngineAgreement.check(root: tree.root)
|
|
44
|
+
return EngineAgreement.clean_line(finding) if finding.problems.empty?
|
|
45
|
+
|
|
46
|
+
raise ConsoleCapture::Failure, EngineAgreement.problem_lines(finding)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @param tree [Tree] the checkout
|
|
50
|
+
# @return [String] the clean verdict
|
|
51
|
+
# @raise [ConsoleCapture::Failure] with each word that lacks prose or an example
|
|
52
|
+
def doc_coverage(tree)
|
|
53
|
+
clean, report = Doc::Reference.coverage_report(tree.path("docs/implemented/reference"))
|
|
54
|
+
raise ConsoleCapture::Failure, report unless clean
|
|
55
|
+
|
|
56
|
+
report
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# @param held [Hash] the record's fields; `confirm` makes the script write
|
|
60
|
+
# @param tree [Tree] the checkout
|
|
61
|
+
# @return [String] what the script printed: the rows kept and dropped, and what it wrote
|
|
62
|
+
# @raise [ConsoleCapture::Failure] when the script ends badly
|
|
63
|
+
def gate_matrix(held, tree)
|
|
64
|
+
confirm = held[:confirm].is_a?(Hash) ? held[:confirm][:value] : held[:confirm]
|
|
65
|
+
child = RubyChild.new(tree)
|
|
66
|
+
child.answer("argument_gate_matrix", *("--write" if confirm == true))
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hecks/corpus"
|
|
4
|
+
require "hecks/query_ir"
|
|
5
|
+
require "hecks/query_ir_mcp"
|
|
6
|
+
require_relative "tree"
|
|
7
|
+
require "hecks/rust_build"
|
|
8
|
+
require_relative "../console_capture"
|
|
9
|
+
|
|
10
|
+
module Hecks
|
|
11
|
+
module Adapters
|
|
12
|
+
module Codebase
|
|
13
|
+
# What Codebase's `CorpusRun` asks of the working tree: which committed domains have a Rust
|
|
14
|
+
# feature, what the language's IR holds, and the two doors that serve it.
|
|
15
|
+
#
|
|
16
|
+
# The questions are pure reads and run in this process, with the code `hecks rust_domains` and
|
|
17
|
+
# `hecks ir_constructs` run (`Hecks::Corpus`, `Hecks::QueryIR`); the coverage question runs
|
|
18
|
+
# `Hecks::RustBuild`'s coverage tool over each generated module, also in this process. The
|
|
19
|
+
# two doors run until they are closed: the query MCP server on stdio, and the forms app on a
|
|
20
|
+
# local port.
|
|
21
|
+
module CorpusTasks
|
|
22
|
+
# Every operation this family carries out.
|
|
23
|
+
OPERATIONS = %w[serve_query_ir_mcp present].freeze
|
|
24
|
+
|
|
25
|
+
# The port `present` listens on when none is named.
|
|
26
|
+
DEFAULT_PORT = 4567
|
|
27
|
+
|
|
28
|
+
class << self
|
|
29
|
+
# @return [#call, nil] serves the MCP door; `Hecks::QueryIrMcp.start` when nil. A spec
|
|
30
|
+
# replaces it so no stdio has to be held.
|
|
31
|
+
attr_accessor :mcp_server
|
|
32
|
+
|
|
33
|
+
# @return [#call, nil] takes `app:` and `port:` and serves until stopped; a WEBrick
|
|
34
|
+
# server through Rack when nil.
|
|
35
|
+
attr_accessor :web_server
|
|
36
|
+
|
|
37
|
+
# @return [#capture, nil] runs each coverage check, as `capture(tool, argv, env:)`
|
|
38
|
+
# answering an object with `out`, `err` and `ok?`; `Hecks::RustBuild` when nil.
|
|
39
|
+
attr_accessor :coverage_runner
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
module_function
|
|
43
|
+
|
|
44
|
+
# Opens one of the doors.
|
|
45
|
+
#
|
|
46
|
+
# @param operation [String] `serve_query_ir_mcp` or `present`
|
|
47
|
+
# @param held [Hash] the `CorpusRun` record's fields: `port`
|
|
48
|
+
# @param tree [Tree] the working tree, already known to be a hecks checkout
|
|
49
|
+
# @param shell [#capture, nil] unused
|
|
50
|
+
# @return [String] a note that the door closed
|
|
51
|
+
# @raise [ConsoleCapture::Failure] when the MCP door refuses the process's setup
|
|
52
|
+
def call(operation, held, tree, shell: nil)
|
|
53
|
+
_ = shell
|
|
54
|
+
return serve_mcp if operation == "serve_query_ir_mcp"
|
|
55
|
+
|
|
56
|
+
present(plain(held)[:port] || DEFAULT_PORT, tree)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# A pure read.
|
|
60
|
+
#
|
|
61
|
+
# @param operation [String] a query's name in snake case
|
|
62
|
+
# @param args [Hash] the query's plain arguments
|
|
63
|
+
# @param tree [Tree] the checkout
|
|
64
|
+
# @param shell [#capture, nil] unused; the coverage checks run in this process
|
|
65
|
+
# @return [String] the answer, one line for each row
|
|
66
|
+
# @raise [ConsoleCapture::Failure] when a coverage check fails, or a query is refused
|
|
67
|
+
def report(operation, args, tree, shell: nil)
|
|
68
|
+
_ = shell
|
|
69
|
+
case operation
|
|
70
|
+
when "rust_domains" then rust_domains(tree)
|
|
71
|
+
when "regen_order" then regen_order(tree)
|
|
72
|
+
when "corpus_rust_coverage" then coverage(tree)
|
|
73
|
+
else ir_query(operation, args)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @param held [Hash] fields, each perhaps a value object's `{ value: x }`
|
|
78
|
+
# @return [Hash] the same fields as plain values
|
|
79
|
+
def plain(held) = held.transform_values { |value| value.is_a?(Hash) ? value[:value] : value }
|
|
80
|
+
|
|
81
|
+
# @return [String] a note that the MCP door closed
|
|
82
|
+
# @raise [ConsoleCapture::Failure] when the door refuses to start
|
|
83
|
+
def serve_mcp
|
|
84
|
+
server = CorpusTasks.mcp_server || ->(**options) { QueryIrMcp.start(**options) }
|
|
85
|
+
server.call(argv: [])
|
|
86
|
+
"query ir mcp door closed"
|
|
87
|
+
rescue SystemExit => e
|
|
88
|
+
raise ConsoleCapture::Failure, "the query ir mcp door refused to start (status #{e.status}); see stderr"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# @param port [Integer] the port to listen on
|
|
92
|
+
# @param tree [Tree] the checkout
|
|
93
|
+
# @return [String] a note that the server stopped
|
|
94
|
+
def present(port, tree)
|
|
95
|
+
require "hecks/forms/banking_presentation"
|
|
96
|
+
app = Forms::BankingPresentation.app(root: tree.root)
|
|
97
|
+
warn "banking forms, in memory, on http://localhost:#{port}/ - no authentication, no CSRF " \
|
|
98
|
+
"protection, no caller identity; do not expose beyond localhost"
|
|
99
|
+
(CorpusTasks.web_server || method(:rack_serve)).call(app: app, port: port)
|
|
100
|
+
"presented on port #{port}, now stopped"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# @param app [#call] the Rack app
|
|
104
|
+
# @param port [Integer] the port
|
|
105
|
+
# @return [void]
|
|
106
|
+
def rack_serve(app:, port:)
|
|
107
|
+
require "rackup"
|
|
108
|
+
Rackup::Server.start(app: app, Port: port, server: "webrick")
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# @param tree [Tree] the checkout
|
|
112
|
+
# @return [String] each domain with a Rust feature: its feature, a tab, and its directory
|
|
113
|
+
def rust_domains(tree)
|
|
114
|
+
Corpus.rust_domains(root: tree.root).map { |domain| "#{domain.feature}\t#{tree.relative(domain.dir)}" }
|
|
115
|
+
.join("\n")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# @param tree [Tree] the checkout
|
|
119
|
+
# @return [String] the directories regeneration walks, in order, one for each line
|
|
120
|
+
def regen_order(tree)
|
|
121
|
+
Corpus.rust_regen_order(root: tree.root).map { |domain| tree.relative(domain.dir) }.join("\n")
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Runs the coverage tool over every generated module. A module named in
|
|
125
|
+
# `Corpus::RUST_COVERAGE_PENDING` must still fail; every other one must pass.
|
|
126
|
+
#
|
|
127
|
+
# @param tree [Tree] the checkout
|
|
128
|
+
# @return [String] one line for each module, and how many were checked
|
|
129
|
+
# @raise [ConsoleCapture::Failure] with every module that failed, or that passes now while
|
|
130
|
+
# still listed as pending
|
|
131
|
+
def coverage(tree)
|
|
132
|
+
modules = Corpus.generated_modules(root: tree.root)
|
|
133
|
+
pending = Corpus::RUST_COVERAGE_PENDING
|
|
134
|
+
unknown = pending.keys - modules
|
|
135
|
+
raise ConsoleCapture::Failure, "pending names #{unknown.join(', ')}, with no generated module" if unknown.any?
|
|
136
|
+
|
|
137
|
+
lines, problems = judge(modules, pending, coverage_results(modules, tree))
|
|
138
|
+
raise ConsoleCapture::Failure, [*lines, "", *problems].join("\n") if problems.any?
|
|
139
|
+
|
|
140
|
+
[*lines, "#{modules.size} generated modules checked"].join("\n")
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# @param modules [Array<String>] the generated modules
|
|
144
|
+
# @param pending [Hash{String => String}] the modules known to fail, and why
|
|
145
|
+
# @param results [Hash{String => Array}] each module's `[passed, output]`
|
|
146
|
+
# @return [Array(Array<String>, Array<String>)] the report lines and the problems
|
|
147
|
+
def judge(modules, pending, results)
|
|
148
|
+
problems = []
|
|
149
|
+
lines = modules.map do |name|
|
|
150
|
+
passed, output = results.fetch(name)
|
|
151
|
+
if pending.key?(name)
|
|
152
|
+
problems << "#{name} passes now - delete it from Hecks::Corpus::RUST_COVERAGE_PENDING" if passed
|
|
153
|
+
"#{name}: pending (#{passed ? 'NOW PASSES' : 'still fails'}) - #{pending[name]}"
|
|
154
|
+
else
|
|
155
|
+
problems << "#{name} failed:\n#{output}" unless passed
|
|
156
|
+
"#{name}: #{passed ? 'ok' : 'FAILED'}"
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
[lines, problems]
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# @param modules [Array<String>] the generated modules
|
|
163
|
+
# @param tree [Tree] the checkout
|
|
164
|
+
# @return [Hash{String => Array}] each module's `[passed, output]`
|
|
165
|
+
def coverage_results(modules, tree)
|
|
166
|
+
runner = CorpusTasks.coverage_runner || RustBuild
|
|
167
|
+
env = { "HECKS_RUST_DIR" => tree.path("rust") }
|
|
168
|
+
modules.to_h do |name|
|
|
169
|
+
result = runner.capture("rust_coverage", [name], env: env)
|
|
170
|
+
[name, [result.ok?, [result.out, result.err].join]]
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# @param operation [String] `ir_constructs`, `ir_duplicates` or `ir_impact`
|
|
175
|
+
# @param args [Hash] the query's plain arguments
|
|
176
|
+
# @return [String] the formatted answer
|
|
177
|
+
# @raise [ConsoleCapture::Failure] when the query refuses its arguments
|
|
178
|
+
def ir_query(operation, args)
|
|
179
|
+
case operation
|
|
180
|
+
when "ir_constructs" then QueryIR.format_constructs(QueryIR.constructs(words(args[:names])))
|
|
181
|
+
when "ir_duplicates" then duplicates(args)
|
|
182
|
+
else QueryIR.format_impact_preview(QueryIR.impact_preview(args[:name], args[:field]))
|
|
183
|
+
end
|
|
184
|
+
rescue ArgumentError => e
|
|
185
|
+
raise ConsoleCapture::Failure, e.message
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# @param args [Hash] `domains` and `meta`
|
|
189
|
+
# @return [String] the duplicated rules; the meta-domain is included when asked for, or
|
|
190
|
+
# when no domain is named
|
|
191
|
+
def duplicates(args)
|
|
192
|
+
domains = args[:domains] ? words(args[:domains]) : nil
|
|
193
|
+
QueryIR.format_duplicates(QueryIR.duplicates(domains: domains, include_meta: args[:meta] == true || domains.nil?))
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# @param list [String, nil] a comma-separated list
|
|
197
|
+
# @return [Array<String>] its items
|
|
198
|
+
def words(list) = list.to_s.split(",").map(&:strip).reject(&:empty?)
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|