hecks 6.0.0 → 7.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 +19 -12
- data/lib/hecks/adapters/driven/claude_code.rb +2 -2
- data/lib/hecks/adapters/driven/folder.rb +10 -1
- data/lib/hecks/adapters/driven/folder_overlay.rb +15 -0
- data/lib/hecks/adapters/driven/heki.rb +1 -1
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +1 -1
- data/lib/hecks/adapters/driven/memory/outbox_rows.rb +2 -2
- data/lib/hecks/adapters/driven/postgres/name_guard.rb +25 -0
- data/lib/hecks/adapters/driven/postgres/outbox.rb +3 -3
- data/lib/hecks/adapters/driven/postgres/repository.rb +1 -1
- data/lib/hecks/adapters/driven/postgres/shared_connection.rb +1 -1
- data/lib/hecks/adapters/driven/postgres.rb +3 -0
- data/lib/hecks/adapters/driven/postgres_era/boot.rb +28 -16
- data/lib/hecks/adapters/driven/postgres_era/class_methods.rb +27 -7
- data/lib/hecks/adapters/driven/postgres_era/dialect.rb +10 -5
- data/lib/hecks/adapters/driven/postgres_era/field_cache_provisioner.rb +39 -0
- data/lib/hecks/adapters/driven/postgres_era/journal.rb +52 -13
- data/lib/hecks/adapters/driven/postgres_era/lineage/chain_sql.rb +29 -49
- data/lib/hecks/adapters/driven/postgres_era/lineage/direct_sql.rb +73 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/direct_verified.json +10 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/direct_verified.rb +65 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/domain_rename.rb +77 -25
- data/lib/hecks/adapters/driven/postgres_era/lineage/domain_rename_state.rb +108 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/era_integrity.rb +11 -6
- data/lib/hecks/adapters/driven/postgres_era/lineage/era_store.rb +63 -5
- data/lib/hecks/adapters/driven/postgres_era/lineage/layered_sql.rb +64 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/ledger_reset.rb +150 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/mint_provisioning.rb +85 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/mint_transaction.rb +11 -7
- data/lib/hecks/adapters/driven/postgres_era/lineage/provisioning.rb +11 -5
- data/lib/hecks/adapters/driven/postgres_era/lineage/split_sql.rb +76 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/sql_templates.rb +9 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage.rb +10 -2
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/context.rb +23 -3
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/coverage_check.rb +3 -1
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/disposable_ledger.rb +119 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/era_resolution.rb +219 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/era_resolver.rb +68 -71
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/held_era.rb +68 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/ledger_change.rb +63 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/merge_coordinator.rb +1 -1
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/mint_decision.rb +163 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/mint_request.rb +24 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/minter.rb +59 -21
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/refusals.rb +24 -11
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager.rb +40 -6
- data/lib/hecks/adapters/driven/postgres_era/shared_tables.rb +32 -0
- data/lib/hecks/adapters/driven/postgres_era.adapter +12 -0
- data/lib/hecks/adapters/driven/postgres_era.rb +25 -10
- data/lib/hecks/adapters/driven/prism.rb +0 -1
- data/lib/hecks/adapters/driven/sql_query_builder.rb +1 -0
- data/lib/hecks/adapters/driven/sqlite/dispatch_claims.rb +1 -1
- data/lib/hecks/adapters/driven/sqlite/name_guard.rb +25 -0
- data/lib/hecks/adapters/driven/sqlite/outbox.rb +2 -2
- data/lib/hecks/adapters/driven/sqlite/repository.rb +1 -1
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +20 -0
- data/lib/hecks/adapters/driven/sqlite.rb +9 -1
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +1 -1
- data/lib/hecks/adapters/driven/worktree_overlay.rb +117 -0
- data/lib/hecks/adapters/driving/cli/shorthand.rb +5 -1
- data/lib/hecks/adapters/driving/cli.rb +1 -1
- data/lib/hecks/adapters/driving/cli_runner/answers.rb +77 -22
- data/lib/hecks/adapters/driving/cli_runner/bang_rule.rb +35 -0
- data/lib/hecks/adapters/driving/cli_runner/completion_words.rb +36 -0
- data/lib/hecks/adapters/driving/cli_runner/json_words.rb +55 -0
- data/lib/hecks/adapters/driving/cli_runner/name_index.rb +63 -0
- data/lib/hecks/adapters/driving/cli_runner/read_context.rb +32 -0
- data/lib/hecks/adapters/driving/cli_runner/renamed_names.rb +49 -0
- data/lib/hecks/adapters/driving/cli_runner/resolution.rb +43 -17
- data/lib/hecks/adapters/driving/cli_runner/resume_words.rb +63 -0
- data/lib/hecks/adapters/driving/cli_runner/run_reference.rb +66 -0
- data/lib/hecks/adapters/driving/cli_runner/run_replay.rb +118 -0
- data/lib/hecks/adapters/driving/cli_runner/search_words.rb +35 -0
- data/lib/hecks/adapters/driving/cli_runner/settling.rb +6 -5
- data/lib/hecks/adapters/driving/cli_runner/streaming.rb +11 -10
- data/lib/hecks/adapters/driving/cli_runner/suggestions.rb +1 -1
- data/lib/hecks/adapters/driving/cli_runner/tour_words.rb +77 -0
- data/lib/hecks/adapters/driving/cli_runner/verb_names.rb +36 -4
- data/lib/hecks/adapters/driving/cli_runner/verbosity.rb +75 -0
- data/lib/hecks/adapters/driving/cli_runner.rb +106 -7
- data/lib/hecks/adapters/driving/context_line.rb +85 -0
- data/lib/hecks/adapters/driving/exit_status.rb +31 -0
- data/lib/hecks/adapters/driving/launcher_options.rb +7 -5
- data/lib/hecks/adapters/driving/mcp/progress_sink.rb +45 -0
- data/lib/hecks/adapters/driving/mcp/tool_calls.rb +7 -5
- data/lib/hecks/adapters/driving/mcp.rb +25 -5
- data/lib/hecks/adapters/driving/mcp_scope/argument_policy.rb +40 -7
- data/lib/hecks/adapters/driving/mcp_scope.rb +8 -7
- data/lib/hecks/adapters/driving/ruby/aggregate_module/hooks.rb +154 -0
- data/lib/hecks/adapters/driving/ruby/aggregate_module.rb +64 -93
- data/lib/hecks/adapters/driving/usage_log.rb +146 -0
- data/lib/hecks/adapters/driving/write_guard.rb +55 -0
- data/lib/hecks/adapters/driving.rb +0 -1
- data/lib/hecks/behaviors/dsl.rb +5 -1
- data/lib/hecks/behaviors/expectations/boot.rb +0 -1
- data/lib/hecks/bluebook/acknowledgements.rb +1 -1
- data/lib/hecks/bluebook/aggregate.rb +21 -18
- data/lib/hecks/bluebook/assembly/contracts/sagas.rb +2 -0
- data/lib/hecks/bluebook/assembly/contracts/structure.rb +18 -13
- data/lib/hecks/bluebook/behaviour/aggregate.rb +27 -3
- data/lib/hecks/bluebook/command.rb +16 -5
- data/lib/hecks/bluebook/declaration.rb +32 -4
- data/lib/hecks/bluebook/domain_port.rb +7 -3
- data/lib/hecks/bluebook/dsl/aggregate_builder/relationships.rb +0 -2
- data/lib/hecks/bluebook/dsl/aggregate_builder/renaming.rb +25 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +3 -0
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +0 -1
- data/lib/hecks/bluebook/dsl/audience_word.rb +40 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/metadata.rb +56 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/modules.rb +80 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/correlation_keys.rb +51 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +1 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +17 -34
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +8 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +13 -3
- data/lib/hecks/bluebook/dsl/entity_builder.rb +0 -2
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +6 -4
- data/lib/hecks/bluebook/dsl/kinds.rb +53 -0
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +9 -2
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +25 -15
- data/lib/hecks/bluebook/dsl/translation_builder.rb +16 -1
- data/lib/hecks/bluebook/dsl/word_gate.rb +1 -1
- data/lib/hecks/bluebook/dsl.rb +1 -0
- data/lib/hecks/bluebook/meta_validator/judge/identity.rb +9 -4
- data/lib/hecks/bluebook/meta_validator/plan/category.rb +10 -2
- data/lib/hecks/bluebook/meta_validator/plan/verbs.rb +9 -0
- data/lib/hecks/bluebook/meta_validator/plan.rb +2 -1
- data/lib/hecks/bluebook/meta_validator/reconstruction/heads.rb +3 -1
- data/lib/hecks/bluebook/meta_validator/reconstruction/reading.rb +7 -0
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +7 -2
- data/lib/hecks/bluebook/meta_validator/shapes.rb +3 -0
- data/lib/hecks/bluebook/meta_validator/syntax_boot/disk_cache.rb +1 -1
- data/lib/hecks/bluebook/model_check/aggregate_checks.rb +27 -0
- data/lib/hecks/bluebook/model_check/ask_checks.rb +25 -0
- data/lib/hecks/bluebook/model_check/reaction_chains.rb +114 -0
- data/lib/hecks/bluebook/model_check/reaction_cycles.rb +53 -0
- data/lib/hecks/bluebook/model_check/reaction_depth_checks.rb +58 -0
- data/lib/hecks/bluebook/model_check/reaction_walk.rb +62 -0
- data/lib/hecks/bluebook/model_check.rb +5 -27
- data/lib/hecks/bluebook/process_manager.rb +20 -3
- data/lib/hecks/bluebook/translation.rb +5 -3
- data/lib/hecks/bluebook/translation_plan.rb +9 -2
- data/lib/hecks/ci_pipeline/adapters/ci_cache.adapter +7 -0
- data/lib/hecks/ci_pipeline/adapters/ci_cache.rb +39 -0
- data/lib/hecks/ci_pipeline/adapters/ci_runner.adapter +7 -0
- data/lib/hecks/ci_pipeline/adapters/ci_runner.rb +55 -0
- data/lib/hecks/ci_pipeline/ci_pipeline.behaviors +322 -0
- data/lib/hecks/ci_pipeline/ci_pipeline.bluebook +938 -0
- data/lib/hecks/ci_pipeline/ci_pipeline.hecksagon +12 -0
- data/lib/hecks/ci_pipeline/ci_pipeline.ports.hecksagon +16 -0
- data/lib/hecks/ci_speed/adapters/ci_reader.adapter +7 -0
- data/lib/hecks/ci_speed/adapters/ci_reader.rb +141 -0
- data/lib/hecks/ci_speed/adapters/local_runner/answer.rb +41 -0
- data/lib/hecks/ci_speed/adapters/local_runner/catalog.rb +89 -0
- data/lib/hecks/ci_speed/adapters/local_runner/checkouts.rb +89 -0
- data/lib/hecks/ci_speed/adapters/local_runner/launcher.rb +69 -0
- data/lib/hecks/ci_speed/adapters/local_runner/load_average.rb +38 -0
- data/lib/hecks/ci_speed/adapters/local_runner/refs.rb +39 -0
- data/lib/hecks/ci_speed/adapters/local_runner/refused.rb +11 -0
- data/lib/hecks/ci_speed/adapters/local_runner/verdict.rb +71 -0
- data/lib/hecks/ci_speed/adapters/local_runner.adapter +7 -0
- data/lib/hecks/ci_speed/adapters/local_runner.rb +168 -0
- data/lib/hecks/ci_speed/adapters/replayed_runner.rb +39 -0
- data/lib/hecks/ci_speed/ci_speed.behaviors +341 -0
- data/lib/hecks/ci_speed/ci_speed.bluebook +1073 -0
- data/lib/hecks/ci_speed/ci_speed.hecksagon +11 -0
- data/lib/hecks/ci_speed/ci_speed.ports.hecksagon +21 -0
- data/lib/hecks/ci_workflows/ci_workflows.behaviors +13 -0
- data/lib/hecks/ci_workflows/ci_workflows.bluebook +173 -0
- data/lib/hecks/ci_workflows/ci_workflows.hecksagon +15 -0
- data/lib/hecks/coder/coder.bluebook +385 -0
- data/lib/hecks/coder/coder.hecksagon +12 -0
- data/lib/hecks/command_names/check.rb +39 -0
- data/lib/hecks/command_names/extractor.rb +67 -0
- data/lib/hecks/command_names/index.rb +40 -0
- data/lib/hecks/command_names/resolution.rb +81 -0
- data/lib/hecks/command_names/scan.rb +54 -0
- data/lib/hecks/command_names/sources.rb +73 -0
- data/lib/hecks/command_names.rb +26 -0
- data/lib/hecks/composer/jobs.rb +1 -1
- data/lib/hecks/composer/toolbox.rb +8 -1
- data/lib/hecks/corpus/generated_key.rb +205 -0
- data/lib/hecks/corpus/listing.rb +34 -0
- data/lib/hecks/corpus/rust.rb +27 -11
- data/lib/hecks/corpus/sources.rb +2 -1
- data/lib/hecks/corpus.rb +37 -42
- data/lib/hecks/deprecation.rb +23 -0
- data/lib/hecks/doc/reference/readme.rb +1 -1
- data/lib/hecks/doc/reference/rendering.rb +2 -2
- data/lib/hecks/doc/reference.rb +1 -1
- data/lib/hecks/embryonaut_bluebook/manifest.rb +1 -1
- data/lib/hecks/fixer/fixer.bluebook +240 -0
- data/lib/hecks/fixer/fixer.hecksagon +17 -0
- data/lib/hecks/framework_bluebooks.rb +32 -7
- data/lib/hecks/fuzzing/concurrent_dispatch/world_copy.rb +1 -0
- data/lib/hecks/fuzzing/isolated_boot/postgres_scratch.rb +11 -12
- data/lib/hecks/fuzzing/isolated_boot/worlds.rb +23 -5
- data/lib/hecks/fuzzing/isolated_boot.rb +1 -1
- data/lib/hecks/fuzzing/mutation/checks.rb +3 -3
- data/lib/hecks/grammar/translation.bluebook +1 -1
- data/lib/hecks/grammar/translation_rules.json +64 -0
- data/lib/hecks/hecks/adapter_words.json +7 -0
- data/lib/hecks/hecks/adapters/area_overlap.rb +156 -0
- data/lib/hecks/hecks/adapters/claude_agents.rb +8 -3
- data/lib/hecks/hecks/adapters/codebase/batch_runs.rb +60 -0
- data/lib/hecks/hecks/adapters/codebase/changed_specs.rb +52 -0
- data/lib/hecks/hecks/adapters/codebase/codemods.rb +3 -2
- data/lib/hecks/hecks/adapters/codebase/conformance.rb +14 -1
- data/lib/hecks/hecks/adapters/codebase/corpus_queries.rb +53 -0
- data/lib/hecks/hecks/adapters/codebase/generated_keys.rb +31 -0
- data/lib/hecks/hecks/adapters/codebase/impacted_specs.rb +110 -0
- data/lib/hecks/hecks/adapters/codebase/language.rb +2 -2
- data/lib/hecks/hecks/adapters/codebase/launcher_text.rb +39 -0
- data/lib/hecks/hecks/adapters/codebase/minitest_runs.rb +52 -0
- data/lib/hecks/hecks/adapters/codebase/regeneration.rb +51 -8
- data/lib/hecks/hecks/adapters/codebase/run_options.rb +35 -0
- data/lib/hecks/hecks/adapters/codebase/scripting.rb +39 -0
- data/lib/hecks/hecks/adapters/codebase/search.rb +1 -1
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +79 -51
- data/lib/hecks/hecks/adapters/codebase/spec_files/report.rb +71 -0
- data/lib/hecks/hecks/adapters/codebase/spec_files/shared_run.rb +73 -0
- data/lib/hecks/hecks/adapters/codebase/spec_files.rb +95 -0
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +53 -4
- data/lib/hecks/hecks/adapters/codebase/test_scope.rb +105 -0
- data/lib/hecks/hecks/adapters/codebase/test_suite.rb +48 -34
- data/lib/hecks/hecks/adapters/console_capture.rb +1 -1
- data/lib/hecks/hecks/adapters/cost_explorer.rb +9 -1
- data/lib/hecks/hecks/adapters/dispatching/brief_text.rb +51 -0
- data/lib/hecks/hecks/adapters/dispatching/launcher.rb +97 -0
- data/lib/hecks/hecks/adapters/dispatching/plan.rb +82 -0
- data/lib/hecks/hecks/adapters/dispatching/rows.rb +66 -0
- data/lib/hecks/hecks/adapters/dispatching/settings.rb +81 -0
- data/lib/hecks/hecks/adapters/dispatching/settler.rb +40 -0
- data/lib/hecks/hecks/adapters/dispatching.adapter +7 -0
- data/lib/hecks/hecks/adapters/dispatching.rb +93 -0
- data/lib/hecks/hecks/adapters/doctor_reading/report.rb +27 -0
- data/lib/hecks/hecks/adapters/doctor_reading.adapter +7 -0
- data/lib/hecks/hecks/adapters/doctor_reading.rb +161 -0
- data/lib/hecks/hecks/adapters/git_checkout.rb +1 -1
- data/lib/hecks/hecks/adapters/git_commit.adapter +7 -0
- data/lib/hecks/hecks/adapters/git_commit.rb +61 -0
- data/lib/hecks/hecks/adapters/git_landing/finisher.rb +82 -0
- data/lib/hecks/hecks/adapters/git_landing/foreign_hold.rb +111 -0
- data/lib/hecks/hecks/adapters/git_landing/line.rb +171 -0
- data/lib/hecks/hecks/adapters/git_landing/lineup.rb +98 -0
- data/lib/hecks/hecks/adapters/git_landing/merger.rb +1 -1
- data/lib/hecks/hecks/adapters/git_landing/numbering.rb +54 -0
- data/lib/hecks/hecks/adapters/git_landing/rivals.rb +59 -0
- data/lib/hecks/hecks/adapters/git_landing/ticket.rb +156 -0
- data/lib/hecks/hecks/adapters/git_landing/tidy.rb +147 -0
- data/lib/hecks/hecks/adapters/git_landing/train.rb +104 -0
- data/lib/hecks/hecks/adapters/git_landing/verdict.rb +64 -0
- data/lib/hecks/hecks/adapters/git_landing/waiting.rb +120 -0
- data/lib/hecks/hecks/adapters/git_landing.rb +106 -20
- data/lib/hecks/hecks/adapters/git_main_pull.rb +99 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/bench.rb +82 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/due.rb +84 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/impacted/skips.rb +47 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/impacted/spec_run.rb +75 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/impacted.rb +181 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/line.rb +76 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/reading.rb +24 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/record.rb +146 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/spec_map/corpus.rb +25 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/spec_map/needles.rb +61 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/spec_map.rb +105 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/stages.rb +118 -0
- data/lib/hecks/hecks/adapters/git_rehearsal.adapter +7 -0
- data/lib/hecks/hecks/adapters/git_rehearsal.rb +141 -0
- data/lib/hecks/hecks/adapters/git_train/bench.rb +15 -15
- data/lib/hecks/hecks/adapters/git_train/branch_guard.rb +62 -0
- data/lib/hecks/hecks/adapters/git_train/carriage.rb +27 -28
- data/lib/hecks/hecks/adapters/git_train/launcher_shape.rb +75 -0
- data/lib/hecks/hecks/adapters/git_train/mint_step.rb +47 -0
- data/lib/hecks/hecks/adapters/git_train/settlement.rb +167 -0
- data/lib/hecks/hecks/adapters/git_train.rb +73 -15
- data/lib/hecks/hecks/adapters/git_worktree_add.adapter +7 -0
- data/lib/hecks/hecks/adapters/git_worktree_add.rb +68 -0
- data/lib/hecks/hecks/adapters/github_pull_requests.adapter +7 -0
- data/lib/hecks/hecks/adapters/github_pull_requests.rb +109 -0
- data/lib/hecks/hecks/adapters/host_http.rb +6 -6
- data/lib/hecks/hecks/adapters/in_process_boot.rb +10 -8
- data/lib/hecks/hecks/adapters/in_process_operations.rb +11 -17
- data/lib/hecks/hecks/adapters/in_process_undo.rb +17 -0
- data/lib/hecks/hecks/adapters/journal_store/changes.rb +13 -4
- data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +28 -15
- data/lib/hecks/hecks/adapters/journal_store/examination.rb +17 -1
- data/lib/hecks/hecks/adapters/journal_store/held_domain/renaming.rb +45 -0
- data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +31 -5
- data/lib/hecks/hecks/adapters/journal_store/held_mint.rb +128 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/attestation.rb +1 -1
- data/lib/hecks/hecks/adapters/journal_store/readings/boot_status.rb +58 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/composition.rb +39 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/outcomes.rb +76 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/postgres_reader.rb +44 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/reader.rb +64 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/report.rb +102 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/sqlite_reader.rb +43 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/stores.rb +80 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/subscribers.rb +72 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/summary.rb +39 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/event_trace.rb +39 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/command_line.rb +57 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/edge_guards.rb +107 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/inspection.rb +94 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/plan.rb +66 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/report.rb +72 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/store.rb +34 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/watermark_guards.rb +86 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/world.rb +66 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan.rb +36 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/projection_audit.rb +64 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/scaffolding.rb +3 -3
- data/lib/hecks/hecks/adapters/journal_store/readings/sqlite_status/database_reading.rb +113 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/sqlite_status/read_only.rb +33 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/sqlite_status.rb +32 -0
- data/lib/hecks/hecks/adapters/journal_store/readings.rb +13 -1
- data/lib/hecks/hecks/adapters/journal_store/reconcile_report.rb +60 -0
- data/lib/hecks/hecks/adapters/journal_store/reconciliation.rb +121 -0
- data/lib/hecks/hecks/adapters/journal_store/translation_write/shape_edge.rb +59 -0
- data/lib/hecks/hecks/adapters/journal_store/translation_write.rb +123 -0
- data/lib/hecks/hecks/adapters/journal_store.rb +16 -0
- data/lib/hecks/hecks/adapters/landed_work.adapter +7 -0
- data/lib/hecks/hecks/adapters/landed_work.rb +52 -0
- data/lib/hecks/hecks/adapters/local_files.rb +6 -6
- data/lib/hecks/hecks/adapters/process_pool.rb +3 -2
- data/lib/hecks/hecks/adapters/push_size.rb +57 -0
- data/lib/hecks/hecks/adapters/qa_quiescing.adapter +7 -0
- data/lib/hecks/hecks/adapters/qa_quiescing.rb +57 -0
- data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +30 -3
- data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +63 -3
- data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +29 -6
- data/lib/hecks/hecks/adapters/rehearsal/subject.rb +11 -5
- data/lib/hecks/hecks/adapters/rust_toolchain.rb +10 -1
- data/lib/hecks/hecks/adapters/rust_workspace.rb +1 -1
- data/lib/hecks/hecks/adapters/settings_reading/disposable_ledger.rb +32 -0
- data/lib/hecks/hecks/adapters/settings_reading/push_nudge.rb +26 -0
- data/lib/hecks/hecks/adapters/settings_reading/stores.rb +76 -0
- data/lib/hecks/hecks/adapters/settings_reading/test_scope.rb +28 -0
- data/lib/hecks/hecks/adapters/settings_reading.rb +28 -2
- data/lib/hecks/hecks/adapters/started_work.rb +42 -0
- data/lib/hecks/hecks/adapters/terminal.rb +12 -10
- data/lib/hecks/hecks/adapters/where_reading.adapter +7 -0
- data/lib/hecks/hecks/adapters/where_reading.rb +64 -0
- data/lib/hecks/hecks/adapters/work_areas.rb +57 -0
- data/lib/hecks/hecks/adapters/work_limits.rb +57 -0
- data/lib/hecks/hecks/build/build.bluebook +576 -0
- data/lib/hecks/hecks/check/check.bluebook +966 -0
- data/lib/hecks/hecks/codebase.behaviors +61 -5
- data/lib/hecks/hecks/composer.behaviors +1 -1
- data/lib/hecks/hecks/context_map.hecksagon +1 -1
- data/lib/hecks/hecks/custodian.behaviors +33 -7
- data/lib/hecks/hecks/delegate/delegate.bluebook +355 -0
- data/lib/hecks/hecks/deliver/deliver.bluebook +945 -0
- data/lib/hecks/hecks/deploy.behaviors +1 -1
- data/lib/hecks/hecks/evolve/evolve.bluebook +788 -0
- data/lib/hecks/hecks/find/find.bluebook +81 -0
- data/lib/hecks/hecks/hecks.behaviors +1 -1
- data/lib/hecks/hecks/hecks.bluebook +0 -133
- data/lib/hecks/hecks/hecks.contextmap +2 -0
- data/lib/hecks/hecks/hecks.hecksagon +185 -29
- data/lib/hecks/hecks/hecks.world +35 -9
- data/lib/hecks/hecks/implicit_moves.json +24 -0
- data/lib/hecks/hecks/migrate/migrate.bluebook +1066 -0
- data/lib/hecks/hecks/operate/operate.bluebook +695 -0
- data/lib/hecks/hecks/quiesce/quiesce.bluebook +117 -0
- data/lib/hecks/hecks/rehearse/rehearse.bluebook +133 -0
- data/lib/hecks/hecks/release/release.bluebook +916 -0
- data/lib/hecks/hecks/serve/serve.bluebook +134 -0
- data/lib/hecks/hecks/shapes.json +1 -15
- data/lib/hecks/hecks/site.behaviors +1 -1
- data/lib/hecks/hecks/start/start.bluebook +194 -0
- data/lib/hecks/hecks/test/test.bluebook +638 -0
- data/lib/hecks/hecks/tickets.world +14 -0
- data/lib/hecks/language/bluebook/aggregate.bluebook +22 -0
- data/lib/hecks/language/bluebook/bluebook.bluebook +34 -0
- data/lib/hecks/language/bluebook/command.bluebook +35 -0
- data/lib/hecks/language/bluebook/process_manager.bluebook +14 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +174 -54
- data/lib/hecks/language/hecksagon/domain_port.bluebook +1 -0
- data/lib/hecks/language/hecksagon/port_operation.bluebook +14 -0
- data/lib/hecks/language/kinds/run.kind +120 -0
- data/lib/hecks/language/oidc.json +10 -0
- data/lib/hecks/language/translation/translation_aggregate.bluebook +24 -0
- data/lib/hecks/module_folders.rb +31 -0
- data/lib/hecks/ports/key_vault.rb +1 -1
- data/lib/hecks/ports/persistence/append_only.rb +26 -8
- data/lib/hecks/ports/persistence/binding_policy.rb +3 -1
- data/lib/hecks/ports/persistence/codec_boundary.rb +1 -1
- data/lib/hecks/ports/persistence/mint_door.rb +74 -0
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +6 -5
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +5 -1
- data/lib/hecks/ports/persistence/plugins/era/era_status.rb +48 -0
- data/lib/hecks/ports/persistence/plugins/era/projected_bluebook.rb +127 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/additive_shape.rb +107 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/compose.rb +155 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/compose_render.rb +79 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/edge_files.rb +49 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/edge_route.rb +61 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/implicit_edge.rb +67 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/reconcile.rb +130 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/reconcile_files.rb +35 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +13 -5
- data/lib/hecks/ports/persistence/plugins/era/translation.rb +5 -0
- data/lib/hecks/ports/persistence/plugins/era.rb +1 -0
- data/lib/hecks/ports/persistence/repository_factory.rb +11 -3
- data/lib/hecks/ports/persistence/stored_name_guard.rb +36 -0
- data/lib/hecks/ports/persistence.rb +2 -0
- data/lib/hecks/projections/ci_pipeline/table/checks.rb +137 -0
- data/lib/hecks/projections/ci_pipeline/table/lookup.rb +59 -0
- data/lib/hecks/projections/ci_pipeline/table/refusals.rb +58 -0
- data/lib/hecks/projections/ci_pipeline/table/rows.rb +119 -0
- data/lib/hecks/projections/ci_pipeline/table/tree.rb +30 -0
- data/lib/hecks/projections/ci_pipeline/table/typing.rb +92 -0
- data/lib/hecks/projections/ci_pipeline/table.rb +112 -0
- data/lib/hecks/projections/ci_pipeline/workflows.rb +162 -0
- data/lib/hecks/projections/ci_pipeline/yaml_text.rb +104 -0
- data/lib/hecks/projections/deploy/box/hosting.rb +1 -1
- data/lib/hecks/projections/deploy/box/roll_recipe.rb +1 -1
- data/lib/hecks/projections/deploy/box/roll_scripts.rb +1 -1
- data/lib/hecks/projections/deploy/box.rb +1 -1
- data/lib/hecks/projections/deploy/templates/fargate/Makefile.tmpl +1 -1
- data/lib/hecks/projections/deploy/templates/lambda/Makefile.tmpl +3 -3
- data/lib/hecks/projections/undo.rb +46 -0
- data/lib/hecks/projections.rb +2 -0
- data/lib/hecks/projector/cli_projector/claims.rb +11 -0
- data/lib/hecks/projector/cli_projector/command_help.rb +6 -1
- data/lib/hecks/projector/cli_projector/command_specs.rb +11 -1
- data/lib/hecks/projector/cli_projector/completion.rb +87 -0
- data/lib/hecks/projector/cli_projector/everyday.rb +40 -0
- data/lib/hecks/projector/cli_projector/json_listing.rb +77 -0
- data/lib/hecks/projector/cli_projector/listing.rb +1 -1
- data/lib/hecks/projector/cli_projector/modules.rb +39 -0
- data/lib/hecks/projector/cli_projector/query_specs.rb +1 -0
- data/lib/hecks/projector/cli_projector/renames.rb +58 -0
- data/lib/hecks/projector/cli_projector/search.rb +74 -0
- data/lib/hecks/projector/cli_projector/usage.rb +23 -6
- data/lib/hecks/projector/cli_projector.rb +24 -1
- data/lib/hecks/quality_control/adapters/ci_head.adapter +8 -0
- data/lib/hecks/quality_control/adapters/image_roll.adapter +9 -0
- data/lib/hecks/quality_control/adapters/image_roll.rb +158 -0
- data/lib/hecks/quality_control/adapters/main_maintenance.adapter +8 -0
- data/lib/hecks/quality_control/adapters/main_maintenance.rb +275 -0
- data/lib/hecks/quality_control/adapters/pump_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/pump_tools.rb +18 -0
- data/lib/hecks/quality_control/adapters/push_due_reader.adapter +8 -0
- data/lib/hecks/quality_control/adapters/push_due_reader.rb +99 -0
- data/lib/hecks/quality_control/adapters/qa_auto_mint.rb +183 -0
- data/lib/hecks/quality_control/adapters/qa_era_mint.rb +94 -0
- data/lib/hecks/quality_control/adapters/qa_quiesce_marker.rb +126 -0
- data/lib/hecks/quality_control/adapters/run_keys.rb +36 -0
- data/lib/hecks/quality_control/adapters/run_reader.adapter +7 -0
- data/lib/hecks/quality_control/adapters/run_reader.rb +188 -0
- data/lib/hecks/quality_control/cli/child.rb +1 -0
- data/lib/hecks/quality_control/cli/qa_discover_external_domains/scanning.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +3 -3
- data/lib/hecks/quality_control/cli/qa_event_pump/attached.rb +67 -0
- data/lib/hecks/quality_control/cli/qa_event_pump/consumer.rb +103 -0
- data/lib/hecks/quality_control/cli/qa_event_pump/cursors.rb +60 -0
- data/lib/hecks/quality_control/cli/qa_event_pump/reactions.rb +46 -0
- data/lib/hecks/quality_control/cli/qa_event_pump/reader.rb +30 -0
- data/lib/hecks/quality_control/cli/qa_event_pump.rb +76 -0
- data/lib/hecks/quality_control/cli/qa_fix_main_bug/publishing.rb +79 -0
- data/lib/hecks/quality_control/cli/qa_fix_main_bug.rb +167 -0
- data/lib/hecks/quality_control/cli/qa_generated_domains/evaluation.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_generated_domains/promotion.rb +3 -3
- data/lib/hecks/quality_control/cli/qa_main_gate/gate_run.rb +52 -0
- data/lib/hecks/quality_control/cli/qa_main_gate/logging.rb +70 -0
- data/lib/hecks/quality_control/cli/qa_main_gate/shell.rb +47 -0
- data/lib/hecks/quality_control/cli/qa_main_gate/sweeping.rb +59 -0
- data/lib/hecks/quality_control/cli/qa_main_gate/worktree.rb +62 -0
- data/lib/hecks/quality_control/cli/qa_main_gate.rb +135 -0
- data/lib/hecks/quality_control/cli/qa_postgres_role.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_pr_check/checking.rb +2 -3
- data/lib/hecks/quality_control/cli/qa_push_nudge.rb +48 -0
- data/lib/hecks/quality_control/cli/qa_seed_angles/seed.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_sweep/seat.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_tick/event_pump_step.rb +34 -0
- data/lib/hecks/quality_control/cli/qa_tick/output.rb +16 -1
- data/lib/hecks/quality_control/cli/qa_tick/push_nudge_step.rb +28 -0
- data/lib/hecks/quality_control/cli/qa_tick/quiesce_guard.rb +51 -0
- data/lib/hecks/quality_control/cli/qa_tick.rb +36 -8
- data/lib/hecks/quality_control/quality_control.behaviors +65 -0
- data/lib/hecks/quality_control/quality_control.bluebook +1131 -11
- data/lib/hecks/quality_control/quality_control.hecksagon +11 -0
- data/lib/hecks/quality_control/quality_control.ports.hecksagon +138 -0
- data/lib/hecks/query_ir/touchpoints.rb +5 -7
- data/lib/hecks/refused.rb +21 -0
- data/lib/hecks/release/approval.rb +6 -3
- data/lib/hecks/release/conditions.rb +46 -0
- data/lib/hecks/release/runner/gem_publisher.rb +4 -4
- data/lib/hecks/release/version_commit.rb +48 -0
- data/lib/hecks/runtime/command_interpreter/hydration.rb +5 -4
- data/lib/hecks/runtime/command_interpreter/persisting.rb +11 -3
- data/lib/hecks/runtime/command_interpreter.rb +2 -2
- data/lib/hecks/runtime/command_rules/admissibility.rb +0 -2
- data/lib/hecks/runtime/command_rules/emission.rb +0 -1
- data/lib/hecks/runtime/dependency_planning/expression_reads.rb +0 -1
- data/lib/hecks/runtime/dispatcher/planning.rb +24 -0
- data/lib/hecks/runtime/dispatcher/routing.rb +6 -0
- data/lib/hecks/runtime/dispatcher/value_routing.rb +54 -0
- data/lib/hecks/runtime/dispatcher.rb +7 -1
- data/lib/hecks/runtime/dry_run_plan.rb +60 -0
- data/lib/hecks/runtime/entity_element/location.rb +0 -1
- data/lib/hecks/runtime/entity_element/mutations.rb +0 -1
- data/lib/hecks/runtime/entity_interpreter/locating.rb +5 -0
- data/lib/hecks/runtime/entity_interpreter.rb +0 -1
- data/lib/hecks/runtime/errors.rb +26 -0
- data/lib/hecks/runtime/instance.rb +0 -1
- data/lib/hecks/runtime/invocation_builder.rb +0 -1
- data/lib/hecks/runtime/outbox/consumer_runner.rb +9 -1
- data/lib/hecks/runtime/outbox/relay.rb +25 -4
- data/lib/hecks/runtime/outbox/runnable.rb +3 -1
- data/lib/hecks/runtime/outbox.rb +6 -2
- data/lib/hecks/runtime/policy_interpreter/fan_out.rb +18 -2
- data/lib/hecks/runtime/policy_interpreter.rb +17 -9
- data/lib/hecks/runtime/port_operation_interpreter.rb +21 -7
- data/lib/hecks/runtime/query_interpreter/arguments.rb +6 -5
- data/lib/hecks/runtime/reaction_invocation.rb +0 -1
- data/lib/hecks/runtime/refusal_wording.rb +12 -1
- data/lib/hecks/runtime/registry/context_maps.rb +1 -5
- data/lib/hecks/runtime/registry/declarations.rb +34 -4
- data/lib/hecks/runtime/registry/hecksagon_rules.rb +15 -1
- data/lib/hecks/runtime/registry/runtime_state.rb +10 -0
- data/lib/hecks/runtime/registry/world_defaults.rb +24 -2
- data/lib/hecks/runtime/registry/world_names.rb +99 -0
- data/lib/hecks/runtime/registry.rb +33 -27
- data/lib/hecks/runtime/saga_interpreter/compensation.rb +10 -0
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +29 -3
- data/lib/hecks/runtime/saga_interpreter/delivery.rb +0 -1
- data/lib/hecks/runtime/saga_interpreter/lifecycle.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter.rb +46 -2
- data/lib/hecks/runtime/value/coercion.rb +36 -8
- data/lib/hecks/rust_build/conformance.rb +16 -5
- data/lib/hecks/rust_build/parser.rb +25 -0
- data/lib/hecks/rust_build/project_rust/codegen_run.rb +45 -12
- data/lib/hecks/rust_build/project_rust/declared.rb +80 -0
- data/lib/hecks/rust_build/project_rust/features_header.rb +22 -0
- data/lib/hecks/rust_build/project_rust/manifest.rb +96 -0
- data/lib/hecks/rust_build/project_rust/meta.rb +36 -0
- data/lib/hecks/rust_build/project_rust.rb +28 -90
- data/lib/hecks/rust_build/write_if_changed.rb +19 -4
- data/lib/hecks/rust_build.rb +2 -1
- data/lib/hecks/rust_format.rb +32 -2
- data/lib/hecks/secrets.rb +111 -0
- data/lib/hecks/shape_ledger/edge_hygiene.rb +170 -0
- data/lib/hecks/shape_ledger/scope.rb +51 -0
- data/lib/hecks/shape_ledger/status.rb +89 -0
- data/lib/hecks/shape_ledger.rb +78 -33
- data/lib/hecks/site/bluebook/site.bluebook +1 -0
- data/lib/hecks/storehouse/audit.rb +34 -4
- data/lib/hecks/storehouse/callers.rb +8 -0
- data/lib/hecks/storehouse/dispatching.rb +14 -3
- data/lib/hecks/storehouse/identity.rb +41 -0
- data/lib/hecks/storehouse/querying.rb +7 -7
- data/lib/hecks/storehouse.rb +28 -10
- data/lib/hecks/tickets/bluebook/adapters/work_areas.adapter +7 -0
- data/lib/hecks/tickets/bluebook/adapters/work_limits.adapter +7 -0
- data/lib/hecks/tickets/bluebook/tickets.behaviors +70 -0
- data/lib/hecks/tickets/bluebook/tickets.bluebook +323 -28
- data/lib/hecks/tickets/bluebook/tickets.hecksagon +7 -0
- data/lib/hecks/tickets/bluebook/tickets.ports.hecksagon +17 -0
- data/lib/hecks/{cli → tools}/behaviors.rb +1 -1
- data/lib/hecks/{cli → tools}/canonicalise.rb +1 -1
- data/lib/hecks/{cli → tools}/check_engine_agreement.rb +1 -1
- data/lib/hecks/{cli → tools}/check_era.rb +6 -6
- data/lib/hecks/tools/ci_gates.rb +8 -2
- data/lib/hecks/tools/ci_workflows.rb +98 -0
- data/lib/hecks/{cli → tools}/console.rb +1 -1
- data/lib/hecks/{cli → tools}/deploy_template_diff.rb +1 -1
- data/lib/hecks/{cli → tools}/doc_coverage.rb +2 -2
- data/lib/hecks/{cli → tools}/document.rb +1 -1
- data/lib/hecks/{cli → tools}/domain_stub.rb +1 -1
- data/lib/hecks/{cli → tools}/domain_writer.rb +1 -1
- data/lib/hecks/tools/downstream_model_check.rb +122 -0
- data/lib/hecks/tools/evolve_run.rb +2 -2
- data/lib/hecks/{cli → tools}/expected_era.rb +1 -1
- data/lib/hecks/{cli → tools}/expression_projection.rb +1 -1
- data/lib/hecks/{cli → tools}/follow.rb +1 -1
- data/lib/hecks/tools/fuzz_sweep/options.rb +40 -5
- data/lib/hecks/tools/fuzz_sweep/pool.rb +12 -1
- data/lib/hecks/tools/fuzz_sweep/shards.rb +155 -0
- data/lib/hecks/tools/fuzz_sweep.rb +11 -5
- data/lib/hecks/tools/gate.rb +38 -29
- data/lib/hecks/tools/gate_memo/local_files.rb +33 -0
- data/lib/hecks/tools/gate_memo/rehearsal.rb +54 -0
- data/lib/hecks/tools/gate_memo/ruby_files.rb +152 -0
- data/lib/hecks/tools/gate_memo/session.rb +114 -0
- data/lib/hecks/tools/gate_memo/standalone.rb +67 -0
- data/lib/hecks/tools/gate_memo.rb +196 -0
- data/lib/hecks/tools/gate_report.rb +56 -0
- data/lib/hecks/{cli → tools}/history.rb +1 -1
- data/lib/hecks/{cli → tools}/interview_agent.rb +1 -1
- data/lib/hecks/{cli → tools}/interview_aggregate.rb +1 -1
- data/lib/hecks/{cli → tools}/interview_draft.rb +1 -1
- data/lib/hecks/{cli → tools}/interview_record.rb +1 -1
- data/lib/hecks/{cli → tools}/interview_run.rb +2 -2
- data/lib/hecks/{cli → tools}/interview_session/agent_state.rb +1 -1
- data/lib/hecks/{cli → tools}/interview_session/catalog.rb +1 -1
- data/lib/hecks/{cli → tools}/interview_session/manual_findings.rb +1 -1
- data/lib/hecks/{cli → tools}/interview_session/proposals.rb +1 -1
- data/lib/hecks/{cli → tools}/interview_session.rb +2 -2
- data/lib/hecks/{cli → tools}/interview_shape/lifecycle.rb +1 -1
- data/lib/hecks/{cli → tools}/interview_shape.rb +1 -1
- data/lib/hecks/{cli → tools}/ir.rb +1 -1
- data/lib/hecks/tools/lanes/files.rb +1 -1
- data/lib/hecks/tools/lanes/watch_workflow.rb +6 -8
- data/lib/hecks/tools/lanes/workflow.rb +1 -1
- data/lib/hecks/tools/lanes.rb +1 -1
- data/lib/hecks/{cli → tools}/mcp.rb +1 -1
- data/lib/hecks/{cli → tools}/model_check/examination.rb +1 -1
- data/lib/hecks/{cli → tools}/model_check/sources.rb +1 -1
- data/lib/hecks/{cli → tools}/model_check.rb +1 -1
- data/lib/hecks/tools/move_disables/reasons.rb +35 -0
- data/lib/hecks/tools/move_disables.rb +211 -0
- data/lib/hecks/tools/move_former_name.rb +105 -0
- data/lib/hecks/{cli → tools}/pattern_cases.rb +1 -1
- data/lib/hecks/{cli → tools}/persistence_notice.rb +1 -1
- data/lib/hecks/{cli → tools}/present.rb +1 -1
- data/lib/hecks/{cli → tools}/project_cli/launcher_source.rb +32 -22
- data/lib/hecks/{cli → tools}/project_cli/templates.rb +22 -15
- data/lib/hecks/{cli → tools}/project_cli.rb +11 -7
- data/lib/hecks/{cli → tools}/project_diagrams.rb +3 -3
- data/lib/hecks/{cli → tools}/project_glossary.rb +2 -2
- data/lib/hecks/{cli → tools}/reference.rb +2 -2
- data/lib/hecks/{cli → tools}/refresh_projections.rb +1 -1
- data/lib/hecks/{cli → tools}/refresh_rspec_runtime_baseline.rb +1 -1
- data/lib/hecks/tools/regeneration_run/pool.rb +148 -0
- data/lib/hecks/tools/regeneration_run/scratch.rb +76 -0
- data/lib/hecks/tools/regeneration_run.rb +125 -103
- data/lib/hecks/{cli → tools}/release.rb +1 -1
- data/lib/hecks/{cli → tools}/release_gem.rb +1 -1
- data/lib/hecks/{cli → tools}/rspec_io_parallel_files.rb +1 -1
- data/lib/hecks/{cli → tools}/rspec_shard_files.rb +1 -1
- data/lib/hecks/{cli → tools}/run.rb +9 -2
- data/lib/hecks/{cli → tools}/run_expectations.rb +1 -1
- data/lib/hecks/{cli → tools}/seed_semantics_corpus.rb +4 -4
- data/lib/hecks/{cli → tools}/serve_query_ir_mcp.rb +1 -1
- data/lib/hecks/{cli → tools}/shape.rb +3 -2
- data/lib/hecks/{cli → tools}/smoke_http/checks.rb +1 -1
- data/lib/hecks/{cli → tools}/smoke_http/settings.rb +1 -1
- data/lib/hecks/{cli → tools}/smoke_http/transport.rb +1 -1
- data/lib/hecks/{cli → tools}/smoke_http.rb +1 -1
- data/lib/hecks/{cli → tools}/smoke_test.rb +1 -1
- data/lib/hecks/{cli → tools}/spec_example.rb +1 -1
- data/lib/hecks/tools/stage_progress.rb +54 -0
- data/lib/hecks/{cli → tools}/statements.rb +1 -1
- data/lib/hecks/{cli → tools}/stores.rb +1 -1
- data/lib/hecks/{cli → tools}/stress_concurrency_specs/reporting.rb +1 -1
- data/lib/hecks/{cli → tools}/stress_concurrency_specs/usage.rb +4 -4
- data/lib/hecks/{cli → tools}/stress_concurrency_specs.rb +3 -3
- data/lib/hecks/tools/tenant_provisioning.rb +23 -0
- data/lib/hecks/tools/tools_doc.rb +10 -8
- data/lib/hecks/tools/translation_audit/edges.rb +21 -16
- data/lib/hecks/tools/translation_audit/verdicts.rb +1 -1
- data/lib/hecks/tools/translation_audit.rb +1 -1
- data/lib/hecks/tools/translation_scaffold.rb +1 -1
- data/lib/hecks/tools.rb +4 -1
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +61 -31
- data/lib/hecks/workflow_pagination/judge.rb +48 -0
- data/lib/hecks/workflow_pagination/statement.rb +50 -0
- data/lib/hecks/workflow_pagination.rb +58 -0
- data/lib/hecks.rb +2 -0
- data/lib/rubocop/cop/hecks/bare_event_and_query_names.rb +58 -0
- data/lib/rubocop/cop/hecks/correlates_by_aggregate.rb +39 -0
- data/lib/rubocop/cop/hecks/creating_command_sets.rb +75 -0
- data/lib/rubocop/cop/hecks/invariant_expression_language.rb +47 -0
- data/lib/rubocop/cop/hecks/lifecycle_needs_attribute.rb +58 -0
- data/lib/rubocop/cop/hecks/no_id_string_attribute.rb +49 -0
- data/lib/rubocop/cop/hecks/one_terminal_mark_per_lifecycle.rb +47 -0
- data/lib/rubocop/cop/hecks/value_object_shorthand.rb +56 -0
- data/prebuilt/13c04bdc5970295b310f0a980d397ba722232c8da99cc1a0cdb383249921e871.marshal +0 -0
- data/prebuilt/e3857b77f47d9ecc1787b2e1f5136364f3cb3444ef4aea5479f3b98167affea2.marshal +0 -0
- data/qa/settings.yml +23 -0
- data/rust/.gitignore +0 -6
- data/rust/Cargo.toml +2 -1
- data/rust/build/Cargo.toml +1 -1
- data/rust/build/src/binding_pass.rs +57 -4
- data/rust/build/src/pipeline.rs +6 -4
- data/rust/build/src/resolve.rs +1 -0
- data/rust/codegen/Cargo.toml +1 -1
- data/rust/codegen/src/domain_generator.rs +3 -0
- data/rust/codegen/src/json_codec.rs +182 -23
- data/rust/codegen/src/one_of_references.rs +1 -0
- data/rust/codegen/src/queries.rs +20 -2
- data/rust/codegen/src/registry.rs +21 -5
- data/rust/expr/Cargo.toml +1 -1
- data/rust/host/Cargo.toml +1 -1
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/core/Cargo.toml +1 -1
- data/rust/host/src/bin/mint_harness.rs +1 -0
- data/rust/host/src/boot.rs +1 -0
- data/rust/host/src/store/mint.rs +40 -6
- data/rust/lsp/Cargo.toml +1 -1
- data/rust/parser/Cargo.toml +1 -1
- data/rust/parser/src/build/correlation_keys.rs +125 -0
- data/rust/parser/src/build/mod.rs +1 -0
- data/rust/parser/src/emit.rs +55 -3
- data/rust/parser/src/ir.rs +33 -1
- data/rust/parser/src/keywords.rs +289 -1
- data/rust/parser/src/main.rs +2 -0
- data/rust/parser/src/parse/aggregate.rs +10 -1
- data/rust/parser/src/parse/audience.rs +65 -0
- data/rust/parser/src/parse/command.rs +14 -0
- data/rust/parser/src/parse/declaration.rs +97 -0
- data/rust/parser/src/parse/domain_port.rs +6 -0
- data/rust/parser/src/parse/mod.rs +1 -0
- data/rust/parser/src/parse/process_manager.rs +17 -10
- data/rust/parser/src/parse/world.rs +12 -8
- data/rust/parser/src/render_ruby.rs +9 -0
- data/rust/parser/src/sentence/format.rs +3 -0
- data/rust/parser/src/sentence/process.rs +6 -2
- data/rust/src/exemplar/json.rs +17 -0
- data/rust/src/kernel/cli.rs +27 -3
- data/rust/src/kernel/dispatch.rs +4 -2
- data/rust/src/kernel/json.rs +62 -0
- data/rust/src/kernel/orchestrate.rs +28 -3
- data/rust/src/kernel/orchestrate_guard_tests.rs +12 -12
- data/rust/web/Cargo.toml +1 -1
- metadata +389 -96
- data/lib/hecks/cli/command_table.rb +0 -72
- data/lib/hecks/cli/launcher_words.rb +0 -168
- data/lib/hecks/cli.rb +0 -133
- data/lib/hecks/hecks/agents.bluebook +0 -246
- data/lib/hecks/hecks/codebase.bluebook +0 -3209
- data/lib/hecks/hecks/custodian.bluebook +0 -1300
- data/lib/hecks/hecks/ledger.bluebook +0 -190
- data/lib/hecks/hecks/rehearsal.bluebook +0 -437
- data/lib/hecks/hecks/tooling.bluebook +0 -1234
- data/lib/hecks/hecks/translations/2-148748.bluebook +0 -2
- data/lib/hecks/hecks/translations/2-15a2e4.bluebook +0 -2
- data/lib/hecks/hecks/translations/2-1b447c.bluebook +0 -5
- data/lib/hecks/hecks/translations/2-240474.bluebook +0 -2
- data/lib/hecks/hecks/translations/2-44446d.bluebook +0 -2
- data/lib/hecks/hecks/translations/2-46ca26.bluebook +0 -2
- data/lib/hecks/hecks/translations/2-473eab.bluebook +0 -2
- data/lib/hecks/hecks/translations/2-75cc28.bluebook +0 -2
- data/lib/hecks/hecks/translations/2-781c07.bluebook +0 -2
- data/lib/hecks/hecks/translations/2-84c18b.bluebook +0 -2
- data/lib/hecks/hecks/translations/2-aa5fff.bluebook +0 -2
- data/lib/hecks/hecks/translations/2-bfdb48.bluebook +0 -2
- data/lib/hecks/hecks/translations/2-cd96d8.bluebook +0 -10
- data/lib/hecks/hecks/translations/2-d43f71.bluebook +0 -2
- data/lib/hecks/hecks/translations/2-eea577.bluebook +0 -2
- data/lib/hecks/hecks/translations/2-f53248.bluebook +0 -2
- data/lib/hecks/hecks/translations/2-fbb677.bluebook +0 -2
- data/lib/hecks/hecks/translations/2-ffdf12.bluebook +0 -6
- data/lib/hecks/quality_control/adapters/clearance_recorder.rb +0 -36
- data/lib/hecks/quality_control/adapters/github_ci_webhook.rb +0 -56
- data/prebuilt/31cf81e12f16236ad26249be590cfea3799c9db67432e6a4ef54b801c355b6ae.marshal +0 -0
- data/prebuilt/68ecb2bd3abb1a221007db4f2b0375834c13aaccac8f2b99778e8517f11aa113.marshal +0 -0
- data/prebuilt/verdicts-0328c2403383837d780ad5ed81d1da351c56436b894cf9015b5d56b8f1f1bf10.json +0 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Adapters
|
|
5
|
+
module Codebase
|
|
6
|
+
class SpecFiles
|
|
7
|
+
# How one file ended.
|
|
8
|
+
#
|
|
9
|
+
# @!attribute [r] file
|
|
10
|
+
# @return [String] the spec file, as named
|
|
11
|
+
# @!attribute [r] examples
|
|
12
|
+
# @return [Integer] how many examples ran
|
|
13
|
+
# @!attribute [r] failures
|
|
14
|
+
# @return [Integer] how many failed
|
|
15
|
+
# @!attribute [r] failed
|
|
16
|
+
# @return [Boolean] whether the file failed: a failed example, or a file that did not load
|
|
17
|
+
# @!attribute [r] output
|
|
18
|
+
# @return [String] what the runner printed for the file (empty when it shared a process)
|
|
19
|
+
Ending = Struct.new(:file, :examples, :failures, :failed, :output)
|
|
20
|
+
|
|
21
|
+
# The report of several files: what failing files printed, a line for each file, the
|
|
22
|
+
# isolation, the files that failed, and the total counts as the test runner spells them.
|
|
23
|
+
class Report
|
|
24
|
+
NOTES = {
|
|
25
|
+
"process" => "isolation: process (a new process for each file)",
|
|
26
|
+
"shared" => "isolation: shared (one process; may expose order dependence)"
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
29
|
+
# @param endings [Array<Ending>] how each file ended
|
|
30
|
+
# @param isolation [String] one of `SpecFiles::ISOLATIONS`
|
|
31
|
+
# @param printed [String, nil] what the one shared process printed
|
|
32
|
+
def initialize(endings, isolation, printed: nil)
|
|
33
|
+
@endings = endings
|
|
34
|
+
@isolation = isolation
|
|
35
|
+
@printed = printed
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @return [Boolean] whether any file failed
|
|
39
|
+
def failed? = @endings.any?(&:failed)
|
|
40
|
+
|
|
41
|
+
# @return [String] the report, ending with the total example and failure counts
|
|
42
|
+
def to_s = [@printed, *details, table, verdict, totals].compact.join("\n\n")
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def failing = @endings.select(&:failed)
|
|
47
|
+
|
|
48
|
+
def details
|
|
49
|
+
failing.reject { |one| one.output.to_s.empty? }.map { |one| "--- #{one.file} ---\n#{one.output}" }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def table = (["files:"] + @endings.map { |one| " #{line(one)}" }).join("\n")
|
|
53
|
+
|
|
54
|
+
def line(one)
|
|
55
|
+
"#{one.file}: #{plural(one.examples, "example")}, #{plural(one.failures, "failure")}#{" FAILED" if one.failed}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def verdict
|
|
59
|
+
return NOTES.fetch(@isolation) unless failed?
|
|
60
|
+
|
|
61
|
+
"#{NOTES.fetch(@isolation)}\nfailed: #{failing.map(&:file).join(" ")} (#{failing.size} of #{@endings.size} files)"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def totals = "#{plural(@endings.sum(&:examples), "example")}, #{plural(@endings.sum(&:failures), "failure")}"
|
|
65
|
+
|
|
66
|
+
def plural(count, word) = "#{count} #{word}#{"s" unless count == 1}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "tempfile"
|
|
5
|
+
require_relative "report"
|
|
6
|
+
|
|
7
|
+
module Hecks
|
|
8
|
+
module Adapters
|
|
9
|
+
module Codebase
|
|
10
|
+
class SpecFiles
|
|
11
|
+
# Every file in one test-runner process; the JSON formatter says which file each example
|
|
12
|
+
# belongs to, and the runner's own message which file did not load.
|
|
13
|
+
class SharedRun
|
|
14
|
+
LOAD_ERROR = /error occurred while loading (.+?)\.?$/
|
|
15
|
+
|
|
16
|
+
# @param tree [Tree] the checkout
|
|
17
|
+
# @param runner [#outcome] runs the test runner on arguments
|
|
18
|
+
def initialize(tree, runner)
|
|
19
|
+
@tree = tree
|
|
20
|
+
@runner = runner
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @param names [Array<String>] the spec files
|
|
24
|
+
# @param options [Array<String>] the runner's filters, such as `--example x`
|
|
25
|
+
# @param env [Hash{String => String}] variables the specs read
|
|
26
|
+
# @return [Array(Array<Ending>, String)] how each file ended, and what the process printed
|
|
27
|
+
def call(names, options, env)
|
|
28
|
+
Tempfile.create(["spec-files", ".json"]) do |json|
|
|
29
|
+
args = ["--format", "documentation", "--format", "json", "--out", json.path, *options, *names]
|
|
30
|
+
status, printed = @runner.outcome(args, env: env)
|
|
31
|
+
by_file = examples_by_file(json.path)
|
|
32
|
+
endings = names.map { |name| ending(name, by_file.fetch(key(name), [])) }
|
|
33
|
+
blame_load_errors(endings, status, printed)
|
|
34
|
+
[endings, printed]
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def examples_by_file(path)
|
|
41
|
+
JSON.parse(File.read(path)).fetch("examples", []).group_by { |entry| key(entry["file_path"].to_s) }
|
|
42
|
+
rescue JSON::ParserError, Errno::ENOENT
|
|
43
|
+
{}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# A file as the runner may spell it (relative, absolute, through a symlink), made
|
|
47
|
+
# comparable.
|
|
48
|
+
def key(path)
|
|
49
|
+
full = File.expand_path(path, @tree.root)
|
|
50
|
+
File.exist?(full) ? File.realpath(full) : full
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def ending(name, examples)
|
|
54
|
+
failures = examples.count { |entry| entry["status"] == "failed" }
|
|
55
|
+
Ending.new(name, examples.size, failures, failures.positive?, "")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# A file that did not load has no examples, and the runner then runs none of the others.
|
|
59
|
+
# The runner names the file; when the run failed with no failed example and no name,
|
|
60
|
+
# every file is blamed.
|
|
61
|
+
def blame_load_errors(endings, status, printed)
|
|
62
|
+
return if status.zero? || endings.any?(&:failed)
|
|
63
|
+
|
|
64
|
+
named = loading(printed)
|
|
65
|
+
(named.empty? ? endings : endings.select { |one| named.include?(key(one.file)) }).each { |one| one.failed = true }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def loading(printed) = printed.scan(LOAD_ERROR).flatten.map { |path| key(path) }
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "tree"
|
|
4
|
+
require_relative "test_runner"
|
|
5
|
+
require_relative "spec_files/report"
|
|
6
|
+
require_relative "spec_files/shared_run"
|
|
7
|
+
require_relative "../console_capture"
|
|
8
|
+
|
|
9
|
+
module Hecks
|
|
10
|
+
module Adapters
|
|
11
|
+
module Codebase
|
|
12
|
+
# Several spec files run in one launcher boot, and how each ended.
|
|
13
|
+
#
|
|
14
|
+
# `process` isolation gives every file a new process of its own, so nothing one file defines,
|
|
15
|
+
# stubs or registers reaches the next: it saves the launcher's boot, not each file's load.
|
|
16
|
+
# `shared` isolation runs every file in one test-runner process, the fastest, and may expose
|
|
17
|
+
# an order dependence between files (constants, stubs, Memory registries are shared).
|
|
18
|
+
#
|
|
19
|
+
# The report names each file with its example and failure counts, and ends with the total
|
|
20
|
+
# counts as the test runner spells them. A run in which any file failed is refused with it.
|
|
21
|
+
class SpecFiles
|
|
22
|
+
# Every way the files can be isolated; the first is the default.
|
|
23
|
+
ISOLATIONS = %w[process shared].freeze
|
|
24
|
+
|
|
25
|
+
# The example filter that matches every example.
|
|
26
|
+
ALL_EXAMPLES = " "
|
|
27
|
+
|
|
28
|
+
COUNTS = /(\d+) examples?, (\d+) failures?/
|
|
29
|
+
|
|
30
|
+
# @param tree [Tree] the checkout whose specs are run
|
|
31
|
+
# @param runner [TestRunner] runs the test runner on arguments
|
|
32
|
+
def initialize(tree, runner: TestRunner.new(tree))
|
|
33
|
+
@tree = tree
|
|
34
|
+
@runner = runner
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @param files [String, Array<String>] spec files, relative to the checkout or absolute; a
|
|
38
|
+
# string is split on whitespace
|
|
39
|
+
# @param example [String, nil] part of an example's description; every example when nil
|
|
40
|
+
# @param isolation [String] one of `ISOLATIONS`
|
|
41
|
+
# @param env [Hash{String => String}] variables the specs read, set for these runs only
|
|
42
|
+
# @param tags [String, nil] RSpec tag filters as the runner spells them
|
|
43
|
+
# @return [String] the per-file counts and the total, when every file passed
|
|
44
|
+
# @raise [ConsoleCapture::Failure] naming the files that failed, or that are not there
|
|
45
|
+
def run(files:, example: nil, isolation: "process", env: {}, tags: nil)
|
|
46
|
+
names = Array(files).flat_map { |name| name.to_s.split }.uniq
|
|
47
|
+
check(names, isolation)
|
|
48
|
+
report = isolate(isolation, names, filters(example, tags), env)
|
|
49
|
+
raise ConsoleCapture::Failure, report.to_s if report.failed?
|
|
50
|
+
|
|
51
|
+
report.to_s
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
# A single space matches every example, and a named example overrides the tag exclusions
|
|
57
|
+
# (`:io`, `:process`, `:fuzzing`) as a one-file run does, so an absent filter is that space.
|
|
58
|
+
def filters(example, tags) = ["--example", example.to_s.empty? ? ALL_EXAMPLES : example, *tags.to_s.split]
|
|
59
|
+
|
|
60
|
+
def isolate(isolation, names, options, env)
|
|
61
|
+
isolation == "shared" ? shared(names, options, env) : fresh(names, options, env)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def check(names, isolation)
|
|
65
|
+
raise ConsoleCapture::Failure, "no spec files named" if names.empty?
|
|
66
|
+
unless ISOLATIONS.include?(isolation)
|
|
67
|
+
raise ConsoleCapture::Failure, "isolation is process or shared, not #{isolation.inspect}"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
missing = names.reject { |name| File.file?(File.expand_path(name, @tree.root)) }
|
|
71
|
+
raise ConsoleCapture::Failure, "no such spec file #{missing.join(" ")}" unless missing.empty?
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def fresh(names, options, env)
|
|
75
|
+
endings = names.map do |name|
|
|
76
|
+
status, printed = @runner.outcome([*options, name], env: env, fresh: true)
|
|
77
|
+
examples, failures = counts(printed)
|
|
78
|
+
Ending.new(name, examples, failures, !status.zero?, printed)
|
|
79
|
+
end
|
|
80
|
+
Report.new(endings, "process")
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def shared(names, options, env)
|
|
84
|
+
endings, printed = SharedRun.new(@tree, @runner).call(names, options, env)
|
|
85
|
+
Report.new(endings, "shared", printed: printed)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def counts(printed)
|
|
89
|
+
found = printed.match(COUNTS)
|
|
90
|
+
found ? [found[1].to_i, found[2].to_i] : [0, 0]
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "stringio"
|
|
4
|
+
require "open3"
|
|
5
|
+
require "rbconfig"
|
|
4
6
|
require_relative "tree"
|
|
5
7
|
require_relative "../console_capture"
|
|
6
8
|
|
|
@@ -13,6 +15,9 @@ module Hecks
|
|
|
13
15
|
# description for a shell. What the runner prints is captured and comes back as the answer;
|
|
14
16
|
# a run that fails refuses with the same text.
|
|
15
17
|
class TestRunner
|
|
18
|
+
# The program a fresh process runs: the test runner, on the arguments it is handed.
|
|
19
|
+
FRESH_RUNNER = 'require "rspec/core"; exit RSpec::Core::Runner.run(ARGV)'
|
|
20
|
+
|
|
16
21
|
class << self
|
|
17
22
|
# @return [#call, nil] takes `(args, err, out)` and answers the exit status; the test
|
|
18
23
|
# runner when nil. A spec replaces it so no example has to run to test what is asked.
|
|
@@ -58,16 +63,60 @@ module Hecks
|
|
|
58
63
|
report
|
|
59
64
|
end
|
|
60
65
|
|
|
66
|
+
# Runs every spec in the given files or directories, from the checkout's root: a whole suite
|
|
67
|
+
# is one directory, a changed-file run is the specs those files map to.
|
|
68
|
+
#
|
|
69
|
+
# @param paths [Array<String>] spec files or directories, relative to the checkout or
|
|
70
|
+
# absolute
|
|
71
|
+
# @param env [Hash{String => String}] variables the specs read, set for this run only
|
|
72
|
+
# @param tags [String, nil] RSpec tag filters as the runner spells them
|
|
73
|
+
# @return [String] what the runner printed when every spec it ran passed
|
|
74
|
+
# @raise [ConsoleCapture::Failure] when a path is not there, or a spec failed
|
|
75
|
+
def run_paths(paths:, env: {}, tags: nil)
|
|
76
|
+
missing = paths.reject { |path| File.exist?(File.expand_path(path, @tree.root)) }
|
|
77
|
+
raise ConsoleCapture::Failure, "no such spec #{missing.first}" unless missing.empty?
|
|
78
|
+
|
|
79
|
+
report = with_env(env) { run_args([*tags.to_s.split, *paths]) }
|
|
80
|
+
raise ConsoleCapture::Failure, "the test runner printed nothing for #{paths.join(" ")}" if report.empty?
|
|
81
|
+
|
|
82
|
+
report
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Runs the runner on the arguments and answers how it ended, whatever the result: a
|
|
86
|
+
# caller that reports each of several runs reads the status, not a refusal.
|
|
87
|
+
#
|
|
88
|
+
# @param args [Array<String>] the runner's arguments
|
|
89
|
+
# @param env [Hash{String => String}] variables the specs read, set for this run only
|
|
90
|
+
# @param fresh [Boolean] whether to run it in a new process of its own (nothing a spec
|
|
91
|
+
# defined, stubbed or registered carries over) or in this one
|
|
92
|
+
# @return [Array(Integer, String)] the exit status and what the runner printed
|
|
93
|
+
def outcome(args, env: {}, fresh: false)
|
|
94
|
+
return with_env(env) { in_process(args) } unless fresh
|
|
95
|
+
|
|
96
|
+
printed, status = Open3.capture2e(env, RbConfig.ruby, "-e", FRESH_RUNNER, "--", *args, chdir: @tree.root)
|
|
97
|
+
[status.exitstatus || 1, printed.strip]
|
|
98
|
+
end
|
|
99
|
+
|
|
61
100
|
private
|
|
62
101
|
|
|
63
102
|
# What the runner printed for the example, when it passed.
|
|
64
103
|
def run_example(path, example, tags = [])
|
|
104
|
+
run_args(["--example", example, *tags, path])
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# What the runner printed for the arguments, when every spec it ran passed.
|
|
108
|
+
def run_args(args)
|
|
109
|
+
status, printed = in_process(args)
|
|
110
|
+
raise ConsoleCapture::Failure, printed unless status.zero?
|
|
111
|
+
|
|
112
|
+
printed
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# The runner's exit status and what it printed, for the arguments, in this process.
|
|
116
|
+
def in_process(args)
|
|
65
117
|
out = StringIO.new
|
|
66
|
-
args = ["--example", example, *tags, path]
|
|
67
118
|
status = Dir.chdir(@tree.root) { at_reaction_depth_zero { runner.call(args, out, out) } }
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
out.string.strip
|
|
119
|
+
[status, out.string.strip]
|
|
71
120
|
end
|
|
72
121
|
|
|
73
122
|
def at_reaction_depth_zero(&) = self.class.at_reaction_depth_zero(&)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../console_capture"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module Adapters
|
|
7
|
+
module Codebase
|
|
8
|
+
# How much of the spec suite this machine may run, from the world's
|
|
9
|
+
# `test_scope "Local", suite_runs_in: "ci", spec_files_limit: n` setting. Owner decision: the
|
|
10
|
+
# full suite runs only in CI (the `rspec` check is `runs_in: "ci"` in the Vocabulary rows),
|
|
11
|
+
# and a change is checked locally by the quick gate and the specs named for it, at most
|
|
12
|
+
# `spec_files_limit` files per call.
|
|
13
|
+
#
|
|
14
|
+
# CI is detected here, by the environment (`CI` or `GITHUB_ACTIONS`), never by the bluebook. A
|
|
15
|
+
# world that declares no `test_scope` (a client's project) is not limited.
|
|
16
|
+
class TestScope
|
|
17
|
+
# The world verb that carries the setting.
|
|
18
|
+
VERB = "test_scope"
|
|
19
|
+
|
|
20
|
+
# The spec files a directory holds.
|
|
21
|
+
SPEC_GLOB = "**/*_spec.rb"
|
|
22
|
+
|
|
23
|
+
# @param registry [Runtime::Registry, nil] the booted registry whose Hecks world is read
|
|
24
|
+
# @return [Hash{Symbol => Object}] the declared setting, empty when none is
|
|
25
|
+
def self.setting(registry) = registry&.world("Hecks")&.for_verb(VERB) || {}
|
|
26
|
+
|
|
27
|
+
# @param registry [Runtime::Registry, nil]
|
|
28
|
+
# @return [Integer, nil] the most spec files a local call may run, nil when none is declared
|
|
29
|
+
def self.limit(registry)
|
|
30
|
+
declared = setting(registry)[:spec_files_limit]
|
|
31
|
+
declared && Integer(declared)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @param registry [Runtime::Registry, nil] the booted registry whose Hecks world is read
|
|
35
|
+
# @param env [#[]] the process environment
|
|
36
|
+
def initialize(registry, env: ENV)
|
|
37
|
+
@setting = self.class.setting(registry)
|
|
38
|
+
@env = env
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @return [Integer, nil] the limit that holds here: nil in CI or when none is declared
|
|
42
|
+
def limit = (Integer(@setting[:spec_files_limit]) if @setting[:spec_files_limit] && !ci?)
|
|
43
|
+
|
|
44
|
+
# @return [Boolean] whether this process runs in CI
|
|
45
|
+
def ci? = %w[CI GITHUB_ACTIONS].any? { |name| !["", "false"].include?(@env[name].to_s) }
|
|
46
|
+
|
|
47
|
+
# Lets a request through, or refuses one that would run more than this machine may. An
|
|
48
|
+
# impacted run that names no cap gets the limit as its cap.
|
|
49
|
+
#
|
|
50
|
+
# @param operation [String] the `TestSuiteRun` operation
|
|
51
|
+
# @param held [Hash] the record's fields, each perhaps a value object's `{ value: x }`
|
|
52
|
+
# @param tree [Tree] the folder whose specs would run
|
|
53
|
+
# @return [Hash] the fields to carry the operation out with
|
|
54
|
+
# @raise [ConsoleCapture::Failure] naming the limit and CI
|
|
55
|
+
def admit(operation, held, tree)
|
|
56
|
+
args = held.transform_values { |value| value.is_a?(Hash) ? value[:value] : value }
|
|
57
|
+
refuse!(operation, args, tree) unless ci?
|
|
58
|
+
return held unless operation == "run_impacted_specs" && limit && args[:cap].nil?
|
|
59
|
+
|
|
60
|
+
held.merge(cap: { value: limit })
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def refuse!(operation, args, tree)
|
|
66
|
+
case operation
|
|
67
|
+
when "run_suite" then refuse_suite
|
|
68
|
+
when "run_spec_files" then refuse_files(files_in(args[:files], tree))
|
|
69
|
+
when "run_impacted_specs" then refuse_cap(args[:cap])
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def refuse_suite
|
|
74
|
+
return unless @setting[:suite_runs_in].to_s == "ci"
|
|
75
|
+
|
|
76
|
+
fail!("the full suite runs in CI (rspec runs_in \"ci\"); run named specs, or run_impacted_specs!")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def refuse_files(count)
|
|
80
|
+
return if limit.nil? || count <= limit
|
|
81
|
+
|
|
82
|
+
fail!("run_spec_files! runs at most #{limit} spec files per call locally (spec_files_limit); #{count} named. " \
|
|
83
|
+
"The full suite runs in CI (rspec runs_in \"ci\"): name fewer files, or run_impacted_specs!")
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def refuse_cap(cap)
|
|
87
|
+
return if limit.nil? || cap.nil? || Integer(cap) <= limit
|
|
88
|
+
|
|
89
|
+
fail!("run_impacted_specs! caps at #{limit} spec files locally (spec_files_limit); cap.value=#{cap} is more. " \
|
|
90
|
+
"The full suite runs in CI (rspec runs_in \"ci\")")
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# A named directory counts as the spec files it holds.
|
|
94
|
+
def files_in(files, tree)
|
|
95
|
+
Array(files).flat_map { |name| name.to_s.split }.uniq.sum do |name|
|
|
96
|
+
path = File.expand_path(name, tree.root)
|
|
97
|
+
File.directory?(path) ? Dir.glob(File.join(path, SPEC_GLOB)).size : 1
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def fail!(message) = raise(ConsoleCapture::Failure, message)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
require "fileutils"
|
|
4
4
|
require_relative "tree"
|
|
5
|
+
require_relative "minitest_runs"
|
|
6
|
+
require_relative "run_options"
|
|
5
7
|
require_relative "test_runner"
|
|
8
|
+
require_relative "changed_specs"
|
|
9
|
+
require_relative "batch_runs"
|
|
6
10
|
require_relative "sqlite_fixture"
|
|
7
11
|
require_relative "cargo_tests"
|
|
8
12
|
require_relative "cache_clearing"
|
|
@@ -13,15 +17,23 @@ module Hecks
|
|
|
13
17
|
# What Codebase's `TestSuiteRun` asks of the working tree: the tooling around this
|
|
14
18
|
# repository's specs.
|
|
15
19
|
#
|
|
16
|
-
# The tooling owns its options, so each ask runs one of the `Hecks::
|
|
20
|
+
# The tooling owns its options, so each ask runs one of the `Hecks::Tools` commands in this
|
|
17
21
|
# process from the checkout's root, except a single example (the `TestRunner` adapter runs it)
|
|
18
22
|
# and the persistence fixtures (the `SqliteFixture` adapter). A listing is a pure read.
|
|
19
23
|
# Refreshing the committed timings, regenerating the fixtures and writing the committed spec
|
|
20
24
|
# list change tracked files, so unconfirmed they report what they would do and write nothing.
|
|
21
25
|
module TestSuite
|
|
22
26
|
# Every operation this family carries out.
|
|
23
|
-
OPERATIONS = %w[refresh_runtime_baseline run_spec_example
|
|
24
|
-
regenerate_legacy_fixtures seed_semantics_corpus
|
|
27
|
+
OPERATIONS = %w[refresh_runtime_baseline run_spec_example run_spec_files run_impacted_specs run_suite run_changed
|
|
28
|
+
stress_concurrency run_cargo_tests clear_caches regenerate_legacy_fixtures seed_semantics_corpus
|
|
29
|
+
write_io_parallel_spec_list].freeze
|
|
30
|
+
|
|
31
|
+
# The operations that run specs in a folder of their own: they need that folder to hold
|
|
32
|
+
# specs, not to be this checkout, so any project's suite runs through them.
|
|
33
|
+
FOLDER_OPERATIONS = %w[run_spec_example run_spec_files run_impacted_specs run_suite run_changed].freeze
|
|
34
|
+
|
|
35
|
+
# The spec directory a folder's whole suite lives in.
|
|
36
|
+
SPEC_DIRECTORY = "spec"
|
|
25
37
|
|
|
26
38
|
# The tag filter the postgres job lists its specs by when none is named.
|
|
27
39
|
DEFAULT_TAGS = "--tag io --tag ~fuzzing"
|
|
@@ -31,14 +43,14 @@ module Hecks
|
|
|
31
43
|
|
|
32
44
|
module_function
|
|
33
45
|
|
|
34
|
-
# Loads the command a `Hecks::
|
|
46
|
+
# Loads the command a `Hecks::Tools` file defines, the first time an ask needs it: several
|
|
35
47
|
# read the checkout's specs or need a development gem, which an installed gem lacks.
|
|
36
48
|
#
|
|
37
|
-
# @param file [String] the command's file under `lib/hecks/
|
|
49
|
+
# @param file [String] the command's file under `lib/hecks/tools/`, without the extension
|
|
38
50
|
# @return [Module] the command, named as the file is, in camel case
|
|
39
51
|
def command(file)
|
|
40
|
-
require "hecks/
|
|
41
|
-
Hecks::
|
|
52
|
+
require "hecks/tools/#{file}"
|
|
53
|
+
Hecks::Tools.const_get(file.split("_").map(&:capitalize).join)
|
|
42
54
|
end
|
|
43
55
|
|
|
44
56
|
# Carries out one operation.
|
|
@@ -52,7 +64,9 @@ module Hecks
|
|
|
52
64
|
def call(operation, held, tree, shell: nil)
|
|
53
65
|
args = plain(held)
|
|
54
66
|
case operation
|
|
55
|
-
when
|
|
67
|
+
when *BatchRuns::OPERATIONS then BatchRuns.call(operation, args, tree)
|
|
68
|
+
when "run_suite" then suite(args, tree)
|
|
69
|
+
when "run_changed" then changed(args, tree)
|
|
56
70
|
when "run_cargo_tests" then CargoTests.run(args, tree, shell)
|
|
57
71
|
when "clear_caches" then CacheClearing.clear
|
|
58
72
|
else tooling(operation, args, tree, shell)
|
|
@@ -73,30 +87,41 @@ module Hecks
|
|
|
73
87
|
end
|
|
74
88
|
end
|
|
75
89
|
|
|
76
|
-
#
|
|
77
|
-
#
|
|
90
|
+
# Runs every spec of the folder's spec directory in one process.
|
|
91
|
+
#
|
|
92
|
+
# @param args [Hash] `tags`, `strict`, `seeds` and `environment`, each perhaps absent
|
|
93
|
+
# @param tree [Tree] the folder whose specs run
|
|
78
94
|
# @return [String] what the runner printed
|
|
79
|
-
def
|
|
80
|
-
TestRunner.new(tree).
|
|
95
|
+
def suite(args, tree)
|
|
96
|
+
rspec = TestRunner.new(tree).run_paths(paths: [SPEC_DIRECTORY], env: RunOptions.spec_env(args), tags: args[:tags])
|
|
97
|
+
minitest = MinitestRuns.files(tree)
|
|
98
|
+
return rspec if minitest.empty?
|
|
99
|
+
|
|
100
|
+
([rspec] + MinitestRuns.reports(tree, minitest)).join("\n")
|
|
81
101
|
end
|
|
82
102
|
|
|
83
|
-
#
|
|
84
|
-
#
|
|
103
|
+
# Runs the specs the files changed since a git ref map to, by name. Says so and runs nothing
|
|
104
|
+
# when no spec maps to a change, so an unmapped change never falls back to the whole suite.
|
|
85
105
|
#
|
|
86
|
-
# @param args [Hash] `
|
|
87
|
-
#
|
|
88
|
-
# @return [
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
106
|
+
# @param args [Hash] `base` (the ref, `origin/main` when absent), and the run's variables
|
|
107
|
+
# @param tree [Tree] the folder whose changes are read
|
|
108
|
+
# @return [String] what the runner printed, or that no spec maps to the changes
|
|
109
|
+
# @raise [ConsoleCapture::Failure] when git cannot read the changes
|
|
110
|
+
def changed(args, tree)
|
|
111
|
+
base = args[:base] || "origin/main"
|
|
112
|
+
files = ChangedSpecs.changed_files(tree, base)
|
|
113
|
+
specs = Dir.glob("#{SPEC_DIRECTORY}/**/*_spec.rb", base: tree.root).sort
|
|
114
|
+
mapped = ChangedSpecs.call(files, specs)
|
|
115
|
+
return "no spec maps to the #{files.size} changed file(s) since #{base}; nothing run" if mapped.empty?
|
|
116
|
+
|
|
117
|
+
TestRunner.new(tree).run_paths(paths: mapped, env: RunOptions.spec_env(args), tags: args[:tags])
|
|
93
118
|
end
|
|
94
119
|
|
|
95
120
|
# @param args [Hash] `runs`, `parallel` and `seed_start`
|
|
96
121
|
# @param tree [Tree] the checkout
|
|
97
122
|
# @return [String] what the stress run printed
|
|
98
123
|
def stress(args, tree)
|
|
99
|
-
answer { command("stress_concurrency_specs").call(stress_flags(args), root: tree.root) }
|
|
124
|
+
answer { command("stress_concurrency_specs").call(RunOptions.stress_flags(args), root: tree.root) }
|
|
100
125
|
end
|
|
101
126
|
|
|
102
127
|
# @param args [Hash] `confirm`
|
|
@@ -115,7 +140,7 @@ module Hecks
|
|
|
115
140
|
# @return [String] what the seeding printed
|
|
116
141
|
def seed(args, tree)
|
|
117
142
|
seeding = command("seed_semantics_corpus")
|
|
118
|
-
answer { TestRunner.at_reaction_depth_zero { seeding.call(root: tree.root, env: seed_env(args)) } }
|
|
143
|
+
answer { TestRunner.at_reaction_depth_zero { seeding.call(root: tree.root, env: RunOptions.seed_env(args)) } }
|
|
119
144
|
end
|
|
120
145
|
|
|
121
146
|
# A pure read: a listing, or the recorded pattern cases.
|
|
@@ -191,17 +216,6 @@ module Hecks
|
|
|
191
216
|
"and rewrite #{BASELINES.join(", ")} (add --confirm)"
|
|
192
217
|
end
|
|
193
218
|
|
|
194
|
-
# @param args [Hash] `runs`, `parallel` and `seed_start`
|
|
195
|
-
# @return [Array<String>] the script's flags for those that are named
|
|
196
|
-
def stress_flags(args)
|
|
197
|
-
{ "--runs" => :runs, "--parallel" => :parallel, "--seed-start" => :seed_start }
|
|
198
|
-
.filter_map { |flag, name| [flag, args[name].to_s] if args[name] }.flatten
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
# @param args [Hash] `fixture`, perhaps absent
|
|
202
|
-
# @return [Hash{String => String}] the variable that names the one fixture to re-seed
|
|
203
|
-
def seed_env(args) = args[:fixture] ? { "SEED" => args[:fixture] } : {}
|
|
204
|
-
|
|
205
219
|
# @param args [Hash] `exclude`, `tags`, `write` and `confirm`
|
|
206
220
|
# @param tree [Tree] the checkout whose spec list is written
|
|
207
221
|
# @return [String] what was written, or (unconfirmed) how many files would be
|
|
@@ -6,7 +6,7 @@ require "stringio"
|
|
|
6
6
|
module Hecks
|
|
7
7
|
module Adapters
|
|
8
8
|
# Runs a command-line entry point that prints and exits, and hands back what it said and how
|
|
9
|
-
# it ended, so an adapter can reuse `Hecks::
|
|
9
|
+
# it ended, so an adapter can reuse `Hecks::Tools::*` unchanged.
|
|
10
10
|
#
|
|
11
11
|
# It is the shared base for every Custodian adapter that wraps an existing CLI: the entry
|
|
12
12
|
# point keeps its own printing, `exit` and `abort`, and the adapter turns the outcome into a
|
|
@@ -41,7 +41,7 @@ module Hecks
|
|
|
41
41
|
# @raise [Error] when no complete day has passed since `since`, `aws` fails, or the
|
|
42
42
|
# rate is over the budget
|
|
43
43
|
def measure(**held)
|
|
44
|
-
budget =
|
|
44
|
+
budget = whole_dollars(plain(held[:budget]))
|
|
45
45
|
first = Date.iso8601(plain(held[:since]).to_s)
|
|
46
46
|
last = today
|
|
47
47
|
raise Error, "no complete day since #{first}; today (#{last}) is still being billed" if first >= last
|
|
@@ -55,6 +55,14 @@ module Hecks
|
|
|
55
55
|
|
|
56
56
|
private
|
|
57
57
|
|
|
58
|
+
# @return [Integer] the budget as whole dollars
|
|
59
|
+
# @raise [Error] when none was given
|
|
60
|
+
def whole_dollars(budget)
|
|
61
|
+
raise Error, "no budget given: a whole number of dollars a month" if budget.nil?
|
|
62
|
+
|
|
63
|
+
Integer(budget)
|
|
64
|
+
end
|
|
65
|
+
|
|
58
66
|
# @return [Date] the day the span ends before: today, in UTC
|
|
59
67
|
def today = @today ? Date.iso8601(@today.to_s) : Time.now.utc.to_date
|
|
60
68
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Adapters
|
|
5
|
+
class Dispatching
|
|
6
|
+
# The words a session is started with, laid out from a composed brief. Every sentence in it is the
|
|
7
|
+
# Coder bluebook's; this only orders the sections, fills in the row's key and lists the areas the
|
|
8
|
+
# caller knows.
|
|
9
|
+
class BriefText
|
|
10
|
+
# The sections of a brief after the outcome, in the order they are read.
|
|
11
|
+
SECTIONS = %w[rules verification workflow landing restraints report].freeze
|
|
12
|
+
|
|
13
|
+
# @param brief [Hash] the composed brief: its attribute values by name
|
|
14
|
+
def initialize(brief)
|
|
15
|
+
@brief = brief
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @return [String] the first line of the text: the outcome
|
|
19
|
+
def first_line = "Outcome: #{plain("outcome")}"
|
|
20
|
+
|
|
21
|
+
# @param key [String] the Work row's key, which names the worktree and branch
|
|
22
|
+
# @param area [Array<String>] the areas the session may edit
|
|
23
|
+
# @param held [Array<String>] a line for each area another started row holds
|
|
24
|
+
# @param frozen [Array<String>] the areas and aggregates whose shape may not move
|
|
25
|
+
# @param limit [Integer, nil] the most spec files a session runs per call, from the world's
|
|
26
|
+
# `test_scope` setting; fills `{limit}`
|
|
27
|
+
# @return [String] the whole text
|
|
28
|
+
def render(key, area: [], held: [], frozen: [], limit: nil)
|
|
29
|
+
parts = [first_line, labelled("Detail", plain("detail")), labelled("Area", area.join(", "))]
|
|
30
|
+
parts += SECTIONS.map { |name| plain(name) }
|
|
31
|
+
parts += [listed("coordination", held), listed("freeze", frozen)]
|
|
32
|
+
joined(parts).gsub("{work}", key).gsub("{limit}", limit&.to_s || "the world's spec_files_limit")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def labelled(label, text) = text.empty? ? nil : "#{label}: #{text}"
|
|
38
|
+
|
|
39
|
+
def listed(name, lines) = lines.empty? ? nil : ([plain(name)] + lines.map { |line| "- #{line}" }).join("\n")
|
|
40
|
+
|
|
41
|
+
def joined(parts) = parts.compact.reject(&:empty?).join("\n\n")
|
|
42
|
+
|
|
43
|
+
# A value is a bare string or a one-field hash, and a name is a string or a symbol.
|
|
44
|
+
def plain(name)
|
|
45
|
+
found = @brief.transform_keys(&:to_s)[name]
|
|
46
|
+
(found.is_a?(Hash) ? found.transform_keys(&:to_s)["value"] : found).to_s
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|