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,1073 @@
|
|
|
1
|
+
# CI speed: main CI is measured run by run against a wall-time budget. A change to the pipeline is
|
|
2
|
+
# proposed for the slowest job, and kept only when a following run of the same cache state is
|
|
3
|
+
# measurably faster. The loop stops once the latest warm run and the latest cache-miss run are both
|
|
4
|
+
# under budget.
|
|
5
|
+
#
|
|
6
|
+
# Nothing here reads GitHub. Each run is observed after a push to main through the push train, and
|
|
7
|
+
# an adapter answers the `Reading` query; the loop records what came back.
|
|
8
|
+
#
|
|
9
|
+
# A proposed change is measured on this machine before a CI run is spent on it. An `Experiment`
|
|
10
|
+
# times the same steps in a checkout of the pipeline as it is and a checkout with the change, the
|
|
11
|
+
# two interleaved (see the aggregate below). The bluebook is booted from its own directory, since
|
|
12
|
+
# it is not attached to the launcher: each command is a separate process on Memory, so what an
|
|
13
|
+
# experiment is to re-estimate has to be proposed in the same run, and the process has to stay up
|
|
14
|
+
# until the port has answered. A step list does both and prints the settled experiment, its
|
|
15
|
+
# events and the lever's new estimate; `Experiment.request!` as a single verb prints the record
|
|
16
|
+
# as it stood at the request, before the run. Leave `lever` and the Lever.Propose step out for an
|
|
17
|
+
# experiment that is not about a lever.
|
|
18
|
+
#
|
|
19
|
+
# exe/hecks run lib/hecks/ci_speed '{"steps":[
|
|
20
|
+
# {"verb":"CiSpeed::Lever.Propose","args":{"name":{"value":"parallel_rustfmt"},
|
|
21
|
+
# "change":{"value":"rustfmt on one thread per core"},"pole":{"value":"generated_rust"},
|
|
22
|
+
# "affects":{"value":"miss"},"expected_seconds":{"value":0},"basis":{"value":"unestimated"},
|
|
23
|
+
# "inputs":{"value":""}}},
|
|
24
|
+
# {"verb":"CiSpeed::Experiment.Request","args":{"name":{"value":"parallel_rustfmt_cold"},
|
|
25
|
+
# "lever":{"value":"parallel_rustfmt"},"measures":{"value":"regenerate_corpus_cold"},
|
|
26
|
+
# "cache_state":{"value":"miss"},"base_ref":{"value":"f95bafef9~1"},
|
|
27
|
+
# "candidate_ref":{"value":"f95bafef9"},"repeats":{"value":3}}}]}'
|
|
28
|
+
#
|
|
29
|
+
# What can be measured is the `ExperimentDefinition` rows below.
|
|
30
|
+
|
|
31
|
+
Hecks.bluebook "CiSpeed" do
|
|
32
|
+
vision "A main CI run is measured as it finished, and a change to the pipeline is kept only when a run of the same cache state proves it faster."
|
|
33
|
+
|
|
34
|
+
supporting
|
|
35
|
+
|
|
36
|
+
# One observed CI run on main. `hit` is a warm run, a generated-rust cache hit; `miss` is a cold
|
|
37
|
+
# one that regenerates the corpus.
|
|
38
|
+
aggregate "Run" do
|
|
39
|
+
description "One observed CI run on main: its commit, wall time, cache state, conclusion, and each job's time."
|
|
40
|
+
|
|
41
|
+
identified_by :run_id
|
|
42
|
+
|
|
43
|
+
attribute :run_id, RunId
|
|
44
|
+
attribute :commit, CommitSha
|
|
45
|
+
attribute :wall_seconds, WallSeconds
|
|
46
|
+
attribute :cache_state, CacheState
|
|
47
|
+
attribute :conclusion, Conclusion
|
|
48
|
+
# The target in force when this run was observed, read from the Budget by the caller.
|
|
49
|
+
attribute :budget_seconds, WallSeconds
|
|
50
|
+
attribute :longest_pole, JobName
|
|
51
|
+
attribute :jobs, list_of(JobTiming)
|
|
52
|
+
|
|
53
|
+
# Each analysis is its own command, one per cache state and verdict. A policy can only read an
|
|
54
|
+
# event's name, so the verdict has to be carried by the event's name.
|
|
55
|
+
lifecycle :status, default: "observed" do
|
|
56
|
+
transition "AnalyzeWarmUnderBudget" => "analyzed", from: "observed"
|
|
57
|
+
transition "AnalyzeWarmOverBudget" => "analyzed", from: "observed"
|
|
58
|
+
transition "AnalyzeMissUnderBudget" => "analyzed", from: "observed"
|
|
59
|
+
transition "AnalyzeMissOverBudget" => "analyzed", from: "observed"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
value_object "RunId" do
|
|
63
|
+
# GitHub's numeric run id.
|
|
64
|
+
attribute :value, String, pattern: '^[0-9]+$'
|
|
65
|
+
invariant("a run is identified by its GitHub run id") { !value.to_s.empty? }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
value_object "CommitSha" do
|
|
69
|
+
# A full or abbreviated git commit hash, lowercase hex.
|
|
70
|
+
attribute :value, String, pattern: '^[0-9a-f]{7,40}$'
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
value_object "WallSeconds" do
|
|
74
|
+
attribute :value, Integer
|
|
75
|
+
invariant("a run or job takes a positive number of seconds") { value.positive? }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# `hit` is a warm run: the generated-rust cache restored. `miss` regenerated the corpus.
|
|
79
|
+
value_object "CacheState" do
|
|
80
|
+
attribute :value, String, pattern: '^(hit|miss)$'
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
value_object "Conclusion" do
|
|
84
|
+
attribute :value, String, pattern: '^(success|failure|cancelled)$'
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
value_object "JobName" do
|
|
88
|
+
attribute :value, String, matches: :present
|
|
89
|
+
invariant("a job is named") { !value.to_s.empty? }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
value_object "JobSequence" do
|
|
93
|
+
attribute :value, Integer
|
|
94
|
+
invariant("a job timing sequence is positive") { value.positive? }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
value_object "Critical" do
|
|
98
|
+
# Whether the job sat on the critical path of the run.
|
|
99
|
+
attribute :value, String, pattern: '^(yes|no)$'
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
value_object "Document" do
|
|
103
|
+
attribute :text, String
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# A job's wall time within one run. An entity: it has no meaning outside the run it timed.
|
|
107
|
+
entity "JobTiming" do
|
|
108
|
+
description "One job's wall time within a run, and whether it sat on the critical path."
|
|
109
|
+
|
|
110
|
+
identified_by :sequence
|
|
111
|
+
|
|
112
|
+
attribute :sequence, JobSequence
|
|
113
|
+
attribute :job, JobName
|
|
114
|
+
attribute :seconds, WallSeconds
|
|
115
|
+
attribute :critical, Critical
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
command "Observe" do
|
|
119
|
+
role "CI engineer"
|
|
120
|
+
goal "Write down one main CI run as it finished, before anything is said about it"
|
|
121
|
+
|
|
122
|
+
attribute :run_id, RunId
|
|
123
|
+
attribute :commit, CommitSha
|
|
124
|
+
attribute :wall_seconds, WallSeconds
|
|
125
|
+
attribute :cache_state, CacheState
|
|
126
|
+
attribute :conclusion, Conclusion
|
|
127
|
+
attribute :budget_seconds, WallSeconds
|
|
128
|
+
attribute :longest_pole, JobName
|
|
129
|
+
|
|
130
|
+
emits "RunObserved"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
command "RecordJob" do
|
|
134
|
+
role "CI engineer"
|
|
135
|
+
goal "Add one job's wall time to a run, and say whether it sat on the critical path"
|
|
136
|
+
|
|
137
|
+
reference_to Run
|
|
138
|
+
attribute :job, JobName
|
|
139
|
+
attribute :seconds, WallSeconds
|
|
140
|
+
attribute :critical, Critical
|
|
141
|
+
|
|
142
|
+
given("a run's job times are written before it is analysed") { status == "observed" }
|
|
143
|
+
|
|
144
|
+
sets :jobs, append: { job: :job, seconds: :seconds, critical: :critical }
|
|
145
|
+
|
|
146
|
+
emits "JobRecorded"
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Under budget: `seconds` restates the run's own wall time so the event carries it to the
|
|
150
|
+
# Budget. The given keeps the restated value honest.
|
|
151
|
+
command "AnalyzeWarmUnderBudget" do
|
|
152
|
+
role "CI engineer"
|
|
153
|
+
goal "Judge a warm run, a cache hit, that finished within budget"
|
|
154
|
+
|
|
155
|
+
reference_to Run
|
|
156
|
+
attribute :seconds, WallSeconds
|
|
157
|
+
|
|
158
|
+
given("the seconds are the run's own wall time") { seconds.value == wall_seconds.value }
|
|
159
|
+
given("a warm run is a cache hit that finished within budget") do
|
|
160
|
+
cache_state.value == "hit" && wall_seconds.value <= budget_seconds.value
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
emits "WarmRunUnderBudget"
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Over budget: `pole` restates the longest pole, so the event can propose a lever for it.
|
|
167
|
+
command "AnalyzeWarmOverBudget" do
|
|
168
|
+
role "CI engineer"
|
|
169
|
+
goal "Judge a warm run, a cache hit, that finished over budget, and name its longest pole"
|
|
170
|
+
|
|
171
|
+
reference_to Run
|
|
172
|
+
attribute :seconds, WallSeconds
|
|
173
|
+
attribute :pole, JobName
|
|
174
|
+
|
|
175
|
+
given("the seconds and pole are the run's own") do
|
|
176
|
+
seconds.value == wall_seconds.value && pole.value == longest_pole.value
|
|
177
|
+
end
|
|
178
|
+
given("a warm run is a cache hit that finished over budget") do
|
|
179
|
+
cache_state.value == "hit" && wall_seconds.value > budget_seconds.value
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
emits "WarmRunOverBudget"
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
command "AnalyzeMissUnderBudget" do
|
|
186
|
+
role "CI engineer"
|
|
187
|
+
goal "Judge a cache-miss run that finished within budget"
|
|
188
|
+
|
|
189
|
+
reference_to Run
|
|
190
|
+
attribute :seconds, WallSeconds
|
|
191
|
+
|
|
192
|
+
given("the seconds are the run's own wall time") { seconds.value == wall_seconds.value }
|
|
193
|
+
given("a cache-miss run finished within budget") do
|
|
194
|
+
cache_state.value == "miss" && wall_seconds.value <= budget_seconds.value
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
emits "MissRunUnderBudget"
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
command "AnalyzeMissOverBudget" do
|
|
201
|
+
role "CI engineer"
|
|
202
|
+
goal "Judge a cache-miss run that finished over budget, and name its longest pole"
|
|
203
|
+
|
|
204
|
+
reference_to Run
|
|
205
|
+
attribute :seconds, WallSeconds
|
|
206
|
+
attribute :pole, JobName
|
|
207
|
+
|
|
208
|
+
given("the seconds and pole are the run's own") do
|
|
209
|
+
seconds.value == wall_seconds.value && pole.value == longest_pole.value
|
|
210
|
+
end
|
|
211
|
+
given("a cache-miss run finished over budget") do
|
|
212
|
+
cache_state.value == "miss" && wall_seconds.value > budget_seconds.value
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
emits "MissRunOverBudget"
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# The adapter answers what GitHub reports for one run. This bluebook only names the question.
|
|
219
|
+
query "Reading" do
|
|
220
|
+
description "One run's record as GitHub reports it: wall time, cache state, conclusion, and each job's time. An adapter answers it; this bluebook never fetches."
|
|
221
|
+
attribute :run_id, RunId
|
|
222
|
+
returns Document
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
query "All" do
|
|
226
|
+
description "Every observed run, oldest first."
|
|
227
|
+
order_by :run_id
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# The target and the latest warm and cache-miss times. The stop rule lives here.
|
|
232
|
+
aggregate "Budget" do
|
|
233
|
+
description "The wall-time target for main CI, and the latest warm and cache-miss runs measured against it."
|
|
234
|
+
|
|
235
|
+
identified_by :name
|
|
236
|
+
|
|
237
|
+
attribute :name, BudgetName
|
|
238
|
+
attribute :target_seconds, TargetSeconds
|
|
239
|
+
attribute :warm_seconds, LatestSeconds
|
|
240
|
+
attribute :miss_seconds, LatestSeconds
|
|
241
|
+
|
|
242
|
+
lifecycle :status, default: "open" do
|
|
243
|
+
transition "Complete" => "complete", from: "open"
|
|
244
|
+
transition "Reopen" => "open", from: "complete"
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
value_object "BudgetName" do
|
|
248
|
+
attribute :value, String, matches: :present
|
|
249
|
+
invariant("a budget is named") { !value.to_s.empty? }
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
value_object "TargetSeconds" do
|
|
253
|
+
attribute :value, Integer, default: 180
|
|
254
|
+
invariant("a target is positive") { value.positive? }
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Zero means no run of this cache state has been observed yet.
|
|
258
|
+
value_object "LatestSeconds" do
|
|
259
|
+
attribute :value, Integer, default: 0
|
|
260
|
+
invariant("a latest time is not negative") { !value.negative? }
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
command "Open" do
|
|
264
|
+
role "CI engineer"
|
|
265
|
+
goal "Set the wall-time target that main CI is measured against"
|
|
266
|
+
|
|
267
|
+
attribute :name, BudgetName
|
|
268
|
+
attribute :target_seconds, TargetSeconds
|
|
269
|
+
|
|
270
|
+
emits "BudgetOpened"
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Recorded whatever the verdict: the latest run decides, not the best one. Allowed after the
|
|
274
|
+
# budget is complete, so a regression still shows.
|
|
275
|
+
command "WitnessWarm" do
|
|
276
|
+
role "CI engineer"
|
|
277
|
+
goal "Record the latest warm run's wall time against the target"
|
|
278
|
+
|
|
279
|
+
reference_to Budget
|
|
280
|
+
attribute :seconds, WallSeconds
|
|
281
|
+
|
|
282
|
+
sets :warm_seconds, to: :seconds
|
|
283
|
+
|
|
284
|
+
emits "WarmRunWitnessed"
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
command "WitnessMiss" do
|
|
288
|
+
role "CI engineer"
|
|
289
|
+
goal "Record the latest cache-miss run's wall time against the target"
|
|
290
|
+
|
|
291
|
+
reference_to Budget
|
|
292
|
+
attribute :seconds, WallSeconds
|
|
293
|
+
|
|
294
|
+
sets :miss_seconds, to: :seconds
|
|
295
|
+
|
|
296
|
+
emits "MissRunWitnessed"
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Refused while either latest run is missing or over target. A refusal here is the answer
|
|
300
|
+
# "not yet", and the loop keeps going.
|
|
301
|
+
command "Complete" do
|
|
302
|
+
role "CI engineer"
|
|
303
|
+
goal "Mark the target met, once the latest warm run and the latest cache-miss run are both under budget"
|
|
304
|
+
|
|
305
|
+
reference_to Budget
|
|
306
|
+
|
|
307
|
+
given("both cache states are under budget") do
|
|
308
|
+
warm_seconds.value > 0 && warm_seconds.value < target_seconds.value &&
|
|
309
|
+
miss_seconds.value > 0 && miss_seconds.value < target_seconds.value
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
emits "BudgetMet"
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Meeting a target is a milestone, not the end: every minute off a cycle is saved on every push,
|
|
316
|
+
# so the next target is lower and the backlog keeps being worked.
|
|
317
|
+
command "Reopen" do
|
|
318
|
+
role "CI engineer"
|
|
319
|
+
goal "Set a lower target once the last one is met, so the effort goes on taking time out of the cycle"
|
|
320
|
+
|
|
321
|
+
reference_to Budget
|
|
322
|
+
attribute :new_target_seconds, TargetSeconds
|
|
323
|
+
|
|
324
|
+
given("a new target is lower than the one met") { new_target_seconds.value < target_seconds.value }
|
|
325
|
+
|
|
326
|
+
sets :target_seconds, to: :new_target_seconds
|
|
327
|
+
|
|
328
|
+
emits "BudgetReopened"
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
query "Open" do
|
|
332
|
+
description "The budget in force, and whether the effort has met it."
|
|
333
|
+
where(status: "open")
|
|
334
|
+
order_by :name
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# One hypothesis for saving CI time. A lever is kept or reverted on a following run of the same
|
|
339
|
+
# cache state it was measured on, and never retried unless its inputs changed.
|
|
340
|
+
aggregate "Lever" do
|
|
341
|
+
description "A hypothesis for saving CI time: what it changes, what it should save, and what the runs after it measured."
|
|
342
|
+
|
|
343
|
+
identified_by :name
|
|
344
|
+
|
|
345
|
+
attribute :name, LeverName
|
|
346
|
+
attribute :change, LeverChange
|
|
347
|
+
attribute :pole, JobName
|
|
348
|
+
attribute :affects, Affects
|
|
349
|
+
attribute :expected_seconds, SecondsSaved
|
|
350
|
+
# What the estimate rests on, such as the job times of a named run. "unestimated" ranks last in
|
|
351
|
+
# the backlog until someone replaces it with a basis and a number.
|
|
352
|
+
attribute :basis, EstimateBasis
|
|
353
|
+
# What a following run measured: the baseline less the following time. Set when a lever is
|
|
354
|
+
# kept, so the estimates can be compared with what the changes really saved.
|
|
355
|
+
attribute :saved_seconds, SecondsSaved
|
|
356
|
+
# A fingerprint of what the change depends on, such as a lockfile digest. Reconsidering a
|
|
357
|
+
# reverted lever needs it to differ.
|
|
358
|
+
attribute :inputs, LeverInputs
|
|
359
|
+
attribute :evidence, Evidence
|
|
360
|
+
attribute :baseline_run, RunRef
|
|
361
|
+
attribute :baseline_seconds, MeasuredSeconds
|
|
362
|
+
attribute :baseline_cache, CacheState
|
|
363
|
+
attribute :following_run, RunRef
|
|
364
|
+
attribute :following_seconds, MeasuredSeconds
|
|
365
|
+
attribute :margin_seconds, MarginSeconds
|
|
366
|
+
|
|
367
|
+
lifecycle :status, default: "proposed" do
|
|
368
|
+
transition "Attempt" => "attempted", from: "proposed"
|
|
369
|
+
transition "Keep" => "kept", from: "attempted"
|
|
370
|
+
transition "Revert" => "reverted", from: "attempted"
|
|
371
|
+
transition "Recall" => "kept", from: "proposed"
|
|
372
|
+
transition "Reconsider" => "proposed", from: "reverted"
|
|
373
|
+
transition "Reestimate" => "proposed", from: "proposed"
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
# Why an estimate stands where it does. A plain statement of the run or job times it came from.
|
|
377
|
+
value_object "EstimateBasis" do
|
|
378
|
+
attribute :value, String, default: "unestimated"
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
value_object "LeverName" do
|
|
382
|
+
# A lowercase slug, such as `postgres_io` or `fuzz_sqlite`.
|
|
383
|
+
attribute :value, String, pattern: '^[a-z][a-z0-9_]*$'
|
|
384
|
+
invariant("a lever is named") { !value.to_s.empty? }
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
value_object "LeverChange" do
|
|
388
|
+
attribute :value, String, matches: :present
|
|
389
|
+
invariant("a lever says what it changes") { !value.to_s.empty? }
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
value_object "JobName" do
|
|
393
|
+
attribute :value, String, matches: :present
|
|
394
|
+
invariant("a lever names the job it targets") { !value.to_s.empty? }
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
# The cache state the lever should move: `hit`, `miss`, or `both`.
|
|
398
|
+
value_object "Affects" do
|
|
399
|
+
attribute :value, String, pattern: '^(hit|miss|both)$'
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
value_object "SecondsSaved" do
|
|
403
|
+
attribute :value, Integer, default: 0
|
|
404
|
+
invariant("expected savings are not negative") { !value.negative? }
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
value_object "LeverInputs" do
|
|
408
|
+
attribute :value, String, default: ""
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
# The commit that proves a kept lever. Empty while nothing has proven it.
|
|
412
|
+
value_object "Evidence" do
|
|
413
|
+
attribute :value, String, pattern: '^([0-9a-f]{7,40})?$'
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
# A GitHub run id, or empty before a run has been measured.
|
|
417
|
+
value_object "RunRef" do
|
|
418
|
+
attribute :value, String, pattern: '^([0-9]+)?$'
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
value_object "MeasuredSeconds" do
|
|
422
|
+
attribute :value, Integer, default: 0
|
|
423
|
+
invariant("a measured time is not negative") { !value.negative? }
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
# How much faster a following run must be to keep a lever.
|
|
427
|
+
value_object "MarginSeconds" do
|
|
428
|
+
attribute :value, Integer, default: 10
|
|
429
|
+
invariant("a margin is not negative") { !value.negative? }
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
# A lever's name is its identity, so a second lever under the same name is refused as already on
|
|
433
|
+
# file. The policies below propose under the pole's name; any further lever is named by the
|
|
434
|
+
# caller.
|
|
435
|
+
command "Propose" do
|
|
436
|
+
role "CI engineer"
|
|
437
|
+
goal "Write down a lever for a slow job before anybody tries it"
|
|
438
|
+
|
|
439
|
+
attribute :name, LeverName
|
|
440
|
+
attribute :change, LeverChange
|
|
441
|
+
attribute :pole, JobName
|
|
442
|
+
attribute :affects, Affects
|
|
443
|
+
attribute :expected_seconds, SecondsSaved
|
|
444
|
+
attribute :basis, EstimateBasis
|
|
445
|
+
attribute :inputs, LeverInputs
|
|
446
|
+
|
|
447
|
+
emits "LeverProposed"
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
# The backlog is ordered by this number, so attempts go to the biggest saving first. Revised
|
|
451
|
+
# whenever a run shows the job times have moved, or a kept lever shows what a change is worth.
|
|
452
|
+
command "Reestimate" do
|
|
453
|
+
role "CI engineer"
|
|
454
|
+
goal "Revise what a proposed lever is expected to save, and say what the new number rests on"
|
|
455
|
+
|
|
456
|
+
reference_to Lever
|
|
457
|
+
attribute :new_expected_seconds, SecondsSaved
|
|
458
|
+
attribute :new_basis, EstimateBasis
|
|
459
|
+
|
|
460
|
+
given("a revised estimate says what it rests on") { new_basis.value != "unestimated" }
|
|
461
|
+
|
|
462
|
+
sets :expected_seconds, to: :new_expected_seconds
|
|
463
|
+
sets :basis, to: :new_basis
|
|
464
|
+
|
|
465
|
+
emits "LeverReestimated"
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
# Recorded with the run it was measured against. The given keeps it to the cache state the
|
|
469
|
+
# lever affects, since a lever that moves only warm runs says nothing about a cold one.
|
|
470
|
+
command "Attempt" do
|
|
471
|
+
role "CI engineer"
|
|
472
|
+
goal "Measure a proposed lever against the run it is meant to beat"
|
|
473
|
+
|
|
474
|
+
reference_to Lever
|
|
475
|
+
attribute :baseline_run, RunRef
|
|
476
|
+
attribute :baseline_seconds, MeasuredSeconds
|
|
477
|
+
attribute :baseline_cache, CacheState
|
|
478
|
+
|
|
479
|
+
given("a lever is measured on the cache state it affects") do
|
|
480
|
+
affects.value == "both" || affects.value == baseline_cache.value
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
sets :baseline_run
|
|
484
|
+
sets :baseline_seconds
|
|
485
|
+
sets :baseline_cache
|
|
486
|
+
|
|
487
|
+
emits "LeverAttempted"
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
command "Keep" do
|
|
491
|
+
role "CI engineer"
|
|
492
|
+
goal "Keep an attempted lever, because a following run of its cache state beat the baseline by the margin"
|
|
493
|
+
|
|
494
|
+
reference_to Lever
|
|
495
|
+
attribute :following_run, RunRef
|
|
496
|
+
attribute :following_seconds, MeasuredSeconds
|
|
497
|
+
attribute :following_cache, CacheState
|
|
498
|
+
attribute :evidence, Evidence
|
|
499
|
+
attribute :saved_seconds, SecondsSaved
|
|
500
|
+
|
|
501
|
+
given("a following run is of the cache state the lever was measured on") do
|
|
502
|
+
following_cache.value == baseline_cache.value
|
|
503
|
+
end
|
|
504
|
+
given("a kept lever is faster than its baseline by its margin") do
|
|
505
|
+
following_seconds.value + margin_seconds.value <= baseline_seconds.value
|
|
506
|
+
end
|
|
507
|
+
given("a kept lever says what it really saved") do
|
|
508
|
+
saved_seconds.value == baseline_seconds.value - following_seconds.value
|
|
509
|
+
end
|
|
510
|
+
given("a kept lever names the commit that proves it") { evidence.value != "" }
|
|
511
|
+
|
|
512
|
+
sets :following_run
|
|
513
|
+
sets :following_seconds
|
|
514
|
+
sets :evidence
|
|
515
|
+
sets :saved_seconds
|
|
516
|
+
|
|
517
|
+
emits "LeverKept"
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
command "Revert" do
|
|
521
|
+
role "CI engineer"
|
|
522
|
+
goal "Revert an attempted lever, because the following run did not beat the baseline by the margin"
|
|
523
|
+
|
|
524
|
+
reference_to Lever
|
|
525
|
+
attribute :following_run, RunRef
|
|
526
|
+
attribute :following_seconds, MeasuredSeconds
|
|
527
|
+
attribute :following_cache, CacheState
|
|
528
|
+
|
|
529
|
+
given("a following run is of the cache state the lever was measured on") do
|
|
530
|
+
following_cache.value == baseline_cache.value
|
|
531
|
+
end
|
|
532
|
+
given("a reverted lever is no faster than its baseline by its margin") do
|
|
533
|
+
following_seconds.value + margin_seconds.value > baseline_seconds.value
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
sets :following_run
|
|
537
|
+
sets :following_seconds
|
|
538
|
+
|
|
539
|
+
emits "LeverReverted"
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
# A lever already learned from history, with the commit that shows it. It skips the attempt,
|
|
543
|
+
# because the runs that measured it are older than the bluebook.
|
|
544
|
+
command "Recall" do
|
|
545
|
+
role "CI engineer"
|
|
546
|
+
goal "Record a lever that history already settled, with the commit that settled it"
|
|
547
|
+
|
|
548
|
+
reference_to Lever
|
|
549
|
+
attribute :evidence, Evidence
|
|
550
|
+
|
|
551
|
+
given("a learned lever names the commit it was learned from") { evidence.value != "" }
|
|
552
|
+
|
|
553
|
+
sets :evidence
|
|
554
|
+
|
|
555
|
+
emits "LeverLearned"
|
|
556
|
+
end
|
|
557
|
+
|
|
558
|
+
command "Reconsider" do
|
|
559
|
+
role "CI engineer"
|
|
560
|
+
goal "Put a reverted lever back to proposed, because the inputs it depends on have changed"
|
|
561
|
+
|
|
562
|
+
reference_to Lever
|
|
563
|
+
attribute :new_inputs, LeverInputs
|
|
564
|
+
|
|
565
|
+
given("a reverted lever is retried only when its inputs have changed") do
|
|
566
|
+
new_inputs.value != inputs.value
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
sets :inputs, to: :new_inputs
|
|
570
|
+
|
|
571
|
+
emits "LeverReconsidered"
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
query "Kept" do
|
|
575
|
+
description "Levers that a following run proved, or that history settled, each with its evidence."
|
|
576
|
+
where(status: "kept")
|
|
577
|
+
order_by :name
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
# What to try next: the proposed levers, the biggest expected saving first. This is the list
|
|
581
|
+
# that drives the attempts; `Next` is its head.
|
|
582
|
+
query "Backlog" do
|
|
583
|
+
description "Levers waiting to be tried, the biggest expected saving first."
|
|
584
|
+
where(status: "proposed")
|
|
585
|
+
order_by :expected_seconds, :desc
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
query "Next" do
|
|
589
|
+
description "The one lever to attempt now: the proposed lever with the biggest expected saving."
|
|
590
|
+
where(status: "proposed")
|
|
591
|
+
order_by :expected_seconds, :desc
|
|
592
|
+
limit 1
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
query "Saved" do
|
|
596
|
+
description "Kept levers, the biggest measured saving first, to set the estimates against."
|
|
597
|
+
where(status: "kept")
|
|
598
|
+
order_by :saved_seconds, :desc
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
query "Reverted" do
|
|
602
|
+
description "Levers a following run did not beat by the margin. Not retried unless their inputs change."
|
|
603
|
+
where(status: "reverted")
|
|
604
|
+
order_by :name
|
|
605
|
+
end
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
# One local measurement of a change to the pipeline: the same steps run in a checkout of the
|
|
609
|
+
# pipeline as it is (`base_ref`) and a checkout with the change (`candidate_ref`), interleaved, so
|
|
610
|
+
# a proposal is measured on this machine before a CI run is spent on it. The `LocalRunner` port
|
|
611
|
+
# does the running; this aggregate records that it was asked for and what came back.
|
|
612
|
+
#
|
|
613
|
+
# What an experiment can time is a row of `ExperimentDefinition`, and each row's steps are rows of
|
|
614
|
+
# `ExperimentStep`, joined by the definition's name and read in the order written. A step is a
|
|
615
|
+
# call of a hecks verb, never a shell line: a verb ending in `!` is a command, any other a query,
|
|
616
|
+
# and `arguments` are its `name=value` words, separated by spaces. The rows live in this bluebook,
|
|
617
|
+
# as the language's own tables do (`CiGate`, `ReleaseFile`): the bluebook is not attachable yet,
|
|
618
|
+
# so no client bluebook can hold them, and `hecks run lib/hecks/ci_speed` boots them with it.
|
|
619
|
+
#
|
|
620
|
+
# `needs_generated_tree` says what each sample starts from. `no` is a checkout with no generated
|
|
621
|
+
# Rust, as a cache miss has it; `yes` is a copy of the generated tree of the checkout that asked.
|
|
622
|
+
# Either way the tree is put back before every sample, so a step that writes it measures the
|
|
623
|
+
# same work each time.
|
|
624
|
+
#
|
|
625
|
+
# A step the local machine cannot stand in for (its `needs` is not `none`, or one of the two
|
|
626
|
+
# checkouts does not know its verb) is left out of both sides and named in `unmeasured`, never
|
|
627
|
+
# timed as if it ran. `verdict` is `inconclusive` unless the saving is larger than the spread of
|
|
628
|
+
# the samples, so noise on this shared machine is never reported as a result.
|
|
629
|
+
aggregate "Experiment" do
|
|
630
|
+
description "One local measurement of a proposed change to the pipeline: the base and candidate refs timed on the same steps, interleaved, and what the saving was set against the noise."
|
|
631
|
+
|
|
632
|
+
identified_by :name
|
|
633
|
+
|
|
634
|
+
attribute :name, ExperimentName
|
|
635
|
+
attribute :lever, LeverRef, optional: true
|
|
636
|
+
attribute :measures, DefinitionName
|
|
637
|
+
attribute :cache_state, CacheState
|
|
638
|
+
attribute :base_ref, GitRef
|
|
639
|
+
attribute :candidate_ref, GitRef
|
|
640
|
+
attribute :repeats, Repeats, optional: true
|
|
641
|
+
# The result, written once by `Complete`.
|
|
642
|
+
attribute :base_seconds, Seconds, optional: true
|
|
643
|
+
attribute :candidate_seconds, Seconds, optional: true
|
|
644
|
+
attribute :spread_seconds, Seconds, optional: true
|
|
645
|
+
attribute :saved_seconds, SavedSeconds, optional: true
|
|
646
|
+
attribute :load, LoadAverage, optional: true
|
|
647
|
+
attribute :unmeasured, Note, optional: true
|
|
648
|
+
attribute :verdict, Verdict, optional: true
|
|
649
|
+
attribute :basis, Note, optional: true
|
|
650
|
+
attribute :refusal, Note, optional: true
|
|
651
|
+
|
|
652
|
+
value_object "ExperimentName" do
|
|
653
|
+
attribute :value, String, pattern: '^[a-z][a-z0-9_]*$'
|
|
654
|
+
invariant("an experiment is named") { !value.to_s.empty? }
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
# A lever's name, or empty when the experiment is not about one.
|
|
658
|
+
value_object "LeverRef" do
|
|
659
|
+
attribute :value, String, pattern: '^([a-z][a-z0-9_]*)?$'
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
# The name of an `ExperimentDefinition` row.
|
|
663
|
+
value_object "DefinitionName" do
|
|
664
|
+
attribute :value, String, pattern: '^[a-z][a-z0-9_]*$'
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
# The cache state the measurement stands for, as on a `Run`.
|
|
668
|
+
value_object "CacheState" do
|
|
669
|
+
attribute :value, String, pattern: '^(hit|miss)$'
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
# A ref git can resolve, such as `HEAD~1` or a short hash. It cannot start with a dash, so it
|
|
673
|
+
# is never read as an option.
|
|
674
|
+
value_object "GitRef" do
|
|
675
|
+
attribute :value, String, pattern: '^[A-Za-z0-9_][A-Za-z0-9_./~^@{}-]*$'
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
# One sample is a pass over all the steps on each side. A single sample has no spread, so it
|
|
679
|
+
# could not tell a result from noise.
|
|
680
|
+
value_object "Repeats" do
|
|
681
|
+
attribute :value, Integer, default: 3
|
|
682
|
+
invariant("an experiment is repeated at least twice, or there is no spread to judge it by") { value >= 2 }
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
value_object "Seconds" do
|
|
686
|
+
attribute :value, Float, default: 0.0
|
|
687
|
+
invariant("a measured time is not negative") { !value.negative? }
|
|
688
|
+
end
|
|
689
|
+
|
|
690
|
+
# Base less candidate: negative when the candidate was the slower.
|
|
691
|
+
value_object "SavedSeconds" do
|
|
692
|
+
attribute :value, Float, default: 0.0
|
|
693
|
+
end
|
|
694
|
+
|
|
695
|
+
# The machine's one-minute load average as the experiment started.
|
|
696
|
+
value_object "LoadAverage" do
|
|
697
|
+
attribute :value, Float, default: 0.0
|
|
698
|
+
invariant("a load average is not negative") { !value.negative? }
|
|
699
|
+
end
|
|
700
|
+
|
|
701
|
+
# What a `Lever` is told it is worth: the saving rounded to whole seconds, and zero for any
|
|
702
|
+
# result that is not conclusively faster.
|
|
703
|
+
value_object "EstimateSeconds" do
|
|
704
|
+
attribute :value, Integer, default: 0
|
|
705
|
+
invariant("an estimate is not negative") { !value.negative? }
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
value_object "Verdict" do
|
|
709
|
+
attribute :value, String, pattern: '^(faster|slower|inconclusive)$'
|
|
710
|
+
end
|
|
711
|
+
|
|
712
|
+
value_object "Note" do
|
|
713
|
+
attribute :value, String, default: ""
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
# One thing an experiment can time. `measures` says what the time is of, `cache_state` which
|
|
717
|
+
# state of the generated-rust cache it stands for, `needs_generated_tree` what each sample
|
|
718
|
+
# starts from (`yes` or `no`).
|
|
719
|
+
value_object "ExperimentDefinition" do
|
|
720
|
+
attribute :name, String
|
|
721
|
+
attribute :measures, String
|
|
722
|
+
attribute :cache_state, String
|
|
723
|
+
attribute :needs_generated_tree, String
|
|
724
|
+
|
|
725
|
+
member name: "regenerate_corpus_cold",
|
|
726
|
+
measures: "regenerating every corpus domain's Rust from a checkout with no generated tree, rustfmt included",
|
|
727
|
+
cache_state: "miss", needs_generated_tree: "no"
|
|
728
|
+
end
|
|
729
|
+
|
|
730
|
+
# One step of a definition, in the order the steps are written. `verb` is a hecks command
|
|
731
|
+
# (`Aggregate.verb!`) or query (`Aggregate.verb`), `arguments` its `name=value` words. `needs`
|
|
732
|
+
# is `none` for a step this machine can do, or `postgres` or `network` for one it cannot.
|
|
733
|
+
value_object "ExperimentStep" do
|
|
734
|
+
attribute :definition, String
|
|
735
|
+
attribute :name, String
|
|
736
|
+
attribute :verb, String
|
|
737
|
+
attribute :arguments, String
|
|
738
|
+
attribute :needs, String
|
|
739
|
+
|
|
740
|
+
member definition: "regenerate_corpus_cold", name: "regenerate",
|
|
741
|
+
verb: "RegenerationRun.regenerate_corpus!", arguments: "confirm.value=true"
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
# Asking the port is the next thing that happens: it runs the steps and answers with the
|
|
745
|
+
# numbers, or refuses. The command lines that run it are in this file's header.
|
|
746
|
+
command "Request" do
|
|
747
|
+
goal "Ask for a change to the pipeline to be timed against the pipeline as it is, on this machine"
|
|
748
|
+
|
|
749
|
+
attribute :name, ExperimentName
|
|
750
|
+
attribute :lever, LeverRef, optional: true
|
|
751
|
+
attribute :measures, DefinitionName
|
|
752
|
+
attribute :cache_state, CacheState
|
|
753
|
+
attribute :base_ref, GitRef
|
|
754
|
+
attribute :candidate_ref, GitRef
|
|
755
|
+
attribute :repeats, Repeats, optional: true
|
|
756
|
+
|
|
757
|
+
given("a candidate is a different ref from its base") { candidate_ref.value != base_ref.value }
|
|
758
|
+
|
|
759
|
+
sets :name
|
|
760
|
+
sets :lever
|
|
761
|
+
sets :measures
|
|
762
|
+
sets :cache_state
|
|
763
|
+
sets :base_ref
|
|
764
|
+
sets :candidate_ref
|
|
765
|
+
sets :repeats
|
|
766
|
+
|
|
767
|
+
emits "ExperimentRequested"
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
# The answer is written as the adapter computed it. `reestimates` restates the lever and
|
|
771
|
+
# `expected_seconds` and `basis` are what that lever is to be told, because a policy passes on
|
|
772
|
+
# only the fields of the event it reacts to. The givens keep the adapter's verdict honest:
|
|
773
|
+
# a verdict says what the numbers say, and only a conclusive faster result carries an estimate.
|
|
774
|
+
command "Complete" do
|
|
775
|
+
goal "Record what the local run measured, and whether the saving stands out from the noise"
|
|
776
|
+
|
|
777
|
+
reference_to Experiment
|
|
778
|
+
attribute :base_seconds, Seconds
|
|
779
|
+
attribute :candidate_seconds, Seconds
|
|
780
|
+
attribute :spread_seconds, Seconds
|
|
781
|
+
attribute :saved_seconds, SavedSeconds
|
|
782
|
+
attribute :load, LoadAverage
|
|
783
|
+
attribute :unmeasured, Note
|
|
784
|
+
attribute :verdict, Verdict
|
|
785
|
+
attribute :basis, Note
|
|
786
|
+
attribute :reestimates, LeverRef
|
|
787
|
+
attribute :expected_seconds, EstimateSeconds
|
|
788
|
+
|
|
789
|
+
given("a saving is the base time less the candidate time") do
|
|
790
|
+
saved_seconds.value < base_seconds.value - candidate_seconds.value + 0.001 &&
|
|
791
|
+
saved_seconds.value > base_seconds.value - candidate_seconds.value - 0.001
|
|
792
|
+
end
|
|
793
|
+
given("a faster verdict needs a saving larger than the spread") do
|
|
794
|
+
verdict.value != "faster" || saved_seconds.value > spread_seconds.value
|
|
795
|
+
end
|
|
796
|
+
given("a slower verdict needs a loss larger than the spread") do
|
|
797
|
+
verdict.value != "slower" || saved_seconds.value + spread_seconds.value < 0
|
|
798
|
+
end
|
|
799
|
+
given("an inconclusive verdict is a saving within the spread") do
|
|
800
|
+
verdict.value != "inconclusive" ||
|
|
801
|
+
(saved_seconds.value <= spread_seconds.value && saved_seconds.value + spread_seconds.value >= 0)
|
|
802
|
+
end
|
|
803
|
+
given("only a faster verdict carries an estimate") do
|
|
804
|
+
verdict.value == "faster" || expected_seconds.value == 0
|
|
805
|
+
end
|
|
806
|
+
given("the restated lever is the experiment's own") do
|
|
807
|
+
(lever.unset? && reestimates.value == "") || (lever.set? && reestimates.value == lever.value)
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
sets :base_seconds
|
|
811
|
+
sets :candidate_seconds
|
|
812
|
+
sets :spread_seconds
|
|
813
|
+
sets :saved_seconds
|
|
814
|
+
sets :load
|
|
815
|
+
sets :unmeasured
|
|
816
|
+
sets :verdict
|
|
817
|
+
sets :basis
|
|
818
|
+
|
|
819
|
+
emits "ExperimentMeasured"
|
|
820
|
+
end
|
|
821
|
+
|
|
822
|
+
command "Fault" do
|
|
823
|
+
goal "Record that the experiment could not be run, and why"
|
|
824
|
+
|
|
825
|
+
reference_to Experiment
|
|
826
|
+
attribute :refusal, Note
|
|
827
|
+
|
|
828
|
+
sets :refusal
|
|
829
|
+
|
|
830
|
+
emits "ExperimentFaulted"
|
|
831
|
+
end
|
|
832
|
+
|
|
833
|
+
lifecycle :status, default: "requested" do
|
|
834
|
+
mark :failure, "faulted"
|
|
835
|
+
transition "Complete" => "measured", from: "requested"
|
|
836
|
+
transition "Fault" => "faulted", from: "requested"
|
|
837
|
+
end
|
|
838
|
+
|
|
839
|
+
query "Outcome" do
|
|
840
|
+
description "What one experiment did: its status, its numbers and verdict, or why it stopped."
|
|
841
|
+
attribute :name, ExperimentName
|
|
842
|
+
where(name: :name)
|
|
843
|
+
order_by :name
|
|
844
|
+
end
|
|
845
|
+
|
|
846
|
+
query "Measured" do
|
|
847
|
+
description "Every experiment that was measured, by name."
|
|
848
|
+
where(status: "measured")
|
|
849
|
+
order_by :name
|
|
850
|
+
end
|
|
851
|
+
|
|
852
|
+
query "Faulted" do
|
|
853
|
+
description "Every experiment that could not be run, by name."
|
|
854
|
+
where(status: "faulted")
|
|
855
|
+
order_by :name
|
|
856
|
+
end
|
|
857
|
+
end
|
|
858
|
+
|
|
859
|
+
# What has been learned about where main CI loses time: the way it showed, what caused it, and
|
|
860
|
+
# what fixed it or would. A finding is a way of losing time, a lever is one attempt to stop it; a
|
|
861
|
+
# lever that comes of a finding is proposed against the pole the finding names. The rows of
|
|
862
|
+
# `KnownFinding` are what the 2026-10-29 effort that took main CI from over ten minutes to under
|
|
863
|
+
# three taught, each with the run that shows it. `status` is `fixed` (the remedy is in and a run
|
|
864
|
+
# shows it), `open` (a cost that stays, with how to live with it) or `untried` (a remedy not yet
|
|
865
|
+
# made, to be proposed as a lever when its time comes). `Record` adds a finding as one is made.
|
|
866
|
+
aggregate "Finding" do
|
|
867
|
+
description "A way main CI was found to lose time, what caused it, and the remedy made or still to try."
|
|
868
|
+
|
|
869
|
+
identified_by :name
|
|
870
|
+
|
|
871
|
+
attribute :name, FindingName
|
|
872
|
+
attribute :status, FindingStatus
|
|
873
|
+
attribute :symptom, FindingText
|
|
874
|
+
attribute :cause, FindingText
|
|
875
|
+
attribute :remedy, FindingText
|
|
876
|
+
attribute :evidence, FindingText, optional: true
|
|
877
|
+
|
|
878
|
+
value_object "FindingName" do
|
|
879
|
+
attribute :value, String, matches: :present
|
|
880
|
+
invariant("a finding is named") { !value.to_s.empty? }
|
|
881
|
+
end
|
|
882
|
+
|
|
883
|
+
value_object "FindingStatus" do
|
|
884
|
+
attribute :value, String, one_of: %w[fixed open untried]
|
|
885
|
+
end
|
|
886
|
+
|
|
887
|
+
value_object "FindingText" do
|
|
888
|
+
attribute :value, String, matches: :present
|
|
889
|
+
invariant("a finding says something") { !value.to_s.empty? }
|
|
890
|
+
end
|
|
891
|
+
|
|
892
|
+
value_object "KnownFinding" do
|
|
893
|
+
attribute :name, String
|
|
894
|
+
attribute :status, String
|
|
895
|
+
attribute :symptom, String
|
|
896
|
+
attribute :cause, String
|
|
897
|
+
attribute :remedy, String
|
|
898
|
+
attribute :evidence, String
|
|
899
|
+
|
|
900
|
+
member name: "generated_rust_regenerated_per_job", status: "fixed",
|
|
901
|
+
symptom: "Every Rust job spent 30 to 90 seconds regenerating the corpus, and its cache moved with every merge of main.",
|
|
902
|
+
cause: "The generated tree was untracked, so each job rebuilt or restored it, keyed on the generator's inputs.",
|
|
903
|
+
remedy: "Commit rust/src/generated. The generated-rust action only checks it; the drift job alone regenerates and diffs.",
|
|
904
|
+
evidence: "main runs 156s and 150s, from 233 to 1091s"
|
|
905
|
+
member name: "drift_check_compared_a_tree_with_itself", status: "fixed",
|
|
906
|
+
symptom: "A stale meta module went through green.",
|
|
907
|
+
cause: "With generated-rust a no-op, the drift job's git diff compared the committed tree with itself.",
|
|
908
|
+
remedy: "generated-rust takes regenerate; only checks_codegen_drift sets it, and a spec holds that.",
|
|
909
|
+
evidence: "spec/ci_generated_rust_domains_spec.rb"
|
|
910
|
+
member name: "cargo_cache_key_missed_its_inputs", status: "fixed",
|
|
911
|
+
symptom: "A restored target was rebuilt in full and never replaced, because an exact hit does not save.",
|
|
912
|
+
cause: "The key hashed Cargo.lock and src only, so a target built from another manifest or path dependency matched.",
|
|
913
|
+
remedy: "The key also hashes the manifest, the path dependencies and the toolchain pin.",
|
|
914
|
+
evidence: ".github/actions/cargo-cache/action.yml"
|
|
915
|
+
member name: "shared_cache_built_two_ways", status: "fixed",
|
|
916
|
+
symptom: "rspec_rust_host_lineage compiled 349 crates on every run, 230 seconds, with an exact cache hit.",
|
|
917
|
+
cause: "Two jobs saved the cache named host from different directories: a --manifest-path build from the repo root skips rust/host/.cargo/config.toml, whose rustflags are in every crate's hash. The first to save won.",
|
|
918
|
+
remedy: "Build from rust/host in both jobs. Two jobs that share a cache name build the same thing from the same directory with the same flags.",
|
|
919
|
+
evidence: "lineage 230s to 95s"
|
|
920
|
+
member name: "warm_job_built_one_form", status: "fixed",
|
|
921
|
+
symptom: "The first full main run took 346s; one job spent 209s in a step that rebuilt the dependencies.",
|
|
922
|
+
cause: "The warm job built cargo test --workspace --no-run; the consumer ran cargo test -p rust_host_core --lib, which resolves features differently. On main the warm job saves first and the key never changes.",
|
|
923
|
+
remedy: "The warm job builds every form its consumers build, and the cache is renamed so a fresh entry is saved.",
|
|
924
|
+
evidence: "rspec_rust_host_cargo 331s to 139s"
|
|
925
|
+
member name: "cache_held_bytes_nobody_used", status: "fixed",
|
|
926
|
+
symptom: "The Rust io legs spent 40 to 54 seconds restoring the main crate's cache.",
|
|
927
|
+
cause: "It held debug/incremental, most of its bytes, and the rust-* feature binaries those legs delete after restoring.",
|
|
928
|
+
remedy: "The saved target leaves both out.",
|
|
929
|
+
evidence: "rust io legs 141s and 154s to 73s and 82s"
|
|
930
|
+
member name: "binary_key_hashed_the_generator", status: "fixed",
|
|
931
|
+
symptom: "Editing a bluebook or lib/hecks sent every feature binary leg cold.",
|
|
932
|
+
cause: "The key hashed the generator's Ruby inputs as well as rust/src, which since the tree is committed decides the binaries alone.",
|
|
933
|
+
remedy: "The key is the committed sources and the workflow file.",
|
|
934
|
+
evidence: "ci-rust-io.yml"
|
|
935
|
+
member name: "unsplittable_spec_file", status: "fixed",
|
|
936
|
+
symptom: "One rspec shard ran 158s while its neighbours ran 125s.",
|
|
937
|
+
cause: "parallel_tests cannot split a file, and one spec file cost 107s, more than a shard's share.",
|
|
938
|
+
remedy: "Split the file so the halves are dealt to different shards.",
|
|
939
|
+
evidence: "ci_speed_local_runner_spec.rb and its sampling spec"
|
|
940
|
+
member name: "fuzz_costs_measured_on_a_laptop", status: "fixed",
|
|
941
|
+
symptom: "The two SQLite fuzz legs ran 185s and 94s.",
|
|
942
|
+
cause: "Costs measured on a laptop's disk price banking and deploy in other proportions than a runner's.",
|
|
943
|
+
remedy: "The cost table comes from CI sweeps, not a laptop.",
|
|
944
|
+
evidence: "spec/fuzzing/fuzz_runtime_baseline.log"
|
|
945
|
+
member name: "parser_specs_in_postgres_shards", status: "fixed",
|
|
946
|
+
symptom: "Two Postgres shards failed on the skip backstop.",
|
|
947
|
+
cause: "Specs that need rust/parser skip where it is not built. The file-list check runs only on pull requests, so a list or pattern gone stale shows on a PR first.",
|
|
948
|
+
remedy: "Keep those specs in IO_PARALLEL_EXCLUDE_PATTERN.",
|
|
949
|
+
evidence: "ci-postgres-io-parallel.yml"
|
|
950
|
+
member name: "serial_front_gate", status: "fixed",
|
|
951
|
+
symptom: "Every job started 33 to 58 seconds into the run.",
|
|
952
|
+
cause: "Each called workflow waited on inherit_certification, and so on the release_only decision, which needs a checkout, a Ruby setup and a launcher call.",
|
|
953
|
+
remedy: "The calls no longer wait; a release bump may run the full suite. ADR 0107 is amended and the commit reverts alone.",
|
|
954
|
+
evidence: "front 11s; runs 148s to 176s"
|
|
955
|
+
member name: "reruns_skip_attested_trees", status: "open",
|
|
956
|
+
symptom: "A rerun reads fast: 109 to 128s with about 30 jobs skipped.",
|
|
957
|
+
cause: "An attestation note keyed by tree is written for a pull request's tree, and by the push train's hook for a pushed one; a run on a tree with a note skips its heavy jobs.",
|
|
958
|
+
remedy: "Measure attempt 1 of a push run on a tree no earlier run had, and count the skipped jobs. Never take a rerun as a sample.",
|
|
959
|
+
evidence: "runs 40822078842 and 40906130337 attempt 2"
|
|
960
|
+
member name: "cache_download_stall", status: "untried",
|
|
961
|
+
symptom: "One job's gem cache download stopped at 91% for about 80 seconds, and its run took 197s instead of about 151s.",
|
|
962
|
+
cause: "GitHub's cache service; not in our workflows. It was seen once.",
|
|
963
|
+
remedy: "Set SEGMENT_DOWNLOAD_TIMEOUT_MINS to about a minute at workflow level, so a stalled download aborts and the job proceeds as a miss; check the variable and what a timeout does in this version of actions/cache first. Also pass rust-toolchain false to setup-ruby in the Ruby-only jobs, which skips the 390 MB toolchain restore.",
|
|
964
|
+
evidence: "run 40906130337, rspec_rust_io leg c"
|
|
965
|
+
end
|
|
966
|
+
|
|
967
|
+
# A finding is written down when it is made, whatever its status, so the next effort starts
|
|
968
|
+
# from what this one learned.
|
|
969
|
+
command "Record" do
|
|
970
|
+
role "CI engineer"
|
|
971
|
+
goal "Write down how CI was found to lose time, what caused it, and the remedy made or still to try"
|
|
972
|
+
|
|
973
|
+
attribute :name, FindingName
|
|
974
|
+
attribute :status, FindingStatus
|
|
975
|
+
attribute :symptom, FindingText
|
|
976
|
+
attribute :cause, FindingText
|
|
977
|
+
attribute :remedy, FindingText
|
|
978
|
+
attribute :evidence, FindingText, optional: true
|
|
979
|
+
|
|
980
|
+
emits "FindingRecorded"
|
|
981
|
+
end
|
|
982
|
+
end
|
|
983
|
+
|
|
984
|
+
# An experiment runs as soon as it is asked for: the port runs it and answers with the numbers.
|
|
985
|
+
policy "MeasureWhenRequested" do
|
|
986
|
+
on "Experiment.ExperimentRequested"
|
|
987
|
+
trigger Experiment::LocalRunner::Measure, with: { name: :name }
|
|
988
|
+
end
|
|
989
|
+
|
|
990
|
+
policy "RecordTheMeasure" do
|
|
991
|
+
on "Experiment.ExperimentAnswered"
|
|
992
|
+
trigger Experiment::Complete, with: {
|
|
993
|
+
base_seconds: :base_seconds, candidate_seconds: :candidate_seconds, spread_seconds: :spread_seconds,
|
|
994
|
+
saved_seconds: :saved_seconds, load: :load, unmeasured: :unmeasured, verdict: :verdict,
|
|
995
|
+
basis: :basis, reestimates: :reestimates, expected_seconds: :expected_seconds
|
|
996
|
+
}
|
|
997
|
+
end
|
|
998
|
+
|
|
999
|
+
policy "RecordTheFaultedExperiment" do
|
|
1000
|
+
on "Experiment.ExperimentRefused"
|
|
1001
|
+
trigger Experiment::Fault, with: { refusal: :refusal }
|
|
1002
|
+
end
|
|
1003
|
+
|
|
1004
|
+
# A conclusive faster result that names a lever replaces that lever's estimate with what was
|
|
1005
|
+
# measured. A slower or inconclusive result says nothing to the backlog, so the estimate stands.
|
|
1006
|
+
policy "ReestimateFromAConclusiveExperiment" do
|
|
1007
|
+
on "Experiment.ExperimentMeasured"
|
|
1008
|
+
where { verdict.value == "faster" && reestimates.value != "" }
|
|
1009
|
+
trigger Lever::Reestimate, with: { lever: :reestimates, new_expected_seconds: :expected_seconds, new_basis: :basis }
|
|
1010
|
+
end
|
|
1011
|
+
|
|
1012
|
+
# When a run's warm or miss verdict lands, the Budget records it. Over-budget runs are
|
|
1013
|
+
# witnessed too: the latest run decides, not the best one.
|
|
1014
|
+
policy "WitnessWarmUnderBudget" do
|
|
1015
|
+
on "Run.WarmRunUnderBudget"
|
|
1016
|
+
trigger Budget::WitnessWarm, with: { budget: "main", seconds: :seconds }
|
|
1017
|
+
end
|
|
1018
|
+
|
|
1019
|
+
policy "WitnessWarmOverBudget" do
|
|
1020
|
+
on "Run.WarmRunOverBudget"
|
|
1021
|
+
trigger Budget::WitnessWarm, with: { budget: "main", seconds: :seconds }
|
|
1022
|
+
end
|
|
1023
|
+
|
|
1024
|
+
policy "WitnessMissUnderBudget" do
|
|
1025
|
+
on "Run.MissRunUnderBudget"
|
|
1026
|
+
trigger Budget::WitnessMiss, with: { budget: "main", seconds: :seconds }
|
|
1027
|
+
end
|
|
1028
|
+
|
|
1029
|
+
policy "WitnessMissOverBudget" do
|
|
1030
|
+
on "Run.MissRunOverBudget"
|
|
1031
|
+
trigger Budget::WitnessMiss, with: { budget: "main", seconds: :seconds }
|
|
1032
|
+
end
|
|
1033
|
+
|
|
1034
|
+
# The longest pole of an over-budget run becomes a lever, named for the pole. A pole already on
|
|
1035
|
+
# file is refused as already existing; see `Lever::Propose`.
|
|
1036
|
+
policy "ProposeLeverForWarmPole" do
|
|
1037
|
+
on "Run.WarmRunOverBudget"
|
|
1038
|
+
trigger Lever::Propose, with: {
|
|
1039
|
+
name: :pole,
|
|
1040
|
+
change: { value: "a change to the slowest job of a warm run" },
|
|
1041
|
+
pole: :pole,
|
|
1042
|
+
affects: { value: "both" },
|
|
1043
|
+
expected_seconds: { value: 0 },
|
|
1044
|
+
basis: { value: "unestimated" },
|
|
1045
|
+
inputs: { value: "" }
|
|
1046
|
+
}
|
|
1047
|
+
end
|
|
1048
|
+
|
|
1049
|
+
policy "ProposeLeverForMissPole" do
|
|
1050
|
+
on "Run.MissRunOverBudget"
|
|
1051
|
+
trigger Lever::Propose, with: {
|
|
1052
|
+
name: :pole,
|
|
1053
|
+
change: { value: "a change to the slowest job of a cache-miss run" },
|
|
1054
|
+
pole: :pole,
|
|
1055
|
+
affects: { value: "both" },
|
|
1056
|
+
expected_seconds: { value: 0 },
|
|
1057
|
+
basis: { value: "unestimated" },
|
|
1058
|
+
inputs: { value: "" }
|
|
1059
|
+
}
|
|
1060
|
+
end
|
|
1061
|
+
|
|
1062
|
+
# The effort completes when a witness lands and both latest runs are under target. Until then
|
|
1063
|
+
# the Complete given refuses, which is the "not yet" answer.
|
|
1064
|
+
policy "CompleteOnWarmWitness" do
|
|
1065
|
+
on "Budget.WarmRunWitnessed"
|
|
1066
|
+
trigger Budget::Complete, with: { budget: :name }
|
|
1067
|
+
end
|
|
1068
|
+
|
|
1069
|
+
policy "CompleteOnMissWitness" do
|
|
1070
|
+
on "Budget.MissRunWitnessed"
|
|
1071
|
+
trigger Budget::Complete, with: { budget: :name }
|
|
1072
|
+
end
|
|
1073
|
+
end
|