hecks 3.0.3 → 3.0.4
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 +6 -2
- data/lib/hecks/bluebook/assembly/contracts.rb +3 -1
- data/lib/hecks/bluebook/assembly/marks.rb +3 -0
- data/lib/hecks/bluebook/command.rb +7 -5
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +1 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +42 -0
- data/lib/hecks/bluebook/expression/canonical_form.rb +12 -0
- data/lib/hecks/bluebook/expression/projection.json +21 -0
- data/lib/hecks/bluebook/meta_validator/shapes.rb +3 -0
- data/lib/hecks/cli/project_cli.rb +6 -2
- data/lib/hecks/deploy/bluebook/deploy.bluebook +34 -0
- data/lib/hecks/doors/cli_runner.rb +57 -12
- data/lib/hecks/doors/launcher_options.rb +23 -0
- data/lib/hecks/fuzzing/properties.rb +1 -1
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +11 -3
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +9 -7
- data/lib/hecks/gate/stages.yml +51 -0
- data/lib/hecks/grammar/expression.bluebook +6 -1
- data/lib/hecks/grammar/expression_operators.json +135 -0
- data/lib/hecks/hecks/adapters/codebase/gate.rb +37 -0
- data/lib/hecks/hecks/adapters/codebase/regeneration.rb +24 -4
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +3 -1
- data/lib/hecks/hecks/codebase.behaviors +12 -0
- data/lib/hecks/hecks/codebase.bluebook +194 -0
- data/lib/hecks/hecks/custodian.bluebook +2 -0
- data/lib/hecks/hecks/hecks.hecksagon +14 -0
- data/lib/hecks/hecks/hecks.world +3 -2
- data/lib/hecks/language/bluebook/command.bluebook +26 -0
- data/lib/hecks/language/bluebook/vocabulary.bluebook +29 -1
- data/lib/hecks/language/oidc.json +5 -0
- data/lib/hecks/ports/clock.rb +3 -1
- data/lib/hecks/projections/deploy/lambda.rb +3 -4
- data/lib/hecks/quality_control/quality_control.bluebook +6 -0
- data/lib/hecks/runtime/command_interpreter.rb +6 -4
- data/lib/hecks/runtime/entity_interpreter.rb +5 -2
- data/lib/hecks/runtime/interpreting.rb +29 -0
- data/lib/hecks/runtime/invocation.rb +5 -1
- data/lib/hecks/runtime/loader.rb +16 -13
- data/lib/hecks/runtime.rb +11 -0
- data/lib/hecks/three_zero/forms.yml +1 -1
- data/lib/hecks/tools/ci_gate_decision.rb +109 -0
- data/lib/hecks/tools/ci_gates.rb +124 -0
- data/lib/hecks/tools/gate.rb +117 -0
- data/lib/hecks/tools.rb +3 -0
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +6 -1
- data/lib/hecks.rb +12 -2
- data/rust/codegen/src/naming.rs +13 -2
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/src/dispatch.rs +207 -1
- data/rust/host/src/main.rs +1 -0
- data/rust/host/src/needs.rs +243 -0
- data/rust/host/src/resend.rs +52 -10
- data/rust/host/src/server.rs +7 -3
- data/rust/parser/src/canonical.rs +137 -2
- data/rust/parser/src/emit.rs +25 -19
- data/rust/parser/src/ir.rs +2 -0
- data/rust/parser/src/keywords.rs +2 -0
- data/rust/parser/src/main.rs +1 -0
- data/rust/parser/src/parse/command.rs +44 -0
- data/rust/project/naming.rb +4 -2
- data/rust/src/kernel/cli.rs +3 -0
- data/rust/src/kernel/mod.rs +1 -0
- data/rust/src/kernel/needs.rs +194 -0
- data/rust/src/kernel/orchestrate.rs +84 -0
- data/rust/src/kernel/repository.rs +43 -2
- metadata +9 -2
|
@@ -211,6 +211,11 @@ Hecks.bluebook "Expression" do
|
|
|
211
211
|
|
|
212
212
|
member strategy: "collapse_whitespace", source_token: "", replacement: "", boundary: "none", position: 1
|
|
213
213
|
member strategy: "replace", source_token: ".length", replacement: ".size", boundary: "word", position: 2
|
|
214
|
+
# A duration written as a call on a whole number is that many seconds: `days(730)` is
|
|
215
|
+
# `63072000`. `source_token` names the call and `replacement` the seconds one unit is worth.
|
|
216
|
+
member strategy: "scale_call", source_token: "days", replacement: "86400", boundary: "word", position: 3
|
|
217
|
+
member strategy: "scale_call", source_token: "hours", replacement: "3600", boundary: "word", position: 4
|
|
218
|
+
member strategy: "scale_call", source_token: "minutes", replacement: "60", boundary: "word", position: 5
|
|
214
219
|
end
|
|
215
220
|
|
|
216
221
|
value_object "Reading" do
|
|
@@ -233,7 +238,7 @@ Hecks.bluebook "Expression" do
|
|
|
233
238
|
attribute :position, Position, optional: true
|
|
234
239
|
|
|
235
240
|
given("a rule must name a linked strategy") do
|
|
236
|
-
strategy.value == "collapse_whitespace" || strategy.value == "replace"
|
|
241
|
+
strategy.value == "collapse_whitespace" || strategy.value == "replace" || strategy.value == "scale_call"
|
|
237
242
|
end
|
|
238
243
|
|
|
239
244
|
given("a rule must state its boundary") { boundary.value == "none" || boundary.value == "word" }
|
|
@@ -1644,6 +1644,141 @@
|
|
|
1644
1644
|
}
|
|
1645
1645
|
}
|
|
1646
1646
|
},
|
|
1647
|
+
{
|
|
1648
|
+
"verb": "Expression::Normalisation.Propose",
|
|
1649
|
+
"args": {
|
|
1650
|
+
"name": {
|
|
1651
|
+
"value": "days_to_seconds"
|
|
1652
|
+
},
|
|
1653
|
+
"strategy": {
|
|
1654
|
+
"value": "scale_call"
|
|
1655
|
+
},
|
|
1656
|
+
"source_token": {
|
|
1657
|
+
"value": "days"
|
|
1658
|
+
},
|
|
1659
|
+
"replacement": {
|
|
1660
|
+
"value": "86400"
|
|
1661
|
+
},
|
|
1662
|
+
"boundary": {
|
|
1663
|
+
"value": "word"
|
|
1664
|
+
},
|
|
1665
|
+
"position": {
|
|
1666
|
+
"value": 3
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
},
|
|
1670
|
+
{
|
|
1671
|
+
"verb": "Expression::Normalisation.Read",
|
|
1672
|
+
"args": {
|
|
1673
|
+
"name": {
|
|
1674
|
+
"value": "days_to_seconds"
|
|
1675
|
+
},
|
|
1676
|
+
"target": {
|
|
1677
|
+
"value": "ruby"
|
|
1678
|
+
},
|
|
1679
|
+
"form": {
|
|
1680
|
+
"value": "text.gsub(/(?<![[:alnum:]_.])days\\(\\s*(\\d+)\\s*\\)/) { $1.to_i * 86400 }"
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
},
|
|
1684
|
+
{
|
|
1685
|
+
"verb": "Expression::Normalisation.Admit",
|
|
1686
|
+
"args": {
|
|
1687
|
+
"name": {
|
|
1688
|
+
"value": "days_to_seconds"
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
},
|
|
1692
|
+
{
|
|
1693
|
+
"verb": "Expression::Normalisation.Propose",
|
|
1694
|
+
"args": {
|
|
1695
|
+
"name": {
|
|
1696
|
+
"value": "hours_to_seconds"
|
|
1697
|
+
},
|
|
1698
|
+
"strategy": {
|
|
1699
|
+
"value": "scale_call"
|
|
1700
|
+
},
|
|
1701
|
+
"source_token": {
|
|
1702
|
+
"value": "hours"
|
|
1703
|
+
},
|
|
1704
|
+
"replacement": {
|
|
1705
|
+
"value": "3600"
|
|
1706
|
+
},
|
|
1707
|
+
"boundary": {
|
|
1708
|
+
"value": "word"
|
|
1709
|
+
},
|
|
1710
|
+
"position": {
|
|
1711
|
+
"value": 4
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
},
|
|
1715
|
+
{
|
|
1716
|
+
"verb": "Expression::Normalisation.Read",
|
|
1717
|
+
"args": {
|
|
1718
|
+
"name": {
|
|
1719
|
+
"value": "hours_to_seconds"
|
|
1720
|
+
},
|
|
1721
|
+
"target": {
|
|
1722
|
+
"value": "ruby"
|
|
1723
|
+
},
|
|
1724
|
+
"form": {
|
|
1725
|
+
"value": "text.gsub(/(?<![[:alnum:]_.])hours\\(\\s*(\\d+)\\s*\\)/) { $1.to_i * 3600 }"
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
},
|
|
1729
|
+
{
|
|
1730
|
+
"verb": "Expression::Normalisation.Admit",
|
|
1731
|
+
"args": {
|
|
1732
|
+
"name": {
|
|
1733
|
+
"value": "hours_to_seconds"
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
},
|
|
1737
|
+
{
|
|
1738
|
+
"verb": "Expression::Normalisation.Propose",
|
|
1739
|
+
"args": {
|
|
1740
|
+
"name": {
|
|
1741
|
+
"value": "minutes_to_seconds"
|
|
1742
|
+
},
|
|
1743
|
+
"strategy": {
|
|
1744
|
+
"value": "scale_call"
|
|
1745
|
+
},
|
|
1746
|
+
"source_token": {
|
|
1747
|
+
"value": "minutes"
|
|
1748
|
+
},
|
|
1749
|
+
"replacement": {
|
|
1750
|
+
"value": "60"
|
|
1751
|
+
},
|
|
1752
|
+
"boundary": {
|
|
1753
|
+
"value": "word"
|
|
1754
|
+
},
|
|
1755
|
+
"position": {
|
|
1756
|
+
"value": 5
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
},
|
|
1760
|
+
{
|
|
1761
|
+
"verb": "Expression::Normalisation.Read",
|
|
1762
|
+
"args": {
|
|
1763
|
+
"name": {
|
|
1764
|
+
"value": "minutes_to_seconds"
|
|
1765
|
+
},
|
|
1766
|
+
"target": {
|
|
1767
|
+
"value": "ruby"
|
|
1768
|
+
},
|
|
1769
|
+
"form": {
|
|
1770
|
+
"value": "text.gsub(/(?<![[:alnum:]_.])minutes\\(\\s*(\\d+)\\s*\\)/) { $1.to_i * 60 }"
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
},
|
|
1774
|
+
{
|
|
1775
|
+
"verb": "Expression::Normalisation.Admit",
|
|
1776
|
+
"args": {
|
|
1777
|
+
"name": {
|
|
1778
|
+
"value": "minutes_to_seconds"
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
},
|
|
1647
1782
|
{
|
|
1648
1783
|
"verb": "Expression::Operator.Propose",
|
|
1649
1784
|
"args": {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "tree"
|
|
4
|
+
require_relative "ruby_child"
|
|
5
|
+
|
|
6
|
+
module Hecks
|
|
7
|
+
module Adapters
|
|
8
|
+
module Codebase
|
|
9
|
+
# What Codebase's `GateRun` asks of the working tree: running a stage's checks.
|
|
10
|
+
#
|
|
11
|
+
# It runs `Hecks::Tools::Gate` in this process. The tool starts the stage's checks together,
|
|
12
|
+
# each as its own process, and answers 1 when any is red; the refusal is what the red checks
|
|
13
|
+
# printed, so `--wait` and the journal both say which check failed.
|
|
14
|
+
module Gate
|
|
15
|
+
# Every operation this family carries out.
|
|
16
|
+
OPERATIONS = %w[gate].freeze
|
|
17
|
+
|
|
18
|
+
module_function
|
|
19
|
+
|
|
20
|
+
# Runs the stage.
|
|
21
|
+
#
|
|
22
|
+
# @param _operation [String] `gate`
|
|
23
|
+
# @param held [Hash] the `GateRun` record's fields: `stage`, and `only` when given
|
|
24
|
+
# @param tree [Tree] the working tree, already known to be a hecks checkout
|
|
25
|
+
# @param shell [#capture, nil] unused: the tool runs in this process
|
|
26
|
+
# @return [String] which checks passed
|
|
27
|
+
# @raise [ConsoleCapture::Failure] with what the red checks printed
|
|
28
|
+
def call(_operation, held, tree, shell: nil)
|
|
29
|
+
plain = ->(name) { held[name].is_a?(Hash) ? held[name][:value] : held[name] }
|
|
30
|
+
only = plain.call(:only).to_s
|
|
31
|
+
argv = [plain.call(:stage).to_s, *("only=#{only}" unless only.empty?)]
|
|
32
|
+
RubyChild.new(tree).answer("gate", *argv).strip
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -7,7 +7,7 @@ module Hecks
|
|
|
7
7
|
module Adapters
|
|
8
8
|
module Codebase
|
|
9
9
|
# What Codebase's `RegenerationRun` asks of the working tree: regenerating every corpus
|
|
10
|
-
# domain's committed Rust output.
|
|
10
|
+
# domain's committed Rust output, and projecting the CI path gates into the workflows.
|
|
11
11
|
#
|
|
12
12
|
# It runs `Hecks::Tools::RegenerationRun` in this process: the run forks once for each
|
|
13
13
|
# domain, in a fixed order, because the domains share the files they stamp. With `check` it
|
|
@@ -16,7 +16,7 @@ module Hecks
|
|
|
16
16
|
# files reports drift and writes only when it is confirmed.
|
|
17
17
|
module Regeneration
|
|
18
18
|
# Every operation this family carries out.
|
|
19
|
-
OPERATIONS = %w[regenerate_corpus].freeze
|
|
19
|
+
OPERATIONS = %w[regenerate_corpus project_ci_gates decide_ci_gate].freeze
|
|
20
20
|
|
|
21
21
|
# How many lines of a script's output a refusal keeps.
|
|
22
22
|
KEPT_LINES = 60
|
|
@@ -25,23 +25,43 @@ module Hecks
|
|
|
25
25
|
|
|
26
26
|
# Carries out the regeneration.
|
|
27
27
|
#
|
|
28
|
-
# @param
|
|
28
|
+
# @param operation [String] `regenerate_corpus` or `project_ci_gates`
|
|
29
29
|
# @param held [Hash] the `RegenerationRun` record's fields: `check`, `confirm`
|
|
30
30
|
# @param tree [Tree] the working tree, already known to be a hecks checkout
|
|
31
31
|
# @param shell [#capture, nil] unused: the tool runs in this process
|
|
32
32
|
# @return [String] how many domains were checked or regenerated
|
|
33
33
|
# @raise [ConsoleCapture::Failure] with the difference, when a check finds drift, or with
|
|
34
34
|
# what the script printed when it ends badly
|
|
35
|
-
def call(
|
|
35
|
+
def call(operation, held, tree, shell: nil)
|
|
36
36
|
flag = ->(name) { (held[name].is_a?(Hash) ? held[name][:value] : held[name]) == true }
|
|
37
37
|
check = flag.call(:check) || !flag.call(:confirm)
|
|
38
38
|
child = RubyChild.new(tree)
|
|
39
|
+
return project_ci_gates(child, check) if operation == "project_ci_gates"
|
|
40
|
+
return decide_ci_gate(child, held) if operation == "decide_ci_gate"
|
|
41
|
+
|
|
39
42
|
result = child.capture("regen_codegen_domains", *("--check" if check))
|
|
40
43
|
raise ConsoleCapture::Failure, refusal(result) unless result.ok?
|
|
41
44
|
|
|
42
45
|
summary(result.out, check)
|
|
43
46
|
end
|
|
44
47
|
|
|
48
|
+
# @param child [RubyChild] the checkout's tool runner
|
|
49
|
+
# @param check [Boolean] whether to only compare
|
|
50
|
+
# @return [String] what the tool printed
|
|
51
|
+
# @raise [ConsoleCapture::Failure] with the stale workflows, when a check finds drift
|
|
52
|
+
def project_ci_gates(child, check)
|
|
53
|
+
child.answer("project_ci_gates", *("--check" if check))
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @param child [RubyChild] the checkout's tool runner
|
|
57
|
+
# @param held [Hash] the record's fields: `gate`, a `CiGate` row's name
|
|
58
|
+
# @return [String] `touched=true` or `touched=false`
|
|
59
|
+
# @raise [ConsoleCapture::Failure] when no row has that name
|
|
60
|
+
def decide_ci_gate(child, held)
|
|
61
|
+
gate = held[:gate].is_a?(Hash) ? held[:gate][:value] : held[:gate]
|
|
62
|
+
child.answer("decide_ci_gate", "gate=#{gate}")
|
|
63
|
+
end
|
|
64
|
+
|
|
45
65
|
# @param output [String] what the script printed
|
|
46
66
|
# @param check [Boolean] whether it only compared
|
|
47
67
|
# @return [String] the verdict, with how many domains it covered
|
|
@@ -5,6 +5,7 @@ require_relative "language"
|
|
|
5
5
|
require_relative "kernel_tables"
|
|
6
6
|
require_relative "conformance"
|
|
7
7
|
require_relative "regeneration"
|
|
8
|
+
require_relative "gate"
|
|
8
9
|
require_relative "style"
|
|
9
10
|
require_relative "codemods"
|
|
10
11
|
require_relative "test_suite"
|
|
@@ -178,7 +179,8 @@ module Hecks
|
|
|
178
179
|
|
|
179
180
|
# The task families, each carrying out the operations it lists.
|
|
180
181
|
FAMILIES = [Codebase::Language, Codebase::KernelTables, Codebase::Conformance, Codebase::Regeneration,
|
|
181
|
-
Codebase::
|
|
182
|
+
Codebase::Gate, Codebase::Style, Codebase::Codemods, Codebase::TestSuite,
|
|
183
|
+
Codebase::CorpusTasks, Codebase::Publishing].freeze
|
|
182
184
|
private_constant :FAMILIES
|
|
183
185
|
|
|
184
186
|
def tree = Codebase::Tree.new
|
|
@@ -79,6 +79,18 @@ Hecks.behaviors "Codebase" do
|
|
|
79
79
|
expect refused: "must match"
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
+
test "ProjectCiGates refuses a run that is not named" do
|
|
83
|
+
tests "ProjectCiGates", on: "RegenerationRun"
|
|
84
|
+
input run: { value: "" }
|
|
85
|
+
expect refused: "must match"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
test "DecideCiGate refuses a run that is not named" do
|
|
89
|
+
tests "DecideCiGate", on: "RegenerationRun"
|
|
90
|
+
input run: { value: "" }, gate: { value: "runtime_changed" }
|
|
91
|
+
expect refused: "must match"
|
|
92
|
+
end
|
|
93
|
+
|
|
82
94
|
test "CheckComments keeps a file with no violations as a completed check" do
|
|
83
95
|
tests "CheckComments", on: "StyleRun"
|
|
84
96
|
input paths: { value: "lib/hecks/canonical_json.rb" }, run: { value: "clean" }
|
|
@@ -960,6 +960,7 @@ Hecks.bluebook "Hecks" do
|
|
|
960
960
|
attribute :operation, Operation, optional: true
|
|
961
961
|
attribute :confirm, Switch, optional: true
|
|
962
962
|
attribute :check, Switch, optional: true
|
|
963
|
+
attribute :gate, GateName, optional: true
|
|
963
964
|
attribute :checkout, Switch, optional: true
|
|
964
965
|
attribute :report, Report, optional: true
|
|
965
966
|
attribute :refusal, Report, optional: true
|
|
@@ -983,6 +984,11 @@ Hecks.bluebook "Hecks" do
|
|
|
983
984
|
attribute :value, String
|
|
984
985
|
end
|
|
985
986
|
|
|
987
|
+
# A `CiGate` row of the Vocabulary chapter, by name.
|
|
988
|
+
value_object "GateName" do
|
|
989
|
+
attribute :value, String, pattern: '[^ \t\n\r]'
|
|
990
|
+
end
|
|
991
|
+
|
|
986
992
|
lifecycle :status, default: "requested" do
|
|
987
993
|
transition "Accept" => "accepted", from: "requested"
|
|
988
994
|
transition "Complete" => "completed", from: "accepted"
|
|
@@ -1005,6 +1011,36 @@ Hecks.bluebook "Hecks" do
|
|
|
1005
1011
|
emits RegenerateCorpusRequested
|
|
1006
1012
|
end
|
|
1007
1013
|
|
|
1014
|
+
command "ProjectCiGates" do
|
|
1015
|
+
role "Maintainer"
|
|
1016
|
+
goal "Project the CiGate rows into the workflows' marked detector jobs, or with check only compare them"
|
|
1017
|
+
|
|
1018
|
+
attribute :check, Switch, optional: true
|
|
1019
|
+
attribute :run, RunKey
|
|
1020
|
+
attribute :confirm, Switch, optional: true
|
|
1021
|
+
|
|
1022
|
+
sets :check
|
|
1023
|
+
sets :run
|
|
1024
|
+
sets :confirm
|
|
1025
|
+
sets :operation, to: "project_ci_gates"
|
|
1026
|
+
|
|
1027
|
+
emits ProjectCiGatesRequested
|
|
1028
|
+
end
|
|
1029
|
+
|
|
1030
|
+
command "DecideCiGate" do
|
|
1031
|
+
role "Maintainer"
|
|
1032
|
+
goal "Say whether this run's change touches what a CiGate row guards, as the gated job's detector"
|
|
1033
|
+
|
|
1034
|
+
attribute :gate, GateName
|
|
1035
|
+
attribute :run, RunKey
|
|
1036
|
+
|
|
1037
|
+
sets :gate
|
|
1038
|
+
sets :run
|
|
1039
|
+
sets :operation, to: "decide_ci_gate"
|
|
1040
|
+
|
|
1041
|
+
emits DecideCiGateRequested
|
|
1042
|
+
end
|
|
1043
|
+
|
|
1008
1044
|
# What the tree reported about itself, held against every request: nothing here runs
|
|
1009
1045
|
# outside a hecks checkout (hecks.gemspec beside lib/).
|
|
1010
1046
|
command "Accept" do
|
|
@@ -1064,6 +1100,16 @@ Hecks.bluebook "Hecks" do
|
|
|
1064
1100
|
trigger RegenerationRun::SourceTree::Examine, with: { run: :run }
|
|
1065
1101
|
end
|
|
1066
1102
|
|
|
1103
|
+
policy "ExamineWhenProjectCiGatesRequested" do
|
|
1104
|
+
on "RegenerationRun.ProjectCiGatesRequested"
|
|
1105
|
+
trigger RegenerationRun::SourceTree::Examine, with: { run: :run }
|
|
1106
|
+
end
|
|
1107
|
+
|
|
1108
|
+
policy "ExamineWhenDecideCiGateRequested" do
|
|
1109
|
+
on "RegenerationRun.DecideCiGateRequested"
|
|
1110
|
+
trigger RegenerationRun::SourceTree::Examine, with: { run: :run }
|
|
1111
|
+
end
|
|
1112
|
+
|
|
1067
1113
|
policy "AcceptRegenerationWhenExamined" do
|
|
1068
1114
|
on "RegenerationRun.RegenerationExamined"
|
|
1069
1115
|
trigger RegenerationRun::Accept, with: { checkout: :checkout }
|
|
@@ -1090,6 +1136,154 @@ Hecks.bluebook "Hecks" do
|
|
|
1090
1136
|
trigger RegenerationRun::Fault, with: { refusal: :refusal }
|
|
1091
1137
|
end
|
|
1092
1138
|
|
|
1139
|
+
# Gate: the checks a stage holds a change against before it is let through. The checks are data
|
|
1140
|
+
# (lib/hecks/gate/stages.yml), run together, and a red one faults the run, so `--wait` fails the
|
|
1141
|
+
# push or the build that asked, and the journal keeps when the gate went red.
|
|
1142
|
+
aggregate "GateRun" do
|
|
1143
|
+
description "One run of a stage's checks: every check of the stage, or the ones named."
|
|
1144
|
+
|
|
1145
|
+
attribute :run, RunKey
|
|
1146
|
+
attribute :operation, Operation, optional: true
|
|
1147
|
+
attribute :stage, Stage
|
|
1148
|
+
attribute :only, CheckList, optional: true
|
|
1149
|
+
attribute :checkout, Switch, optional: true
|
|
1150
|
+
attribute :report, Report, optional: true
|
|
1151
|
+
attribute :refusal, Report, optional: true
|
|
1152
|
+
identified_by :run
|
|
1153
|
+
|
|
1154
|
+
value_object "RunKey" do
|
|
1155
|
+
attribute :value, String, pattern: '[^ \t\n\r]'
|
|
1156
|
+
|
|
1157
|
+
invariant("a run is named") { !value.to_s.empty? }
|
|
1158
|
+
end
|
|
1159
|
+
|
|
1160
|
+
value_object "Operation" do
|
|
1161
|
+
attribute :value, String, pattern: '[^ \t\n\r]'
|
|
1162
|
+
end
|
|
1163
|
+
|
|
1164
|
+
value_object "Stage" do
|
|
1165
|
+
attribute :value, String, pattern: '^[a-z][a-z0-9_]*$'
|
|
1166
|
+
|
|
1167
|
+
invariant("a stage is named") { !value.to_s.empty? }
|
|
1168
|
+
end
|
|
1169
|
+
|
|
1170
|
+
value_object "CheckList" do
|
|
1171
|
+
attribute :value, String, pattern: '^[a-z][a-z0-9_]*(,[a-z][a-z0-9_]*)*$'
|
|
1172
|
+
end
|
|
1173
|
+
|
|
1174
|
+
value_object "Switch" do
|
|
1175
|
+
attribute :value, Boolean
|
|
1176
|
+
end
|
|
1177
|
+
|
|
1178
|
+
value_object "Report" do
|
|
1179
|
+
attribute :value, String
|
|
1180
|
+
end
|
|
1181
|
+
|
|
1182
|
+
lifecycle :status, default: "requested" do
|
|
1183
|
+
transition "Accept" => "accepted", from: "requested"
|
|
1184
|
+
transition "Complete" => "completed", from: "accepted"
|
|
1185
|
+
transition "Fault" => "faulted", from: %w[requested accepted]
|
|
1186
|
+
end
|
|
1187
|
+
|
|
1188
|
+
command "Gate" do
|
|
1189
|
+
role "Maintainer"
|
|
1190
|
+
goal "Run every check of a stage, or the ones named, and keep whether the gate is green"
|
|
1191
|
+
|
|
1192
|
+
attribute :stage, Stage
|
|
1193
|
+
attribute :only, CheckList, optional: true
|
|
1194
|
+
attribute :run, RunKey
|
|
1195
|
+
|
|
1196
|
+
sets :stage
|
|
1197
|
+
sets :only
|
|
1198
|
+
sets :run
|
|
1199
|
+
sets :operation, to: "gate"
|
|
1200
|
+
|
|
1201
|
+
emits GateRequested
|
|
1202
|
+
end
|
|
1203
|
+
|
|
1204
|
+
command "Accept" do
|
|
1205
|
+
role "System"
|
|
1206
|
+
goal "Let a request through only when the working tree is a hecks checkout"
|
|
1207
|
+
|
|
1208
|
+
reference_to GateRun
|
|
1209
|
+
attribute :checkout, Switch
|
|
1210
|
+
|
|
1211
|
+
given("needs a hecks checkout: hecks.gemspec stands beside lib/") { checkout.value }
|
|
1212
|
+
|
|
1213
|
+
sets :checkout
|
|
1214
|
+
|
|
1215
|
+
emits GateAccepted
|
|
1216
|
+
end
|
|
1217
|
+
|
|
1218
|
+
command "Complete" do
|
|
1219
|
+
role "System"
|
|
1220
|
+
goal "Record that every check passed, and which"
|
|
1221
|
+
|
|
1222
|
+
reference_to GateRun
|
|
1223
|
+
attribute :report, Report
|
|
1224
|
+
|
|
1225
|
+
sets :report
|
|
1226
|
+
|
|
1227
|
+
emits GateCompleted
|
|
1228
|
+
end
|
|
1229
|
+
|
|
1230
|
+
command "Fault" do
|
|
1231
|
+
role "System"
|
|
1232
|
+
goal "Record that a check was red or the run could not be made, and why"
|
|
1233
|
+
|
|
1234
|
+
reference_to GateRun
|
|
1235
|
+
attribute :refusal, Report
|
|
1236
|
+
|
|
1237
|
+
sets :refusal
|
|
1238
|
+
|
|
1239
|
+
emits GateFaulted
|
|
1240
|
+
end
|
|
1241
|
+
|
|
1242
|
+
query "GateOutcome" do
|
|
1243
|
+
description "How one run ended: its status, and what it reported or why it failed."
|
|
1244
|
+
attribute :run, RunKey
|
|
1245
|
+
where(run: :run)
|
|
1246
|
+
order_by :run
|
|
1247
|
+
end
|
|
1248
|
+
|
|
1249
|
+
query "GateFaulted" do
|
|
1250
|
+
description "Every run that went red or could not be made."
|
|
1251
|
+
where(status: "faulted")
|
|
1252
|
+
order_by :run
|
|
1253
|
+
end
|
|
1254
|
+
end
|
|
1255
|
+
|
|
1256
|
+
policy "ExamineWhenGateRequested" do
|
|
1257
|
+
on "GateRun.GateRequested"
|
|
1258
|
+
trigger GateRun::SourceTree::Examine, with: { run: :run }
|
|
1259
|
+
end
|
|
1260
|
+
|
|
1261
|
+
policy "AcceptGateWhenExamined" do
|
|
1262
|
+
on "GateRun.GateExamined"
|
|
1263
|
+
trigger GateRun::Accept, with: { checkout: :checkout }
|
|
1264
|
+
end
|
|
1265
|
+
|
|
1266
|
+
policy "FaultGateWhenNotAccepted" do
|
|
1267
|
+
on "GateRun.GateExamined"
|
|
1268
|
+
where { !checkout.value }
|
|
1269
|
+
trigger GateRun::Fault, with: { refusal: "needs a hecks checkout: hecks.gemspec stands beside lib/" }
|
|
1270
|
+
end
|
|
1271
|
+
|
|
1272
|
+
policy "PerformGateWhenAccepted" do
|
|
1273
|
+
on "GateRun.GateAccepted"
|
|
1274
|
+
trigger GateRun::SourceTree::Perform, with: { run: :run }
|
|
1275
|
+
end
|
|
1276
|
+
|
|
1277
|
+
policy "RecordGateOutcome" do
|
|
1278
|
+
on "GateRun.GateAnswered"
|
|
1279
|
+
trigger GateRun::Complete, with: { report: :report }
|
|
1280
|
+
end
|
|
1281
|
+
|
|
1282
|
+
policy "RecordGateFault" do
|
|
1283
|
+
on "GateRun.GateRefused"
|
|
1284
|
+
trigger GateRun::Fault, with: { refusal: :refusal }
|
|
1285
|
+
end
|
|
1286
|
+
|
|
1093
1287
|
# Style: the comment style guides (Ruby and Rust) and the canonical form of a JSON document. A
|
|
1094
1288
|
# check finds every violation; a fix rewrites files, so it lists what it would rewrite unless
|
|
1095
1289
|
# it is confirmed. The baseline of tolerated long blocks is written only when confirmed.
|
|
@@ -460,6 +460,8 @@ Hecks.bluebook "Hecks" do
|
|
|
460
460
|
|
|
461
461
|
# A launcher answers once, so a follow is a bounded poll: it answers the entries past `since`
|
|
462
462
|
# and a `cursor` to pass as the next `since`, waiting up to `wait` seconds for the first one.
|
|
463
|
+
# `hecks follow <domain> --stream` asks again from each cursor and prints every entry as it
|
|
464
|
+
# arrives, until interrupted (the world's `launcher` setting lists the question under `streams`).
|
|
463
465
|
query "Follow" do
|
|
464
466
|
description "A domain's persisted event log past a cursor, for tailing it call by call."
|
|
465
467
|
attribute :domain, Subject
|
|
@@ -229,6 +229,20 @@ Hecks.hecksagon "Hecks" do
|
|
|
229
229
|
end
|
|
230
230
|
end
|
|
231
231
|
|
|
232
|
+
Hecks::GateRun.port "SourceTree" do
|
|
233
|
+
asks "Examine", to: GateRun do
|
|
234
|
+
attribute :run, RunKey
|
|
235
|
+
answers "GateExamined"
|
|
236
|
+
refuses "GateRefused"
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
asks "Perform", to: GateRun do
|
|
240
|
+
attribute :run, RunKey
|
|
241
|
+
answers "GateAnswered"
|
|
242
|
+
refuses "GateRefused"
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
232
246
|
Hecks::StyleRun.port "SourceTree" do
|
|
233
247
|
asks "Examine", to: StyleRun do
|
|
234
248
|
attribute :run, RunKey
|
data/lib/hecks/hecks/hecks.world
CHANGED
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
# `failure_states` are the lifecycle states `--wait` reports as exit 1 (every failure state the
|
|
27
27
|
# chapter's lifecycles declare), `names` gives a command the launcher name clients type,
|
|
28
28
|
# `executable` is where `hecks project_cli` writes this chapter's launcher (the gem's
|
|
29
|
-
# `exe/hecks`),
|
|
30
|
-
# positional forms ADR 0066 shipped.
|
|
29
|
+
# `exe/hecks`), `streams` lists the questions `--stream` may tail (`Follow`), and `legacy` names
|
|
30
|
+
# the verbs that launcher hands to `Hecks::CLI` in the positional forms ADR 0066 shipped.
|
|
31
31
|
Hecks.world "Hecks" do
|
|
32
32
|
realm "Hecks"
|
|
33
33
|
default_adapter "PostgresEra"
|
|
@@ -36,6 +36,7 @@ Hecks.world "Hecks" do
|
|
|
36
36
|
failure_states: %w[flagged failed drifted unreachable refused faulted halted stopped
|
|
37
37
|
abandoned red needs_fix],
|
|
38
38
|
names: { "mcp" => "serve_mcp", "console" => "open_console" },
|
|
39
|
+
streams: %w[Follow],
|
|
39
40
|
executable: "exe/hecks",
|
|
40
41
|
legacy: %w[run docs narrate ir stores model_check smoke_test
|
|
41
42
|
project_diagrams project_cli mcp]
|
|
@@ -21,6 +21,8 @@ Hecks.bluebook "Bluebook" do
|
|
|
21
21
|
attribute :givens, list_of(Rule)
|
|
22
22
|
# Postconditions: same Rule shape as givens, checked after the mutations.
|
|
23
23
|
attribute :ensures, list_of(Rule)
|
|
24
|
+
# Outside facts the runtime supplies before any given runs (ADR 0081): `needs :now`.
|
|
25
|
+
attribute :needs, list_of(Need)
|
|
24
26
|
attribute :mutations, list_of(Change)
|
|
25
27
|
attribute :provenance, CommandText
|
|
26
28
|
# Lifecycle guard as a literal (`from: "open"` or `from: ["open", "frozen"]`); ADR 0025.
|
|
@@ -48,6 +50,10 @@ Hecks.bluebook "Bluebook" do
|
|
|
48
50
|
attribute :name, String
|
|
49
51
|
end
|
|
50
52
|
|
|
53
|
+
value_object "Need" do
|
|
54
|
+
attribute :fact, String
|
|
55
|
+
end
|
|
56
|
+
|
|
51
57
|
value_object "Argument" do
|
|
52
58
|
attribute :name, String
|
|
53
59
|
attribute :type, String
|
|
@@ -254,6 +260,23 @@ Hecks.bluebook "Bluebook" do
|
|
|
254
260
|
emits AnnouncementNamed
|
|
255
261
|
end
|
|
256
262
|
|
|
263
|
+
command "Need" do
|
|
264
|
+
role "Language"
|
|
265
|
+
goal "Name an outside fact the runtime supplies before any given runs"
|
|
266
|
+
|
|
267
|
+
attribute :fact, FactName
|
|
268
|
+
|
|
269
|
+
given("a fact is named") { !fact.value.to_s.empty? }
|
|
270
|
+
|
|
271
|
+
sets :needs, append: { fact: :fact }
|
|
272
|
+
|
|
273
|
+
emits NeedNamed
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
value_object "FactName" do
|
|
277
|
+
attribute :value, String
|
|
278
|
+
end
|
|
279
|
+
|
|
257
280
|
# Keyword rows for this chapter; SyntaxBoot discovers aggregate-local seed tables.
|
|
258
281
|
value_object "KeywordSeed" do
|
|
259
282
|
attribute :word, String
|
|
@@ -283,6 +306,8 @@ Hecks.bluebook "Bluebook" do
|
|
|
283
306
|
member word: "state", context: "Command", body: "none", inner: "", opens: "", fills: "mutations"
|
|
284
307
|
member word: "attribute", context: "Command", body: "none", inner: "", opens: "", fills: "attributes", calls: "attribute_impl"
|
|
285
308
|
member word: "ensures", context: "Command", body: "source", inner: "", opens: "", fills: "ensures"
|
|
309
|
+
# An outside fact the runtime supplies before any given runs (ADR 0081; CommandBuilder#needs_impl).
|
|
310
|
+
member word: "needs", context: "Command", body: "none", inner: "", opens: "", fills: "needs", calls: "needs_impl"
|
|
286
311
|
# Amends a specific past event. Stored as a mutation so it round-trips with the other
|
|
287
312
|
# mutation words (CommandBuilder#corrects_impl).
|
|
288
313
|
member word: "corrects", context: "Command", body: "none", inner: "", opens: "", fills: "mutations", calls: "corrects_impl"
|
|
@@ -314,6 +339,7 @@ Hecks.bluebook "Bluebook" do
|
|
|
314
339
|
member keyword: "reference_to", context: "Command", at: "", named: "optional", kind: "flag", required: "false", fills: "optional"
|
|
315
340
|
member keyword: "given", context: "Command", at: "1", named: "", kind: "text", required: "true", fills: "description"
|
|
316
341
|
member keyword: "ensures", context: "Command", at: "1", named: "", kind: "text", required: "true", fills: "description"
|
|
342
|
+
member keyword: "needs", context: "Command", at: "1", named: "", kind: "symbol", required: "true", fills: "fact"
|
|
317
343
|
member keyword: "sets", context: "Command", at: "1", named: "", kind: "symbol", required: "true", fills: "target"
|
|
318
344
|
member keyword: "sets", context: "Command", at: "", named: "to", kind: "literal", required: "false", fills: "source", selects: "op=set"
|
|
319
345
|
member keyword: "sets", context: "Command", at: "", named: "append", kind: "literal", required: "false", fills: "source", selects: "op=append"
|