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,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../../../../adapters/driven/sqlite"
|
|
4
|
+
require_relative "read_only"
|
|
5
|
+
|
|
6
|
+
module Hecks
|
|
7
|
+
module Adapters
|
|
8
|
+
class JournalStore
|
|
9
|
+
module Readings
|
|
10
|
+
module SqliteStatus
|
|
11
|
+
# One SQLite file read against the aggregates bound to it. The file is opened with
|
|
12
|
+
# read-only (see {ReadOnly}) and only `PRAGMA table_info` and `sqlite_master` are read.
|
|
13
|
+
class DatabaseReading
|
|
14
|
+
ADAPTERS = %w[Sqlite SqlitePersistence].freeze
|
|
15
|
+
# Tables the adapter keeps beside every aggregate's own.
|
|
16
|
+
ABSENT = "absent: created on first open"
|
|
17
|
+
SHARED_TABLE = /\A(events|hecks_\w+|sqlite_\w+)\z/
|
|
18
|
+
|
|
19
|
+
# The files the registry's aggregates are persisted in, each with its aggregates.
|
|
20
|
+
#
|
|
21
|
+
# @param registry [Runtime::Registry] a described registry
|
|
22
|
+
# @return [Hash{String => Array<Bluebook::Aggregate>}] absolute path to aggregates
|
|
23
|
+
def self.targets(registry)
|
|
24
|
+
registry.bluebooks.values.each_with_object({}) do |bluebook, found|
|
|
25
|
+
bluebook.aggregates.each do |aggregate|
|
|
26
|
+
path = path_for(registry, bluebook, aggregate)
|
|
27
|
+
(found[path] ||= []) << aggregate if path
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @return [String, nil] the file the aggregate is bound to, nil unless SQLite
|
|
33
|
+
def self.path_for(registry, bluebook, aggregate)
|
|
34
|
+
bind = Ports::Persistence::BindingPolicy.resolve(registry, bluebook.name, aggregate)
|
|
35
|
+
return unless ADAPTERS.include?(bind.adapter.to_s)
|
|
36
|
+
|
|
37
|
+
settings = registry.binding_settings(bluebook.name, Ports::Persistence::VERB, bind.adapter)
|
|
38
|
+
absolute(registry, registry.with_adapter_defaults(bind.adapter, aggregate, settings))
|
|
39
|
+
rescue Runtime::WiringError
|
|
40
|
+
nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @return [String] the settings' database, resolved against the registry's root
|
|
44
|
+
def self.absolute(registry, settings)
|
|
45
|
+
declared = settings.transform_keys(&:to_sym)[:database].to_s
|
|
46
|
+
declared.start_with?("/") ? declared : File.join(registry.root || Dir.pwd, declared)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @param path [String] the database file
|
|
50
|
+
# @param aggregates [Array<Bluebook::Aggregate>] the aggregates bound to it
|
|
51
|
+
def initialize(path, aggregates)
|
|
52
|
+
@path = path
|
|
53
|
+
@aggregates = aggregates
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @return [Hash] `database`, `tables` (one per aggregate) and `orphans`
|
|
57
|
+
def row
|
|
58
|
+
return { database: @path, state: ABSENT, tables: [], orphans: [] } unless File.file?(@path)
|
|
59
|
+
|
|
60
|
+
db = ReadOnly.open(@path)
|
|
61
|
+
present = db.execute("SELECT name FROM sqlite_master WHERE type = 'table'").flatten
|
|
62
|
+
{ database: @path, state: "read", tables: @aggregates.map { |a| table_row(db, present, a) },
|
|
63
|
+
orphans: orphans(present) }
|
|
64
|
+
ensure
|
|
65
|
+
db&.close
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def table_row(db, present, aggregate)
|
|
71
|
+
base = { aggregate: aggregate.name, table: aggregate.storage_name }
|
|
72
|
+
return base.merge(state: ABSENT) unless present.include?(base[:table])
|
|
73
|
+
|
|
74
|
+
drift = drift_of(columns(db, base[:table]), expected(aggregate))
|
|
75
|
+
base.merge(state: drift.key?(:missing) ? "drift" : "ok", **drift)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Column names compare without regard to capitals, as the adapter's open does.
|
|
79
|
+
def drift_of(held, wanted)
|
|
80
|
+
held_names = held.map(&:downcase)
|
|
81
|
+
wanted_names = wanted.map(&:downcase)
|
|
82
|
+
missing = wanted.reject { |name| held_names.include?(name.downcase) }
|
|
83
|
+
extra = held.reject { |name| wanted_names.include?(name.downcase) || name.casecmp?("id") }
|
|
84
|
+
{ missing: missing, extra: extra }.reject { |_, names| names.empty? }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def columns(db, table)
|
|
88
|
+
db.execute(%(PRAGMA table_info("#{table.gsub('"', '""')}"))).map { |column| column[1] }
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# The column names the adapter's codec persists, read without opening the adapter.
|
|
92
|
+
def expected(aggregate)
|
|
93
|
+
adapter = Hecks::Adapters::Sqlite.allocate
|
|
94
|
+
adapter.instance_variable_set(:@aggregate, aggregate)
|
|
95
|
+
adapter.send(:persisted_fields).map { |field| field[:name].to_s }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def orphans(present)
|
|
99
|
+
known = @aggregates.flat_map { |a| [a.storage_name, "#{a.storage_name}_entries"] }
|
|
100
|
+
(present - known).grep_v(SHARED_TABLE).map { |name| orphan(name) }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def orphan(name)
|
|
104
|
+
stem = name.delete_suffix("_entries")
|
|
105
|
+
owner = @aggregates.find { |a| [a.name, a.former_name].compact.map { |n| Naming.snake(n) }.include?(stem) }
|
|
106
|
+
owner ? { table: name, belongs_to: owner.name, stored_as: owner.storage_name } : { table: name }
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Adapters
|
|
5
|
+
class JournalStore
|
|
6
|
+
module Readings
|
|
7
|
+
module SqliteStatus
|
|
8
|
+
# Opens an existing SQLite file so that reading it creates nothing beside it. A plain
|
|
9
|
+
# read-only open of a database in write-ahead-log mode makes its `-wal` and `-shm`
|
|
10
|
+
# files, so a database with no `-wal` file is opened `immutable`, which skips them
|
|
11
|
+
# altogether. One that has a `-wal` file holds changes the reading must see, and
|
|
12
|
+
# already has the files a read-only open needs.
|
|
13
|
+
module ReadOnly
|
|
14
|
+
UNRESERVED = %r{[^A-Za-z0-9\-._~/]}
|
|
15
|
+
|
|
16
|
+
# @param path [String] a database file that exists
|
|
17
|
+
# @return [SQLite3::Database] the open, read-only database; the caller closes it
|
|
18
|
+
def self.open(path)
|
|
19
|
+
require "sqlite3"
|
|
20
|
+
return SQLite3::Database.new(path, readonly: true) if File.exist?("#{path}-wal")
|
|
21
|
+
|
|
22
|
+
SQLite3::Database.new("file:#{escape(path)}?immutable=1",
|
|
23
|
+
flags: SQLite3::Constants::Open::READONLY | SQLite3::Constants::Open::URI)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @return [String] the path with every byte a URI reserves written as a percent escape
|
|
27
|
+
def self.escape(path) = path.gsub(UNRESERVED) { |char| char.bytes.map { |byte| format("%%%02X", byte) }.join }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require_relative "sqlite_status/database_reading"
|
|
5
|
+
|
|
6
|
+
module Hecks
|
|
7
|
+
module Adapters
|
|
8
|
+
class JournalStore
|
|
9
|
+
module Readings
|
|
10
|
+
# How the SQLite databases a domain's worlds name stand against its aggregates. Each file
|
|
11
|
+
# is opened read-only, never through the adapter (whose open adds missing columns), so the
|
|
12
|
+
# reading writes nothing and shows the drift before an open heals it or a write meets it.
|
|
13
|
+
module SqliteStatus
|
|
14
|
+
# @param domain [Hash, String] the domain directory
|
|
15
|
+
# @return [Hash] `text:` the status as JSON, one row per database file
|
|
16
|
+
# @raise [Runtime::NotFound] if the directory cannot be loaded
|
|
17
|
+
def sqlite_status(domain:)
|
|
18
|
+
answering { JSON.pretty_generate(SqliteStatus.of(Hecks.describe(plain(domain)).registry)) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @param registry [Runtime::Registry] a described registry
|
|
22
|
+
# @return [Array<Hash>] one row per SQLite file, in the order the aggregates name them
|
|
23
|
+
def self.of(registry)
|
|
24
|
+
DatabaseReading.targets(registry).map do |path, aggregates|
|
|
25
|
+
DatabaseReading.new(path, aggregates).row
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -7,6 +7,12 @@ require_relative "edge_audit"
|
|
|
7
7
|
require_relative "compaction"
|
|
8
8
|
require_relative "readings/scaffolding"
|
|
9
9
|
require_relative "readings/attestation"
|
|
10
|
+
require_relative "readings/composition"
|
|
11
|
+
require_relative "readings/boot_status"
|
|
12
|
+
require_relative "readings/sqlite_status"
|
|
13
|
+
require_relative "readings/event_trace"
|
|
14
|
+
require_relative "readings/projection_audit"
|
|
15
|
+
require_relative "readings/mint_plan"
|
|
10
16
|
|
|
11
17
|
module Hecks
|
|
12
18
|
module Adapters
|
|
@@ -22,6 +28,12 @@ module Hecks
|
|
|
22
28
|
|
|
23
29
|
include Scaffolding
|
|
24
30
|
include Attestation
|
|
31
|
+
include Composition
|
|
32
|
+
include BootStatus
|
|
33
|
+
include SqliteStatus
|
|
34
|
+
include EventTrace
|
|
35
|
+
include ProjectionAudit
|
|
36
|
+
include MintPlan
|
|
25
37
|
|
|
26
38
|
# Words the answer for a domain that holds no era.
|
|
27
39
|
#
|
|
@@ -29,7 +41,7 @@ module Hecks
|
|
|
29
41
|
# @param path [String] the domain path, as the operator typed it
|
|
30
42
|
# @return [String] the sentence
|
|
31
43
|
def self.no_era(name, path)
|
|
32
|
-
"#{name} holds no era yet. `hecks hold_first #{path} --confirm` holds the first."
|
|
44
|
+
"#{name} holds no era yet. `hecks era.hold_first! #{path} --confirm` holds the first."
|
|
33
45
|
end
|
|
34
46
|
|
|
35
47
|
# The edge file that would lead the latest held era to the current shape, as its text.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Adapters
|
|
5
|
+
class JournalStore
|
|
6
|
+
# Words the reconciliation's dry run and its written report, from the rows it planned.
|
|
7
|
+
module ReconcileReport
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
# @param row [Reconciliation::Row] one bluebook's row
|
|
11
|
+
# @return [String] the block that reports the row's plan
|
|
12
|
+
def render_row(row)
|
|
13
|
+
return "#{row.name}: its store keeps no eras, so there is nothing to reconcile" unless row.plan
|
|
14
|
+
|
|
15
|
+
plan = row.plan
|
|
16
|
+
head = "#{row.name}: current #{plan.current}"
|
|
17
|
+
head += ", latest era #{plan.latest[:ordinal]} (#{plan.latest[:label]})" if plan.latest
|
|
18
|
+
[head, *verdict_lines(plan)].join("\n")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @param writes [Array<Reconciliation::Row>] the rows whose plans were carried out
|
|
22
|
+
# @return [String] what was written and deleted, per domain
|
|
23
|
+
def written(writes)
|
|
24
|
+
lines = writes.flat_map { |row| written_lines(row.name, row.plan) }
|
|
25
|
+
["the audit passed for each composed edge", *lines].join("\n")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def written_lines(name, plan)
|
|
29
|
+
written = plan.write_path ? [" wrote #{File.basename(plan.write_path)}"] : []
|
|
30
|
+
["#{name}: #{plan.labels.join(" -> ")}", *written,
|
|
31
|
+
*plan.removals.map { |path| " removed #{File.basename(path)}" }]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def verdict_lines(plan)
|
|
35
|
+
case plan.status
|
|
36
|
+
when :no_era then [" no era held yet; nothing to reconcile"]
|
|
37
|
+
when :refused then [" verdict: REFUSED — #{plan.refusal}"]
|
|
38
|
+
when :clean then [" verdict: clean — nothing to write or delete", " mints: #{mint_line(plan)}"]
|
|
39
|
+
else write_lines(plan)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def write_lines(plan)
|
|
44
|
+
lines = [" chain: #{plan.labels.join(" -> ")}"]
|
|
45
|
+
lines << " would write: #{File.basename(plan.write_path)}" if plan.write_path
|
|
46
|
+
lines += plan.removals.map { |path| " would delete: #{File.basename(path)}" }
|
|
47
|
+
lines << " verdict: write"
|
|
48
|
+
lines << " mints: #{mint_line(plan)}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def mint_line(plan)
|
|
52
|
+
return "no era on the next boot" unless plan.mints
|
|
53
|
+
|
|
54
|
+
"era #{plan.latest[:ordinal] + 1} (#{plan.current}) on the next boot of a copy whose world " \
|
|
55
|
+
"declares mint_eras true"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "held_domain"
|
|
4
|
+
require_relative "edge_audit"
|
|
5
|
+
require_relative "reconcile_report"
|
|
6
|
+
|
|
7
|
+
module Hecks
|
|
8
|
+
module Adapters
|
|
9
|
+
class JournalStore
|
|
10
|
+
# The `reconcile` change: plans every bluebook of a project against the eras its store holds,
|
|
11
|
+
# and when every plan is admitted writes the composed edges, deletes the edges they supersede,
|
|
12
|
+
# and audits each written edge. Reading is the dry run; only `apply_reconcile` writes, and a
|
|
13
|
+
# refused plan in any domain writes nothing.
|
|
14
|
+
module Reconciliation
|
|
15
|
+
# The project directory a reconciliation reads when none is named: the hecks checkout's own
|
|
16
|
+
# domain directory, whose `translations/` holds the edges of every domain it loads.
|
|
17
|
+
DEFAULT_PROJECT = File.expand_path("../..", __dir__)
|
|
18
|
+
|
|
19
|
+
# One bluebook's plan; `plan` is nil when its store keeps no eras.
|
|
20
|
+
Row = Struct.new(:name, :plan, keyword_init: true) do
|
|
21
|
+
def refusal = plan&.refusal
|
|
22
|
+
|
|
23
|
+
def status = plan&.status
|
|
24
|
+
|
|
25
|
+
def edge = plan&.edge
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# The dry run: one report of every domain's plan, written nowhere.
|
|
29
|
+
#
|
|
30
|
+
# @param domain [Hash, String, nil] the project directory; the checkout's own when absent
|
|
31
|
+
# @return [Hash] `text:` the report, one block per domain
|
|
32
|
+
# @raise [Runtime::NotFound] if the project cannot be loaded
|
|
33
|
+
def reconcile_plan(domain: nil)
|
|
34
|
+
answering { project_rows(project_path(domain)).map { |row| ReconcileReport.render_row(row) }.join("\n\n") }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
# Examines nothing: the plans decide what is admitted, and `apply_reconcile` refuses a store
|
|
40
|
+
# that keeps no eras.
|
|
41
|
+
def examine_reconcile(_held) = { capable: true }
|
|
42
|
+
|
|
43
|
+
def apply_reconcile(held)
|
|
44
|
+
path = project_path(held[:domain])
|
|
45
|
+
rows = project_rows(path)
|
|
46
|
+
refusals = rows.filter_map(&:refusal)
|
|
47
|
+
unless refusals.empty?
|
|
48
|
+
raise Runtime::WiringError, "nothing was written; the reconciliation refused:\n #{refusals.join("\n ")}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
write_reconciled(path, rows.select { |row| row.status == :write })
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def write_reconciled(path, writes)
|
|
55
|
+
return "every domain already leads to its current shape — nothing to reconcile." if writes.empty?
|
|
56
|
+
|
|
57
|
+
originals = writes.reduce({}) { |found, row| found.merge(Translation::ReconcileFiles.write!(row.plan)) }
|
|
58
|
+
begin
|
|
59
|
+
writes.each { |row| audit_written!(path, row) }
|
|
60
|
+
rescue StandardError
|
|
61
|
+
Translation::ReconcileFiles.restore!(originals)
|
|
62
|
+
raise
|
|
63
|
+
end
|
|
64
|
+
ReconcileReport.written(writes)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Audits the composed edge a row wrote; a row that only deleted edges has nothing to audit.
|
|
68
|
+
#
|
|
69
|
+
# @raise [Runtime::WiringError] when the audit refuses the composed edge
|
|
70
|
+
def audit_written!(path, row)
|
|
71
|
+
return unless row.edge
|
|
72
|
+
|
|
73
|
+
project = HeldDomain.open(path)
|
|
74
|
+
domain = HeldDomain.new(project.registry, project.directory, row.name)
|
|
75
|
+
finding = domain.reading { |lineage| EdgeAudit.call(domain, lineage, domain.held_eras(lineage)) }
|
|
76
|
+
return if finding.nil? || finding.ok
|
|
77
|
+
|
|
78
|
+
raise Runtime::WiringError,
|
|
79
|
+
"cannot reconcile #{row.name}: the audit refused the composed edge —\n " \
|
|
80
|
+
"#{finding.refusals.join("\n ")}"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def project_path(domain) = plain(domain) || DEFAULT_PROJECT
|
|
84
|
+
|
|
85
|
+
# @return [Array<Row>] one row per bluebook the project loads, in the registry's order
|
|
86
|
+
def project_rows(path)
|
|
87
|
+
project = HeldDomain.open(path)
|
|
88
|
+
project.bluebook_names.map { |name| bluebook_row(project, name) }
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def bluebook_row(project, name)
|
|
92
|
+
domain = HeldDomain.new(project.registry, project.directory, name)
|
|
93
|
+
return Row.new(name: name) unless domain.capable?
|
|
94
|
+
|
|
95
|
+
domain.reading { |lineage| Row.new(name: name, plan: domain_plan(domain, lineage, project)) }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def domain_plan(domain, lineage, project)
|
|
99
|
+
Translation::Reconcile.plan(
|
|
100
|
+
Translation::Reconcile::Scope.new(
|
|
101
|
+
domain: domain.domain_name, held: held_labels(domain, lineage), current: current_label(domain),
|
|
102
|
+
edges: domain.translations.select { |edge| edge.domain == domain.domain_name },
|
|
103
|
+
files: Translation::EdgeFiles.scan(project.directory), directory: project.directory
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def held_labels(domain, lineage)
|
|
109
|
+
domain.held_eras(lineage).map do |era|
|
|
110
|
+
named = domain.named(era)
|
|
111
|
+
{ ordinal: named[:ordinal], label: named[:label] }
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def current_label(domain)
|
|
116
|
+
Runtime::StorageShape.mint_hash(domain.bluebook)[0, Runtime::StorageShape::LABEL_LENGTH]
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Adapters
|
|
5
|
+
class JournalStore
|
|
6
|
+
module TranslationWrite
|
|
7
|
+
# The edge for a shape that changed under an unchanged label: the scaffold's diff, written from
|
|
8
|
+
# the label to itself. Only `TranslationWrite` includes it.
|
|
9
|
+
module ShapeEdge
|
|
10
|
+
# A line of an edge file that still marks a field as undecided.
|
|
11
|
+
UNRESOLVED_LINE = /^\s*unresolved /
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
# A held label that equals the current one with a different projection: the edge is the
|
|
16
|
+
# scaffold's diff, from the label to itself. A field the diff cannot decide refuses the write.
|
|
17
|
+
#
|
|
18
|
+
# @raise [Runtime::WiringError] when the diff leaves a field or an aggregate to decide
|
|
19
|
+
def shape_edge_plan(domain, latest, current)
|
|
20
|
+
diffed = diff_for(domain, latest)
|
|
21
|
+
refuse_undecided!(domain, "UNCLAIMED #{diffed[:unclaimed].join(", ")}") unless diffed[:unclaimed].empty?
|
|
22
|
+
text = Translation::Scaffold.render(shape_edge(domain, latest, current, diffed))
|
|
23
|
+
refuse_undecided!(domain, "unresolved fields") if text.match?(UNRESOLVED_LINE)
|
|
24
|
+
Plan.new(path: Translation::EdgeFiles.path_for(domain.directory, from: latest[:label], to: current),
|
|
25
|
+
text: text, removals: [], labels: [latest[:label], current])
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def diff_for(domain, latest)
|
|
29
|
+
Translation::Scaffold.diff(PostgresEra::LineageManager.held_bluebook(latest), domain.bluebook)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def shape_edge(domain, latest, current, diffed)
|
|
33
|
+
Translation::Scaffold::Edge.new(domain: domain.domain_name, from: latest[:label], to: current,
|
|
34
|
+
ordinal: latest[:ordinal] + 1, label: current,
|
|
35
|
+
aggregates: diffed[:aggregates], retired: diffed[:retired])
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def refuse_undecided!(domain, reason)
|
|
39
|
+
raise Runtime::WiringError,
|
|
40
|
+
"cannot write the translation of #{domain.domain_name}: the shape changed under the same label " \
|
|
41
|
+
"and the scaffold leaves #{reason} to decide; write the edge by hand"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def already_leads(from, to)
|
|
45
|
+
Plan.new(message: "one edge already leads #{from} to #{to} — nothing to write.")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def edge_plan(domain, from, to, chain)
|
|
49
|
+
edge = Translation::Compose.compose(domain: domain.domain_name, from: from, to: to, chain: chain)
|
|
50
|
+
labels = [from, *chain.map(&:to)]
|
|
51
|
+
stale = Translation::EdgeFiles.stale(domain.directory, domain: domain.domain_name, chain: chain)
|
|
52
|
+
Plan.new(path: Translation::EdgeFiles.path_for(domain.directory, from: from, to: to),
|
|
53
|
+
text: Translation::ComposeRender.render(edge, labels: labels), removals: stale, labels: labels)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require_relative "held_domain"
|
|
5
|
+
require_relative "edge_audit"
|
|
6
|
+
require_relative "translation_write/shape_edge"
|
|
7
|
+
|
|
8
|
+
module Hecks
|
|
9
|
+
module Adapters
|
|
10
|
+
class JournalStore
|
|
11
|
+
# The `write_translation` change: composes the chain of edges from the latest held era to the
|
|
12
|
+
# current shape into one edge, writes it, deletes the intermediate edges only the chain used,
|
|
13
|
+
# and audits what was written. A refused audit puts the translations directory back as it was.
|
|
14
|
+
#
|
|
15
|
+
# Planning is a read; only `apply_write_translation` writes, and only to the domain's own
|
|
16
|
+
# `translations/` directory.
|
|
17
|
+
module TranslationWrite
|
|
18
|
+
# What a write would do: the file it writes and its text, the stale files it deletes, and
|
|
19
|
+
# the shape labels the chain passes through. `message` alone answers when nothing is to be done.
|
|
20
|
+
Plan = Struct.new(:message, :path, :text, :removals, :labels, keyword_init: true)
|
|
21
|
+
|
|
22
|
+
include ShapeEdge
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def examine_write_translation(held)
|
|
27
|
+
domain = HeldDomain.open(plain(held[:domain]))
|
|
28
|
+
return {} unless domain.capable?
|
|
29
|
+
|
|
30
|
+
domain.reading do |lineage|
|
|
31
|
+
translation_plan(domain, lineage)
|
|
32
|
+
{ capable: true, held: domain.held_eras(lineage).size }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def apply_write_translation(held)
|
|
37
|
+
path = plain(held[:domain])
|
|
38
|
+
domain = HeldDomain.open(path)
|
|
39
|
+
plan = domain.reading { |lineage| translation_plan(domain, lineage) }
|
|
40
|
+
return plan.message if plan.message
|
|
41
|
+
|
|
42
|
+
write_and_audit(path, plan)
|
|
43
|
+
written_report(plan)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Writes the plan, then audits what it wrote; a refusal or a failure restores the files.
|
|
47
|
+
def write_and_audit(path, plan)
|
|
48
|
+
originals = write_translation!(plan)
|
|
49
|
+
begin
|
|
50
|
+
audit_written_translation!(path)
|
|
51
|
+
rescue StandardError
|
|
52
|
+
restore_translations!(originals)
|
|
53
|
+
raise
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# The plan a write would carry out, or the sentence that says none is needed.
|
|
58
|
+
#
|
|
59
|
+
# @return [Plan] see `Plan`
|
|
60
|
+
# @raise [Runtime::WiringError] when the chain cannot be composed into one edge
|
|
61
|
+
def translation_plan(domain, lineage)
|
|
62
|
+
eras = domain.held_eras(lineage)
|
|
63
|
+
return Plan.new(message: "#{domain.domain_name} holds no era yet — nothing to write.") if eras.empty?
|
|
64
|
+
|
|
65
|
+
latest = domain.named(eras.last)
|
|
66
|
+
return Plan.new(message: "#{domain.domain_name} matches era #{latest[:ordinal]} — nothing to write.") if
|
|
67
|
+
held_shape(latest) == domain.shape
|
|
68
|
+
|
|
69
|
+
composed_plan(domain, latest)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def held_shape(era) = PostgresEra::LineageManager.held_shape(era)
|
|
73
|
+
|
|
74
|
+
def composed_plan(domain, latest)
|
|
75
|
+
current = Runtime::StorageShape.mint_hash(domain.bluebook)[0, Runtime::StorageShape::LABEL_LENGTH]
|
|
76
|
+
chain = Translation::Compose.chain(domain.translations, domain: domain.domain_name,
|
|
77
|
+
from: latest[:label], to: current)
|
|
78
|
+
return already_leads(latest[:label], current) if chain.size == 1
|
|
79
|
+
return shape_edge_plan(domain, latest, current) if chain.empty?
|
|
80
|
+
|
|
81
|
+
edge_plan(domain, latest[:label], current, chain)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Writes the composed edge and deletes the stale ones; returns what each file held before.
|
|
85
|
+
#
|
|
86
|
+
# @return [Hash{String => String, nil}] each touched path with its earlier text, nil if absent
|
|
87
|
+
def write_translation!(plan)
|
|
88
|
+
originals = [plan.path, *plan.removals].to_h { |path| [path, read_if_present(path)] }
|
|
89
|
+
File.write(plan.path, plan.text)
|
|
90
|
+
plan.removals.each { |path| FileUtils.rm_f(path) }
|
|
91
|
+
originals
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def read_if_present(path) = File.exist?(path) ? File.read(path) : nil
|
|
95
|
+
|
|
96
|
+
# Puts each touched file back as `write_translation!` found it.
|
|
97
|
+
def restore_translations!(originals)
|
|
98
|
+
originals.each do |path, text|
|
|
99
|
+
text ? File.write(path, text) : FileUtils.rm_f(path)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Audits the edge that was just written, as the boot does.
|
|
104
|
+
#
|
|
105
|
+
# @raise [Runtime::WiringError] when the audit refuses the composed edge
|
|
106
|
+
def audit_written_translation!(path)
|
|
107
|
+
domain = HeldDomain.open(path)
|
|
108
|
+
finding = domain.reading { |lineage| EdgeAudit.call(domain, lineage, domain.held_eras(lineage)) }
|
|
109
|
+
return if finding.nil? || finding.ok
|
|
110
|
+
|
|
111
|
+
raise Runtime::WiringError,
|
|
112
|
+
"cannot write the translation of #{domain.domain_name}: the audit refused the composed " \
|
|
113
|
+
"edge —\n #{finding.refusals.join("\n ")}"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def written_report(plan)
|
|
117
|
+
removed = plan.removals.map { |path| " removed #{File.basename(path)}" }
|
|
118
|
+
["wrote #{plan.path}", *removed, "the audit passed: #{plan.labels.join(" -> ")}"].join("\n")
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
require_relative "journal_store/examination"
|
|
4
4
|
require_relative "journal_store/changes"
|
|
5
5
|
require_relative "journal_store/readings"
|
|
6
|
+
require_relative "journal_store/translation_write"
|
|
7
|
+
require_relative "journal_store/reconciliation"
|
|
8
|
+
require_relative "journal_store/held_mint"
|
|
6
9
|
|
|
7
10
|
module Hecks
|
|
8
11
|
module Adapters
|
|
@@ -19,6 +22,8 @@ module Hecks
|
|
|
19
22
|
include Examination
|
|
20
23
|
include Changes
|
|
21
24
|
include Readings
|
|
25
|
+
include TranslationWrite
|
|
26
|
+
include Reconciliation
|
|
22
27
|
|
|
23
28
|
# Accepts the arguments every driven adapter is built with and keeps none of them.
|
|
24
29
|
#
|
|
@@ -27,6 +32,17 @@ module Hecks
|
|
|
27
32
|
# @param root [String, nil] unused
|
|
28
33
|
def initialize(aggregate: nil, settings: {}, root: nil); end
|
|
29
34
|
|
|
35
|
+
# Answers the held mint of the Hecks domain: see `HeldMint#answer`.
|
|
36
|
+
#
|
|
37
|
+
# @param held [Hash] the `MintHeld` record: `run`, and optionally `rehearsal_verdict`,
|
|
38
|
+
# `watermark` and `confirm`
|
|
39
|
+
# @return [Hash{Symbol => Hash}] `run`, `outcome` and `label`
|
|
40
|
+
# @raise [Runtime::WiringError] naming why the mint was refused
|
|
41
|
+
def mint(**held)
|
|
42
|
+
HeldMint.new.answer(run: plain(held[:run]), confirm: plain(held[:confirm]),
|
|
43
|
+
rehearsal_verdict: plain(held[:rehearsal_verdict]), watermark: plain(held[:watermark]))
|
|
44
|
+
end
|
|
45
|
+
|
|
30
46
|
private
|
|
31
47
|
|
|
32
48
|
def plain(argument) = argument.is_a?(Hash) ? argument[:value] : argument
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Required by name, not relative to this file: a fuzz or model check boots a copy of this directory,
|
|
2
|
+
# and the implementation must load once, from `lib/`, not from the copy.
|
|
3
|
+
require "hecks/hecks/adapters/landed_work"
|
|
4
|
+
|
|
5
|
+
Hecks.adapter "LandedWork" do
|
|
6
|
+
port "LandedWork"
|
|
7
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "started_work"
|
|
4
|
+
require_relative "console_capture"
|
|
5
|
+
|
|
6
|
+
module Hecks
|
|
7
|
+
module Adapters
|
|
8
|
+
# The `LandedWork` port's adapter for `Tickets::Work`: finds the open step a landed branch was
|
|
9
|
+
# cut for, so the bluebook can finish it.
|
|
10
|
+
#
|
|
11
|
+
# A branch names its step by the whole branch name, else by the name without a leading `qa/`
|
|
12
|
+
# (QA's branches carry that prefix, the steps they close do not). Only a `planned` or `started`
|
|
13
|
+
# step is found; a branch with no step, or one already done or dropped, is refused by name and
|
|
14
|
+
# the bluebook reacts to neither. It writes nothing.
|
|
15
|
+
class LandedWork
|
|
16
|
+
# The prefix QA puts on a branch whose step carries the bare name.
|
|
17
|
+
QA_PREFIX = "qa/"
|
|
18
|
+
|
|
19
|
+
# @param registry [Runtime::Registry, nil] the booted registry whose Work rows are read
|
|
20
|
+
# @param aggregate [Object, nil] unused
|
|
21
|
+
def initialize(registry: nil, aggregate: nil)
|
|
22
|
+
@registry = registry
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @param held [Hash] the landing's `branch`
|
|
26
|
+
# @return [Hash{Symbol => Hash}] `branch:` the key of the open step the branch names
|
|
27
|
+
# @raise [ConsoleCapture::Failure] when no open step is named by the branch
|
|
28
|
+
def find_landed_work(**held)
|
|
29
|
+
branch = plain(held[:branch]).to_s
|
|
30
|
+
found = (candidates(branch) & open_keys).first
|
|
31
|
+
raise ConsoleCapture::Failure, "no open step is named #{branch}" unless found
|
|
32
|
+
|
|
33
|
+
{ branch: { value: found } }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def candidates(branch) = [branch, branch.delete_prefix(QA_PREFIX)].reject(&:empty?).uniq
|
|
39
|
+
|
|
40
|
+
def open_keys
|
|
41
|
+
aggregate = StartedWork.work_aggregate(@registry)
|
|
42
|
+
return [] unless aggregate
|
|
43
|
+
|
|
44
|
+
records = @registry.repository("Tickets", aggregate).all
|
|
45
|
+
records.select { |row| %w[planned started].include?(StartedWork.value(row, :status)) }
|
|
46
|
+
.map { |row| StartedWork.value(row, :work).to_s }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def plain(argument) = argument.is_a?(Hash) ? argument[:value] : argument
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|