hecks 7.0.0 → 7.1.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/lib/hecks/adapters/driven/postgres_era/lineage/chain_sql.rb +2 -1
- data/lib/hecks/adapters/driven/postgres_era/lineage/provisioning.rb +4 -2
- data/lib/hecks/adapters/driven/postgres_era/lineage/split_sql.rb +16 -8
- data/lib/hecks/adapters/driven/postgres_era/lineage/transform_sql.rb +7 -3
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/era_resolver.rb +2 -1
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/mint_request.rb +2 -1
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/refusals.rb +2 -1
- data/lib/hecks/adapters/driving/cli_runner/bang_rule.rb +3 -0
- data/lib/hecks/adapters/driving/cli_runner/completion_words.rb +2 -0
- data/lib/hecks/adapters/driving/cli_runner/read_context.rb +6 -3
- data/lib/hecks/adapters/driving/cli_runner/renamed_names.rb +2 -1
- data/lib/hecks/adapters/driving/cli_runner/resolution.rb +2 -2
- data/lib/hecks/adapters/driving/cli_runner/resume_words.rb +1 -0
- data/lib/hecks/adapters/driving/cli_runner/run_reference.rb +6 -3
- data/lib/hecks/adapters/driving/cli_runner/search_words.rb +2 -0
- data/lib/hecks/adapters/driving/cli_runner/tour_words.rb +1 -0
- data/lib/hecks/adapters/driving/cli_runner/verb_names.rb +2 -1
- data/lib/hecks/adapters/driving/cli_runner/verbosity.rb +2 -0
- data/lib/hecks/adapters/driving/cli_runner/word_help.rb +40 -0
- data/lib/hecks/adapters/driving/cli_runner.rb +2 -0
- data/lib/hecks/adapters/driving/mcp/progress_sink.rb +4 -2
- data/lib/hecks/adapters/driving/mcp.rb +2 -1
- data/lib/hecks/adapters/driving/mcp_scope/argument_policy.rb +2 -1
- data/lib/hecks/adapters/driving/ruby/aggregate_module/hooks.rb +28 -14
- data/lib/hecks/adapters/driving/ruby/aggregate_module.rb +4 -2
- data/lib/hecks/assembly.rb +18 -4
- data/lib/hecks/behaviors/expectations/adapter_selection.rb +82 -0
- data/lib/hecks/behaviors/expectations/boot.rb +12 -5
- data/lib/hecks/bluebook/acknowledgements.rb +1 -1
- data/lib/hecks/bluebook/command.rb +3 -2
- data/lib/hecks/bluebook/declaration.rb +2 -1
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +9 -1
- data/lib/hecks/bluebook/dsl/bluebook_builder/metadata.rb +6 -4
- data/lib/hecks/bluebook/dsl/bluebook_builder/modules.rb +6 -3
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/correlation_keys.rb +2 -1
- data/lib/hecks/bluebook/dsl/command_builder/folded_sets.rb +4 -2
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +2 -1
- data/lib/hecks/bluebook/hecksagon.rb +6 -3
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +9 -2
- data/lib/hecks/bluebook/meta_validator.rb +1 -1
- data/lib/hecks/bluebook/model_check/aggregate_checks.rb +2 -1
- data/lib/hecks/bluebook/model_check/classification_checks.rb +51 -0
- data/lib/hecks/bluebook/model_check/context_checks.rb +2 -0
- data/lib/hecks/bluebook/model_check/redundant_relationships.rb +2 -1
- data/lib/hecks/ci_speed/adapters/shard_partitioner.adapter +7 -0
- data/lib/hecks/ci_speed/adapters/shard_partitioner.rb +66 -0
- data/lib/hecks/ci_speed/adapters/timings_source.adapter +7 -0
- data/lib/hecks/ci_speed/adapters/timings_source.rb +68 -0
- data/lib/hecks/ci_speed/ci_speed.bluebook +111 -0
- data/lib/hecks/ci_speed/ci_speed.ports.hecksagon +22 -0
- data/lib/hecks/ci_workflows/ci_workflows.bluebook +50 -6
- data/lib/hecks/coder/coder.bluebook +3 -1
- data/lib/hecks/command_names/index.rb +1 -1
- data/lib/hecks/command_names/resolution.rb +15 -5
- data/lib/hecks/composer/toolbox.rb +2 -1
- data/lib/hecks/corpus.rb +4 -0
- data/lib/hecks/doc/reference/readme.rb +1 -1
- data/lib/hecks/framework_bluebooks.rb +3 -2
- data/lib/hecks/fuzzing/differential.rb +1 -1
- data/lib/hecks/fuzzing/era_boundary.rb +1 -1
- data/lib/hecks/fuzzing/isolated_boot/postgres_scratch.rb +1 -1
- data/lib/hecks/fuzzing/persistence_parity.rb +1 -1
- data/lib/hecks/fuzzing/target_capabilities.rb +1 -1
- data/lib/hecks/hecks/adapters/ci_runs.adapter +7 -0
- data/lib/hecks/hecks/adapters/ci_runs.rb +80 -0
- data/lib/hecks/hecks/adapters/claude_agent_list.rb +43 -0
- data/lib/hecks/hecks/adapters/claude_agents.rb +50 -19
- data/lib/hecks/hecks/adapters/claude_jobs.rb +36 -0
- data/lib/hecks/hecks/adapters/codebase/corpus_queries.rb +2 -1
- data/lib/hecks/hecks/adapters/codebase/minitest_runs.rb +4 -2
- data/lib/hecks/hecks/adapters/codebase/run_options.rb +2 -1
- data/lib/hecks/hecks/adapters/codebase/test_suite.rb +3 -19
- data/lib/hecks/hecks/adapters/dispatching/brief_text.rb +5 -3
- data/lib/hecks/hecks/adapters/dispatching/launcher.rb +2 -1
- data/lib/hecks/hecks/adapters/dispatching/plan.rb +8 -4
- data/lib/hecks/hecks/adapters/dispatching/settings.rb +2 -1
- data/lib/hecks/hecks/adapters/dispatching/settler.rb +4 -2
- data/lib/hecks/hecks/adapters/dispatching.rb +6 -3
- data/lib/hecks/hecks/adapters/doctor_reading.rb +4 -2
- data/lib/hecks/hecks/adapters/git_landing/carried.rb +42 -0
- data/lib/hecks/hecks/adapters/git_landing/finisher.rb +2 -1
- data/lib/hecks/hecks/adapters/git_landing/flagged.rb +32 -0
- data/lib/hecks/hecks/adapters/git_landing/tidy.rb +27 -14
- data/lib/hecks/hecks/adapters/git_landing.rb +30 -13
- data/lib/hecks/hecks/adapters/git_leftovers.adapter +7 -0
- data/lib/hecks/hecks/adapters/git_leftovers.rb +49 -0
- data/lib/hecks/hecks/adapters/git_main_pull.rb +2 -2
- data/lib/hecks/hecks/adapters/git_train.rb +9 -5
- data/lib/hecks/hecks/adapters/git_worktree_add.rb +8 -4
- data/lib/hecks/hecks/adapters/github_pull_requests.rb +2 -1
- data/lib/hecks/hecks/adapters/in_process_operations.rb +2 -1
- data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +1 -1
- data/lib/hecks/hecks/adapters/journal_store/readings/composition.rb +2 -1
- data/lib/hecks/hecks/adapters/journal_store/translation_write/shape_edge.rb +4 -2
- data/lib/hecks/hecks/adapters/journal_store/translation_write.rb +4 -2
- data/lib/hecks/hecks/adapters/landed_branches.rb +38 -0
- data/lib/hecks/hecks/adapters/main_boarding.adapter +7 -0
- data/lib/hecks/hecks/adapters/main_boarding.rb +85 -0
- data/lib/hecks/hecks/adapters/main_integration/checks.rb +63 -0
- data/lib/hecks/hecks/adapters/main_integration/command_runner.rb +32 -0
- data/lib/hecks/hecks/adapters/main_integration/range_guard.rb +49 -0
- data/lib/hecks/hecks/adapters/main_integration/repository.rb +83 -0
- data/lib/hecks/hecks/adapters/main_integration/runs.rb +51 -0
- data/lib/hecks/hecks/adapters/main_integration/train.rb +74 -0
- data/lib/hecks/hecks/adapters/main_integration.adapter +7 -0
- data/lib/hecks/hecks/adapters/main_integration.rb +77 -0
- data/lib/hecks/hecks/adapters/main_verification.adapter +7 -0
- data/lib/hecks/hecks/adapters/main_verification.rb +89 -0
- data/lib/hecks/hecks/adapters/push_recorder.adapter +7 -0
- data/lib/hecks/hecks/adapters/push_recorder.rb +78 -0
- data/lib/hecks/hecks/adapters/red_flag_guard.rb +62 -0
- data/lib/hecks/hecks/adapters/red_flag_marker.rb +101 -0
- data/lib/hecks/hecks/adapters/red_flag_report.rb +59 -0
- data/lib/hecks/hecks/adapters/red_flag_survey.rb +65 -0
- data/lib/hecks/hecks/adapters/red_flagging.adapter +7 -0
- data/lib/hecks/hecks/adapters/red_flagging.rb +98 -0
- data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +6 -1
- data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +6 -3
- data/lib/hecks/hecks/adapters/settings_reading/event_tap_row.rb +33 -0
- data/lib/hecks/hecks/adapters/settings_reading.rb +3 -1
- data/lib/hecks/hecks/codebase.behaviors +0 -6
- data/lib/hecks/hecks/custodian.behaviors +22 -1
- data/lib/hecks/hecks/delegate/delegate.bluebook +128 -15
- data/lib/hecks/hecks/deliver/deliver.bluebook +450 -2
- data/lib/hecks/hecks/environments/memory.world +5 -0
- data/lib/hecks/hecks/hecks.contextmap +8 -0
- data/lib/hecks/hecks/hecks.hecksagon +106 -6
- data/lib/hecks/hecks/hecks.world +9 -1
- data/lib/hecks/hecks/migrate/migrate.bluebook +6 -1
- data/lib/hecks/hecks/redflag/redflag.bluebook +249 -0
- data/lib/hecks/hecks/test/test.bluebook +0 -19
- data/lib/hecks/language/adapter.bluebook +13 -0
- data/lib/hecks/language/bluebook/vocabulary.bluebook +8 -9
- data/lib/hecks/language/sentence/sentence.bluebook +2 -0
- data/lib/hecks/pipeline/pipeline.bluebook +251 -0
- data/lib/hecks/pipeline/pipeline.hecksagon +17 -0
- data/lib/hecks/pipeline_board/adapters/cache.rb +43 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_fill.rb +56 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_git.rb +82 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_github/runs.rb +66 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_github.adapter +7 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_github.rb +70 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_jobs.adapter +7 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_jobs.rb +87 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_journal/chain.rb +40 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_journal/tap.rb +75 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_journal.adapter +7 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_journal.rb +83 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_stages/ci_tiles.rb +85 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_stages/fanout.rb +73 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_stages/gather.rb +57 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_stages/metrics.rb +36 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_stages/strip.rb +102 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_stages/tile_kit.rb +57 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_stages/work_tiles.rb +59 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_stages.adapter +7 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_stages.rb +84 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_tickets.adapter +7 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_tickets.rb +77 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_web/page.html +292 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_web/sequence.html +190 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_web/server.rb +105 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_web/snapshot.rb +57 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_web/stream.rb +59 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_web/wiring.html +536 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_web.adapter +7 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_web.rb +80 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_wiring/details.rb +85 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_wiring/graph.rb +120 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_wiring.adapter +7 -0
- data/lib/hecks/pipeline_board/adapters/pipeline_wiring.rb +36 -0
- data/lib/hecks/pipeline_board/adapters/probe.rb +48 -0
- data/lib/hecks/pipeline_board/pipeline_board.bluebook +144 -0
- data/lib/hecks/pipeline_board/pipeline_board.hecksagon +12 -0
- data/lib/hecks/pipeline_board/pipeline_board.ports.hecksagon +47 -0
- data/lib/hecks/ports/persistence/binding_policy.rb +2 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/compose.rb +4 -3
- data/lib/hecks/ports/persistence/repository_factory.rb +2 -1
- data/lib/hecks/projections/ci_pipeline/table.rb +2 -1
- data/lib/hecks/projections/deploy/lambda/stack/recipes.rb +2 -2
- data/lib/hecks/projections/undo.rb +2 -1
- data/lib/hecks/projector/cli_projector/command_help.rb +5 -4
- data/lib/hecks/projector/cli_projector/renames.rb +4 -2
- data/lib/hecks/projector/cli_projector/usage.rb +4 -3
- data/lib/hecks/projector/exporter/adapters.rb +3 -2
- data/lib/hecks/projector/narrate_projector/use_cases.rb +74 -0
- data/lib/hecks/projector/narrate_projector.rb +10 -3
- data/lib/hecks/quality_control/adapters/main_maintenance.rb +9 -5
- data/lib/hecks/quality_control/adapters/qa_auto_mint.rb +21 -2
- data/lib/hecks/quality_control/cli/qa_concurrency_racer.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +2 -1
- data/lib/hecks/quality_control/cli/qa_fix_main_bug/publishing.rb +8 -4
- data/lib/hecks/quality_control/cli/qa_fix_main_bug.rb +10 -8
- data/lib/hecks/quality_control/cli/qa_generated_domains/promotion.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_main_gate/gate_run.rb +6 -3
- data/lib/hecks/quality_control/cli/qa_main_gate/logging.rb +8 -4
- data/lib/hecks/quality_control/cli/qa_main_gate/shell.rb +4 -2
- data/lib/hecks/quality_control/cli/qa_main_gate/sweeping.rb +8 -4
- data/lib/hecks/quality_control/cli/qa_main_gate/worktree.rb +3 -2
- data/lib/hecks/quality_control/cli/qa_main_gate.rb +10 -5
- data/lib/hecks/quality_control/cli/qa_sweep/all_mode/children.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_sweep/checks.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_sweep.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_tick.rb +6 -3
- data/lib/hecks/quality_control/quality_control.bluebook +11 -11
- data/lib/hecks/quality_control/quality_control.ports.hecksagon +6 -3
- data/lib/hecks/query_ir/touchpoints.rb +2 -1
- data/lib/hecks/refused.rb +2 -1
- data/lib/hecks/release/approval.rb +4 -2
- data/lib/hecks/runtime/event_log.rb +27 -0
- data/lib/hecks/runtime/event_tap.rb +106 -0
- data/lib/hecks/runtime/loader/boot_steps.rb +1 -1
- data/lib/hecks/runtime/loader.rb +35 -19
- data/lib/hecks/runtime/registry/durability_warnings.rb +12 -5
- data/lib/hecks/runtime/registry/runtime_state.rb +4 -1
- data/lib/hecks/runtime/registry/saga_anchor.rb +54 -0
- data/lib/hecks/runtime/registry/saga_durability_rule.rb +66 -0
- data/lib/hecks/runtime/registry/saga_persistence.rb +58 -12
- data/lib/hecks/runtime/registry/verification.rb +7 -3
- data/lib/hecks/runtime/saga_interpreter/lifecycle.rb +2 -1
- data/lib/hecks/runtime/saga_interpreter.rb +1 -1
- data/lib/hecks/rust_format.rb +2 -1
- data/lib/hecks/shape_ledger/edge_hygiene.rb +10 -5
- data/lib/hecks/shape_ledger.rb +1 -1
- data/lib/hecks/storehouse/audit.rb +2 -1
- data/lib/hecks/storehouse/identity.rb +6 -3
- data/lib/hecks/storehouse.rb +2 -1
- data/lib/hecks/tickets/bluebook/tickets.bluebook +5 -4
- data/lib/hecks/tools/ci_workflows.rb +2 -1
- data/lib/hecks/tools/document.rb +12 -1
- data/lib/hecks/tools/downstream_model_check.rb +4 -2
- data/lib/hecks/tools/fuzz_sweep/shards.rb +9 -30
- data/lib/hecks/tools/model_check/examination.rb +25 -2
- data/lib/hecks/tools/move_disables.rb +4 -2
- data/lib/hecks/tools/regeneration_run/scratch.rb +2 -1
- data/lib/hecks/tools/rspec_shard_files.rb +12 -15
- data/lib/hecks/tools/sequence_script.rb +1 -1
- data/lib/hecks/tools/shard_planner.rb +65 -0
- data/lib/hecks/tools/stage_progress.rb +2 -1
- data/lib/hecks/tools/stress_concurrency_specs.rb +2 -1
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +8 -9
- data/lib/hecks.rb +5 -2
- data/prebuilt/{13c04bdc5970295b310f0a980d397ba722232c8da99cc1a0cdb383249921e871.marshal → 4b81646f769ae5b1e7457f9b11c48d8bc1093e96a1dd6fb61864065121ca6f01.marshal} +0 -0
- data/prebuilt/{e3857b77f47d9ecc1787b2e1f5136364f3cb3444ef4aea5479f3b98167affea2.marshal → 681890632e3f4a99646623dcae44af36b1bad66c515f5abf67a2460372b9c6fd.marshal} +0 -0
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/parser/src/emit.rs +5 -1
- data/rust/parser/src/ir.rs +2 -0
- data/rust/parser/src/keywords.rs +12 -0
- data/rust/parser/src/main.rs +1 -0
- data/rust/parser/src/parse/adapter.rs +1 -0
- data/rust/parser/src/phrases.rs +16 -0
- data/rust/parser/src/sentence/examples.rs +93 -0
- data/rust/parser/src/sentence/mod.rs +1 -0
- metadata +89 -5
- data/lib/hecks/tools/refresh_rspec_runtime_baseline.rb +0 -166
|
@@ -5,7 +5,8 @@ require "json"
|
|
|
5
5
|
module Hecks
|
|
6
6
|
module ShapeLedger
|
|
7
7
|
# Finds the translation edges that no longer earn their file; it deletes nothing. The graph is
|
|
8
|
-
# a bluebook's edge files plus its recorded implicit moves, aimed at the current shape. A file
|
|
8
|
+
# a bluebook's edge files plus its recorded implicit moves, aimed at the current shape. A file
|
|
9
|
+
# is:
|
|
9
10
|
#
|
|
10
11
|
# - a **self-loop** when its `from` equals its `to`.
|
|
11
12
|
# - a **fork** branch when two edges leave a label and both reach the current shape; the branch
|
|
@@ -19,11 +20,13 @@ module Hecks
|
|
|
19
20
|
# One edge: the file that declares it (nil for a recorded implicit move) and its two labels.
|
|
20
21
|
Edge = Struct.new(:path, :from, :to)
|
|
21
22
|
|
|
22
|
-
# The list beside the ledger of stale edges already known, each a finding key. A listed edge
|
|
23
|
+
# The list beside the ledger of stale edges already known, each a finding key. A listed edge
|
|
24
|
+
# is
|
|
23
25
|
# reported as a warning and does not fail the check; any other stale edge fails it.
|
|
24
26
|
KNOWN_FILE = "known_stale_edges.json"
|
|
25
27
|
|
|
26
|
-
# One problem: its kind, the bluebook, the file to remove, a stable key naming the edge, and
|
|
28
|
+
# One problem: its kind, the bluebook, the file to remove, a stable key naming the edge, and
|
|
29
|
+
# what
|
|
27
30
|
# to do.
|
|
28
31
|
Finding = Struct.new(:kind, :domain, :path, :key, :message, keyword_init: true)
|
|
29
32
|
|
|
@@ -42,7 +45,8 @@ module Hecks
|
|
|
42
45
|
listed.map { |finding| "\nwarning, known stale edge (#{KNOWN_FILE}) #{lines([finding]).first}" }.join
|
|
43
46
|
end
|
|
44
47
|
|
|
45
|
-
# @return [Array<String>] the finding keys of the known stale edges, empty when there is no
|
|
48
|
+
# @return [Array<String>] the finding keys of the known stale edges, empty when there is no
|
|
49
|
+
# list
|
|
46
50
|
def known_stale(path) = File.exist?(path) ? JSON.parse(File.read(path)) : []
|
|
47
51
|
|
|
48
52
|
# @param directory [String] the domain directory holding `translations/`
|
|
@@ -144,7 +148,8 @@ module Hecks
|
|
|
144
148
|
|
|
145
149
|
def leads_to?(label, current, graph) = label == current || reached_from(label, graph).include?(current)
|
|
146
150
|
|
|
147
|
-
# @return [Array<String>] every label a chain of edges leads to from one label, itself
|
|
151
|
+
# @return [Array<String>] every label a chain of edges leads to from one label, itself
|
|
152
|
+
# included
|
|
148
153
|
def reached_from(label, graph)
|
|
149
154
|
seen = [label]
|
|
150
155
|
seen.each { |at| graph.each { |edge| seen << edge.to if edge.from == at && !seen.include?(edge.to) } }
|
data/lib/hecks/shape_ledger.rb
CHANGED
|
@@ -130,7 +130,7 @@ module Hecks
|
|
|
130
130
|
"#{name} moved with no edge. A change that only adds needs no edge file: settle it in one step " \
|
|
131
131
|
"with `hecks conformance_run.check_shape_ledger! --confirm --additive --wait`, which the boot and " \
|
|
132
132
|
"the mint treat as an implicit empty edge from #{from} to #{now}. Data that is renamed or reshaped " \
|
|
133
|
-
"needs rules: `hecks
|
|
133
|
+
"needs rules: `hecks era.scaffold_translation domain.value=<directory>` writes them, reading " \
|
|
134
134
|
"the eras from the database the domain's own world names."
|
|
135
135
|
end
|
|
136
136
|
|
|
@@ -13,7 +13,8 @@ module Hecks
|
|
|
13
13
|
# CamelCase word takes an underscore.
|
|
14
14
|
CAMEL_CASE_BOUNDARY = /([a-z\d])([A-Z])/
|
|
15
15
|
|
|
16
|
-
# The last segment of a qualified constant name, such as `TypeMismatch` in
|
|
16
|
+
# The last segment of a qualified constant name, such as `TypeMismatch` in
|
|
17
|
+
# `Runtime::TypeMismatch`.
|
|
17
18
|
UNQUALIFIED_NAME = /[^:]+\z/
|
|
18
19
|
|
|
19
20
|
# Kept under Hecks::CacheDir, not the domain directory (part of the domain's
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Storehouse
|
|
3
|
-
# The ids a call used, named in its result: the record it addressed, and the references its
|
|
3
|
+
# The ids a call used, named in its result: the record it addressed, and the references its
|
|
4
|
+
# arguments
|
|
4
5
|
# named. Extended onto `Storehouse`.
|
|
5
6
|
module Identity
|
|
6
|
-
# The arguments that name an id a call used: a run key, an earlier run it refers to, and the
|
|
7
|
+
# The arguments that name an id a call used: a run key, an earlier run it refers to, and the
|
|
8
|
+
# checkout
|
|
7
9
|
# and branch it resolved to.
|
|
8
10
|
IDENTITY_KEYS = %i[run from checkout branch].freeze
|
|
9
11
|
|
|
10
|
-
# The ids a call used: the record it addressed, and the references its arguments named, each
|
|
12
|
+
# The ids a call used: the record it addressed, and the references its arguments named, each
|
|
13
|
+
# unwrapped
|
|
11
14
|
# to its value. Absent ones are left out.
|
|
12
15
|
# :nodoc:
|
|
13
16
|
def ids_of(request, outcome)
|
data/lib/hecks/storehouse.rb
CHANGED
|
@@ -29,7 +29,8 @@ module Hecks
|
|
|
29
29
|
SOURCE_TAGS = %w[process-manager operator hook sidequest-agent cascade daemon].freeze
|
|
30
30
|
|
|
31
31
|
# Which surface a call came in through, so the audit line says what made it: a person at a shell
|
|
32
|
-
# (`cli`), an agent over the MCP server (`mcp`), or the domain itself (`policy`, `saga`,
|
|
32
|
+
# (`cli`), an agent over the MCP server (`mcp`), or the domain itself (`policy`, `saga`,
|
|
33
|
+
# `reaction`).
|
|
33
34
|
SURFACES = %w[cli mcp policy saga reaction].freeze
|
|
34
35
|
|
|
35
36
|
# The root every domain path resolves under; HECKS_STOREHOUSE_ROOT widens it.
|
|
@@ -387,11 +387,12 @@ Hecks.bluebook "Tickets" do
|
|
|
387
387
|
emits "WorkStarted"
|
|
388
388
|
end
|
|
389
389
|
|
|
390
|
-
#
|
|
391
|
-
#
|
|
392
|
-
#
|
|
390
|
+
# A started step that may not go on goes back to the plan with the refusal as its reason: its
|
|
391
|
+
# area overlapped another started step's, it would start more steps than the cap allows (the
|
|
392
|
+
# WorkAreas or WorkLimits port refuses it by name), or the architect's review of its plan said
|
|
393
|
+
# rethink and the coder session handed it back.
|
|
393
394
|
command "Yield" do
|
|
394
|
-
goal "Return a step
|
|
395
|
+
goal "Return a started step to the plan, with the refusal that sent it back"
|
|
395
396
|
|
|
396
397
|
reference_to Work
|
|
397
398
|
attribute :reason, WorkText, sets: true
|
|
@@ -16,7 +16,8 @@ module Hecks
|
|
|
16
16
|
# A workflow with no row is hand-written and left alone. With `--check` nothing is written: the
|
|
17
17
|
# tool answers 1 and names each file that differs from its rows.
|
|
18
18
|
module CiWorkflows
|
|
19
|
-
# The directory of the bluebook that declares the rows, which attaches the CiPipeline
|
|
19
|
+
# The directory of the bluebook that declares the rows, which attaches the CiPipeline
|
|
20
|
+
# bluebook.
|
|
20
21
|
BLUEBOOK_DIRECTORY = File.expand_path("../ci_workflows", __dir__)
|
|
21
22
|
|
|
22
23
|
# The directory `ports/` and `adapters/` live under, loaded before the rows are.
|
data/lib/hecks/tools/document.rb
CHANGED
|
@@ -32,12 +32,23 @@ module Hecks
|
|
|
32
32
|
|
|
33
33
|
bluebook = boot_bluebook(path)
|
|
34
34
|
begin
|
|
35
|
-
puts Projector.call(projection, bluebook: bluebook, options:
|
|
35
|
+
puts Projector.call(projection, bluebook: bluebook, options: options_for(projection, path, aggregate))
|
|
36
36
|
rescue Runtime::NotFound => e
|
|
37
37
|
abort e.message
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
+
# The projection's options: the aggregate it narrows to, and for `:narrate` the `.examples`
|
|
42
|
+
# files under the domain, whose goals it tells as use cases (ADR 0173).
|
|
43
|
+
#
|
|
44
|
+
# @api private
|
|
45
|
+
def options_for(projection, path, aggregate)
|
|
46
|
+
options = aggregate ? { aggregate: aggregate } : {}
|
|
47
|
+
return options unless projection == :narrate
|
|
48
|
+
|
|
49
|
+
options.merge(examples: Dir.glob(File.join(path, "**", "*.examples")))
|
|
50
|
+
end
|
|
51
|
+
|
|
41
52
|
# The domain's own bluebook, not a framework member it attached: insertion
|
|
42
53
|
# order, the same distinction `Runtime::Loader.dispatcher_for` draws.
|
|
43
54
|
#
|
|
@@ -4,7 +4,8 @@ require "fileutils"
|
|
|
4
4
|
|
|
5
5
|
module Hecks
|
|
6
6
|
module Tools
|
|
7
|
-
# The model check a domain repository runs in its CI, generated from Hecks' own definition of
|
|
7
|
+
# The model check a domain repository runs in its CI, generated from Hecks' own definition of
|
|
8
|
+
# it:
|
|
8
9
|
# the client profile, over the domain's bluebooks, from the hecks the Gemfile pins.
|
|
9
10
|
#
|
|
10
11
|
# A downstream repository commits the file this writes (`.github/workflows/model-check.yml`) and
|
|
@@ -80,7 +81,8 @@ module Hecks
|
|
|
80
81
|
YAML
|
|
81
82
|
end
|
|
82
83
|
|
|
83
|
-
# Writes the workflow under `root`, or with `check` compares it and reports whether it is
|
|
84
|
+
# Writes the workflow under `root`, or with `check` compares it and reports whether it is
|
|
85
|
+
# current.
|
|
84
86
|
#
|
|
85
87
|
# @param root [String] the checkout
|
|
86
88
|
# @param domain_directory [String] see {workflow}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../../tools"
|
|
4
|
+
require_relative "../shard_planner"
|
|
4
5
|
|
|
5
6
|
module Hecks
|
|
6
7
|
module Tools
|
|
@@ -44,23 +45,8 @@ module Hecks
|
|
|
44
45
|
# @param costs [Hash{String => Float}] each domain directory's cost, perhaps lacking some
|
|
45
46
|
# @return [Array<Array<String>>] the domains of each shard, each in the order `domains` has
|
|
46
47
|
def partition(domains, shards, costs = {})
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
deal(order, price, shards).map { |indexes| indexes.sort.map { |index| domains[index] } }
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# @param order [Array<Integer>] the domain positions, in the order to deal them
|
|
53
|
-
# @param price [Array<Float>] each position's cost
|
|
54
|
-
# @param shards [Integer] how many shards to deal into
|
|
55
|
-
# @return [Array<Array<Integer>>] the positions each shard took, each to the lightest shard
|
|
56
|
-
# so far, the lowest numbered when they tie
|
|
57
|
-
def deal(order, price, shards)
|
|
58
|
-
loads = Array.new(shards, 0.0)
|
|
59
|
-
order.each_with_object(Array.new(shards) { [] }) do |index, taken|
|
|
60
|
-
lightest = loads.each_index.min_by { |shard| [loads[shard], shard] }
|
|
61
|
-
loads[lightest] += price[index]
|
|
62
|
-
taken[lightest] << index
|
|
63
|
-
end
|
|
48
|
+
dealt = ShardPlanner.partition(domains, shards, price_table(domains, costs))
|
|
49
|
+
dealt.map { |members| domains.select { |domain| members.include?(domain) } }
|
|
64
50
|
end
|
|
65
51
|
|
|
66
52
|
# @param domains [Array<String>] the domain directories
|
|
@@ -84,7 +70,7 @@ module Hecks
|
|
|
84
70
|
split = partition(domains, shards, costs)
|
|
85
71
|
raise NotAPartition, "the shards are not a partition of the domains" unless split.flatten.sort == domains.sort
|
|
86
72
|
|
|
87
|
-
price =
|
|
73
|
+
price = price_table(domains, costs)
|
|
88
74
|
split.each_with_index.flat_map do |members, index|
|
|
89
75
|
members.map { |domain| row("#{index + 1}/#{shards}", price[domain], domain.delete_prefix("#{root}/")) }
|
|
90
76
|
end
|
|
@@ -112,7 +98,7 @@ module Hecks
|
|
|
112
98
|
# @param costs [Hash{String => Float}] each domain directory's cost
|
|
113
99
|
# @return [Array<String>] the same domains, dearest first, ties in path order
|
|
114
100
|
def dearest_first(domains, costs)
|
|
115
|
-
price =
|
|
101
|
+
price = price_table(domains, costs)
|
|
116
102
|
domains.sort_by { |domain| [-price[domain], domain] }
|
|
117
103
|
end
|
|
118
104
|
|
|
@@ -137,18 +123,11 @@ module Hecks
|
|
|
137
123
|
.to_h { |line| line.split(":", 2).then { |dir, seconds| [File.join(root, dir), Float(seconds)] } }
|
|
138
124
|
end
|
|
139
125
|
|
|
140
|
-
# @return [
|
|
141
|
-
def prices(domains, costs)
|
|
142
|
-
fallback = median(costs.values) || FLAT_COST
|
|
143
|
-
domains.map { |domain| costs.fetch(domain, fallback) }
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
def median(values)
|
|
147
|
-
return nil if values.empty?
|
|
126
|
+
# @return [Hash{String => Float}] each domain's cost, a missing one at the table's median
|
|
127
|
+
def price_table(domains, costs) = ShardPlanner.prices(domains, costs, ->(_domain) { FLAT_COST })
|
|
148
128
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
end
|
|
129
|
+
# @return [Array<Float>] each domain's cost, in the order of `domains`
|
|
130
|
+
def prices(domains, costs) = price_table(domains, costs).values_at(*domains)
|
|
152
131
|
end
|
|
153
132
|
end
|
|
154
133
|
end
|
|
@@ -40,7 +40,8 @@ module Hecks
|
|
|
40
40
|
edges = booted.flat_map do |_, registry|
|
|
41
41
|
Projections::ContextRelationships.call(registry: registry, owners: owners)
|
|
42
42
|
end
|
|
43
|
-
findings = Bluebook::ModelCheck.context_cycle_findings(edges.uniq) +
|
|
43
|
+
findings = Bluebook::ModelCheck.context_cycle_findings(edges.uniq) +
|
|
44
|
+
classification_findings(booted, owners) + context_map_findings(booted, owners)
|
|
44
45
|
return true if findings.empty?
|
|
45
46
|
|
|
46
47
|
puts "── contexts"
|
|
@@ -56,11 +57,33 @@ module Hecks
|
|
|
56
57
|
booted.flat_map do |_, registry|
|
|
57
58
|
registry.context_maps.flat_map do |map|
|
|
58
59
|
edges = Projections::ContextRelationships.call(registry: registry, owners: owners)
|
|
59
|
-
Bluebook::ModelCheck.context_map_findings(map, edges)
|
|
60
|
+
Bluebook::ModelCheck.context_map_findings(map, edges) +
|
|
61
|
+
Bluebook::ModelCheck.core_conforms_findings(map, classifications_of(registry))
|
|
60
62
|
end
|
|
61
63
|
end
|
|
62
64
|
end
|
|
63
65
|
|
|
66
|
+
# What each booted system's classifications say about its own wiring (ADR 0172). Each system
|
|
67
|
+
# is read alone: two systems may each declare a context of the same name.
|
|
68
|
+
#
|
|
69
|
+
# @api private
|
|
70
|
+
# @return [Array<Bluebook::ModelCheck::Finding>]
|
|
71
|
+
def classification_findings(booted, owners)
|
|
72
|
+
booted.flat_map do |_, registry|
|
|
73
|
+
edges = Projections::ContextRelationships.call(registry: registry, owners: owners)
|
|
74
|
+
Bluebook::ModelCheck.generic_depends_on_core_findings(edges, classifications_of(registry))
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Each context's classification (`core`, `supporting`, `generic`), as its bluebook declares it.
|
|
79
|
+
#
|
|
80
|
+
# @api private
|
|
81
|
+
# @return [Hash{String => String}] only the contexts that declare one
|
|
82
|
+
def classifications_of(registry)
|
|
83
|
+
classified = registry.bluebooks.values.select(&:classification)
|
|
84
|
+
classified.to_h { |declaration| [declaration.name.to_s, declaration.classification.to_s] }
|
|
85
|
+
end
|
|
86
|
+
|
|
64
87
|
# @api private
|
|
65
88
|
def emitted_events_of(booted)
|
|
66
89
|
booted.flat_map do |_, registry|
|
|
@@ -121,7 +121,8 @@ module Hecks
|
|
|
121
121
|
# Removes the directives from one file's lines.
|
|
122
122
|
#
|
|
123
123
|
# @param lines [Array<String>] the file's lines, without their newlines
|
|
124
|
-
# @return [Array(Array<String>, String)] the cops the directives named, and the file's new
|
|
124
|
+
# @return [Array(Array<String>, String)] the cops the directives named, and the file's new
|
|
125
|
+
# text
|
|
125
126
|
def strip(lines)
|
|
126
127
|
touched = []
|
|
127
128
|
out = lines.filter_map { |line| keep_line(line, touched) }
|
|
@@ -190,7 +191,8 @@ module Hecks
|
|
|
190
191
|
|
|
191
192
|
# @param cop [String] a cop name
|
|
192
193
|
# @param paths [Array<String>] the `Exclude` entries
|
|
193
|
-
# @return [String] the cop's header, its reason, and its `Exclude`, to replace an existing
|
|
194
|
+
# @return [String] the cop's header, its reason, and its `Exclude`, to replace an existing
|
|
195
|
+
# header
|
|
194
196
|
def merged_block(cop, paths) = ["#{cop}:", " # #{REASONS.fetch(cop)}", " Exclude:", *paths].join("\n")
|
|
195
197
|
|
|
196
198
|
# @param cop [String] a cop name
|
|
@@ -32,7 +32,8 @@ module Hecks
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
# Puts back, in the scratch crate, every module the named domains do not own, as the tree
|
|
35
|
-
# has it: a run of some domains rewrites the shared bluebooks and the grammar as its own
|
|
35
|
+
# has it: a run of some domains rewrites the shared bluebooks and the grammar as its own
|
|
36
|
+
# last
|
|
36
37
|
# writer would, which is not what the whole corpus's last writer left in the tree.
|
|
37
38
|
#
|
|
38
39
|
# @param scratch [String] the scratch crate
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require_relative "shard_planner"
|
|
4
4
|
|
|
5
5
|
module Hecks
|
|
6
6
|
module Tools
|
|
7
7
|
# The command behind `hecks shard_specs`: prints the spec files assigned to one group
|
|
8
|
-
# (1-indexed) of
|
|
9
|
-
#
|
|
8
|
+
# (1-indexed) of an N-way split by the shared `ShardPlanner`. It is the split a leg of
|
|
9
|
+
# `rspec_shard` in ci-rspec.yml falls back on when the plan job's plan cannot be read; with no
|
|
10
|
+
# runtime log it balances by file size, which every leg computes alike.
|
|
10
11
|
#
|
|
11
12
|
# It does not use `parallel_rspec --only-group`, which forces one process per leg; this suite
|
|
12
|
-
# is CPU-bound, so each leg would use one of four vCPUs. The group's list comes from
|
|
13
|
-
#
|
|
13
|
+
# is CPU-bound, so each leg would use one of four vCPUs. The group's list comes from the
|
|
14
|
+
# planner, leaving `parallel_rspec` its own workers.
|
|
14
15
|
module RspecShardFiles
|
|
15
16
|
USAGE = "usage: hecks shard_specs <group 1-indexed> <num_groups> [runtime_log_path]"
|
|
16
17
|
|
|
@@ -60,21 +61,17 @@ module Hecks
|
|
|
60
61
|
"refusing to hand parallel_rspec nothing to run"
|
|
61
62
|
end
|
|
62
63
|
|
|
63
|
-
# Splits the files by
|
|
64
|
-
#
|
|
65
|
-
# `allowed_missing_percent` covers gaps inside a log, not a missing file (`Runner.runtimes`
|
|
66
|
-
# raises `Errno::ENOENT`).
|
|
64
|
+
# Splits the files by the seconds in the runtime log when one is given and exists, and by file
|
|
65
|
+
# size otherwise.
|
|
67
66
|
#
|
|
68
67
|
# @param files [Array<String>] the spec files, relative to the checkout
|
|
69
68
|
# @param num_groups [Integer] how many groups to split into
|
|
70
|
-
# @param runtime_log [String, nil]
|
|
69
|
+
# @param runtime_log [String, nil] a log of `path:seconds` lines, used when the file exists
|
|
71
70
|
# @return [Array<Array<String>>] the files of each group
|
|
72
71
|
def groups_of(files, num_groups, runtime_log)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
rescue Errno::ENOENT
|
|
77
|
-
ParallelTests::Test::Runner.tests_in_groups(files, num_groups, group_by: :filesize)
|
|
72
|
+
table = runtime_log && File.exist?(runtime_log) ? ShardPlanner.parse(File.read(runtime_log)) : {}
|
|
73
|
+
prices = ShardPlanner.prices(files, table, ->(file) { File.size(file) })
|
|
74
|
+
ShardPlanner.partition(files, num_groups, prices)
|
|
78
75
|
end
|
|
79
76
|
end
|
|
80
77
|
end
|
|
@@ -10,7 +10,7 @@ module Hecks
|
|
|
10
10
|
#
|
|
11
11
|
# hecks generate_sequence <domain> [--seed N] [--steps N] [--adversarial fraction]
|
|
12
12
|
#
|
|
13
|
-
# `--adversarial` mutates the sequence as `hecks quality_control
|
|
13
|
+
# `--adversarial` mutates the sequence as `hecks quality_control sweep.run` does; 0 (off)
|
|
14
14
|
# by default.
|
|
15
15
|
module SequenceScript
|
|
16
16
|
USAGE = "usage: hecks generate_sequence <domain> [--seed N] [--steps N] [--adversarial FRACTION]"
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Tools
|
|
5
|
+
# The longest-processing-time split every sharded CI family shares: spec files of the rspec and
|
|
6
|
+
# postgres io jobs, and the domains of a fuzz sweep. It is a pure function of the items, the
|
|
7
|
+
# shard count and the prices, so the same input gives the same partition.
|
|
8
|
+
#
|
|
9
|
+
# A price is a recorded run time when the table holds one for the item, the table's median when
|
|
10
|
+
# it holds others but not this one, and the item's size when there is no table at all.
|
|
11
|
+
module ShardPlanner
|
|
12
|
+
# One `key:seconds` line; seconds can arrive in exponent form (`9.69e-05`). Anything else,
|
|
13
|
+
# such as RSpec's "Run options" line, is not a timing.
|
|
14
|
+
LINE = /\A(?<key>\S+):(?<seconds>\d+(?:\.\d+)?(?:e-?\d+)?)\z/
|
|
15
|
+
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
# @param text [String] lines of `key:seconds`
|
|
19
|
+
# @return [Hash{String => Float}] the seconds of each key; a malformed line is skipped
|
|
20
|
+
def parse(text)
|
|
21
|
+
lines = text.to_s.lines.map(&:strip)
|
|
22
|
+
timed = lines.filter_map { |line| LINE.match(line) }
|
|
23
|
+
timed.to_h { |match| [match[:key], Float(match[:seconds])] }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @param texts [Array<String>] several shards' timing logs
|
|
27
|
+
# @return [Hash{String => Float}] one table; a key in two logs keeps the later seconds
|
|
28
|
+
def merge(texts) = texts.reduce({}) { |table, text| table.merge(parse(text)) }
|
|
29
|
+
|
|
30
|
+
# @param items [Array<String>] what is to be split
|
|
31
|
+
# @param table [Hash{String => Float}] recorded seconds, perhaps empty or lacking some items
|
|
32
|
+
# @param sizes [#call] answers an item's size in bytes, used only when the table is empty
|
|
33
|
+
# @return [Hash{String => Float}] each item's price
|
|
34
|
+
def prices(items, table, sizes)
|
|
35
|
+
return items.to_h { |item| [item, sizes.call(item).to_f] } if table.empty?
|
|
36
|
+
|
|
37
|
+
fallback = median(table.values)
|
|
38
|
+
items.to_h { |item| [item, table.fetch(item, fallback)] }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @param items [Array<String>] what is to be split, each unique
|
|
42
|
+
# @param groups [Integer] how many groups to deal into
|
|
43
|
+
# @param prices [Hash{String => Float}] each item's price
|
|
44
|
+
# @return [Array<Array<String>>] the items of each group, dearest first, each to the lightest
|
|
45
|
+
# group so far, the lowest numbered when they tie
|
|
46
|
+
def partition(items, groups, prices)
|
|
47
|
+
loads = Array.new(groups, 0.0)
|
|
48
|
+
dealt = Array.new(groups) { [] }
|
|
49
|
+
items.sort_by { |item| [-prices.fetch(item), item] }.each do |item|
|
|
50
|
+
lightest = loads.each_index.min_by { |group| [loads[group], group] }
|
|
51
|
+
loads[lightest] += prices.fetch(item)
|
|
52
|
+
dealt[lightest] << item
|
|
53
|
+
end
|
|
54
|
+
dealt
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @param values [Array<Float>] at least one
|
|
58
|
+
# @return [Float] the middle value
|
|
59
|
+
def median(values)
|
|
60
|
+
ranked = values.sort
|
|
61
|
+
(ranked[(ranked.size - 1) / 2] + ranked[ranked.size / 2]) / 2.0
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -10,7 +10,8 @@ module Hecks
|
|
|
10
10
|
# standard output, so a pipe still receives only the result.
|
|
11
11
|
class StageProgress
|
|
12
12
|
class << self
|
|
13
|
-
# Whether the lines are left out, as `-q` asks: set by the launcher for the length of a
|
|
13
|
+
# Whether the lines are left out, as `-q` asks: set by the launcher for the length of a
|
|
14
|
+
# line.
|
|
14
15
|
# @return [Boolean]
|
|
15
16
|
attr_accessor :quiet
|
|
16
17
|
end
|
|
@@ -8,7 +8,8 @@ require_relative "stress_concurrency_specs/reporting"
|
|
|
8
8
|
|
|
9
9
|
module Hecks
|
|
10
10
|
module Tools
|
|
11
|
-
# The command behind `hecks test_suite_run.stress_concurrency!`: reruns the thread-safety specs
|
|
11
|
+
# The command behind `hecks test_suite_run.stress_concurrency!`: reruns the thread-safety specs
|
|
12
|
+
# under many
|
|
12
13
|
# seeds, several OS processes at a time. A clean run shows only "not in this many tries", never
|
|
13
14
|
# "impossible".
|
|
14
15
|
#
|
data/lib/hecks/version.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
# Read directly by the gemspec, so this file must never require the
|
|
3
3
|
# rest of the gem (its dependencies aren't resolved yet at that point).
|
|
4
|
-
VERSION = "7.
|
|
4
|
+
VERSION = "7.1.0".freeze # rust/host/HECKS_RELEASE names the same release
|
|
5
5
|
end
|
data/lib/hecks/vocabulary.rb
CHANGED
|
@@ -115,7 +115,7 @@ module Hecks
|
|
|
115
115
|
{"name"=>"textarea", "pattern"=>"\\b(text|body|note|notes|description|message|comment)\\b", "resolves_to"=>"kind"}.freeze
|
|
116
116
|
].freeze,
|
|
117
117
|
"GateCheck" => [
|
|
118
|
-
{"name"=>"rspec", "id"=>"rspec", "title"=>"rspec (parallel)", "runs_in"=>"ci", "argv"=>"", "script"=>"# Workers are grouped by this machine's own last timings, kept under the git directory\n# (shared by every worktree, never committed)
|
|
118
|
+
{"name"=>"rspec", "id"=>"rspec", "title"=>"rspec (parallel)", "runs_in"=>"ci", "argv"=>"", "script"=>"# Workers are grouped by this machine's own last timings, kept under the git directory\n# (shared by every worktree, never committed); the first run, with none, groups by file size and\n# writes the log.\nlog=\"$(git rev-parse --git-common-dir)/hecks_rspec_runtime.log\"\nif [ -f \"$log\" ]; then grouping=(--group-by runtime --runtime-log \"$log\" --allowed-missing 100); else grouping=(--group-by filesize); fi\nbundle exec parallel_rspec spec -n {workers} \"${grouping[@]}\" \\\n -o \"--format progress --format ParallelTests::RSpec::RuntimeLogger --out $log\"\n", "blocked"=>"the suite is red."}.freeze,
|
|
119
119
|
{"name"=>"fuzzing", "id"=>"fuzzing", "title"=>"spec/fuzzing, which the parallel run above excludes", "argv"=>"bundle exec rspec spec/fuzzing --tag fuzzing", "script"=>"", "blocked"=>"spec/fuzzing is red."}.freeze,
|
|
120
120
|
{"name"=>"query_agreement", "id"=>"query_agreement", "title"=>"adapter agreement, one declared query and every engine giving the same answer", "argv"=>"bundle exec rspec spec/adapters/query_agreement_spec.rb --tag io", "script"=>"", "blocked"=>"the engines disagree about a declared query; one of them is wrong, and a self-consistent adapter spec cannot tell which."}.freeze,
|
|
121
121
|
{"name"=>"engine_agreement", "id"=>"engine_agreement", "title"=>"engine agreement, every declared comparator on both engines", "argv"=>"bundle exec exe/hecks conformance_run.check_engine_agreement! --wait", "script"=>"", "blocked"=>"a comparator is missing a shared case or a cross-engine spec."}.freeze,
|
|
@@ -149,8 +149,8 @@ module Hecks
|
|
|
149
149
|
{"name"=>"ci_gates", "id"=>"ci_gates", "title"=>"ci gates, the CiGate rows must match the marked regions of the workflows", "argv"=>"bundle exec exe/hecks regeneration_run.project_ci_gates! --wait", "script"=>"", "blocked"=>"a CiGate row changed with no projection, or a region was edited by hand; run `hecks regeneration_run.project_ci_gates! --confirm`."}.freeze,
|
|
150
150
|
{"name"=>"ci_pipeline", "id"=>"ci_pipeline", "title"=>"ci pipeline, the workflow rows must match the files they project", "argv"=>"bundle exec exe/hecks regeneration_run.project_ci_pipeline! --wait", "script"=>"", "blocked"=>"a workflow row changed with no projection, or a projected workflow was edited by hand; run `hecks regeneration_run.project_ci_pipeline! --confirm`."}.freeze,
|
|
151
151
|
{"name"=>"tools_doc", "id"=>"tools_doc", "title"=>"tools doc, docs/tools.md must match the RetiredScript rows", "argv"=>"bundle exec exe/hecks regeneration_run.project_tools_doc! --wait", "script"=>"", "blocked"=>"a command's arguments changed with no regenerated doc; run `hecks regeneration_run.project_tools_doc! --confirm`."}.freeze,
|
|
152
|
-
{"name"=>"rust_coverage", "id"=>"rust_coverage", "title"=>"rust coverage, every generated module", "argv"=>"bundle exec exe/hecks
|
|
153
|
-
{"name"=>"rust_coverage_codegen", "id"=>"rust_coverage_codegen", "title"=>"rust coverage against hecks-codegen's own manifest, pizzas governance identity banking meta", "argv"=>"", "script"=>"for module in pizzas governance identity banking meta; do\n bundle exec exe/hecks
|
|
152
|
+
{"name"=>"rust_coverage", "id"=>"rust_coverage", "title"=>"rust coverage, every generated module", "argv"=>"bundle exec exe/hecks corpus_run.corpus_rust_coverage --wait", "script"=>"", "blocked"=>"a construct a domain declares has no generated Rust, or an entry on RUST_COVERAGE_PENDING is now fixed and must be deleted."}.freeze,
|
|
153
|
+
{"name"=>"rust_coverage_codegen", "id"=>"rust_coverage_codegen", "title"=>"rust coverage against hecks-codegen's own manifest, pizzas governance identity banking meta", "argv"=>"", "script"=>"for module in pizzas governance identity banking meta; do\n bundle exec exe/hecks build.rust_coverage module_name=$module codegen=rust --wait\ndone\n", "blocked"=>"hecks-codegen generated nothing for a construct a domain declares."}.freeze,
|
|
154
154
|
{"name"=>"kernel_coverage", "id"=>"kernel_coverage", "title"=>"rust kernel coverage, every capability variant has a kernel file", "argv"=>"bundle exec exe/hecks kernel_run.measure_kernel_coverage! --wait", "script"=>"", "blocked"=>"a capability's enum variant has no rust/src/kernel file behind it."}.freeze,
|
|
155
155
|
{"name"=>"rust_vocabulary_drift", "id"=>"rust_vocabulary_drift", "title"=>"rust vocabulary, the kernel vocab tables regenerated must match the committed ones", "argv"=>"", "script"=>"bundle exec exe/hecks language_run.project_rust_vocabulary! --confirm --wait\ngit diff --exit-code -- rust/src/kernel/vocab/\nuntracked=\"$(git status --porcelain --untracked-files=all -- rust/src/kernel/vocab/)\"\nif [ -n \"$untracked\" ]; then\n echo \"rust/src/kernel/vocab has uncommitted generated files:\" >&2\n echo \"$untracked\" >&2\n exit 1\nfi\n", "blocked"=>"the generated kernel vocab tables are stale or untracked; the check rewrote them, so commit the result."}.freeze
|
|
156
156
|
].freeze,
|
|
@@ -209,7 +209,7 @@ module Hecks
|
|
|
209
209
|
{"name"=>"cadence_seconds", "type"=>"Integer", "meaning"=>"Minimum gap in seconds between one tick's report and the next dispatch; 0 is immediately. Read by the `/loop hecks_qa` caller, not by any script."}.freeze,
|
|
210
210
|
{"name"=>"pr_cap_per_day", "type"=>"Integer", "meaning"=>"Most PRs (Patch plus Improvement) `hecks quality_control Patch.open!` opens per calendar day; 0 is uncapped."}.freeze,
|
|
211
211
|
{"name"=>"widening_tiers", "type"=>"Array", "meaning"=>"Fuzz depth by `Target.clean_streak`, in three discrete tiers, read by `SweepDepth.for_streak`. The last row (`upto: Float::INFINITY`) is the ceiling."}.freeze,
|
|
212
|
-
{"name"=>"sweep_max_parallel", "type"=>"Integer", "meaning"=>"How many `hecks quality_control
|
|
212
|
+
{"name"=>"sweep_max_parallel", "type"=>"Integer", "meaning"=>"How many `hecks quality_control sweep.run <target>` children `--all` keeps running at once."}.freeze,
|
|
213
213
|
{"name"=>"liveness_fallback_seconds", "type"=>"Integer", "meaning"=>"How long the `/loop hecks_qa` caller waits for a tick's report before assuming it stalled."}.freeze,
|
|
214
214
|
{"name"=>"draft_only", "type"=>"Boolean", "meaning"=>"Whether `hecks quality_control Patch.open!` opens its PR as a draft; false opens it ready for review."}.freeze,
|
|
215
215
|
{"name"=>"auto_merge", "type"=>"Boolean", "meaning"=>"Whether `hecks quality_control Patch.open!` queues the PR for auto-merge once it is open."}.freeze,
|
|
@@ -218,15 +218,15 @@ module Hecks
|
|
|
218
218
|
{"name"=>"corpus_splice_probability", "type"=>"Numeric", "meaning"=>"Fraction (0.0..1.0) of seeds that start from a prefix of an earlier seed's new coverage. Off by default: measured, it trades breadth for depth (banking reached 1.41x the deep-state tuples, chess lost 0.12x of all tuples), while favoring rare verbs alone never lost."}.freeze,
|
|
219
219
|
{"name"=>"favor_rare_verbs", "type"=>"Integer", "meaning"=>"How many of the least-hit verbs the next seed weights up; 0 turns the lever off."}.freeze,
|
|
220
220
|
{"name"=>"runtime_coverage_feedback", "type"=>"Boolean", "meaning"=>"Whether guided generation also admits a seed to the corpus for reaching a new line or branch of the Ruby runtime (`Hecks::Fuzzing::RuntimeCoverage`), not only a new (verb, kind, state, mutation, outcome) tuple. Off by default: tracing every line slows each seed."}.freeze,
|
|
221
|
-
{"name"=>"self_consistency_checks", "type"=>"Boolean", "meaning"=>"Whether `hecks quality_control
|
|
222
|
-
{"name"=>"shrink_budget", "type"=>"Integer", "meaning"=>"Candidate replays `hecks quality_control
|
|
221
|
+
{"name"=>"self_consistency_checks", "type"=>"Boolean", "meaning"=>"Whether `hecks quality_control sweep.run` also runs the self-consistency pass (cold rehydration, replay idempotency, value-object JSON round trip) on the same generated sequence."}.freeze,
|
|
222
|
+
{"name"=>"shrink_budget", "type"=>"Integer", "meaning"=>"Candidate replays `hecks quality_control sweep.run` may spend shrinking one surprised check; 0 turns shrinking off. `concurrency` is never shrunk: a race does not reproduce on demand."}.freeze,
|
|
223
223
|
{"name"=>"yield_weight_seconds", "type"=>"Integer", "meaning"=>"Seconds of staleness that one point of yield score is worth in `RotationPriority.pick`. Zero would make yield invisible, so pick a number that lets it matter."}.freeze,
|
|
224
224
|
{"name"=>"yield_decay_percent", "type"=>"Integer", "meaning"=>"Percent of the prior yield score kept at each release, so recent findings dominate. Applied by the caller: the mutation language cannot express \"decay, then add\" in `Release`."}.freeze,
|
|
225
225
|
{"name"=>"rotation_stale_floor_seconds", "type"=>"Integer", "meaning"=>"Once a waiting target is this stale, `RotationPriority.pick` takes it whatever its yield score, so a quiet target cannot starve."}.freeze,
|
|
226
|
-
{"name"=>"persistence_parity_seed_cap", "type"=>"Integer", "meaning"=>"Ceiling on `--seeds` for `--persistence-parity`, which pays for real Postgres per dispatch. `hecks quality_control
|
|
226
|
+
{"name"=>"persistence_parity_seed_cap", "type"=>"Integer", "meaning"=>"Ceiling on `--seeds` for `--persistence-parity`, which pays for real Postgres per dispatch. `hecks quality_control sweep.run` clamps down, never up, and prints a note."}.freeze,
|
|
227
227
|
{"name"=>"concurrency_seed_cap", "type"=>"Integer", "meaning"=>"Ceiling on `--seeds` for the concurrency mode, which needs two disposable schemas per seed."}.freeze,
|
|
228
228
|
{"name"=>"adapter_parity_pairs", "type"=>"Hash", "meaning"=>"The two adapters each adapter-parity mode compares, by mode name; a new pairing is a new entry. `adapter_parity_sqlite` needs no `database:`/`schema:` because sqlite has no disposable database."}.freeze,
|
|
229
|
-
{"name"=>"modes", "type"=>"Hash", "meaning"=>"Which comparison modes `hecks quality_control
|
|
229
|
+
{"name"=>"modes", "type"=>"Hash", "meaning"=>"Which comparison modes `hecks quality_control sweep.run` may run: the enabled ones intersected with what `TargetCapabilities.infer(path)` finds in the target. `--modes a,b,c` overrides for one run. `wasm_front` and `adapter_parity_postgres` are off because nothing runs them; the others are built and off because their I/O cost across the rotation is unmeasured. `hecks quality_control sweep.run` refuses to start when a mode outside `TargetCapabilities::RUNNABLE_MODES` is enabled."}.freeze,
|
|
230
230
|
{"name"=>"role_draw_probability", "type"=>"Numeric", "meaning"=>"How often a generated role-gated command step carries a drawn caller (`role:`/`actor_id:`); 0 turns the draw off."}.freeze,
|
|
231
231
|
{"name"=>"dry_run_fraction", "type"=>"Numeric", "meaning"=>"Fraction of generated command steps offered as a dry run instead of a real dispatch; 0 is off. Kept low: a dry run creates no state for later steps to act on."}.freeze,
|
|
232
232
|
{"name"=>"generated_domains_per_tick", "type"=>"Integer", "meaning"=>"How many generated domains one `hecks quality_control sweep.tick` checks after the rotation sweep; 0 skips the step."}.freeze,
|
|
@@ -589,7 +589,6 @@ module Hecks
|
|
|
589
589
|
{"script"=>"rspec_shard_files", "section"=>"Codebase", "aggregate"=>"TestSuiteRun", "verb"=>"shard_specs"}.freeze,
|
|
590
590
|
{"script"=>"rspec_io_parallel_files", "section"=>"Codebase", "aggregate"=>"TestSuiteRun", "verb"=>"list_io_parallel_specs"}.freeze,
|
|
591
591
|
{"script"=>"rspec_io_parallel_files", "section"=>"Codebase", "aggregate"=>"TestSuiteRun", "verb"=>"write_io_parallel_spec_list"}.freeze,
|
|
592
|
-
{"script"=>"refresh_rspec_runtime_baseline", "section"=>"Codebase", "aggregate"=>"TestSuiteRun", "verb"=>"refresh_runtime_baseline"}.freeze,
|
|
593
592
|
{"script"=>"spec_example", "section"=>"Codebase", "aggregate"=>"TestSuiteRun", "verb"=>"run_spec_example"}.freeze,
|
|
594
593
|
{"script"=>"stress_concurrency_specs", "section"=>"Codebase", "aggregate"=>"TestSuiteRun", "verb"=>"stress_concurrency"}.freeze,
|
|
595
594
|
{"script"=>"regenerate_persistence_legacy_fixtures", "section"=>"Codebase", "aggregate"=>"TestSuiteRun", "verb"=>"regenerate_legacy_fixtures"}.freeze,
|
data/lib/hecks.rb
CHANGED
|
@@ -98,9 +98,12 @@ module Hecks
|
|
|
98
98
|
# @param shared [String, nil] a shared-root override; see `Runtime::Loader.boot_files`
|
|
99
99
|
# @param install_driving [Boolean] install the `Widget::Item.Add`-style facade
|
|
100
100
|
# @param environment [String, nil] environment name passed to the selected-file loader
|
|
101
|
+
# @param adapters [Array<String>, nil] exactly the `.adapter` files to load, leaving the
|
|
102
|
+
# project's own unloaded; nil keeps the loader's own choice
|
|
101
103
|
# @return [Runtime::Dispatcher, Adapters::Lambda::RemoteDispatcher] the domain's dispatcher
|
|
102
|
-
def boot_files(paths, shared: nil, install_driving: true, environment: Assembly::FROM_ENV)
|
|
103
|
-
Assembly.boot_files(paths, shared: shared, install_driving: install_driving, environment: environment
|
|
104
|
+
def boot_files(paths, shared: nil, install_driving: true, environment: Assembly::FROM_ENV, adapters: nil)
|
|
105
|
+
Assembly.boot_files(paths, shared: shared, install_driving: install_driving, environment: environment,
|
|
106
|
+
adapters: adapters)
|
|
104
107
|
end
|
|
105
108
|
|
|
106
109
|
# Installs the Ruby driving adapter (`Widget::Item.Add(...)`) over a dispatcher built by hand.
|
|
Binary file
|
|
Binary file
|
data/rust/host/HECKS_RELEASE
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.
|
|
1
|
+
7.1.0
|
data/rust/parser/src/emit.rs
CHANGED
|
@@ -1030,7 +1030,8 @@ fn ruby_value_json(value: &ruby_value::Value) -> JsonValue {
|
|
|
1030
1030
|
}
|
|
1031
1031
|
|
|
1032
1032
|
/// One declared adapter, keys in the order the Ruby side prints them; an adapter that provides
|
|
1033
|
-
/// capabilities adds `provides`,
|
|
1033
|
+
/// capabilities adds `provides`, one that stays inside adds `stays_inside`, and a plugin adds
|
|
1034
|
+
/// `answers` and `runs` after them.
|
|
1034
1035
|
pub fn adapter_json(adapter: &ir::Adapter) -> JsonValue {
|
|
1035
1036
|
let JsonValue::Object(mut keys) = declared_adapter_json(adapter) else {
|
|
1036
1037
|
unreachable!("an adapter is a JSON object")
|
|
@@ -1041,6 +1042,9 @@ pub fn adapter_json(adapter: &ir::Adapter) -> JsonValue {
|
|
|
1041
1042
|
JsonValue::strings(&adapter.provides),
|
|
1042
1043
|
));
|
|
1043
1044
|
}
|
|
1045
|
+
if adapter.stays_inside {
|
|
1046
|
+
keys.push(("stays_inside".to_string(), JsonValue::Bool(true)));
|
|
1047
|
+
}
|
|
1044
1048
|
if let Some(runs) = &adapter.runs {
|
|
1045
1049
|
keys.push(("answers".to_string(), JsonValue::strings(&adapter.answers)));
|
|
1046
1050
|
keys.push(("runs".to_string(), JsonValue::str(runs.clone())));
|
data/rust/parser/src/ir.rs
CHANGED
|
@@ -486,4 +486,6 @@ pub struct Adapter {
|
|
|
486
486
|
pub runs: Option<String>,
|
|
487
487
|
// The capabilities the adapter provides beyond its port's contract, in declaration order.
|
|
488
488
|
pub provides: Vec<String>,
|
|
489
|
+
// `stays_inside`: the adapter reaches nothing outside the process and the checkout (ADR 0171).
|
|
490
|
+
pub stays_inside: bool,
|
|
489
491
|
}
|