hecks 3.1.0 → 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 +1 -1
- data/lib/hecks/chapters.rb +4 -3
- data/lib/hecks/cli/domain_writer.rb +39 -0
- data/lib/hecks/cli/interview_agent.rb +59 -0
- data/lib/hecks/cli/interview_run.rb +87 -0
- data/lib/hecks/cli/interview_session.rb +299 -0
- data/lib/hecks/corpus.rb +1 -0
- data/lib/hecks/doors/cli_runner.rb +1 -1
- data/lib/hecks/doors/usage_cache.rb +123 -0
- 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/terminal.rb +15 -0
- data/lib/hecks/hecks/context_map.hecksagon +1 -1
- data/lib/hecks/hecks/custodian.bluebook +36 -0
- data/lib/hecks/hecks/hecks.bluebook +18 -0
- data/lib/hecks/hecks/hecks.hecksagon +8 -2
- data/lib/hecks/hecks/hecks.world +3 -2
- data/lib/hecks/projections/deploy/box/settings.rb +80 -4
- data/lib/hecks/projections/deploy/box/templates/box.yaml.tmpl +1 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +3 -5
- data/lib/hecks/projections/deploy/box/templates/rds.yaml.tmpl +17 -1
- 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 +1 -1
- 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 +185 -19
- data/lib/hecks/projector/cli_projector.rb +38 -14
- data/lib/hecks/runtime/loader.rb +31 -8
- 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/version.rb +1 -1
- data/lib/hecks.rb +1 -0
- 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
- metadata +15 -2
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
# The Tickets chapter: what a maintainer observed about Hecks that needs a decision or a fix, the
|
|
2
|
+
# and the decisions that settle them.
|
|
3
|
+
#
|
|
4
|
+
# QualityControl has its own Ticket, the issue raised for a Bug in an outside tracker, and the
|
|
5
|
+
# Hecks domain has its own Release, the record of a gem version. A Finding sits before both: it is
|
|
6
|
+
# any observation, not yet a proven bug, and it needs no sweep or failing test to be recorded.
|
|
7
|
+
#
|
|
8
|
+
# A Finding is the record. A gap, a parity divergence, a QA finding and an idea are kinds of it, not
|
|
9
|
+
# separate aggregates. Hecks owns the finding's state; GitHub is driven from it through the
|
|
10
|
+
# GitHubIssues port, which a hecksagon binds (nothing here reads GitHub back). A finding exists whether
|
|
11
|
+
# or not GitHub answers: each lifecycle event asks the port, and a refused ask is recorded on the
|
|
12
|
+
# finding as a sync failure that a later run retries.
|
|
13
|
+
#
|
|
14
|
+
# An Adr is a decision record with its own lifecycle, linked to the finding it settles.
|
|
15
|
+
Hecks.bluebook "Tickets" do
|
|
16
|
+
vision "Track what Hecks needs, decide it, ship it, and drive GitHub from the record."
|
|
17
|
+
|
|
18
|
+
aggregate "Finding" do
|
|
19
|
+
description "One thing observed about Hecks that needs a decision or a fix: reported, triaged, then resolved or dismissed."
|
|
20
|
+
|
|
21
|
+
attribute :finding, FindingKey
|
|
22
|
+
attribute :title, Title
|
|
23
|
+
attribute :body, Body, optional: true
|
|
24
|
+
attribute :kind, Kind, optional: true
|
|
25
|
+
attribute :severity, Severity, optional: true
|
|
26
|
+
attribute :source, Source
|
|
27
|
+
attribute :fingerprint, Fingerprint, optional: true
|
|
28
|
+
attribute :issue_number, Number, optional: true
|
|
29
|
+
attribute :fix_pr_number, Number, optional: true
|
|
30
|
+
attribute :dismissal, Body, optional: true
|
|
31
|
+
attribute :sync_failure, Body, optional: true
|
|
32
|
+
identified_by :finding
|
|
33
|
+
|
|
34
|
+
value_object "FindingKey" do
|
|
35
|
+
attribute :value, String, pattern: '[^ \t\n\r]'
|
|
36
|
+
|
|
37
|
+
invariant("a finding is named") { !value.to_s.empty? }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
value_object "Title" do
|
|
41
|
+
attribute :value, String
|
|
42
|
+
|
|
43
|
+
invariant("a finding has a title") { !value.to_s.empty? }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
value_object "Body" do
|
|
47
|
+
attribute :value, String
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# What the finding is: a `gap` Hecks cannot yet express, a `parity` divergence between the Ruby
|
|
51
|
+
# and Rust runtimes, a `qa` finding from the QA loop, or an `idea`.
|
|
52
|
+
value_object "Kind" do
|
|
53
|
+
attribute :value, String, one_of: ["gap", "parity", "qa", "idea"]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
value_object "Severity" do
|
|
57
|
+
attribute :value, String, one_of: ["low", "medium", "high"]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Where it came from: the QA loop, work for a client, a parity check, or a maintainer by hand.
|
|
61
|
+
value_object "Source" do
|
|
62
|
+
attribute :value, String, one_of: ["qa_loop", "client_work", "parity_check", "maintainer"]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# The same finding refiled by the QA loop carries the same fingerprint, so a report of it while an
|
|
66
|
+
# open finding holds it changes nothing.
|
|
67
|
+
value_object "Fingerprint" do
|
|
68
|
+
attribute :value, String, pattern: '[^ \t\n\r]'
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
value_object "Number" do
|
|
72
|
+
attribute :value, Integer
|
|
73
|
+
|
|
74
|
+
invariant("a number is positive") { value.positive? }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
lifecycle :status, default: "reported" do
|
|
78
|
+
transition "Triage" => "triaged", from: %w[reported triaged]
|
|
79
|
+
transition "Resolve" => "resolved", from: "triaged"
|
|
80
|
+
transition "Dismiss" => "dismissed", from: %w[reported triaged]
|
|
81
|
+
transition "Reopen" => "triaged", from: %w[resolved dismissed]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
command "Report" do
|
|
85
|
+
goal "Record something observed about Hecks as a finding"
|
|
86
|
+
|
|
87
|
+
attribute :finding, FindingKey
|
|
88
|
+
attribute :title, Title
|
|
89
|
+
attribute :body, Body, optional: true
|
|
90
|
+
attribute :source, Source
|
|
91
|
+
attribute :fingerprint, Fingerprint, optional: true
|
|
92
|
+
|
|
93
|
+
sets :finding
|
|
94
|
+
sets :title
|
|
95
|
+
sets :body
|
|
96
|
+
sets :source
|
|
97
|
+
sets :fingerprint
|
|
98
|
+
|
|
99
|
+
emits "FindingReported"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# A failed Codebase run reports itself: the run's key names the finding and doubles as its
|
|
103
|
+
# fingerprint, so the same run failing again while its finding is open changes nothing.
|
|
104
|
+
command "ReportFailedRun" do
|
|
105
|
+
goal "Record a failed conformance or gate run as a finding"
|
|
106
|
+
|
|
107
|
+
attribute :finding, FindingKey
|
|
108
|
+
attribute :body, Body
|
|
109
|
+
|
|
110
|
+
sets :finding
|
|
111
|
+
sets :body
|
|
112
|
+
sets :fingerprint, to: "run"
|
|
113
|
+
sets :title, to: "A Codebase run failed"
|
|
114
|
+
sets :source, to: "qa_loop"
|
|
115
|
+
|
|
116
|
+
emits "RunFindingReported"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
command "Triage" do
|
|
120
|
+
role "Maintainer"
|
|
121
|
+
goal "Give a finding its kind and severity"
|
|
122
|
+
|
|
123
|
+
reference_to Finding
|
|
124
|
+
attribute :kind, Kind
|
|
125
|
+
attribute :severity, Severity
|
|
126
|
+
|
|
127
|
+
sets :kind
|
|
128
|
+
sets :severity
|
|
129
|
+
|
|
130
|
+
emits "FindingTriaged"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
command "LinkFix" do
|
|
134
|
+
role "Maintainer"
|
|
135
|
+
goal "Record the pull request that fixes a triaged finding"
|
|
136
|
+
|
|
137
|
+
reference_to Finding
|
|
138
|
+
attribute :fix_pr_number, Number
|
|
139
|
+
|
|
140
|
+
sets :fix_pr_number
|
|
141
|
+
|
|
142
|
+
emits "FixLinked"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
command "Resolve" do
|
|
146
|
+
role "Maintainer"
|
|
147
|
+
goal "Close a triaged finding as resolved"
|
|
148
|
+
|
|
149
|
+
reference_to Finding
|
|
150
|
+
|
|
151
|
+
emits "FindingResolved"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
command "Dismiss" do
|
|
155
|
+
role "Maintainer"
|
|
156
|
+
goal "Close a finding without a fix, with the reason"
|
|
157
|
+
|
|
158
|
+
reference_to Finding
|
|
159
|
+
attribute :dismissal, Body
|
|
160
|
+
|
|
161
|
+
sets :dismissal
|
|
162
|
+
|
|
163
|
+
emits "FindingDismissed"
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
command "Reopen" do
|
|
167
|
+
role "Maintainer"
|
|
168
|
+
goal "Take a resolved or dismissed finding back to triaged, as when a fix regresses"
|
|
169
|
+
|
|
170
|
+
reference_to Finding
|
|
171
|
+
|
|
172
|
+
emits "FindingReopened"
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
command "RecordIssue" do
|
|
176
|
+
goal "Record the GitHub issue number once the issue is opened"
|
|
177
|
+
|
|
178
|
+
reference_to Finding
|
|
179
|
+
attribute :issue_number, Number
|
|
180
|
+
|
|
181
|
+
sets :issue_number
|
|
182
|
+
|
|
183
|
+
emits "IssueRecorded"
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
command "RecordSyncFailure" do
|
|
187
|
+
goal "Record that GitHub could not be driven, so a later run retries"
|
|
188
|
+
|
|
189
|
+
reference_to Finding
|
|
190
|
+
attribute :sync_failure, Body
|
|
191
|
+
|
|
192
|
+
sets :sync_failure
|
|
193
|
+
|
|
194
|
+
emits "SyncFailed"
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
query "Open" do
|
|
198
|
+
description "Every finding not yet resolved or dismissed."
|
|
199
|
+
where(status: { in: "reported,triaged" })
|
|
200
|
+
order_by :finding
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
aggregate "Adr" do
|
|
205
|
+
description "A decision record: drafted, accepted, and possibly superseded, linked to the tickets it settles."
|
|
206
|
+
|
|
207
|
+
attribute :adr, AdrNumber
|
|
208
|
+
attribute :title, AdrTitle
|
|
209
|
+
attribute :supersedes, AdrNumber, optional: true
|
|
210
|
+
attribute :settles, FindingKey, optional: true
|
|
211
|
+
identified_by :adr
|
|
212
|
+
|
|
213
|
+
value_object "AdrNumber" do
|
|
214
|
+
attribute :value, String, pattern: '\A[0-9]{4}\z'
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
value_object "AdrTitle" do
|
|
218
|
+
attribute :value, String
|
|
219
|
+
|
|
220
|
+
invariant("a decision has a title") { !value.to_s.empty? }
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
value_object "FindingKey" do
|
|
224
|
+
attribute :value, String, pattern: '[^ \t\n\r]'
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
lifecycle :status, default: "draft" do
|
|
228
|
+
transition "Accept" => "accepted", from: "draft"
|
|
229
|
+
transition "Supersede" => "superseded", from: "accepted"
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
command "Draft" do
|
|
233
|
+
role "Maintainer"
|
|
234
|
+
goal "Open a decision record, optionally for the finding it settles"
|
|
235
|
+
|
|
236
|
+
attribute :adr, AdrNumber
|
|
237
|
+
attribute :title, AdrTitle
|
|
238
|
+
attribute :settles, FindingKey, optional: true
|
|
239
|
+
|
|
240
|
+
sets :adr
|
|
241
|
+
sets :title
|
|
242
|
+
sets :settles
|
|
243
|
+
|
|
244
|
+
emits "AdrDrafted"
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
command "Accept" do
|
|
248
|
+
role "Maintainer"
|
|
249
|
+
goal "Record that the decision was accepted"
|
|
250
|
+
|
|
251
|
+
reference_to Adr
|
|
252
|
+
|
|
253
|
+
emits "AdrAccepted"
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
command "Supersede" do
|
|
257
|
+
role "Maintainer"
|
|
258
|
+
goal "Record that a later decision replaces this one"
|
|
259
|
+
|
|
260
|
+
reference_to Adr
|
|
261
|
+
attribute :supersedes, AdrNumber
|
|
262
|
+
|
|
263
|
+
sets :supersedes
|
|
264
|
+
|
|
265
|
+
emits "AdrSuperseded"
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
query "Standing" do
|
|
269
|
+
description "Every decision that is accepted and not superseded."
|
|
270
|
+
where(status: "accepted")
|
|
271
|
+
order_by :adr
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# Each lifecycle event drives GitHub. A finding's own state never waits on the answer.
|
|
276
|
+
policy "OpenIssueWhenReported" do
|
|
277
|
+
on "Finding.FindingReported"
|
|
278
|
+
trigger Finding::GitHubIssues::OpenIssue, with: { finding: :finding }
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
policy "OpenIssueWhenRunReported" do
|
|
282
|
+
on "Finding.RunFindingReported"
|
|
283
|
+
trigger Finding::GitHubIssues::OpenIssue, with: { finding: :finding }
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
policy "LabelIssueWhenTriaged" do
|
|
287
|
+
on "Finding.FindingTriaged"
|
|
288
|
+
trigger Finding::GitHubIssues::LabelIssue, with: { finding: :finding }
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
policy "CommentWhenFixLinked" do
|
|
292
|
+
on "Finding.FixLinked"
|
|
293
|
+
trigger Finding::GitHubIssues::CommentOnIssue, with: { finding: :finding }
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
policy "CloseIssueWhenResolved" do
|
|
297
|
+
on "Finding.FindingResolved"
|
|
298
|
+
trigger Finding::GitHubIssues::CloseIssue, with: { finding: :finding }
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
policy "CloseIssueWhenDismissed" do
|
|
302
|
+
on "Finding.FindingDismissed"
|
|
303
|
+
trigger Finding::GitHubIssues::CloseIssue, with: { finding: :finding }
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
policy "ReopenIssueWhenReopened" do
|
|
307
|
+
on "Finding.FindingReopened"
|
|
308
|
+
trigger Finding::GitHubIssues::ReopenIssue, with: { finding: :finding }
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# A conformance or gate run that faults files its own finding.
|
|
312
|
+
policy "ReportWhenConformanceFaulted" do
|
|
313
|
+
on "ConformanceRun.ConformanceFaulted"
|
|
314
|
+
trigger Finding::ReportFailedRun, with: { finding: :run, body: :refusal }
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
policy "ReportWhenGateFaulted" do
|
|
318
|
+
on "GateRun.GateFaulted"
|
|
319
|
+
trigger Finding::ReportFailedRun, with: { finding: :run, body: :refusal }
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
policy "RecordTheIssue" do
|
|
323
|
+
on "Finding.IssueOpened"
|
|
324
|
+
trigger Finding::RecordIssue, with: { issue_number: :issue_number }
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
policy "RecordTheSyncFailure" do
|
|
328
|
+
on "Finding.GitHubRefused"
|
|
329
|
+
trigger Finding::RecordSyncFailure, with: { sync_failure: :refusal }
|
|
330
|
+
end
|
|
331
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# The Tickets domain, bound so it boots standing alone (a fuzz or model_check boot of this
|
|
2
|
+
# directory). The GitHubIssues port every attaching hecksagon shares is in tickets.ports.hecksagon.
|
|
3
|
+
Hecks.hecksagon "Tickets" do
|
|
4
|
+
attaches "Governance"
|
|
5
|
+
persisted_by "Memory"
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# The anti-corruption layer for Governance, which the chapter's roles are checked against.
|
|
9
|
+
Hecks.hecksagon "Governance" do
|
|
10
|
+
persisted_by "Memory"
|
|
11
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# The port Tickets declares, for every hecksagon that attaches the chapter (`Chapters` loads this
|
|
2
|
+
# file with it). Each lifecycle event of a finding asks the GitHubIssues port, which the Hecks domain
|
|
3
|
+
# binds (lib/hecks/hecks/adapters/finding_github.rb): the outside a finding reaches. The ask is
|
|
4
|
+
# answered with the record's own arguments or refused with a reason, and a refusal is recorded on the
|
|
5
|
+
# finding and never blocks it. The port has to be declared even where no adapter binds it, so the
|
|
6
|
+
# policies have a target and model_check's deaf-policy check can see it.
|
|
7
|
+
#
|
|
8
|
+
# Only OpenIssue answers with the issue number, so only its answer is recorded on the finding; the
|
|
9
|
+
# other asks answer with a line saying what was done.
|
|
10
|
+
Hecks.hecksagon "Tickets" do
|
|
11
|
+
Tickets::Finding.port "GitHubIssues" do
|
|
12
|
+
asks "OpenIssue", to: Finding do
|
|
13
|
+
attribute :finding, FindingKey
|
|
14
|
+
answers "IssueOpened"
|
|
15
|
+
refuses "GitHubRefused"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
%w[LabelIssue CommentOnIssue CloseIssue ReopenIssue].each do |ask|
|
|
19
|
+
asks ask, to: Finding do
|
|
20
|
+
attribute :finding, FindingKey
|
|
21
|
+
answers "GitHubAnswered"
|
|
22
|
+
refuses "GitHubRefused"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
data/lib/hecks/version.rb
CHANGED
data/lib/hecks.rb
CHANGED
|
@@ -29,6 +29,7 @@ require_relative "hecks/projector"
|
|
|
29
29
|
require_relative "hecks/projections"
|
|
30
30
|
# After `Projector`, which dispatches against the `:cli` projection.
|
|
31
31
|
require_relative "hecks/doors/cli_door"
|
|
32
|
+
require_relative "hecks/doors/usage_cache"
|
|
32
33
|
require_relative "hecks/doors/cli_runner"
|
|
33
34
|
require_relative "hecks/storehouse"
|
|
34
35
|
require_relative "hecks/mcp_stdio_guard"
|
|
@@ -44,6 +44,7 @@ pub fn scalar_json_accessor(scalar_type: &str) -> &'static str {
|
|
|
44
44
|
"String" => "as_str",
|
|
45
45
|
"Integer" => "as_i64",
|
|
46
46
|
"Float" => "as_f64",
|
|
47
|
+
"TrueClass" | "FalseClass" => "as_bool",
|
|
47
48
|
other => panic!("no JSON accessor for scalar type {other:?}"),
|
|
48
49
|
}
|
|
49
50
|
}
|
|
@@ -77,6 +78,7 @@ pub fn scalar_to_json_expr(scalar_type: &str, rust_expr: &str) -> String {
|
|
|
77
78
|
"String" => format!("crate::kernel::Json::Str({rust_expr}.clone())"),
|
|
78
79
|
"Integer" => format!("crate::kernel::Json::int({rust_expr})"),
|
|
79
80
|
"Float" => format!("crate::kernel::Json::Float({rust_expr})"),
|
|
81
|
+
"TrueClass" | "FalseClass" => format!("crate::kernel::Json::Bool({rust_expr})"),
|
|
80
82
|
other => panic!("no to_json expr for scalar type {other:?}"),
|
|
81
83
|
}
|
|
82
84
|
}
|
|
@@ -565,6 +567,7 @@ pub fn emit_closed_set_table_codec(exemplar: &Exemplar, vo: &Json) -> String {
|
|
|
565
567
|
Some("String") => format!("crate::kernel::Json::Str(self.{ident}.to_string())"),
|
|
566
568
|
Some("Integer") => format!("crate::kernel::Json::int(self.{ident})"),
|
|
567
569
|
Some("Float") => format!("crate::kernel::Json::Float(self.{ident})"),
|
|
570
|
+
Some("TrueClass" | "FalseClass") => format!("crate::kernel::Json::Bool(self.{ident})"),
|
|
568
571
|
_ => String::new(),
|
|
569
572
|
};
|
|
570
573
|
exemplar.render("to_json_field", &[("\"tmpl_field_name\"", naming::ruby_inspect_string(&key)), ("tmpl_json_value_placeholder()", value_expr)])
|
data/rust/codegen/src/naming.rs
CHANGED
|
@@ -7,6 +7,7 @@ pub fn scalar_rust_type(type_name: &str) -> Option<&'static str> {
|
|
|
7
7
|
"String" => Some("String"),
|
|
8
8
|
"Integer" => Some("i64"),
|
|
9
9
|
"Float" => Some("f64"),
|
|
10
|
+
"TrueClass" | "FalseClass" => Some("bool"),
|
|
10
11
|
_ => None,
|
|
11
12
|
}
|
|
12
13
|
}
|
|
@@ -29,6 +30,8 @@ pub fn effective_scalar_type(type_name: &str) -> Option<&'static str> {
|
|
|
29
30
|
"String" => Some("String"),
|
|
30
31
|
"Integer" => Some("Integer"),
|
|
31
32
|
"Float" => Some("Float"),
|
|
33
|
+
"TrueClass" => Some("TrueClass"),
|
|
34
|
+
"FalseClass" => Some("FalseClass"),
|
|
32
35
|
_ => None,
|
|
33
36
|
}
|
|
34
37
|
}
|
|
@@ -250,6 +253,7 @@ pub fn scalar_to_value(type_name: &str, rust_expr: &str) -> Option<String> {
|
|
|
250
253
|
"String" => Some(format!("Value::Str({rust_expr}.clone())")),
|
|
251
254
|
"Integer" => Some(format!("Value::Int({rust_expr})")),
|
|
252
255
|
"Float" => Some(format!("Value::Float({rust_expr})")),
|
|
256
|
+
"TrueClass" | "FalseClass" => Some(format!("Value::Bool({rust_expr})")),
|
|
253
257
|
_ => None,
|
|
254
258
|
}
|
|
255
259
|
}
|
data/rust/codegen/src/types.rs
CHANGED
|
@@ -360,3 +360,48 @@ pub fn emit_projected_field_table(aggregate: &Json) -> String {
|
|
|
360
360
|
.collect();
|
|
361
361
|
format!("pub static {name}_PROJECTED_FIELDS: &[crate::kernel::ProjectedFieldSpec] = &[\n{}\n];\n", rows.join("\n"))
|
|
362
362
|
}
|
|
363
|
+
|
|
364
|
+
#[cfg(test)]
|
|
365
|
+
mod tests {
|
|
366
|
+
use super::*;
|
|
367
|
+
|
|
368
|
+
/// A value object with one attribute per Ruby spelling of a boolean.
|
|
369
|
+
fn flag_value_object() -> Json {
|
|
370
|
+
Json::parse(
|
|
371
|
+
r#"{
|
|
372
|
+
"name":"Entry",
|
|
373
|
+
"attributes":[
|
|
374
|
+
{"name":"on","type":"TrueClass","list":false,"optional":false,"default":false},
|
|
375
|
+
{"name":"off","type":"FalseClass","list":false,"optional":false}
|
|
376
|
+
]
|
|
377
|
+
}"#,
|
|
378
|
+
)
|
|
379
|
+
.unwrap()
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
#[test]
|
|
383
|
+
fn boolean_attributes_are_rust_bool_not_a_type_named_for_the_ruby_class() {
|
|
384
|
+
let vo = flag_value_object();
|
|
385
|
+
let generated = emit_value_object(&Exemplar::load(), &vo, &HashMap::new(), &HashMap::new());
|
|
386
|
+
|
|
387
|
+
assert!(generated.contains("pub on: bool,"));
|
|
388
|
+
assert!(generated.contains("pub off: bool,"));
|
|
389
|
+
assert!(generated.contains("Value::Bool(self.on)"));
|
|
390
|
+
assert!(!generated.contains("TrueClass,"));
|
|
391
|
+
assert!(!generated.contains("FalseClass,"));
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
#[test]
|
|
395
|
+
fn boolean_attributes_round_trip_through_json_as_bool() {
|
|
396
|
+
let vo = flag_value_object();
|
|
397
|
+
let attributes = vo.get("attributes").map(Json::each).unwrap_or(&[]);
|
|
398
|
+
let by_name = HashMap::new();
|
|
399
|
+
let exemplar = Exemplar::load();
|
|
400
|
+
let from_json = crate::json_codec::emit_from_json_flat(&exemplar, "Entry", attributes, &by_name, None, None, false, false, None);
|
|
401
|
+
let to_json = crate::json_codec::emit_to_json_flat(&exemplar, "Entry", attributes, &by_name, false, &[], None);
|
|
402
|
+
|
|
403
|
+
assert!(from_json.contains("x.as_bool()"));
|
|
404
|
+
assert!(!from_json.contains("TrueClass::from_json"));
|
|
405
|
+
assert!(to_json.contains("crate::kernel::Json::Bool(self.on)"));
|
|
406
|
+
}
|
|
407
|
+
}
|
data/rust/host/HECKS_RELEASE
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.1.
|
|
1
|
+
3.1.1
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hecks
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.1.
|
|
4
|
+
version: 3.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chris Young
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: prism
|
|
@@ -216,10 +216,14 @@ files:
|
|
|
216
216
|
- lib/hecks/cli/doc_coverage.rb
|
|
217
217
|
- lib/hecks/cli/document.rb
|
|
218
218
|
- lib/hecks/cli/domain_stub.rb
|
|
219
|
+
- lib/hecks/cli/domain_writer.rb
|
|
219
220
|
- lib/hecks/cli/expression_projection.rb
|
|
220
221
|
- lib/hecks/cli/follow.rb
|
|
221
222
|
- lib/hecks/cli/history.rb
|
|
223
|
+
- lib/hecks/cli/interview_agent.rb
|
|
222
224
|
- lib/hecks/cli/interview_draft.rb
|
|
225
|
+
- lib/hecks/cli/interview_run.rb
|
|
226
|
+
- lib/hecks/cli/interview_session.rb
|
|
223
227
|
- lib/hecks/cli/ir.rb
|
|
224
228
|
- lib/hecks/cli/mcp.rb
|
|
225
229
|
- lib/hecks/cli/model_check.rb
|
|
@@ -264,6 +268,7 @@ files:
|
|
|
264
268
|
- lib/hecks/doors/ruby_door.rb
|
|
265
269
|
- lib/hecks/doors/ruby_door/aggregate_door.rb
|
|
266
270
|
- lib/hecks/doors/ruby_door/chapter.rb
|
|
271
|
+
- lib/hecks/doors/usage_cache.rb
|
|
267
272
|
- lib/hecks/embryonaut_bluebook.rb
|
|
268
273
|
- lib/hecks/embryonaut_bluebook/lock.rb
|
|
269
274
|
- lib/hecks/embryonaut_bluebook/shape.rb
|
|
@@ -369,6 +374,8 @@ files:
|
|
|
369
374
|
- lib/hecks/hecks/adapters/console_capture.rb
|
|
370
375
|
- lib/hecks/hecks/adapters/deploy_toolchain.adapter
|
|
371
376
|
- lib/hecks/hecks/adapters/deploy_toolchain.rb
|
|
377
|
+
- lib/hecks/hecks/adapters/finding_github.adapter
|
|
378
|
+
- lib/hecks/hecks/adapters/finding_github.rb
|
|
372
379
|
- lib/hecks/hecks/adapters/git.adapter
|
|
373
380
|
- lib/hecks/hecks/adapters/git.rb
|
|
374
381
|
- lib/hecks/hecks/adapters/host_http.adapter
|
|
@@ -533,7 +540,10 @@ files:
|
|
|
533
540
|
- lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl
|
|
534
541
|
- lib/hecks/projections/deploy/box/templates/fetch-secrets.sh
|
|
535
542
|
- lib/hecks/projections/deploy/box/templates/rds.yaml.tmpl
|
|
543
|
+
- lib/hecks/projections/deploy/box/templates/render-compose-taskdef.sh.tmpl
|
|
536
544
|
- lib/hecks/projections/deploy/box/templates/render-compose.sh.tmpl
|
|
545
|
+
- lib/hecks/projections/deploy/box/templates/restore-to-rds.sh.tmpl
|
|
546
|
+
- lib/hecks/projections/deploy/box/templates/verify-copy.sh.tmpl
|
|
537
547
|
- lib/hecks/projections/deploy/fargate.rb
|
|
538
548
|
- lib/hecks/projections/deploy/fargate/assembly.rb
|
|
539
549
|
- lib/hecks/projections/deploy/fargate/cdn.rb
|
|
@@ -763,6 +773,9 @@ files:
|
|
|
763
773
|
- lib/hecks/tenancy/bluebook/tenancy.hecksagon
|
|
764
774
|
- lib/hecks/three_zero.rb
|
|
765
775
|
- lib/hecks/three_zero/forms.yml
|
|
776
|
+
- lib/hecks/tickets/bluebook/tickets.bluebook
|
|
777
|
+
- lib/hecks/tickets/bluebook/tickets.hecksagon
|
|
778
|
+
- lib/hecks/tickets/bluebook/tickets.ports.hecksagon
|
|
766
779
|
- lib/hecks/tools.rb
|
|
767
780
|
- lib/hecks/tools/argument_gate_matrix.rb
|
|
768
781
|
- lib/hecks/tools/ci_gate_decision.rb
|