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
|
@@ -33,13 +33,14 @@ module Hecks
|
|
|
33
33
|
}.merge(optional_adapter_keys(adapter))
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
# The keys only some adapters carry: `provides` when it names a capability,
|
|
37
|
-
# plugin's `answers` and `runs`.
|
|
36
|
+
# The keys only some adapters carry: `provides` when it names a capability, `stays_inside`
|
|
37
|
+
# when the adapter says it, and a plugin's `answers` and `runs`.
|
|
38
38
|
#
|
|
39
39
|
# @param adapter [Bluebook::Hecksagon::Adapter] the adapter to export
|
|
40
40
|
# @return [Hash{String => Object}] the keys it carries beyond the five every adapter has
|
|
41
41
|
def optional_adapter_keys(adapter)
|
|
42
42
|
keys = adapter.capabilities.empty? ? {} : { "provides" => adapter.capabilities }
|
|
43
|
+
keys["stays_inside"] = true if adapter.stays_inside?
|
|
43
44
|
return keys unless adapter.plugin?
|
|
44
45
|
|
|
45
46
|
keys.merge("answers" => adapter.answered_queries, "runs" => adapter.runs)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Projector
|
|
3
|
+
module NarrateProjector
|
|
4
|
+
# The prose for the goals an `.examples` file declares (ADR 0173): each goal as a use case,
|
|
5
|
+
# its first example the main scenario and the rest its extensions. Every line is quoted from
|
|
6
|
+
# the file; `hecks-parse examples` has already checked it against the bluebook.
|
|
7
|
+
module UseCases
|
|
8
|
+
GOAL = /\A(\s*)Goal:\s*(.+?)\s*\z/
|
|
9
|
+
EXAMPLE = /\A\s*Example:\s*(.+?)\s*\z/
|
|
10
|
+
STEP = /\A\s*(Given|When|Then|And)\s+(.+?)\s*\z/
|
|
11
|
+
INDENT = /\A\s*/
|
|
12
|
+
|
|
13
|
+
module_function
|
|
14
|
+
|
|
15
|
+
# The "Use cases" section, or `nil` when no file declares a goal.
|
|
16
|
+
#
|
|
17
|
+
# @param paths [Array<String>] `.examples` files
|
|
18
|
+
# @param depth [Integer] the section's heading level
|
|
19
|
+
# @return [String, nil] Markdown
|
|
20
|
+
def narrative(paths, depth)
|
|
21
|
+
goals = Array(paths).flat_map { |path| goals_in(File.readlines(path, chomp: true)) }
|
|
22
|
+
return nil if goals.empty?
|
|
23
|
+
|
|
24
|
+
[DocsProjector.h(depth, "Use cases"), *goals.map { |goal| goal_text(goal, depth + 1) }].join("\n\n")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @param lines [Array<String>] one `.examples` file
|
|
28
|
+
# @return [Array<Hash>] each goal with its examples, each example with its step lines
|
|
29
|
+
def goals_in(lines)
|
|
30
|
+
open = nil
|
|
31
|
+
lines.each_with_object([]) do |line, goals|
|
|
32
|
+
open = follow(line, open, goals) unless line.strip.empty?
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Reads one line against the goal still open, answering the goal open after it: a `Goal:`
|
|
37
|
+
# opens one, a line indented under it belongs to it, any other line closes it.
|
|
38
|
+
def follow(line, open, goals)
|
|
39
|
+
if (goal = line.match(GOAL))
|
|
40
|
+
goals << { indent: goal[1].size, goal: goal[2], examples: [] }
|
|
41
|
+
return goals.last
|
|
42
|
+
end
|
|
43
|
+
return nil unless open && indent_of(line) > open[:indent]
|
|
44
|
+
|
|
45
|
+
add_to(open, line)
|
|
46
|
+
open
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Files one line of a goal: an example under it, or a step of its latest example.
|
|
50
|
+
def add_to(goal, line)
|
|
51
|
+
if (example = line.match(EXAMPLE))
|
|
52
|
+
goal[:examples] << { title: example[1], steps: [] }
|
|
53
|
+
elsif (step = line.match(STEP)) && goal[:examples].any?
|
|
54
|
+
goal[:examples].last[:steps] << "#{step[1]} #{step[2]}"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def indent_of(line) = line[INDENT].size
|
|
59
|
+
|
|
60
|
+
def goal_text(goal, depth)
|
|
61
|
+
main, *extensions = goal[:examples]
|
|
62
|
+
[DocsProjector.h(depth, goal[:goal]), scenario("Main scenario", main),
|
|
63
|
+
*extensions.map { |extension| scenario("Extension", extension) }].compact.join("\n\n")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def scenario(label, example)
|
|
67
|
+
return nil unless example
|
|
68
|
+
|
|
69
|
+
["**#{label}: #{example[:title]}**", *example[:steps].map { |step| "- #{step}" }].join("\n")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -11,8 +11,9 @@ module Hecks
|
|
|
11
11
|
# Projects `bluebook` as prose; `options[:heading]` sets the top heading level.
|
|
12
12
|
#
|
|
13
13
|
# @param bluebook [Bluebook::Behaviour::Bluebook] the bluebook to narrate
|
|
14
|
-
# @param options [Hash] `:heading` (Integer, default 1)
|
|
15
|
-
# aggregate, omitting the bluebook intro and reactions)
|
|
14
|
+
# @param options [Hash] `:heading` (Integer, default 1), `:aggregate` (narrows to one
|
|
15
|
+
# aggregate, omitting the bluebook intro and reactions) and `:examples` (`.examples` files
|
|
16
|
+
# whose goals are told as use cases, ADR 0173)
|
|
16
17
|
# @return [String] Markdown prose ending in a newline
|
|
17
18
|
# @raise [Runtime::NotFound] if `options[:aggregate]` names no declared aggregate
|
|
18
19
|
def call(bluebook:, options: {})
|
|
@@ -22,10 +23,15 @@ module Hecks
|
|
|
22
23
|
sections = []
|
|
23
24
|
sections << bluebook_intro(bluebook, depth) unless only
|
|
24
25
|
sections.concat(aggregate_narratives(bluebook, only, only ? depth : depth + 1))
|
|
25
|
-
sections
|
|
26
|
+
sections.concat(closing_sections(bluebook, options[:examples], depth + 1)) unless only
|
|
26
27
|
"#{sections.compact.join("\n\n").rstrip}\n"
|
|
27
28
|
end
|
|
28
29
|
|
|
30
|
+
# What the bluebook causes on its own, then the use cases its examples declare.
|
|
31
|
+
def closing_sections(bluebook, examples, depth)
|
|
32
|
+
[Reactions.narrative(bluebook, depth), UseCases.narrative(examples, depth)]
|
|
33
|
+
end
|
|
34
|
+
|
|
29
35
|
# One narrative per aggregate, at heading level `depth`.
|
|
30
36
|
def aggregate_narratives(bluebook, only, depth)
|
|
31
37
|
Array(DocsProjector.aggregates(bluebook, only)).map { |aggregate| aggregate_narrative(aggregate, depth) }
|
|
@@ -140,3 +146,4 @@ end
|
|
|
140
146
|
|
|
141
147
|
require_relative "narrate_projector/commands"
|
|
142
148
|
require_relative "narrate_projector/reactions"
|
|
149
|
+
require_relative "narrate_projector/use_cases"
|
|
@@ -138,12 +138,13 @@ module Hecks
|
|
|
138
138
|
# @param request [Hash] the `MintRequested` attributes: `run`, `domain`, `era`, `sha`,
|
|
139
139
|
# `branch`,
|
|
140
140
|
# `watermark`, `rehearsal_verdict`, `refusal_total`, `ledger_dir` and `confirm`
|
|
141
|
-
# @return [Hash] `run`, `domain`, `era`, `sha
|
|
141
|
+
# @return [Hash] `run`, `domain`, `era`, `sha` and `label` as values; `branch` and
|
|
142
|
+
# `ledger_dir` as the strings the landing takes
|
|
142
143
|
def mint(**request)
|
|
143
144
|
refuse_while_stranded!
|
|
144
145
|
refuse_unrehearsed!(value_of(request[:rehearsal_verdict]))
|
|
145
|
-
label = run_mint!(request, authorize!(request))
|
|
146
|
-
%i[run domain era sha
|
|
146
|
+
label = wrap(run_mint!(request, authorize!(request)))
|
|
147
|
+
%i[run domain era sha].to_h { |key| [key, wrap(request[key])] }.merge(label:, **request.slice(:branch, :ledger_dir))
|
|
147
148
|
end
|
|
148
149
|
|
|
149
150
|
# Answers a held mint's approval request: the mint waits for the owner, and nothing follows
|
|
@@ -160,9 +161,12 @@ module Hecks
|
|
|
160
161
|
# mint
|
|
161
162
|
# refuses until the owner removes it.
|
|
162
163
|
#
|
|
164
|
+
# `ledger_dir` names the minted ledger's bluebook directory; an empty one is the
|
|
165
|
+
# QualityControl ledger, which lands in this checkout.
|
|
166
|
+
#
|
|
163
167
|
# @return [Hash] `run`, `era`, `sha` and `branch`, which `MintStranded` takes on refusal
|
|
164
|
-
def land(run:, era:, sha:, branch:, **)
|
|
165
|
-
output, landed = @commands.land(@root, value_of(branch))
|
|
168
|
+
def land(run:, era:, sha:, branch:, **answer)
|
|
169
|
+
output, landed = @commands.land(QaAutoMint.checkout_for(@root, value_of(answer[:ledger_dir])), value_of(branch))
|
|
166
170
|
strand!(run, era, sha, output) unless landed
|
|
167
171
|
{ run: wrap(run), era: wrap(era), sha: wrap(sha), branch: wrap(branch) }
|
|
168
172
|
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "open3"
|
|
3
4
|
require_relative "qa_era_mint"
|
|
4
5
|
require_relative "../../adapters/driven/postgres_era/lineage_manager/mint_decision"
|
|
5
6
|
|
|
@@ -49,10 +50,28 @@ module Hecks
|
|
|
49
50
|
def held_bluebook(latest) = PostgresEra::LineageManager.held_bluebook(latest)
|
|
50
51
|
|
|
51
52
|
# The bluebook directory a mint is for: the one the request names, or the QualityControl
|
|
52
|
-
# ledger.
|
|
53
|
+
# ledger when it names none or an empty one.
|
|
53
54
|
#
|
|
54
55
|
# @return [String]
|
|
55
|
-
def ledger_for(root, requested)
|
|
56
|
+
def ledger_for(root, requested)
|
|
57
|
+
requested.to_s.empty? ? File.join(root, QaEraMint::LEDGER) : requested
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# The checkout a minted ledger's branch lands in: `root` for the QualityControl ledger, else
|
|
61
|
+
# the git checkout around the ledger's directory.
|
|
62
|
+
#
|
|
63
|
+
# @param root [String] the Hecks checkout
|
|
64
|
+
# @param requested [String, nil] the ledger's bluebook directory, or nil or empty for QA's
|
|
65
|
+
# @return [String] the checkout's top directory
|
|
66
|
+
# @raise [Runtime::WiringError] if the ledger's directory is in no git checkout
|
|
67
|
+
def checkout_for(root, requested)
|
|
68
|
+
return root if requested.to_s.empty?
|
|
69
|
+
|
|
70
|
+
top, status = Open3.capture2e("git", "-C", requested, "rev-parse", "--show-toplevel")
|
|
71
|
+
raise Runtime::WiringError, "#{requested} is in no git checkout: #{top.strip}" unless status.success?
|
|
72
|
+
|
|
73
|
+
top.strip
|
|
74
|
+
end
|
|
56
75
|
|
|
57
76
|
# The ledger a mint is for, refused by name when its world declares it disposable: its boot
|
|
58
77
|
# holds every shape change itself, so there is no era to mint.
|
|
@@ -10,7 +10,7 @@ module Hecks
|
|
|
10
10
|
# The command behind `hecks quality_control sweep.race`: one racer of
|
|
11
11
|
# `Hecks::Fuzzing::ConcurrentDispatch`, run as its own OS process.
|
|
12
12
|
#
|
|
13
|
-
# It is spawned, not forked: the caller (`hecks quality_control
|
|
13
|
+
# It is spawned, not forked: the caller (`hecks quality_control sweep.run`) holds live
|
|
14
14
|
# `PostgresEra` connections, and fork duplicates their file descriptors and SSL state,
|
|
15
15
|
# corrupting them when a child exits.
|
|
16
16
|
#
|
|
@@ -99,7 +99,8 @@ module Hecks
|
|
|
99
99
|
[argv.first(split), argv.drop(split + 1).reject { |arg| arg == "--against" }]
|
|
100
100
|
end
|
|
101
101
|
|
|
102
|
-
# `hecks build.project_rust!` reads exactly `<name>/bluebook/<name>.bluebook`, so refuse
|
|
102
|
+
# `hecks build.project_rust!` reads exactly `<name>/bluebook/<name>.bluebook`, so refuse
|
|
103
|
+
# anything
|
|
103
104
|
# else early.
|
|
104
105
|
def refuse_unless_stress_domain_shaped!(path)
|
|
105
106
|
expected = File.join(path, "bluebook", "#{File.basename(path)}.bluebook")
|
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
module Hecks
|
|
4
4
|
module QualityControlCli
|
|
5
5
|
class QaFixMainBug
|
|
6
|
-
# What happens after a fix is committed: the ledger records the investigation and the fix, the
|
|
7
|
-
#
|
|
8
|
-
#
|
|
6
|
+
# What happens after a fix is committed: the ledger records the investigation and the fix, the
|
|
7
|
+
# branch
|
|
8
|
+
# lands on local main, the ledger records the verification, and main is pushed through the
|
|
9
|
+
# push train.
|
|
10
|
+
# The push runs only after the quick gate is green on main's head, and origin's main is
|
|
11
|
+
# fetched right
|
|
9
12
|
# before it.
|
|
10
13
|
module Publishing
|
|
11
14
|
private
|
|
@@ -62,7 +65,8 @@ module Hecks
|
|
|
62
65
|
unpushed("the push train refused main: #{output.strip}")
|
|
63
66
|
end
|
|
64
67
|
|
|
65
|
-
# Fetches origin's main right before the push. @return [String, nil] why main cannot be
|
|
68
|
+
# Fetches origin's main right before the push. @return [String, nil] why main cannot be
|
|
69
|
+
# pushed as it is
|
|
66
70
|
def behind_origin
|
|
67
71
|
QaMainGate::Worktree.git!(@root, "fetch", "origin", "main")
|
|
68
72
|
return nil if QaMainGate::Worktree.git?(@root, "merge-base", "--is-ancestor", "origin/main", "main")
|
|
@@ -11,14 +11,15 @@ require_relative "qa_fix_main_bug/publishing"
|
|
|
11
11
|
|
|
12
12
|
module Hecks
|
|
13
13
|
module QualityControlCli
|
|
14
|
-
# The command behind `hecks quality_control bug.fix_and_land --bug BUG#n`: takes one
|
|
15
|
-
# bug through a fix on a fresh branch off main, the quick gate on that
|
|
16
|
-
# and a push through the push train. A bug triaged
|
|
17
|
-
#
|
|
18
|
-
#
|
|
14
|
+
# The command behind `hecks quality_control bug.fix_and_land --bug BUG#n`: takes one
|
|
15
|
+
# self-contained red-main bug through a fix on a fresh branch off main, the quick gate on that
|
|
16
|
+
# exact commit, a landing on local main, and a push through the push train. A bug triaged
|
|
17
|
+
# bigger, or one whose fixer changes nothing or leaves a check red, stops before anything lands
|
|
18
|
+
# and stays open for the owner. Nothing is landed unless the gate is green on the commit, and
|
|
19
|
+
# nothing is pushed unless the gate is green on main's head.
|
|
19
20
|
#
|
|
20
|
-
# Exit 0 when the fix is landed (pushed, or the push's refusal is printed), 1 when refused
|
|
21
|
-
# 2 when the fix stays red.
|
|
21
|
+
# Exit 0 when the fix is landed (pushed, or the push's refusal is printed), 1 when refused
|
|
22
|
+
# before any fix, 2 when the fix stays red.
|
|
22
23
|
class QaFixMainBug
|
|
23
24
|
include Publishing
|
|
24
25
|
|
|
@@ -140,7 +141,8 @@ module Hecks
|
|
|
140
141
|
QaMainGate::Worktree.git?(@root, "rev-parse", "--verify", "--quiet", "refs/heads/#{branch}")
|
|
141
142
|
end
|
|
142
143
|
|
|
143
|
-
# The fixer runs, then the gate on the exact commit it makes. Returns the commit, or what kept
|
|
144
|
+
# The fixer runs, then the gate on the exact commit it makes. Returns the commit, or what kept
|
|
145
|
+
# it red.
|
|
144
146
|
def fix_in(dir, row)
|
|
145
147
|
@shell.rubocop(dir, files_of(row))
|
|
146
148
|
return Stuck.new("the fixer changed nothing") unless changed?(dir)
|
|
@@ -67,7 +67,7 @@ module Hecks
|
|
|
67
67
|
<<~NOTES
|
|
68
68
|
# #{name}
|
|
69
69
|
|
|
70
|
-
Promoted from a `hecks quality_control
|
|
70
|
+
Promoted from a `hecks quality_control target.check_generated_domains` finding — #{origin_phrase(recorded)}, not
|
|
71
71
|
hand-written. The bluebook is the minimal form of the domain that
|
|
72
72
|
surprised; `QaGenerated` was renamed to `#{camelize(name)}` and nothing
|
|
73
73
|
else changed.
|
|
@@ -102,7 +102,7 @@ module Hecks
|
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
def origin_phrase(recorded)
|
|
105
|
-
recorded&.key?("source") ? "written by `hecks quality_control
|
|
105
|
+
recorded&.key?("source") ? "written by `hecks quality_control target.mine_combinations`' agent" : "generated"
|
|
106
106
|
end
|
|
107
107
|
end
|
|
108
108
|
end
|
|
@@ -5,8 +5,10 @@ require "hecks/tools/gate"
|
|
|
5
5
|
module Hecks
|
|
6
6
|
module QualityControlCli
|
|
7
7
|
class QaMainGate
|
|
8
|
-
# Runs the quick gate's checks in a directory, all at once, and says which passed. The checks
|
|
9
|
-
#
|
|
8
|
+
# Runs the quick gate's checks in a directory, all at once, and says which passed. The checks
|
|
9
|
+
# are
|
|
10
|
+
# the `GateStage` rows of the Vocabulary bluebook, so a red check is reported with the output
|
|
11
|
+
# it
|
|
10
12
|
# printed, the same way `hecks gate_run.gate! stage=quick` reports one.
|
|
11
13
|
module GateRun
|
|
12
14
|
# One check of the quick gate after it ran.
|
|
@@ -21,7 +23,8 @@ module Hecks
|
|
|
21
23
|
# @return [Boolean] whether it exited 0
|
|
22
24
|
Result = Struct.new(:id, :title, :output, :passed)
|
|
23
25
|
|
|
24
|
-
# Stands in for the gate's progress lines, which go to standard error of a person's
|
|
26
|
+
# Stands in for the gate's progress lines, which go to standard error of a person's
|
|
27
|
+
# terminal.
|
|
25
28
|
class Quiet
|
|
26
29
|
# @return [nil]
|
|
27
30
|
def start(_id) = nil
|
|
@@ -3,17 +3,21 @@
|
|
|
3
3
|
module Hecks
|
|
4
4
|
module QualityControlCli
|
|
5
5
|
class QaMainGate
|
|
6
|
-
# Turns one red check into one QA bug, proven by the check's own output, and triages it. A
|
|
6
|
+
# Turns one red check into one QA bug, proven by the check's own output, and triages it. A
|
|
7
|
+
# check the
|
|
7
8
|
# command can fix mechanically is `self_contained`; every other is `bigger`, for the owner.
|
|
8
9
|
module Logging
|
|
9
|
-
# Checks with a mechanical fixer. `rubocop -a` fixes style offenses it can fix; metrics and
|
|
10
|
+
# Checks with a mechanical fixer. `rubocop -a` fixes style offenses it can fix; metrics and
|
|
11
|
+
# the
|
|
10
12
|
# rest need a person.
|
|
11
13
|
SELF_CONTAINED = %w[rubocop].freeze
|
|
12
14
|
SUBMITTER = "qa_main_gate"
|
|
13
15
|
NAME_PREFIX = "main-red-"
|
|
14
16
|
|
|
15
|
-
# A bug already open for the same check is not logged again: the commit moved, the red did
|
|
16
|
-
#
|
|
17
|
+
# A bug already open for the same check is not logged again: the commit moved, the red did
|
|
18
|
+
# not.
|
|
19
|
+
# @return [Array(String, String, Boolean)] the reference, its disposition, and whether it
|
|
20
|
+
# was new
|
|
17
21
|
def log_red(sweep, result, sha)
|
|
18
22
|
open = open_bug_named(bug_name(result.id))
|
|
19
23
|
return [open, disposition(result.id), false] if open
|
|
@@ -5,7 +5,8 @@ require "open3"
|
|
|
5
5
|
module Hecks
|
|
6
6
|
module QualityControlCli
|
|
7
7
|
class QaMainGate
|
|
8
|
-
# The commands outside the ledger that a red-main fix runs: the mechanical fixer, the landing,
|
|
8
|
+
# The commands outside the ledger that a red-main fix runs: the mechanical fixer, the landing,
|
|
9
|
+
# and the
|
|
9
10
|
# push through the push train. Each answers its output and whether it exited 0.
|
|
10
11
|
module Shell
|
|
11
12
|
# The push train's own launcher, in its detached checkout.
|
|
@@ -13,7 +14,8 @@ module Hecks
|
|
|
13
14
|
|
|
14
15
|
module_function
|
|
15
16
|
|
|
16
|
-
# Applies `rubocop -a` to the files, which fixes what it can; whether the rest is fixed is
|
|
17
|
+
# Applies `rubocop -a` to the files, which fixes what it can; whether the rest is fixed is
|
|
18
|
+
# the gate's
|
|
17
19
|
# to say.
|
|
18
20
|
#
|
|
19
21
|
# @return [Array(String, Boolean)]
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
module Hecks
|
|
4
4
|
module QualityControlCli
|
|
5
5
|
class QaMainGate
|
|
6
|
-
# The sweep a red run of the gate on main is written as. Main is not a bluebook, so the sweep
|
|
7
|
-
#
|
|
6
|
+
# The sweep a red run of the gate on main is written as. Main is not a bluebook, so the sweep
|
|
7
|
+
# runs
|
|
8
|
+
# against one standing target, `main-gate`, that is shelved: the rotation never claims it.
|
|
9
|
+
# Each gate
|
|
8
10
|
# check is one `Check`, its expectation written before the answer is read.
|
|
9
11
|
module Sweeping
|
|
10
12
|
TARGET = "main-gate"
|
|
@@ -42,8 +44,10 @@ module Hecks
|
|
|
42
44
|
dispatch("QualityControl::Sweep.Check.Held", id: sweep.id, sequence: { value: sequence },
|
|
43
45
|
observation: { value: "exit 0" })
|
|
44
46
|
else
|
|
45
|
-
# Not `Check.Surprised`: its `SuspendOnSurprise` policy can only suspend a held target,
|
|
46
|
-
#
|
|
47
|
+
# Not `Check.Surprised`: its `SuspendOnSurprise` policy can only suspend a held target,
|
|
48
|
+
# and this
|
|
49
|
+
# one is shelved, so the policy would be refused on every red run. The red is carried by
|
|
50
|
+
# the
|
|
47
51
|
# `MainCheck` record and the bugs logged from the output.
|
|
48
52
|
dispatch("QualityControl::Sweep.Check.Unsettled", id: sweep.id, sequence: { value: sequence },
|
|
49
53
|
observation: { value: result.output })
|
|
@@ -7,7 +7,8 @@ require "tmpdir"
|
|
|
7
7
|
module Hecks
|
|
8
8
|
module QualityControlCli
|
|
9
9
|
class QaMainGate
|
|
10
|
-
# A temporary git worktree, made for one block and removed when it returns, so a gate or a fix
|
|
10
|
+
# A temporary git worktree, made for one block and removed when it returns, so a gate or a fix
|
|
11
|
+
# never
|
|
11
12
|
# runs in the owner's checkout.
|
|
12
13
|
module Worktree
|
|
13
14
|
# Raised when a git command the worktree needs fails.
|
|
@@ -17,7 +18,7 @@ module Hecks
|
|
|
17
18
|
|
|
18
19
|
# @param root [String] the repository the worktree belongs to
|
|
19
20
|
# @param ref [String] the commit the worktree starts at
|
|
20
|
-
# @param branch [String, nil] a new branch checked out in it; nil leaves
|
|
21
|
+
# @param branch [String, nil] a new branch checked out in it; nil leaves head detached
|
|
21
22
|
# @yieldparam dir [String] the worktree's directory
|
|
22
23
|
# @return [Object] whatever the block returns
|
|
23
24
|
def with(root:, ref:, branch: nil)
|
|
@@ -13,13 +13,17 @@ require_relative "qa_fix_main_bug"
|
|
|
13
13
|
|
|
14
14
|
module Hecks
|
|
15
15
|
module QualityControlCli
|
|
16
|
-
# The command behind `hecks quality_control main_check.check_main`, the tick's main step. It
|
|
17
|
-
#
|
|
16
|
+
# The command behind `hecks quality_control main_check.check_main`, the tick's main step. It
|
|
17
|
+
# runs
|
|
18
|
+
# the quick gate on local main in a temporary worktree, records the verdict against the commit,
|
|
19
|
+
# and
|
|
18
20
|
# for each red check logs one bug proven by its output. A self-contained bug is handed to
|
|
19
|
-
# `QaFixMainBug`. A commit already on record is answered from the record, so a tick that finds
|
|
21
|
+
# `QaFixMainBug`. A commit already on record is answered from the record, so a tick that finds
|
|
22
|
+
# it again
|
|
20
23
|
# logs nothing twice.
|
|
21
24
|
#
|
|
22
|
-
# Exit 0 when main is green, 2 when a check is red (the bugs are on the ledger), 1 on an
|
|
25
|
+
# Exit 0 when main is green, 2 when a check is red (the bugs are on the ledger), 1 on an
|
|
26
|
+
# operational
|
|
23
27
|
# error. `QA_SWEEP_DOMAIN_DIR` picks the ledger, as for the other QA commands.
|
|
24
28
|
class QaMainGate
|
|
25
29
|
include Sweeping
|
|
@@ -36,7 +40,8 @@ module Hecks
|
|
|
36
40
|
# @param argv [Array<String>] no arguments
|
|
37
41
|
# @param root [String] the repository root, whose local `main` is checked
|
|
38
42
|
# @param env [Hash{String => String}] `QA_SWEEP_DOMAIN_DIR` picks another ledger
|
|
39
|
-
# @param gate [#run] runs the quick gate in a directory and answers its results; a spec
|
|
43
|
+
# @param gate [#run] runs the quick gate in a directory and answers its results; a spec
|
|
44
|
+
# injects one
|
|
40
45
|
# @param shell [Module] the fixer, landing and push commands; a spec injects one
|
|
41
46
|
# @return [Integer] the exit status
|
|
42
47
|
def self.call(argv, root:, env: ENV, gate: GateRun, shell: Shell)
|
|
@@ -16,7 +16,7 @@ module Hecks
|
|
|
16
16
|
|
|
17
17
|
private
|
|
18
18
|
|
|
19
|
-
# Spawns `hecks quality_control
|
|
19
|
+
# Spawns `hecks quality_control sweep.run target=<target>` as a fresh process, not a
|
|
20
20
|
# fork.
|
|
21
21
|
def spawn_sweep_child(target_reference, flags, modes:, parity: false)
|
|
22
22
|
# Unlinked at once: the open fd stays readable, nothing is left on disk, and no fixed
|
|
@@ -66,7 +66,7 @@ module Hecks
|
|
|
66
66
|
|
|
67
67
|
# Coverage-guided generation's corpus, one JSON file per (target, mode): `--all`'s waves can
|
|
68
68
|
# run a target's primary sweep and its persistence-parity/concurrency variants as separate
|
|
69
|
-
# `hecks quality_control
|
|
69
|
+
# `hecks quality_control sweep.run` processes within the same tick, and each explores
|
|
70
70
|
# a conceptually independent axis of the same domain, so each keeps its own file rather than
|
|
71
71
|
# sharing one that nothing here locks.
|
|
72
72
|
def coverage_corpus_path(target_reference, mode)
|
|
@@ -28,7 +28,7 @@ require_relative "qa_sweep/conclusion"
|
|
|
28
28
|
|
|
29
29
|
module Hecks
|
|
30
30
|
module QualityControlCli
|
|
31
|
-
# The command behind `hecks quality_control
|
|
31
|
+
# The command behind `hecks quality_control sweep.run`: runs one claim, sweep, conclude
|
|
32
32
|
# and release cycle against a `QualityControl` target (`USAGE` lists the forms, from a bare
|
|
33
33
|
# sweep of the least recently swept target to `--all` and `--release`). It stops with a report
|
|
34
34
|
# and exit 2 on the first surprised check, and never logs a `Bug` itself.
|
|
@@ -122,7 +122,8 @@ module Hecks
|
|
|
122
122
|
report(steps, reclaimed_holds(steps[1].output), write_tick_log!(steps))
|
|
123
123
|
end
|
|
124
124
|
|
|
125
|
-
# The steps that look for findings, in the order the report prints them. The event pump is
|
|
125
|
+
# The steps that look for findings, in the order the report prints them. The event pump is
|
|
126
|
+
# last.
|
|
126
127
|
def finding_steps
|
|
127
128
|
[
|
|
128
129
|
guarded("check_pull_requests") { run_pr_check },
|
|
@@ -161,7 +162,8 @@ module Hecks
|
|
|
161
162
|
[output, exit_code]
|
|
162
163
|
end
|
|
163
164
|
|
|
164
|
-
# The quick gate on local main, last: each red check it finds becomes a bug and a
|
|
165
|
+
# The quick gate on local main, last: each red check it finds becomes a bug and a
|
|
166
|
+
# self-contained one
|
|
165
167
|
# is fixed, landed and pushed by `QaMainGate`.
|
|
166
168
|
def run_main_check
|
|
167
169
|
banner "hecks quality_control check_main"
|
|
@@ -170,7 +172,8 @@ module Hecks
|
|
|
170
172
|
[output, exit_code]
|
|
171
173
|
end
|
|
172
174
|
|
|
173
|
-
# @return [Object] the dial's value from `qa/settings.yml`, or the fallback when it is not
|
|
175
|
+
# @return [Object] the dial's value from `qa/settings.yml`, or the fallback when it is not
|
|
176
|
+
# loaded
|
|
174
177
|
def dial(name, fallback)
|
|
175
178
|
return fallback unless defined?(::QualityControlDials) && ::QualityControlDials.const_defined?(name)
|
|
176
179
|
|
|
@@ -45,7 +45,7 @@ Hecks.bluebook "QualityControl" do
|
|
|
45
45
|
attribute :last_swept, SweptIn
|
|
46
46
|
# Recency-decayed count of how often this bluebook was worth sweeping; see `YieldScore`.
|
|
47
47
|
attribute :yield_score, YieldScore
|
|
48
|
-
# Consecutive `Release`s that came back clean; `hecks quality_control
|
|
48
|
+
# Consecutive `Release`s that came back clean; `hecks quality_control sweep.run` reads it to choose sweep depth.
|
|
49
49
|
# Zero means no streak yet, shared on purpose by a never-swept target and a just-surprised one.
|
|
50
50
|
attribute :clean_streak, CleanStreak
|
|
51
51
|
# Comma-joined mode names recorded at release from what the runner inferred. Stored so
|
|
@@ -267,7 +267,7 @@ Hecks.bluebook "QualityControl" do
|
|
|
267
267
|
end
|
|
268
268
|
|
|
269
269
|
query "Suspended" do
|
|
270
|
-
description "Bluebooks a check surprised, waiting for a person — each with the reason the policy wrote. Nothing automatic touches these; hecks quality_control
|
|
270
|
+
description "Bluebooks a check surprised, waiting for a person — each with the reason the policy wrote. Nothing automatic touches these; hecks quality_control sweep.run <target> --release --notes is how one comes back."
|
|
271
271
|
where(status: "suspended")
|
|
272
272
|
order_by :reference
|
|
273
273
|
end
|
|
@@ -2287,7 +2287,7 @@ Hecks.bluebook "QualityControl" do
|
|
|
2287
2287
|
# a boot never mints, and the mint names the commit its landing puts on main, so the ledger and main move
|
|
2288
2288
|
# together. The rehearsal ref and the expected watermark are checked against the live ledger by the era check.
|
|
2289
2289
|
# `refusal_total` is the rehearsal's refusal count and `ledger_dir` the bluebook directory of the ledger
|
|
2290
|
-
# the era belongs to (the QualityControl ledger when
|
|
2290
|
+
# the era belongs to (the QualityControl ledger when empty, its default, so it is always on the event). A mint with no `confirm` runs only when
|
|
2291
2291
|
# the auto-mint rule (`AutoMintAdditive`) holds; the mint entry point checks it.
|
|
2292
2292
|
command "MintEra" do
|
|
2293
2293
|
role "QA engineer"
|
|
@@ -2301,7 +2301,7 @@ Hecks.bluebook "QualityControl" do
|
|
|
2301
2301
|
attribute :watermark, Integer
|
|
2302
2302
|
attribute :rehearsal_verdict, MintVerdict
|
|
2303
2303
|
attribute :refusal_total, Integer, optional: true
|
|
2304
|
-
attribute :ledger_dir, String,
|
|
2304
|
+
attribute :ledger_dir, String, default: ""
|
|
2305
2305
|
attribute :confirm, Confirmation, optional: true
|
|
2306
2306
|
|
|
2307
2307
|
emits "MintRequested"
|
|
@@ -3039,14 +3039,14 @@ Hecks.bluebook "QualityControl" do
|
|
|
3039
3039
|
# Nothing reacts to `TicketRaised` on purpose: a ticket waits until somebody submits it.
|
|
3040
3040
|
|
|
3041
3041
|
# The rotation's one automatic reaction. Releasing on `SweepConcluded` cannot work: that event
|
|
3042
|
-
# carries no target id, `next_streak` or yield score, so `hecks quality_control
|
|
3042
|
+
# carries no target id, `next_streak` or yield score, so `hecks quality_control sweep.run` still releases.
|
|
3043
3043
|
# `on Sweep::CheckSurprised`, not `Sweep::Check::...`: an entity command's event is stamped with
|
|
3044
3044
|
# its parent aggregate. `Check.Surprised` restates `target` so the id rides in.
|
|
3045
3045
|
policy "SuspendOnSurprise" do
|
|
3046
3046
|
on Sweep::CheckSurprised
|
|
3047
3047
|
trigger Target::Suspend, with: {
|
|
3048
3048
|
target: :target,
|
|
3049
|
-
reason: { value: "a check surprised — a person releases with hecks quality_control
|
|
3049
|
+
reason: { value: "a check surprised — a person releases with hecks quality_control sweep.run <target> --release --notes" }
|
|
3050
3050
|
}
|
|
3051
3051
|
end
|
|
3052
3052
|
|
|
@@ -3193,14 +3193,14 @@ Hecks.bluebook "QualityControl" do
|
|
|
3193
3193
|
# (d) no stranded marker exists.
|
|
3194
3194
|
# Otherwise the mint is held: the entry point refuses it as "needs owner approval", and the policy below
|
|
3195
3195
|
# asks for the approval, which answers `MintNeedsApproval` and stops. A stale watermark or a stranded
|
|
3196
|
-
# marker is a refusal, never a held mint. The ask forwards
|
|
3197
|
-
#
|
|
3196
|
+
# marker is a refusal, never a held mint. The ask forwards `ledger_dir`, empty for the QualityControl
|
|
3197
|
+
# ledger, so a mint of another domain's ledger reaches that ledger.
|
|
3198
3198
|
policy "AutoMintAdditive" do
|
|
3199
3199
|
on "Maintenance.MintRequested"
|
|
3200
3200
|
where { rehearsal_verdict.value == "would_mint" }
|
|
3201
3201
|
ask :mint, with: { run: :run, domain: :domain, era: :era, sha: :sha, branch: :branch,
|
|
3202
3202
|
watermark: :watermark, rehearsal_verdict: :rehearsal_verdict,
|
|
3203
|
-
refusal_total: :refusal_total, confirm: :confirm }
|
|
3203
|
+
refusal_total: :refusal_total, ledger_dir: :ledger_dir, confirm: :confirm }
|
|
3204
3204
|
end
|
|
3205
3205
|
|
|
3206
3206
|
# Holds a mint the auto-mint rule does not cover for the owner. Reads the refusal the entry point named.
|
|
@@ -3212,8 +3212,8 @@ Hecks.bluebook "QualityControl" do
|
|
|
3212
3212
|
|
|
3213
3213
|
policy "LandOnMinted" do
|
|
3214
3214
|
on "Maintenance.Minted"
|
|
3215
|
-
where { branch
|
|
3216
|
-
ask :land, with: { run: :run, era: :era, sha: :sha, branch: :branch }
|
|
3215
|
+
where { branch != "" }
|
|
3216
|
+
ask :land, with: { run: :run, era: :era, sha: :sha, branch: :branch, ledger_dir: :ledger_dir }
|
|
3217
3217
|
end
|
|
3218
3218
|
|
|
3219
3219
|
# The release PR chain. A version bump opens its PR; the conditions judged on `ReleaseChecked` either
|
|
@@ -164,10 +164,13 @@ Hecks.hecksagon "QualityControl" do
|
|
|
164
164
|
refuses "MintApprovalRefused"
|
|
165
165
|
end
|
|
166
166
|
|
|
167
|
+
# The landing runs in the repository that holds the minted ledger: the Hecks checkout when
|
|
168
|
+
# `ledger_dir` is empty, else the git checkout around that directory.
|
|
167
169
|
asks "Land", to: Maintenance do
|
|
168
|
-
attribute :era,
|
|
169
|
-
attribute :sha,
|
|
170
|
-
attribute :branch,
|
|
170
|
+
attribute :era, MintedEra
|
|
171
|
+
attribute :sha, MaintenanceSha
|
|
172
|
+
attribute :branch, String
|
|
173
|
+
attribute :ledger_dir, String
|
|
171
174
|
answers "Landed"
|
|
172
175
|
refuses "MintStranded"
|
|
173
176
|
end
|
|
@@ -54,7 +54,8 @@ module Hecks
|
|
|
54
54
|
|
|
55
55
|
# Whether the hand-typed Reconstruction method reads `field`. Answers nil, not false, for a
|
|
56
56
|
# construct without one: nil means "not applicable", false means "applicable, and it fails",
|
|
57
|
-
# and the touchpoint reports each differently. Named without `?` because it is not a
|
|
57
|
+
# and the touchpoint reports each differently. Named without `?` because it is not a
|
|
58
|
+
# predicate.
|
|
58
59
|
def reconstruction_reads(name, field)
|
|
59
60
|
method_name = RECONSTRUCTION_METHODS[name]
|
|
60
61
|
return nil unless method_name
|
data/lib/hecks/refused.rb
CHANGED
|
@@ -18,7 +18,8 @@ module Hecks
|
|
|
18
18
|
# The last constant name of a class's path, such as `NotFound` of `Hecks::Runtime::NotFound`.
|
|
19
19
|
LAST_NAME = /\w+\z/
|
|
20
20
|
|
|
21
|
-
# The boundary between a lower-case or digit letter and a capital, where a snake-case word
|
|
21
|
+
# The boundary between a lower-case or digit letter and a capital, where a snake-case word
|
|
22
|
+
# starts.
|
|
22
23
|
CAMEL_BOUNDARY = /([a-z\d])([A-Z])/
|
|
23
24
|
|
|
24
25
|
# @return [Symbol, nil] the attribute the refusal is about, when it is about one; the
|