hecks 3.0.5 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/exe/hecks +24 -12
- data/lib/hecks/adapters/driven/lambda/client.rb +1 -1
- data/lib/hecks/adapters/driven/lambda.rb +1 -1
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +2 -2
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +59 -32
- data/lib/hecks/bluebook/hexagon.rb +45 -25
- data/lib/hecks/bluebook/model_check.rb +7 -7
- data/lib/hecks/chapters.rb +32 -3
- data/lib/hecks/cli/check_era.rb +1 -1
- data/lib/hecks/cli/deploy_template_diff.rb +1 -1
- data/lib/hecks/cli/doc_coverage.rb +1 -1
- data/lib/hecks/cli/domain_stub.rb +128 -0
- data/lib/hecks/cli/domain_writer.rb +39 -0
- data/lib/hecks/cli/interview_agent.rb +59 -0
- data/lib/hecks/cli/interview_draft.rb +246 -0
- data/lib/hecks/cli/interview_run.rb +87 -0
- data/lib/hecks/cli/interview_session.rb +299 -0
- data/lib/hecks/cli/model_check.rb +1 -1
- data/lib/hecks/cli/project_cli.rb +51 -29
- data/lib/hecks/cli/reference.rb +2 -1
- data/lib/hecks/cli/refresh_rspec_runtime_baseline.rb +1 -1
- data/lib/hecks/cli/seed_semantics_corpus.rb +3 -3
- data/lib/hecks/cli/spec_example.rb +1 -1
- data/lib/hecks/cli/stress_concurrency_specs.rb +14 -5
- data/lib/hecks/corpus.rb +12 -8
- data/lib/hecks/deploy/bluebook/deploy.bluebook +85 -12
- data/lib/hecks/deploy/oidc.json +5 -0
- data/lib/hecks/doc/reference.rb +4 -3
- data/lib/hecks/doors/cli_door.rb +13 -13
- data/lib/hecks/doors/cli_runner.rb +53 -33
- data/lib/hecks/doors/launcher_options.rb +5 -5
- data/lib/hecks/doors/mcp_door.rb +40 -18
- data/lib/hecks/doors/mcp_door_scope.rb +244 -24
- data/lib/hecks/doors/usage_cache.rb +123 -0
- data/lib/hecks/doors.rb +1 -1
- data/lib/hecks/embryonaut_bluebook.rb +2 -2
- data/lib/hecks/forms/banking_presentation.rb +1 -1
- data/lib/hecks/framework/bluebook/compliance.bluebook +1 -1
- data/lib/hecks/framework/bluebook/console_settings.bluebook +1 -1
- data/lib/hecks/framework/bluebook/framework.hecksagon +4 -4
- data/lib/hecks/framework.rb +1 -1
- data/lib/hecks/fuzzing/combination_miner.rb +1 -1
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +1 -1
- data/lib/hecks/fuzzing/differential.rb +4 -2
- data/lib/hecks/fuzzing/era_boundary.rb +1 -1
- data/lib/hecks/fuzzing/form_census.rb +2 -2
- data/lib/hecks/fuzzing/generated_domain_check.rb +1 -1
- data/lib/hecks/fuzzing/isolated_boot.rb +10 -3
- data/lib/hecks/fuzzing/persistence_parity.rb +2 -2
- data/lib/hecks/fuzzing/target_capabilities.rb +3 -3
- data/lib/hecks/gate/stages.yml +5 -5
- data/lib/hecks/grammar/grammar.hecksagon +2 -2
- data/lib/hecks/hecks/adapters/deploy_toolchain.rb +4 -4
- data/lib/hecks/hecks/adapters/finding_github.adapter +7 -0
- data/lib/hecks/hecks/adapters/finding_github.rb +114 -0
- data/lib/hecks/hecks/adapters/host_http.rb +4 -3
- data/lib/hecks/hecks/adapters/local_files.rb +59 -0
- data/lib/hecks/hecks/adapters/site_toolchain.adapter +7 -0
- data/lib/hecks/hecks/adapters/site_toolchain.rb +54 -0
- data/lib/hecks/hecks/adapters/terminal.rb +15 -0
- data/lib/hecks/hecks/codebase.bluebook +4 -3
- data/lib/hecks/hecks/context_map.hecksagon +2 -2
- data/lib/hecks/hecks/custodian.bluebook +81 -3
- data/lib/hecks/hecks/hecks.bluebook +18 -0
- data/lib/hecks/hecks/hecks.hecksagon +16 -4
- data/lib/hecks/hecks/hecks.world +7 -6
- data/lib/hecks/hecks/site.behaviors +17 -0
- data/lib/hecks/hecks/site.world +10 -0
- data/lib/hecks/language/bluebook/bluebook.hecksagon +1 -1
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +24 -47
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +4 -4
- data/lib/hecks/projections/deploy/box/settings.rb +324 -0
- data/lib/hecks/projections/deploy/box/templates/box.yaml.tmpl +268 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +68 -0
- data/lib/hecks/projections/deploy/box/templates/fetch-secrets.sh +21 -0
- data/lib/hecks/projections/deploy/box/templates/rds.yaml.tmpl +181 -0
- data/lib/hecks/projections/deploy/box/templates/render-compose-taskdef.sh.tmpl +49 -0
- data/lib/hecks/projections/deploy/box/templates/render-compose.sh.tmpl +43 -0
- data/lib/hecks/projections/deploy/box/templates/restore-to-rds.sh.tmpl +91 -0
- data/lib/hecks/projections/deploy/box/templates/verify-copy.sh.tmpl +58 -0
- data/lib/hecks/projections/deploy/box.rb +553 -0
- data/lib/hecks/projections/deploy/fargate/cdn.rb +13 -1
- data/lib/hecks/projections/deploy/fargate.rb +1 -1
- data/lib/hecks/projections/deploy/lambda.rb +11 -10
- data/lib/hecks/projections/deploy/scripts/hosting.mk.tmpl +1 -1
- data/lib/hecks/projections/diagrams.rb +2 -2
- data/lib/hecks/projections/site/edge/behaviours.rb +107 -0
- data/lib/hecks/projections/site/edge/checks.rb +137 -0
- data/lib/hecks/projections/site/edge/pattern.rb +50 -0
- data/lib/hecks/projections/site/edge/rules.rb +128 -0
- data/lib/hecks/projections/site/edge.rb +136 -0
- data/lib/hecks/projections/site/regions.rb +44 -0
- data/lib/hecks/projections/site/routes_ts.rb +284 -0
- data/lib/hecks/projections/site/site_cdn.rb +78 -0
- data/lib/hecks/projections/site/table/checks.rb +124 -0
- data/lib/hecks/projections/site/table/row_builder.rb +155 -0
- data/lib/hecks/projections/site/table.rb +98 -0
- data/lib/hecks/projections.rb +2 -0
- data/lib/hecks/projector/cli_projector.rb +153 -111
- data/lib/hecks/quality_control/adapters/agent.adapter +1 -1
- data/lib/hecks/quality_control/adapters/agent.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_concurrency_racer.rb +3 -3
- data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +8 -8
- data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +3 -3
- data/lib/hecks/quality_control/cli/qa_generated_domains.rb +4 -4
- data/lib/hecks/quality_control/cli/qa_log_bug.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +6 -5
- data/lib/hecks/quality_control/cli/qa_postgres_migrate.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_postgres_role.rb +5 -5
- data/lib/hecks/quality_control/cli/qa_pr_check.rb +3 -3
- data/lib/hecks/quality_control/cli/qa_sweep/all_mode.rb +3 -3
- data/lib/hecks/quality_control/cli/qa_sweep/checks.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_sweep.rb +10 -9
- data/lib/hecks/quality_control/cli/qa_tick.rb +3 -3
- data/lib/hecks/quality_control/quality_control.bluebook +18 -18
- data/lib/hecks/quality_control/quality_control.hecksagon +1 -1
- data/lib/hecks/runtime/loader.rb +31 -8
- data/lib/hecks/runtime/registry/verification.rb +15 -29
- data/lib/hecks/runtime/registry.rb +14 -20
- data/lib/hecks/rust_build/append_optionals.rb +79 -0
- data/lib/hecks/rust_build/conformance_fuzz.rb +2 -1
- data/lib/hecks/rust_build/domain_name.rb +26 -0
- data/lib/hecks/rust_build/kernel_input.rb +58 -0
- data/lib/hecks/rust_build/project_rust/codegen_run.rb +143 -0
- data/lib/hecks/rust_build/project_rust/root_mod.rb +1 -1
- data/lib/hecks/rust_build/project_rust.rb +57 -24
- data/lib/hecks/rust_build/rust_literal.rb +33 -0
- data/lib/hecks/rust_build/write_if_changed.rb +86 -0
- data/lib/hecks/site/bluebook/site.bluebook +205 -0
- data/lib/hecks/site/bluebook/site.hecksagon +5 -0
- data/lib/hecks/site/bluebook/site.ports.hecksagon +15 -0
- data/lib/hecks/sme/bluebook/sme.bluebook +336 -0
- data/lib/hecks/sme/bluebook/sme.hecksagon +5 -0
- data/lib/hecks/storehouse.rb +37 -7
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +1 -1
- data/lib/hecks/three_zero/forms.yml +88 -88
- data/lib/hecks/tickets/bluebook/tickets.bluebook +331 -0
- data/lib/hecks/tickets/bluebook/tickets.hecksagon +11 -0
- data/lib/hecks/tickets/bluebook/tickets.ports.hecksagon +26 -0
- data/lib/hecks/tools/ci_gates.rb +1 -1
- data/lib/hecks/tools/deploy_recipe.rb +14 -6
- data/lib/hecks/tools/oidc_manifests.rb +2 -1
- data/lib/hecks/tools/sequence_script.rb +2 -2
- data/lib/hecks/tools/site_routes.rb +152 -0
- data/lib/hecks/tools/tenant_provisioning.rb +1 -1
- data/lib/hecks/tools.rb +1 -0
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks.rb +1 -0
- data/rust/build/src/cargo_sync.rs +1 -1
- data/rust/build/src/pipeline.rs +23 -16
- data/rust/build/src/resolve.rs +39 -6
- data/rust/codegen/src/json_codec.rs +3 -0
- data/rust/codegen/src/naming.rs +4 -0
- data/rust/codegen/src/types.rs +45 -0
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/src/api.rs +1 -1
- data/rust/host/src/dispatch.rs +87 -7
- data/rust/host/src/needs.rs +111 -0
- data/rust/parser/src/keywords.rs +7 -6
- data/rust/parser/src/main.rs +4 -3
- data/rust/parser/src/parse/chapter.rs +1 -1
- data/rust/parser/src/parse/hecksagon.rs +25 -10
- data/rust/project/mutations.rb +4 -30
- data/rust/project/naming.rb +8 -28
- data/rust/project/write_if_changed.rb +4 -80
- data/rust/project_rust_pipeline.rb +16 -12
- data/rust/src/kernel/cli.rs +2 -0
- data/rust/src/kernel/needs.rs +74 -1
- metadata +51 -2
|
@@ -2,7 +2,7 @@ require_relative "../naming"
|
|
|
2
2
|
|
|
3
3
|
module Hecks
|
|
4
4
|
module Projector
|
|
5
|
-
# Projects a bluebook as its own command-line surface: the
|
|
5
|
+
# Projects a bluebook as its own command-line surface: the command tree, argument spec
|
|
6
6
|
# and usage text. Nothing executes here; the generic runner (`hecks run`) parses against it.
|
|
7
7
|
#
|
|
8
8
|
# Argument types come from the declared field types, never from guessing at the
|
|
@@ -10,22 +10,22 @@ module Hecks
|
|
|
10
10
|
module CliProjector
|
|
11
11
|
module_function
|
|
12
12
|
|
|
13
|
-
# Projects the
|
|
13
|
+
# Projects the command and question tables and the usage text. Commands and queries are
|
|
14
14
|
# separate namespaces (a chapter may declare both under one name); ask a query with `ask`.
|
|
15
15
|
#
|
|
16
16
|
# @param bluebook [Bluebook::Chapter] the booted domain to project
|
|
17
17
|
# @param options [Hash{Symbol => Object}] `:program` (default `"hecks run"`) for the usage
|
|
18
|
-
# text; `:
|
|
18
|
+
# text; `:command` and `:ask` select one command's `--help` text; `:names` maps a launcher
|
|
19
19
|
# name to the command it stands for (`{ "mcp" => "serve_mcp" }`); `:mint_run_keys` makes a
|
|
20
|
-
# creating
|
|
21
|
-
# @return [Hash{Symbol => Object}] `:
|
|
20
|
+
# creating command's `run` key optional, since the launcher mints it
|
|
21
|
+
# @return [Hash{Symbol => Object}] `:commands`, `:questions`, `:names` (alias tables) and
|
|
22
22
|
# `:usage` (pre-rendered help text)
|
|
23
|
-
# @raise [Bluebook::DSL::Malformed] if two
|
|
23
|
+
# @raise [Bluebook::DSL::Malformed] if two commands project to the same command-line name
|
|
24
24
|
def call(bluebook:, options: {})
|
|
25
|
-
|
|
25
|
+
commands = {}
|
|
26
26
|
questions = {}
|
|
27
27
|
|
|
28
|
-
bluebook.aggregates.each { |aggregate| claim_aggregate(
|
|
28
|
+
bluebook.aggregates.each { |aggregate| claim_aggregate(commands, questions, bluebook, aggregate) }
|
|
29
29
|
|
|
30
30
|
# A report belongs to the chapter, not an aggregate, so it is addressed
|
|
31
31
|
# `Chapter.Report` (one dot) where a query is `Chapter::Aggregate.Query`.
|
|
@@ -33,59 +33,55 @@ module Hecks
|
|
|
33
33
|
claim(questions, Naming.snake(model.hecks_name), report_spec(bluebook, model))
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
mint_run_keys(
|
|
36
|
+
mint_run_keys(commands) if options[:mint_run_keys]
|
|
37
37
|
|
|
38
38
|
# The display name is the shortest unambiguous one; both spellings are accepted.
|
|
39
|
-
display_names([
|
|
39
|
+
display_names([commands, questions], options[:names])
|
|
40
40
|
|
|
41
|
-
{
|
|
42
|
-
names: { command: aliases(
|
|
43
|
-
usage: usage(bluebook,
|
|
41
|
+
{ commands: commands, questions: questions,
|
|
42
|
+
names: { command: aliases(commands), question: aliases(questions) },
|
|
43
|
+
usage: usage(bluebook, commands, questions, options) }
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
# Claims the
|
|
46
|
+
# Claims the commands and questions of one aggregate and its entities and ports.
|
|
47
47
|
#
|
|
48
|
-
# @param
|
|
48
|
+
# @param commands [Hash{String => Hash}] the command map, mutated in place
|
|
49
49
|
# @param questions [Hash{String => Hash}] the question map, mutated in place
|
|
50
50
|
# @param bluebook [Bluebook::Chapter] the booted domain
|
|
51
51
|
# @param aggregate [Bluebook::Aggregate] the aggregate to claim
|
|
52
52
|
# @return [void]
|
|
53
|
-
def claim_aggregate(
|
|
54
|
-
aggregate.commands.each { |c| claim(
|
|
53
|
+
def claim_aggregate(commands, questions, bluebook, aggregate)
|
|
54
|
+
aggregate.commands.each { |c| claim(commands, name_for(aggregate, c), command_spec(bluebook, aggregate, nil, c)) }
|
|
55
55
|
aggregate.queries.each { |q| claim(questions, name_for(aggregate, q), query_spec(bluebook, aggregate, nil, q)) }
|
|
56
56
|
|
|
57
57
|
aggregate.entities.each do |entity|
|
|
58
58
|
entity.commands.each do |c|
|
|
59
|
-
claim(
|
|
59
|
+
claim(commands, name_for(aggregate, c, entity), command_spec(bluebook, aggregate, entity, c))
|
|
60
60
|
end
|
|
61
61
|
entity.queries.each do |q|
|
|
62
62
|
claim(questions, name_for(aggregate, q, entity), query_spec(bluebook, aggregate, entity, q))
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
# Port operations dispatch by the same name as a command, so they are
|
|
66
|
+
# Port operations dispatch by the same name as a command, so they are commands too.
|
|
67
67
|
aggregate.ports.each do |port|
|
|
68
|
-
port.operations.each { |o| claim(
|
|
68
|
+
port.operations.each { |o| claim(commands, name_for(aggregate, o), port_spec(bluebook, aggregate, port, o)) }
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
-
# Sets `:short` on each spec
|
|
73
|
-
#
|
|
72
|
+
# Sets `:short` on each spec to its full `aggregate.command` name: a command is always
|
|
73
|
+
# called with its aggregate, so adding a command elsewhere never changes what a call means.
|
|
74
74
|
#
|
|
75
|
-
# @param specs [Hash{String => Hash}] the
|
|
75
|
+
# @param specs [Hash{String => Hash}] the command or query map, mutated in place
|
|
76
76
|
# @return [void]
|
|
77
|
-
def
|
|
78
|
-
|
|
79
|
-
specs.each do |name, spec|
|
|
80
|
-
tail = name.split(".").last
|
|
81
|
-
spec[:short] = tails[tail].length == 1 ? tail : name
|
|
82
|
-
end
|
|
77
|
+
def qualify(specs)
|
|
78
|
+
specs.each { |name, spec| spec[:short] = name }
|
|
83
79
|
end
|
|
84
80
|
|
|
85
|
-
# Marks the `run` key of each creating
|
|
81
|
+
# Marks the `run` key of each creating command `minted`: optional, and never filled by a bare
|
|
86
82
|
# word, since the launcher makes one when it is left out.
|
|
87
|
-
def mint_run_keys(
|
|
88
|
-
|
|
83
|
+
def mint_run_keys(commands)
|
|
84
|
+
commands.each_value do |spec|
|
|
89
85
|
next unless spec[:creates]
|
|
90
86
|
|
|
91
87
|
spec[:arguments] = spec[:arguments].map do |argument|
|
|
@@ -96,14 +92,14 @@ module Hecks
|
|
|
96
92
|
end
|
|
97
93
|
end
|
|
98
94
|
|
|
99
|
-
# Sets the display name of every spec in each map:
|
|
95
|
+
# Sets the display name of every spec in each map: its full name, then renamed by `names`.
|
|
100
96
|
#
|
|
101
|
-
# @param maps [Array<Hash{String => Hash}>] the
|
|
97
|
+
# @param maps [Array<Hash{String => Hash}>] the command and question maps, mutated in place
|
|
102
98
|
# @param names [Hash, nil] the chapter's launcher names
|
|
103
99
|
# @return [void]
|
|
104
100
|
def display_names(maps, names)
|
|
105
101
|
maps.each do |specs|
|
|
106
|
-
|
|
102
|
+
qualify(specs)
|
|
107
103
|
rename(specs, names)
|
|
108
104
|
end
|
|
109
105
|
end
|
|
@@ -114,7 +110,7 @@ module Hecks
|
|
|
114
110
|
# replaced keeps working (`:short_was`, read by `aliases`). A table entry naming no
|
|
115
111
|
# command in `specs` belongs to the other namespace and is skipped.
|
|
116
112
|
#
|
|
117
|
-
# @param specs [Hash{String => Hash}] the
|
|
113
|
+
# @param specs [Hash{String => Hash}] the command or question map, mutated in place
|
|
118
114
|
# @param names [Hash{String, Symbol => String, Symbol}, nil] launcher name to command name
|
|
119
115
|
# @return [void]
|
|
120
116
|
# @raise [Bluebook::DSL::Malformed] if a launcher name is already another command's name
|
|
@@ -131,8 +127,8 @@ module Hecks
|
|
|
131
127
|
end
|
|
132
128
|
end
|
|
133
129
|
|
|
134
|
-
# Maps every accepted spelling (full name, `:short` and a renamed command's old
|
|
135
|
-
#
|
|
130
|
+
# Maps every accepted spelling (full name, `:short` and a renamed command's old name)
|
|
131
|
+
# to the full name.
|
|
136
132
|
def aliases(specs)
|
|
137
133
|
specs.each_with_object({}) do |(name, spec), map|
|
|
138
134
|
map[name] = name
|
|
@@ -142,34 +138,34 @@ module Hecks
|
|
|
142
138
|
end
|
|
143
139
|
|
|
144
140
|
# Stores `spec` under `name`, refusing a second claim rather than silently
|
|
145
|
-
# keeping whichever
|
|
141
|
+
# keeping whichever command was walked first.
|
|
146
142
|
#
|
|
147
|
-
# @raise [Bluebook::DSL::Malformed] if `name` is already claimed by another
|
|
148
|
-
def claim(
|
|
149
|
-
if
|
|
143
|
+
# @raise [Bluebook::DSL::Malformed] if `name` is already claimed by another command
|
|
144
|
+
def claim(commands, name, spec)
|
|
145
|
+
if commands.key?(name)
|
|
150
146
|
raise Bluebook::DSL::Malformed,
|
|
151
|
-
"two
|
|
152
|
-
"#{
|
|
147
|
+
"two commands project to the command-line name #{name.inspect}: " \
|
|
148
|
+
"#{commands[name][:command]} and #{spec[:command]} — rename one"
|
|
153
149
|
end
|
|
154
150
|
|
|
155
|
-
|
|
151
|
+
commands[name] = spec
|
|
156
152
|
end
|
|
157
153
|
|
|
158
|
-
# The dotted command-line name, `aggregate[.entity].
|
|
159
|
-
def name_for(aggregate,
|
|
154
|
+
# The dotted command-line name, `aggregate[.entity].command`, snake-cased.
|
|
155
|
+
def name_for(aggregate, command, entity = nil)
|
|
160
156
|
parts = [Naming.snake(aggregate.hecks_name)]
|
|
161
157
|
parts << Naming.snake(entity.hecks_name) if entity
|
|
162
|
-
parts << Naming.snake(
|
|
158
|
+
parts << Naming.snake(command.hecks_name)
|
|
163
159
|
parts.join(".")
|
|
164
160
|
end
|
|
165
161
|
|
|
166
|
-
# The
|
|
167
|
-
def fqn(bluebook, aggregate,
|
|
162
|
+
# The command's language name, `Chapter::Aggregate[.Entity].Verb`, for help text.
|
|
163
|
+
def fqn(bluebook, aggregate, command, entity = nil)
|
|
168
164
|
[bluebook.name, "::", aggregate.hecks_name, ".",
|
|
169
|
-
entity ? "#{entity.hecks_name}." : "",
|
|
165
|
+
entity ? "#{entity.hecks_name}." : "", command.hecks_name].join
|
|
170
166
|
end
|
|
171
167
|
|
|
172
|
-
# The options that name the record a
|
|
168
|
+
# The options that name the record a command acts on, before its own arguments.
|
|
173
169
|
# `nil` (a creating command) takes none: there is no record yet.
|
|
174
170
|
# Ports always pass `:aggregate`, since a port is declared on an aggregate.
|
|
175
171
|
def receiver_options(receiver, aggregate, entity)
|
|
@@ -204,7 +200,7 @@ module Hecks
|
|
|
204
200
|
arguments = receiver_options(receiver, aggregate, entity) + arguments
|
|
205
201
|
legacy_arguments = receiver == :aggregate ? [{ path: "id", type: "String", required: true }] : []
|
|
206
202
|
|
|
207
|
-
{
|
|
203
|
+
{ command: fqn(bluebook, aggregate, command, entity), kind: :command,
|
|
208
204
|
summary: command.goal, role: command.role, role_gated: !command.role.to_s.empty?,
|
|
209
205
|
group: aggregate.hecks_name, internal: command.role.to_s == "System",
|
|
210
206
|
creates: command.creates?,
|
|
@@ -214,15 +210,15 @@ module Hecks
|
|
|
214
210
|
arguments: arguments }
|
|
215
211
|
end
|
|
216
212
|
|
|
217
|
-
# A port operation reads as a
|
|
213
|
+
# A port operation reads as a command but reports as a boundary: it creates no
|
|
218
214
|
# record, and an outbound failure is the other side's sentence, so `refusals` is empty.
|
|
219
215
|
def port_spec(bluebook, aggregate, port, operation)
|
|
220
216
|
arguments = receiver_options(:aggregate, aggregate, nil) +
|
|
221
217
|
operation.attributes.flat_map { |a| options_for(a, aggregate, aggregate) }
|
|
222
218
|
|
|
223
219
|
# `Dispatcher#dispatch` looks the head up as a port, so the wire name is
|
|
224
|
-
# `Aggregate.Port.Operation`; the
|
|
225
|
-
{
|
|
220
|
+
# `Aggregate.Port.Operation`; the launcher name hides the port.
|
|
221
|
+
{ command: [fqn(bluebook, aggregate, operation).sub(/\.[^.]+\z/, ""), port.name, operation.hecks_name].join("."),
|
|
226
222
|
kind: :command, creates: false, receiver: :aggregate, refusals: [],
|
|
227
223
|
# `role:` is help text only; port dispatch never reaches the role check,
|
|
228
224
|
# so `role_gated` is always false here.
|
|
@@ -254,17 +250,17 @@ module Hecks
|
|
|
254
250
|
[]
|
|
255
251
|
end
|
|
256
252
|
|
|
257
|
-
{
|
|
253
|
+
{ command: "#{bluebook.name}.#{model.hecks_name}", kind: :query,
|
|
258
254
|
summary: model.description, arguments: arguments }
|
|
259
255
|
end
|
|
260
256
|
|
|
261
257
|
# A question that only reads the aggregate's own records back: it returns no document and
|
|
262
258
|
# filters on nothing but the record's identity ("how one request ended") or its lifecycle
|
|
263
259
|
# status ("every request that was refused"). Each journaled run has such a pair, which a
|
|
264
|
-
# person reads through `hecks <
|
|
265
|
-
# sets them apart with the bookkeeping
|
|
260
|
+
# person reads through `hecks <command> --wait` rather than asking for by name, so the help
|
|
261
|
+
# sets them apart with the bookkeeping commands. A query that returns a document, or filters
|
|
266
262
|
# on anything else, is a real question. Only an aggregate that journals its own runs (it has
|
|
267
|
-
# system-role commands, the ones `internal`
|
|
263
|
+
# system-role commands, the ones `internal` commands are made of) has such a pair: a release's
|
|
268
264
|
# "every version that was shipped" is a question worth asking by name.
|
|
269
265
|
def bookkeeping_query?(aggregate, query)
|
|
270
266
|
return false if query.returns || query.wheres.empty?
|
|
@@ -280,7 +276,7 @@ module Hecks
|
|
|
280
276
|
attribute ? options_for(attribute, entity || aggregate, aggregate) : []
|
|
281
277
|
end
|
|
282
278
|
|
|
283
|
-
{
|
|
279
|
+
{ command: fqn(bluebook, aggregate, query, entity), kind: :query, group: aggregate.hecks_name,
|
|
284
280
|
internal: entity.nil? && bookkeeping_query?(aggregate, query),
|
|
285
281
|
summary: query.description, arguments: arguments, returns: query.returns }
|
|
286
282
|
end
|
|
@@ -301,7 +297,7 @@ module Hecks
|
|
|
301
297
|
return [reference_option(attribute)] if attribute.reference?
|
|
302
298
|
|
|
303
299
|
value_object = value_object_for(attribute, holder, aggregate)
|
|
304
|
-
return [scalar_option(path, attribute, optional)] unless value_object
|
|
300
|
+
return with_declared_default([scalar_option(path, attribute, optional)], attribute) unless value_object
|
|
305
301
|
|
|
306
302
|
# The list flag rides on each leaf: without it a repeated flag overwrote the
|
|
307
303
|
# leaf silently, and CliDoor only ever sees a path and a spec.
|
|
@@ -312,12 +308,31 @@ module Hecks
|
|
|
312
308
|
scalar_option("#{path}.#{field.name}", field, optional || field.optional?,
|
|
313
309
|
enum: closed_members(value_object, field))
|
|
314
310
|
end
|
|
311
|
+
fields = with_declared_default(fields, attribute)
|
|
315
312
|
|
|
316
313
|
return fields unless attribute.list?
|
|
317
314
|
|
|
318
315
|
fields.map { |option| option.merge(list: true, note: [option[:note], "repeatable"].compact.join("; ")) }
|
|
319
316
|
end
|
|
320
317
|
|
|
318
|
+
# Shows the default the attribute itself declares (`attribute :runs, Count, default: 30`) on
|
|
319
|
+
# the leaf it fills: the matching field for a hash default, the lone field for a bare one.
|
|
320
|
+
# An argument with a default is never required, since the runtime fills it when omitted.
|
|
321
|
+
#
|
|
322
|
+
# @param options [Array<Hash{Symbol => Object}>] the attribute's leaf option specs
|
|
323
|
+
# @param attribute [Object] the attribute, which may declare a default
|
|
324
|
+
# @return [Array<Hash{Symbol => Object}>] `options`, with the default shown where it lands
|
|
325
|
+
def with_declared_default(options, attribute)
|
|
326
|
+
declared = attribute.default if attribute.respond_to?(:default)
|
|
327
|
+
return options if declared.nil?
|
|
328
|
+
|
|
329
|
+
options.map do |option|
|
|
330
|
+
leaf = option[:path].split(".").last
|
|
331
|
+
value = declared.is_a?(Hash) ? declared.fetch(leaf.to_sym) { declared[leaf] } : (declared if options.one?)
|
|
332
|
+
value.nil? ? option : option.merge(default: value, required: false)
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
321
336
|
def reference_option(attribute)
|
|
322
337
|
{ path: attribute.name.to_s, type: "String", required: !attribute.optional?,
|
|
323
338
|
note: "id of a #{attribute.type.target_name}" }
|
|
@@ -353,7 +368,7 @@ module Hecks
|
|
|
353
368
|
nil
|
|
354
369
|
end
|
|
355
370
|
|
|
356
|
-
# The conditions that refuse the
|
|
371
|
+
# The conditions that refuse the command when they hold, in the chapter's own words: a
|
|
357
372
|
# lifecycle state mismatch and a missing referenced record per `reference_to`.
|
|
358
373
|
def refusals(command, holder)
|
|
359
374
|
out = []
|
|
@@ -366,73 +381,100 @@ module Hecks
|
|
|
366
381
|
out
|
|
367
382
|
end
|
|
368
383
|
|
|
369
|
-
# The conditions the
|
|
384
|
+
# The conditions the command needs: each `given` states what must hold, so the command is
|
|
370
385
|
# refused unless it does.
|
|
371
386
|
def requirements(command) = command.givens.map(&:description)
|
|
372
387
|
|
|
373
|
-
# Renders the full
|
|
374
|
-
# `options[:
|
|
375
|
-
def usage(bluebook,
|
|
388
|
+
# Renders the full command/question table, or one command's `--help` text when
|
|
389
|
+
# `options[:command]` names one.
|
|
390
|
+
def usage(bluebook, commands, questions, options)
|
|
376
391
|
program = options[:program] || "hecks run"
|
|
377
|
-
only = options[:
|
|
392
|
+
only = options[:command]
|
|
378
393
|
|
|
379
394
|
# `options[:ask]` picks the namespace when both hold the name; without it a
|
|
380
395
|
# question's `--help` would print the command sharing its name.
|
|
381
396
|
if only
|
|
382
|
-
pool = options[:ask] ? questions :
|
|
397
|
+
pool = options[:ask] ? questions : commands
|
|
383
398
|
key = aliases(pool)[only] || (options[:ask] ? nil : aliases(questions)[only])
|
|
384
399
|
spec = pool[key] || questions[key]
|
|
385
|
-
return
|
|
400
|
+
return command_help(program, spec[:short], spec, ask: options[:ask]) if spec
|
|
386
401
|
end
|
|
387
402
|
|
|
388
|
-
width = (verbs.values + questions.values).map { |spec| spec[:short].length }.max.to_i
|
|
389
403
|
out = ["#{bluebook.name} — #{bluebook.vision}", "",
|
|
390
|
-
" #{program} <
|
|
391
|
-
" #{program}
|
|
404
|
+
" #{program} <command>! [name=value …] do something",
|
|
405
|
+
" #{program} query <query> [name=value …] read something", ""]
|
|
392
406
|
|
|
393
|
-
out << "
|
|
394
|
-
out.concat(listing(
|
|
407
|
+
out << "commands:"
|
|
408
|
+
out.concat(listing(commands) { |spec| spec[:summary] })
|
|
395
409
|
out << ""
|
|
396
|
-
out << "
|
|
397
|
-
out.concat(listing(questions
|
|
410
|
+
out << "queries (nothing here changes anything):"
|
|
411
|
+
out.concat(listing(questions) { |spec| first_sentence(spec[:summary]) })
|
|
398
412
|
out << ""
|
|
399
|
-
out << " #{program} <
|
|
400
|
-
out << " a
|
|
413
|
+
out << " #{program} <command> --help what one command wants, and every way it refuses"
|
|
414
|
+
out << " a command is called with its aggregate — #{example_qualified(commands)}"
|
|
401
415
|
out.join("\n")
|
|
402
416
|
end
|
|
403
417
|
|
|
404
|
-
# The
|
|
405
|
-
# under a heading per aggregate, so related
|
|
406
|
-
#
|
|
407
|
-
#
|
|
408
|
-
|
|
418
|
+
# The command or question lines of the help. A domain with more than one aggregate is listed
|
|
419
|
+
# under a heading per aggregate, so related commands sit together; the heading is the prefix
|
|
420
|
+
# every call to them carries, so the lines under it leave it out. The bookkeeping a run
|
|
421
|
+
# records about itself (`internal`: system-role commands and port operations) is set apart as
|
|
422
|
+
# names only, since a person never types them. A single-aggregate domain keeps the plain
|
|
423
|
+
# list, each name in full.
|
|
424
|
+
def listing(specs)
|
|
409
425
|
shown, internal = specs.values.partition { |spec| !spec[:internal] }
|
|
410
426
|
groups = shown.group_by { |spec| spec[:group] }
|
|
427
|
+
grouped = groups.length > 1
|
|
428
|
+
named = shown.to_h { |spec| [spec, entry_name(spec, grouped)] }
|
|
429
|
+
width = named.values.map(&:length).max.to_i
|
|
411
430
|
lines = []
|
|
412
|
-
if
|
|
431
|
+
if grouped
|
|
413
432
|
groups.each do |group, members|
|
|
414
433
|
lines << " #{heading(group)}" if group
|
|
415
|
-
members.each { |spec| lines << " #{spec
|
|
434
|
+
members.each { |spec| lines << " #{named[spec].ljust(width)} #{yield(spec)}#{alias_note(spec)}" }
|
|
416
435
|
end
|
|
417
436
|
else
|
|
418
|
-
shown.each { |spec| lines << " #{spec
|
|
437
|
+
shown.each { |spec| lines << " #{named[spec].ljust(width)} #{yield(spec)}#{alias_note(spec)}" }
|
|
419
438
|
end
|
|
420
439
|
lines.concat(internal_lines(internal)) unless internal.empty?
|
|
421
440
|
lines
|
|
422
441
|
end
|
|
423
442
|
|
|
424
|
-
#
|
|
425
|
-
# these aggregates share, so it adds nothing under a heading ("Test suite", "Model check").
|
|
443
|
+
# The aggregate's own name as a heading: the prefix of every call to the lines under it.
|
|
426
444
|
def heading(group)
|
|
427
|
-
|
|
428
|
-
|
|
445
|
+
"#{Naming.snake(group)}:"
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
# A spec's name as listed: under its aggregate's heading the aggregate prefix is left out.
|
|
449
|
+
# A command the chapter gives a short name (`mcp`) is listed by its real name, so the
|
|
450
|
+
# heading and the line still spell a call; `alias_note` says the short name.
|
|
451
|
+
def entry_name(spec, grouped)
|
|
452
|
+
name = label(spec, real: true)
|
|
453
|
+
return name unless grouped && spec[:group]
|
|
454
|
+
|
|
455
|
+
name.delete_prefix("#{Naming.snake(spec[:group])}.")
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
# " (also: mcp!)" for a spec the chapter gave a short name, else nothing. A short name that
|
|
459
|
+
# is only the command's own name (`init` for `door.init`) is already in the line.
|
|
460
|
+
def alias_note(spec)
|
|
461
|
+
return "" if spec[:short_was].nil? || spec[:short_was].split(".").last == spec[:short]
|
|
462
|
+
|
|
463
|
+
" (also: #{label(spec)})"
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
# A command is written with the `!` that marks it; a query without one.
|
|
467
|
+
# With `real:`, a short name the chapter gave it gives way to the name it was given for.
|
|
468
|
+
def label(spec, real: false)
|
|
469
|
+
name = real && spec.key?(:short_was) ? spec[:short_was] : spec[:short]
|
|
470
|
+
spec[:kind] == :command ? "#{name}!" : name
|
|
429
471
|
end
|
|
430
472
|
|
|
431
|
-
# The internal
|
|
473
|
+
# The internal commands as bare names, wrapped, under one line saying what they are.
|
|
432
474
|
def internal_lines(specs)
|
|
433
475
|
lines = [" internal — what a run records about itself, named here only (`--help` still works):"]
|
|
434
476
|
line = " "
|
|
435
|
-
specs.map { |spec| spec
|
|
477
|
+
specs.map { |spec| label(spec) }.each do |name|
|
|
436
478
|
if line.length + name.length + 1 > 98
|
|
437
479
|
lines << line
|
|
438
480
|
line = " "
|
|
@@ -442,41 +484,41 @@ module Hecks
|
|
|
442
484
|
lines << line
|
|
443
485
|
end
|
|
444
486
|
|
|
445
|
-
#
|
|
446
|
-
def example_qualified(
|
|
447
|
-
name
|
|
448
|
-
name ? "#{
|
|
487
|
+
# An example call, `aggregate.command!`, from the first command, or `""` when there is none.
|
|
488
|
+
def example_qualified(commands)
|
|
489
|
+
name = commands.keys.first
|
|
490
|
+
name ? "#{name}!" : ""
|
|
449
491
|
end
|
|
450
492
|
|
|
451
|
-
# The
|
|
493
|
+
# The command table wants a query description's first sentence; `--help` prints all.
|
|
452
494
|
def first_sentence(text)
|
|
453
495
|
text.to_s.split(/(?<=\.)\s/).first.to_s
|
|
454
496
|
end
|
|
455
497
|
|
|
456
498
|
# Four independent blocks, concatenated in fixed order: meta, invocation,
|
|
457
499
|
# arguments, refusals.
|
|
458
|
-
def
|
|
459
|
-
out =
|
|
460
|
-
out.concat(
|
|
461
|
-
out.concat(
|
|
462
|
-
out.concat(
|
|
500
|
+
def command_help(program, name, spec, ask: false)
|
|
501
|
+
out = command_help_meta_lines(name, spec)
|
|
502
|
+
out.concat(command_help_invocation_lines(program, name, spec, ask))
|
|
503
|
+
out.concat(command_help_argument_lines(spec))
|
|
504
|
+
out.concat(command_help_refusal_lines(spec))
|
|
463
505
|
out.join("\n")
|
|
464
506
|
end
|
|
465
507
|
|
|
466
|
-
def
|
|
508
|
+
def command_help_meta_lines(name, spec)
|
|
467
509
|
out = ["#{name} — #{spec[:summary]}", ""]
|
|
468
|
-
out << "dispatches #{spec[:
|
|
469
|
-
out << "reads #{spec[:
|
|
470
|
-
out << "issued by #{spec[:role]}"
|
|
510
|
+
out << "dispatches #{spec[:command]}" if spec[:kind] == :command
|
|
511
|
+
out << "reads #{spec[:command]}" if spec[:kind] == :query
|
|
512
|
+
out << "issued by #{spec[:role]}" if spec[:role]
|
|
471
513
|
out
|
|
472
514
|
end
|
|
473
515
|
|
|
474
|
-
def
|
|
475
|
-
invocation = ask ? "#{program}
|
|
516
|
+
def command_help_invocation_lines(program, name, spec, ask)
|
|
517
|
+
invocation = ask ? "#{program} query #{name}" : "#{program} #{name}!"
|
|
476
518
|
["", " #{invocation}#{spec[:arguments].map { |a| " #{a[:path]}=…" }.join}", ""]
|
|
477
519
|
end
|
|
478
520
|
|
|
479
|
-
def
|
|
521
|
+
def command_help_argument_lines(spec)
|
|
480
522
|
return [] if spec[:arguments].empty?
|
|
481
523
|
|
|
482
524
|
width = spec[:arguments].map { |a| a[:path].length }.max
|
|
@@ -493,7 +535,7 @@ module Hecks
|
|
|
493
535
|
lines << ""
|
|
494
536
|
end
|
|
495
537
|
|
|
496
|
-
def
|
|
538
|
+
def command_help_refusal_lines(spec)
|
|
497
539
|
[["refused when:", spec[:refusals]], ["refused unless:", spec[:requirements]]].flat_map do |heading, items|
|
|
498
540
|
Array(items).empty? ? [] : [heading, *items.map { |item| " #{item}" }, ""]
|
|
499
541
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Binds the `Agent` port to `Agent`; `hecks quality_control mine_combinations` asks it to write candidate domains.
|
|
1
|
+
# Binds the `Agent` port to `Agent`; `hecks quality_control target.mine_combinations` asks it to write candidate domains.
|
|
2
2
|
# Required by name, not relative, so a copy of this directory shares the one implementation loaded.
|
|
3
3
|
require "hecks/quality_control/adapters/agent"
|
|
4
4
|
|
|
@@ -6,8 +6,8 @@ require "shellwords"
|
|
|
6
6
|
module Hecks
|
|
7
7
|
module Adapters
|
|
8
8
|
# The `Agent` port's adapter: hands a prompt to an agent on the command line and lets it work
|
|
9
|
-
# in the checkout, the way `hecks quality_control mine_combinations` asks one for
|
|
10
|
-
# domains.
|
|
9
|
+
# in the checkout, the way `hecks quality_control target.mine_combinations` asks one for
|
|
10
|
+
# candidate domains.
|
|
11
11
|
#
|
|
12
12
|
# Not the interviewer behind the framework's `agent` port (`ClaudeCode`), which reads one
|
|
13
13
|
# JSON answer back: this agent edits files, so the only answer is whether it finished.
|
|
@@ -7,14 +7,14 @@ require_relative "../../fuzzing/concurrent_dispatch"
|
|
|
7
7
|
|
|
8
8
|
module Hecks
|
|
9
9
|
module QualityControlCli
|
|
10
|
-
# The command behind `hecks quality_control race`: one racer of
|
|
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 query 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
|
#
|
|
17
|
-
# hecks quality_control race <domain-path> <database> <schema> <verb> <args-json>
|
|
17
|
+
# hecks quality_control sweep.race <domain-path> <database> <schema> <verb> <args-json>
|
|
18
18
|
#
|
|
19
19
|
# It prints one line, `succeeded`, `refused` or `crashed:<class>: <message>`, and always exits
|
|
20
20
|
# 0.
|
|
@@ -5,15 +5,15 @@ require "open3"
|
|
|
5
5
|
|
|
6
6
|
module Hecks
|
|
7
7
|
module QualityControlCli
|
|
8
|
-
# The command behind `hecks quality_control discover_external_domains`: reports
|
|
9
|
-
# bluebook domains under `--projects-dir` that already depend on the hecks gem but
|
|
10
|
-
# enrolled yet. It never enrolls anything itself.
|
|
8
|
+
# The command behind `hecks quality_control target.discover_external_domains`: reports
|
|
9
|
+
# sibling-repo bluebook domains under `--projects-dir` that already depend on the hecks gem but
|
|
10
|
+
# are not enrolled yet. It never enrolls anything itself.
|
|
11
11
|
#
|
|
12
|
-
# hecks quality_control discover_external_domains
|
|
13
|
-
# hecks quality_control discover_external_domains --projects-dir ~/Projects
|
|
14
|
-
# hecks quality_control discover_external_domains --max-depth 4
|
|
15
|
-
# hecks quality_control discover_external_domains --known-path <path> ... # bypass
|
|
16
|
-
# ledger read
|
|
12
|
+
# hecks quality_control target.discover_external_domains
|
|
13
|
+
# hecks quality_control target.discover_external_domains --projects-dir ~/Projects
|
|
14
|
+
# hecks quality_control target.discover_external_domains --max-depth 4
|
|
15
|
+
# hecks quality_control target.discover_external_domains --known-path <path> ... # bypass
|
|
16
|
+
# the ledger read
|
|
17
17
|
class QaDiscoverExternalDomains
|
|
18
18
|
USAGE = "usage: hecks quality_control discover_external_domains [--projects-dir <path>] " \
|
|
19
19
|
"[--max-depth N] [--known-path <path> ...]"
|
|
@@ -7,10 +7,10 @@ require_relative "../../fuzzing/form_census"
|
|
|
7
7
|
|
|
8
8
|
module Hecks
|
|
9
9
|
module QualityControlCli
|
|
10
|
-
# The command behind `hecks quality_control judge_novelty`: does a new stress domain earn
|
|
11
|
-
# place? It is the gate before a domain becomes a `Target`.
|
|
10
|
+
# The command behind `hecks quality_control target.judge_novelty`: does a new stress domain earn
|
|
11
|
+
# its place? It is the gate before a domain becomes a `Target`.
|
|
12
12
|
#
|
|
13
|
-
# hecks quality_control judge_novelty <domain-path> [--against <path> ...]
|
|
13
|
+
# hecks quality_control target.judge_novelty <domain-path> [--against <path> ...]
|
|
14
14
|
#
|
|
15
15
|
# A domain is new when it puts two forms together on one aggregate that no existing target does
|
|
16
16
|
# (`Hecks::Fuzzing::FormCensus`). It is compared against the ledger's `Target.path` rows unless
|
|
@@ -12,8 +12,8 @@ require_relative "child"
|
|
|
12
12
|
|
|
13
13
|
module Hecks
|
|
14
14
|
module QualityControlCli
|
|
15
|
-
# The command behind `hecks quality_control check_generated_domains`: generates domains
|
|
16
|
-
# wrote and checks them the way the rotation does (`USAGE` lists the forms: generate,
|
|
15
|
+
# The command behind `hecks quality_control target.check_generated_domains`: generates domains
|
|
16
|
+
# nobody wrote and checks them the way the rotation does (`USAGE` lists the forms: generate,
|
|
17
17
|
# `--blueprint`, `--source` and `--promote`).
|
|
18
18
|
#
|
|
19
19
|
# Exit codes: 0 every valid domain clean; 2 at least one finding; 1 operational error.
|
|
@@ -202,8 +202,8 @@ module Hecks
|
|
|
202
202
|
::QualityControlDials.const_get(name)
|
|
203
203
|
end
|
|
204
204
|
|
|
205
|
-
# `--from-dials` is how `hecks quality_control tick` runs this. The dials come from the
|
|
206
|
-
# of the bluebook before `Hecks.bluebook`, so the ledger's Postgres is not needed.
|
|
205
|
+
# `--from-dials` is how `hecks quality_control sweep.tick` runs this. The dials come from the
|
|
206
|
+
# text of the bluebook before `Hecks.bluebook`, so the ledger's Postgres is not needed.
|
|
207
207
|
# `QA_GENERATED_DOMAINS_PER_TICK` overrides the count for one run.
|
|
208
208
|
#
|
|
209
209
|
# @return [Boolean] false when the dials turn the step off, true once they are applied
|
|
@@ -7,11 +7,11 @@ require_relative "../../ports/persistence/plugins/era"
|
|
|
7
7
|
|
|
8
8
|
module Hecks
|
|
9
9
|
module QualityControlCli
|
|
10
|
-
# The command behind `hecks quality_control log`: logs a bug to the QualityControl ledger,
|
|
10
|
+
# The command behind `hecks quality_control bug.log`: logs a bug to the QualityControl ledger,
|
|
11
11
|
# proven, numbered and triaged. It runs `--demonstration` first and refuses unless it exits
|
|
12
12
|
# non-zero.
|
|
13
13
|
#
|
|
14
|
-
# hecks quality_control log --sweep <sweep-id> --title "…" \
|
|
14
|
+
# hecks quality_control bug.log --sweep <sweep-id> --title "…" \
|
|
15
15
|
# --demonstration "bundle exec rspec spec/x_spec.rb -e 'the case'" \
|
|
16
16
|
# --symptom "…" --expectation "…" --submitter "<who>" \
|
|
17
17
|
# --triage self_contained|bigger [--tag word]… [--reproduced yes|no]
|