hecks 3.2.1 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/bluebook/assembly/contracts.rb +2 -1
  3. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +1 -0
  4. data/lib/hecks/bluebook/dsl/command_builder.rb +2 -39
  5. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +1 -1
  6. data/lib/hecks/bluebook/dsl/need_word.rb +50 -0
  7. data/lib/hecks/bluebook/dsl/query_builder.rb +6 -1
  8. data/lib/hecks/bluebook/query.rb +8 -5
  9. data/lib/hecks/cli/interview_draft.rb +86 -31
  10. data/lib/hecks/cli/interview_session.rb +77 -26
  11. data/lib/hecks/cli/interview_shape.rb +179 -0
  12. data/lib/hecks/doors/cli_runner.rb +14 -1
  13. data/lib/hecks/doors/launcher_options.rb +13 -3
  14. data/lib/hecks/doors/mcp_door_scope.rb +2 -1
  15. data/lib/hecks/doors.rb +1 -1
  16. data/lib/hecks/embryonaut_bluebook/manifest.rb +117 -0
  17. data/lib/hecks/embryonaut_bluebook/registry.rb +209 -0
  18. data/lib/hecks/embryonaut_bluebook/vendor.rb +1 -1
  19. data/lib/hecks/embryonaut_bluebook.rb +2 -0
  20. data/lib/hecks/fuzzing/qa_settings.rb +15 -34
  21. data/lib/hecks/gate/stages.yml +113 -8
  22. data/lib/hecks/hecks/adapters/codebase/regeneration.rb +10 -1
  23. data/lib/hecks/hecks/adapters/first_admin.rb +108 -0
  24. data/lib/hecks/hecks/adapters/git.rb +59 -0
  25. data/lib/hecks/hecks/adapters/in_process_access.rb +25 -0
  26. data/lib/hecks/hecks/adapters/in_process_boot.rb +2 -0
  27. data/lib/hecks/hecks/adapters/rust_toolchain.rb +18 -3
  28. data/lib/hecks/hecks/adapters/site_toolchain.rb +67 -0
  29. data/lib/hecks/hecks/codebase.behaviors +6 -0
  30. data/lib/hecks/hecks/codebase.bluebook +21 -0
  31. data/lib/hecks/hecks/custodian.behaviors +6 -0
  32. data/lib/hecks/hecks/custodian.bluebook +217 -0
  33. data/lib/hecks/hecks/hecks.hecksagon +24 -0
  34. data/lib/hecks/hecks/hecks.world +8 -3
  35. data/lib/hecks/hecks/site.world +5 -2
  36. data/lib/hecks/language/bluebook/query.bluebook +25 -0
  37. data/lib/hecks/language/bluebook/vocabulary.bluebook +213 -0
  38. data/lib/hecks/language/hecksagon/hecksagon.bluebook +1 -1
  39. data/lib/hecks/language/oidc.json +5 -0
  40. data/lib/hecks/projections/deploy/box/hosting.rb +170 -0
  41. data/lib/hecks/projections/deploy/box/hosting_settings.rb +123 -0
  42. data/lib/hecks/projections/deploy/box/settings.rb +17 -4
  43. data/lib/hecks/projections/deploy/box/templates/deploy-service.services-roll.part +33 -0
  44. data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +79 -0
  45. data/lib/hecks/projections/deploy/box/templates/deploy-service.taskdef-roll.part +77 -0
  46. data/lib/hecks/projections/deploy/box/templates/deploy-service.taskdef.part +12 -0
  47. data/lib/hecks/projections/deploy/box/templates/hosting.mk.tmpl +24 -0
  48. data/lib/hecks/projections/deploy/box/templates/smoke-after-deploy.sh.tmpl +271 -0
  49. data/lib/hecks/projections/deploy/box.rb +15 -2
  50. data/lib/hecks/projections/deploy/fargate.rb +3 -3
  51. data/lib/hecks/projections/deploy/lambda.rb +8 -8
  52. data/lib/hecks/projections/model/deviations.rb +1 -1
  53. data/lib/hecks/projections/site/live_edge.rb +162 -0
  54. data/lib/hecks/quality_control/adapters/git_pr.rb +0 -14
  55. data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +1 -1
  56. data/lib/hecks/quality_control/cli/qa_open_pr.rb +32 -16
  57. data/lib/hecks/quality_control/cli/qa_seed_angles.rb +2 -2
  58. data/lib/hecks/quality_control/cli/qa_seed_targets.rb +1 -2
  59. data/lib/hecks/quality_control/quality_control.bluebook +76 -124
  60. data/lib/hecks/runtime/interpreting.rb +3 -22
  61. data/lib/hecks/runtime/needs.rb +48 -0
  62. data/lib/hecks/runtime/query_interpreter.rb +3 -0
  63. data/lib/hecks/runtime/registry/verification.rb +3 -3
  64. data/lib/hecks/rust_build/host.rb +147 -0
  65. data/lib/hecks/rust_build/kernel_input.rb +40 -18
  66. data/lib/hecks/rust_build/project_rust/codegen_run.rb +0 -18
  67. data/lib/hecks/rust_build/project_rust.rb +3 -10
  68. data/lib/hecks/rust_build.rb +1 -0
  69. data/lib/hecks/site/bluebook/site.bluebook +53 -1
  70. data/lib/hecks/site/bluebook/site.ports.hecksagon +6 -0
  71. data/lib/hecks/sme/bluebook/sme.bluebook +128 -1
  72. data/lib/hecks/tools/gate.rb +1 -1
  73. data/lib/hecks/tools/rust_comment_style.rb +1 -3
  74. data/lib/hecks/tools/tools_doc.rb +204 -0
  75. data/lib/hecks/tools.rb +1 -0
  76. data/lib/hecks/version.rb +1 -1
  77. data/lib/hecks/vocabulary.rb +151 -0
  78. data/qa/settings.yml +1 -2
  79. data/rust/build/src/lineage_pass.rs +30 -15
  80. data/rust/build/src/main.rs +4 -2
  81. data/rust/build/src/pipeline.rs +8 -19
  82. data/rust/codegen/src/bridging.rs +7 -7
  83. data/rust/codegen/src/json.rs +214 -23
  84. data/rust/codegen/src/literal.rs +6 -3
  85. data/rust/codegen/src/main.rs +20 -9
  86. data/rust/codegen/src/naming.rs +4 -2
  87. data/rust/codegen/src/optional_pass.rs +226 -0
  88. data/rust/{build → codegen}/src/sidecars.rs +9 -5
  89. data/rust/host/HECKS_RELEASE +1 -1
  90. data/rust/host/src/api.rs +1 -1
  91. data/rust/host/src/dispatch.rs +5 -0
  92. data/rust/host/src/needs.rs +99 -2
  93. data/rust/host/src/resend.rs +4 -4
  94. data/rust/host/src/ui_schema.rs +1 -1
  95. data/rust/parser/src/emit.rs +12 -0
  96. data/rust/parser/src/ir.rs +2 -0
  97. data/rust/parser/src/keywords.rs +2 -0
  98. data/rust/parser/src/main.rs +1 -0
  99. data/rust/parser/src/parse/command.rs +2 -44
  100. data/rust/parser/src/parse/mod.rs +1 -0
  101. data/rust/parser/src/parse/needs.rs +60 -0
  102. data/rust/parser/src/parse/query.rs +3 -0
  103. data/rust/src/kernel/cli.rs +16 -6
  104. data/rust/src/kernel/needs.rs +104 -9
  105. metadata +22 -8
  106. data/lib/hecks/rust_build/append_optionals.rb +0 -79
  107. data/lib/hecks/rust_build/rust_literal.rb +0 -33
  108. data/lib/hecks/three_zero/forms.yml +0 -127
  109. data/lib/hecks/three_zero.rb +0 -12
  110. data/rust/build/src/json.rs +0 -421
  111. data/rust/build/src/optional_pass.rs +0 -150
@@ -25,6 +25,8 @@ pub fn parse_body(
25
25
 
26
26
  loop {
27
27
  let Some(gated) = super::next_line(file, lines, pos, "Query")? else {
28
+ let end = lines.get(pos.saturating_sub(1)).map_or(0, |l| l.number);
29
+ super::needs::refuse_undeclared(file, end, name, &query.needs, &query.attributes)?;
28
30
  return Ok(query);
29
31
  };
30
32
  let line = gated.line.number;
@@ -83,6 +85,7 @@ pub fn parse_body(
83
85
  type_name
84
86
  });
85
87
  }
88
+ "needs" => super::needs::declare(file, line, name, &gated.args, &mut query.needs)?,
86
89
  word if OPTION_WORDS.contains(&word) => {
87
90
  query_options::apply(file, line, word, &gated.args, &mut query.options)?
88
91
  }
@@ -18,6 +18,8 @@ pub fn run(input: &str) -> String {
18
18
 
19
19
  // Optional `"needs"`: which commands need which outside facts (ADR 0081), passed by the host.
20
20
  super::needs::install(parsed.get("needs"));
21
+ // Optional `"query_needs"`: which queries need which outside facts, filled the same way.
22
+ super::needs::install_queries(parsed.get("query_needs"));
21
23
  // Optional `"defaults"`: each command's declared argument defaults, filled the same way.
22
24
  super::needs::install_defaults(parsed.get("defaults"));
23
25
 
@@ -86,6 +88,14 @@ pub fn run(input: &str) -> String {
86
88
  // A query step's `args` sit at the sibling `"args"` key, as for a command step.
87
89
  let empty_args = Json::Object(vec![]);
88
90
  let args = step.get("args").unwrap_or(&empty_args);
91
+ // What the caller offered is what the log echoes; a fact the query `needs` is answered
92
+ // for the run alone (ADR 0081), so the log holds no value that depends on the clock.
93
+ let offered = args;
94
+ let filled = match step.get("query") {
95
+ Some(Json::Str(question)) => super::needs::enrich_query(question, args, step.get("occurred_at").and_then(Json::as_str)),
96
+ _ => None,
97
+ };
98
+ let args = filled.as_ref().unwrap_or(args);
89
99
 
90
100
  // A `"query"` step carries no verb and takes one of three shapes:
91
101
  // - `"Domain::Aggregate.Query"`: a declared aggregate query, looked up in `QUERIES`.
@@ -106,7 +116,7 @@ pub fn run(input: &str) -> String {
106
116
  let rows = Json::Array(rows);
107
117
  query_results.push(Json::obj(vec![
108
118
  ("query", Json::Str(question.clone())),
109
- ("args", args.clone()),
119
+ ("args", offered.clone()),
110
120
  ("rows", rows.clone()),
111
121
  ("reference_rows", rows),
112
122
  ]));
@@ -115,7 +125,7 @@ pub fn run(input: &str) -> String {
115
125
  let message = refusal.to_string();
116
126
  query_results.push(Json::obj(vec![
117
127
  ("query", Json::Str(question.clone())),
118
- ("args", args.clone()),
128
+ ("args", offered.clone()),
119
129
  ("rows", Json::Null),
120
130
  ("error", Json::Str(message.clone())),
121
131
  ("reference_rows", Json::Null),
@@ -131,7 +141,7 @@ pub fn run(input: &str) -> String {
131
141
  let rows = Json::Array(entries.into_iter().map(|(id, record)| repository::row_json(id, record)).collect());
132
142
  query_results.push(Json::obj(vec![
133
143
  ("query", Json::Str(question.clone())),
134
- ("args", args.clone()),
144
+ ("args", offered.clone()),
135
145
  ("rows", rows.clone()),
136
146
  // Same as `rows`: one compiled path (named_query.rs).
137
147
  ("reference_rows", rows),
@@ -143,7 +153,7 @@ pub fn run(input: &str) -> String {
143
153
  let message = refusal.to_string();
144
154
  query_results.push(Json::obj(vec![
145
155
  ("query", Json::Str(question.clone())),
146
- ("args", args.clone()),
156
+ ("args", offered.clone()),
147
157
  ("rows", Json::Null),
148
158
  ("error", Json::Str(message.clone())),
149
159
  ("reference_rows", Json::Null),
@@ -170,7 +180,7 @@ pub fn run(input: &str) -> String {
170
180
  Ok(row) => {
171
181
  query_results.push(Json::obj(vec![
172
182
  ("query", Json::Str(question.clone())),
173
- ("args", args.clone()),
183
+ ("args", offered.clone()),
174
184
  ("rows", Json::Array(vec![row])),
175
185
  // No `reference_rows` key: Ruby sets it only for questions with a
176
186
  // reference twin, and a read model has none.
@@ -181,7 +191,7 @@ pub fn run(input: &str) -> String {
181
191
  let message = refusal.to_string();
182
192
  query_results.push(Json::obj(vec![
183
193
  ("query", Json::Str(question.clone())),
184
- ("args", args.clone()),
194
+ ("args", offered.clone()),
185
195
  ("rows", Json::Null),
186
196
  ("error", Json::Str(message)),
187
197
  ]));
@@ -26,15 +26,16 @@ pub struct Need {
26
26
  thread_local! {
27
27
  static TABLE: RefCell<HashMap<String, Vec<Need>>> = RefCell::new(HashMap::new());
28
28
  static DEFAULTS: RefCell<HashMap<String, Vec<(String, Json)>>> = RefCell::new(HashMap::new());
29
+ static QUERY_TABLE: RefCell<HashMap<String, Vec<Need>>> = RefCell::new(HashMap::new());
29
30
  static FIXED_CLOCK: Cell<Option<i64>> = const { Cell::new(None) };
30
31
  }
31
32
 
32
- /// Installs the table the host passed (an absent or malformed `"needs"` installs an empty one).
33
- pub fn install(table: Option<&Json>) {
34
- let parsed = match table {
33
+ /// Reads a `{ name: [{fact, type}] }` table (an absent or malformed one reads as empty).
34
+ fn parse_table(table: Option<&Json>) -> HashMap<String, Vec<Need>> {
35
+ match table {
35
36
  Some(Json::Object(entries)) => entries
36
37
  .iter()
37
- .map(|(verb, needs)| {
38
+ .map(|(name, needs)| {
38
39
  let needs = needs
39
40
  .as_array()
40
41
  .unwrap_or(&[])
@@ -45,12 +46,23 @@ pub fn install(table: Option<&Json>) {
45
46
  Some(Need { fact, attribute_type })
46
47
  })
47
48
  .collect();
48
- (verb.clone(), needs)
49
+ (name.clone(), needs)
49
50
  })
50
51
  .collect(),
51
52
  _ => HashMap::new(),
52
- };
53
- TABLE.with(|t| *t.borrow_mut() = parsed);
53
+ }
54
+ }
55
+
56
+ /// Installs the table the host passed, command verb to its needs (an absent or malformed
57
+ /// `"needs"` installs an empty one).
58
+ pub fn install(table: Option<&Json>) {
59
+ TABLE.with(|t| *t.borrow_mut() = parse_table(table));
60
+ }
61
+
62
+ /// Installs the table of queries that need a fact, `"Domain::Aggregate.Query"` to its needs. Kept
63
+ /// apart from the commands': a command and a query may share a qualified name.
64
+ pub fn install_queries(table: Option<&Json>) {
65
+ QUERY_TABLE.with(|t| *t.borrow_mut() = parse_table(table));
54
66
  }
55
67
 
56
68
  /// Installs the declared defaults the host passed, `{ verb: { attribute: value } }` (an absent or
@@ -106,9 +118,11 @@ fn parse_iso8601_utc(text: &str) -> Option<i64> {
106
118
 
107
119
  fn answer(need: &Need, occurred_at: Option<&str>) -> Option<Json> {
108
120
  match need.fact.as_str() {
109
- "now" => {
121
+ // `now` is epoch seconds; `today` the day those fall in, whole days since the epoch (UTC).
122
+ "now" | "today" => {
110
123
  let secs = now_secs(occurred_at);
111
- Some(if need.attribute_type == "Integer" { Json::int(secs) } else { Json::obj(vec![("value", Json::int(secs))]) })
124
+ let answer = if need.fact == "today" { secs.div_euclid(86_400) } else { secs };
125
+ Some(if need.attribute_type == "Integer" { Json::int(answer) } else { Json::obj(vec![("value", Json::int(answer))]) })
112
126
  }
113
127
  _ => None,
114
128
  }
@@ -151,6 +165,27 @@ pub fn enrich(verb: &str, args: &Json, occurred_at: Option<&str>) -> Option<Json
151
165
  Some(Json::Object(envelope.iter().map(|(k, v)| if k == "with" { (k.clone(), Json::Object(filled.clone())) } else { (k.clone(), v.clone()) }).collect()))
152
166
  }
153
167
 
168
+ /// `args` with each fact the query `question` needs and the caller left out answered, or `None`
169
+ /// when nothing needed filling (the query needs nothing, or every fact is supplied). A query's
170
+ /// arguments are the flat object of its step.
171
+ pub fn enrich_query(question: &str, args: &Json, occurred_at: Option<&str>) -> Option<Json> {
172
+ let needs = QUERY_TABLE.with(|t| t.borrow().get(question).cloned())?;
173
+ let Json::Object(held) = args else { return None };
174
+ let mut filled = held.clone();
175
+ for need in &needs {
176
+ if held.iter().any(|(k, _)| k == &need.fact) {
177
+ continue;
178
+ }
179
+ if let Some(value) = answer(need, occurred_at) {
180
+ filled.push((need.fact.clone(), value));
181
+ }
182
+ }
183
+ if filled.len() == held.len() {
184
+ return None;
185
+ }
186
+ Some(Json::Object(filled))
187
+ }
188
+
154
189
  #[cfg(test)]
155
190
  mod tests {
156
191
  use super::*;
@@ -264,4 +299,64 @@ mod tests {
264
299
  install_defaults(None);
265
300
  assert_eq!(enrich("D::A.Start", &with(vec![]), None), None);
266
301
  }
302
+
303
+ fn query_table() -> Json {
304
+ Json::parse(
305
+ r#"{"D::A.Expired": [{"fact": "now", "type": "Integer"}],
306
+ "D::A.Today": [{"fact": "today", "type": "DayNumber"}]}"#,
307
+ )
308
+ .unwrap()
309
+ }
310
+
311
+ #[test]
312
+ fn a_needed_query_fact_the_caller_left_out_is_answered_by_the_clock() {
313
+ install_queries(Some(&query_table()));
314
+ fix_clock(Some(1_700_000_000));
315
+ let filled = enrich_query("D::A.Expired", &Json::obj(vec![]), None).unwrap();
316
+ assert_eq!(filled.get("now"), Some(&Json::int(1_700_000_000)));
317
+ fix_clock(None);
318
+ install_queries(None);
319
+ }
320
+
321
+ #[test]
322
+ fn a_query_value_the_caller_supplied_is_kept() {
323
+ install_queries(Some(&query_table()));
324
+ fix_clock(Some(1));
325
+ assert_eq!(enrich_query("D::A.Expired", &Json::obj(vec![("now", Json::int(7))]), None), None);
326
+ fix_clock(None);
327
+ install_queries(None);
328
+ }
329
+
330
+ #[test]
331
+ fn a_query_that_needs_nothing_is_untouched() {
332
+ install_queries(Some(&query_table()));
333
+ assert_eq!(enrich_query("D::A.Plain", &Json::obj(vec![]), None), None);
334
+ install_queries(None);
335
+ assert_eq!(enrich_query("D::A.Expired", &Json::obj(vec![]), None), None);
336
+ }
337
+
338
+ #[test]
339
+ fn a_command_and_a_query_of_one_name_keep_their_own_needs() {
340
+ install(Some(&table()));
341
+ install_queries(Some(&query_table()));
342
+ fix_clock(Some(5));
343
+ assert_eq!(enrich_query("D::A.Instant", &Json::obj(vec![]), None), None);
344
+ assert!(enrich("D::A.Instant", &with(vec![]), None).is_some());
345
+ fix_clock(None);
346
+ install(None);
347
+ install_queries(None);
348
+ }
349
+
350
+ #[test]
351
+ fn today_is_the_day_the_clock_falls_in_whole_days_since_the_epoch() {
352
+ install_queries(Some(&query_table()));
353
+ fix_clock(Some((19_000 * 86_400) + 86_399));
354
+ let filled = enrich_query("D::A.Today", &Json::obj(vec![]), None).unwrap();
355
+ assert_eq!(filled.get("today"), Some(&Json::obj(vec![("value", Json::int(19_000))])));
356
+ fix_clock(Some(19_001 * 86_400));
357
+ let next = enrich_query("D::A.Today", &Json::obj(vec![]), None).unwrap();
358
+ assert_eq!(next.get("today"), Some(&Json::obj(vec![("value", Json::int(19_001))])));
359
+ fix_clock(None);
360
+ install_queries(None);
361
+ }
267
362
  }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hecks
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.1
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Young
@@ -162,6 +162,7 @@ files:
162
162
  - lib/hecks/bluebook/dsl/identity_declaration.rb
163
163
  - lib/hecks/bluebook/dsl/lifecycle_builder.rb
164
164
  - lib/hecks/bluebook/dsl/malformed.rb
165
+ - lib/hecks/bluebook/dsl/need_word.rb
165
166
  - lib/hecks/bluebook/dsl/policy_builder.rb
166
167
  - lib/hecks/bluebook/dsl/port_builder.rb
167
168
  - lib/hecks/bluebook/dsl/port_operation_builder.rb
@@ -232,6 +233,7 @@ files:
232
233
  - lib/hecks/cli/interview_draft.rb
233
234
  - lib/hecks/cli/interview_run.rb
234
235
  - lib/hecks/cli/interview_session.rb
236
+ - lib/hecks/cli/interview_shape.rb
235
237
  - lib/hecks/cli/ir.rb
236
238
  - lib/hecks/cli/mcp.rb
237
239
  - lib/hecks/cli/model_check.rb
@@ -280,6 +282,8 @@ files:
280
282
  - lib/hecks/doors/usage_cache.rb
281
283
  - lib/hecks/embryonaut_bluebook.rb
282
284
  - lib/hecks/embryonaut_bluebook/lock.rb
285
+ - lib/hecks/embryonaut_bluebook/manifest.rb
286
+ - lib/hecks/embryonaut_bluebook/registry.rb
283
287
  - lib/hecks/embryonaut_bluebook/shape.rb
284
288
  - lib/hecks/embryonaut_bluebook/vendor.rb
285
289
  - lib/hecks/embryonaut_bluebook/vendor_cli.rb
@@ -387,10 +391,12 @@ files:
387
391
  - lib/hecks/hecks/adapters/deploy_toolchain.rb
388
392
  - lib/hecks/hecks/adapters/finding_github.adapter
389
393
  - lib/hecks/hecks/adapters/finding_github.rb
394
+ - lib/hecks/hecks/adapters/first_admin.rb
390
395
  - lib/hecks/hecks/adapters/git.adapter
391
396
  - lib/hecks/hecks/adapters/git.rb
392
397
  - lib/hecks/hecks/adapters/host_http.adapter
393
398
  - lib/hecks/hecks/adapters/host_http.rb
399
+ - lib/hecks/hecks/adapters/in_process_access.rb
394
400
  - lib/hecks/hecks/adapters/in_process_boot.adapter
395
401
  - lib/hecks/hecks/adapters/in_process_boot.rb
396
402
  - lib/hecks/hecks/adapters/in_process_operations.rb
@@ -546,14 +552,22 @@ files:
546
552
  - lib/hecks/projections.rb
547
553
  - lib/hecks/projections/bootstrap_table.rb
548
554
  - lib/hecks/projections/deploy/box.rb
555
+ - lib/hecks/projections/deploy/box/hosting.rb
556
+ - lib/hecks/projections/deploy/box/hosting_settings.rb
549
557
  - lib/hecks/projections/deploy/box/settings.rb
550
558
  - lib/hecks/projections/deploy/box/templates/box.yaml.tmpl
551
559
  - lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl
560
+ - lib/hecks/projections/deploy/box/templates/deploy-service.services-roll.part
561
+ - lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl
562
+ - lib/hecks/projections/deploy/box/templates/deploy-service.taskdef-roll.part
563
+ - lib/hecks/projections/deploy/box/templates/deploy-service.taskdef.part
552
564
  - lib/hecks/projections/deploy/box/templates/fetch-secrets.sh
565
+ - lib/hecks/projections/deploy/box/templates/hosting.mk.tmpl
553
566
  - lib/hecks/projections/deploy/box/templates/rds.yaml.tmpl
554
567
  - lib/hecks/projections/deploy/box/templates/render-compose-taskdef.sh.tmpl
555
568
  - lib/hecks/projections/deploy/box/templates/render-compose.sh.tmpl
556
569
  - lib/hecks/projections/deploy/box/templates/restore-to-rds.sh.tmpl
570
+ - lib/hecks/projections/deploy/box/templates/smoke-after-deploy.sh.tmpl
557
571
  - lib/hecks/projections/deploy/box/templates/verify-copy.sh.tmpl
558
572
  - lib/hecks/projections/deploy/fargate.rb
559
573
  - lib/hecks/projections/deploy/fargate/assembly.rb
@@ -612,6 +626,7 @@ files:
612
626
  - lib/hecks/projections/site/edge/checks.rb
613
627
  - lib/hecks/projections/site/edge/pattern.rb
614
628
  - lib/hecks/projections/site/edge/rules.rb
629
+ - lib/hecks/projections/site/live_edge.rb
615
630
  - lib/hecks/projections/site/regions.rb
616
631
  - lib/hecks/projections/site/routes_ts.rb
617
632
  - lib/hecks/projections/site/site_cdn.rb
@@ -734,6 +749,7 @@ files:
734
749
  - lib/hecks/runtime/interpreting.rb
735
750
  - lib/hecks/runtime/invocation.rb
736
751
  - lib/hecks/runtime/loader.rb
752
+ - lib/hecks/runtime/needs.rb
737
753
  - lib/hecks/runtime/outbox.rb
738
754
  - lib/hecks/runtime/policy_interpreter.rb
739
755
  - lib/hecks/runtime/port_operation_interpreter.rb
@@ -762,17 +778,16 @@ files:
762
778
  - lib/hecks/runtime/value/field_checks.rb
763
779
  - lib/hecks/runtime/value/invariant_violation.rb
764
780
  - lib/hecks/rust_build.rb
765
- - lib/hecks/rust_build/append_optionals.rb
766
781
  - lib/hecks/rust_build/conformance.rb
767
782
  - lib/hecks/rust_build/conformance_fuzz.rb
768
783
  - lib/hecks/rust_build/coverage.rb
769
784
  - lib/hecks/rust_build/domain_name.rb
785
+ - lib/hecks/rust_build/host.rb
770
786
  - lib/hecks/rust_build/kernel_input.rb
771
787
  - lib/hecks/rust_build/native_build.rb
772
788
  - lib/hecks/rust_build/project_rust.rb
773
789
  - lib/hecks/rust_build/project_rust/codegen_run.rb
774
790
  - lib/hecks/rust_build/project_rust/root_mod.rb
775
- - lib/hecks/rust_build/rust_literal.rb
776
791
  - lib/hecks/rust_build/wasm.rb
777
792
  - lib/hecks/rust_build/wasm_browser.rb
778
793
  - lib/hecks/rust_build/write_if_changed.rb
@@ -784,8 +799,6 @@ files:
784
799
  - lib/hecks/storehouse.rb
785
800
  - lib/hecks/tenancy/bluebook/tenancy.bluebook
786
801
  - lib/hecks/tenancy/bluebook/tenancy.hecksagon
787
- - lib/hecks/three_zero.rb
788
- - lib/hecks/three_zero/forms.yml
789
802
  - lib/hecks/tickets/bluebook/tickets.bluebook
790
803
  - lib/hecks/tickets/bluebook/tickets.hecksagon
791
804
  - lib/hecks/tickets/bluebook/tickets.ports.hecksagon
@@ -814,6 +827,7 @@ files:
814
827
  - lib/hecks/tools/site_routes.rb
815
828
  - lib/hecks/tools/tail_merge.rb
816
829
  - lib/hecks/tools/tenant_provisioning.rb
830
+ - lib/hecks/tools/tools_doc.rb
817
831
  - lib/hecks/tools/translation_audit.rb
818
832
  - lib/hecks/tools/translation_scaffold.rb
819
833
  - lib/hecks/vendoring.rb
@@ -835,15 +849,12 @@ files:
835
849
  - rust/build/Cargo.toml
836
850
  - rust/build/src/build_artifact.rs
837
851
  - rust/build/src/cargo_sync.rs
838
- - rust/build/src/json.rs
839
852
  - rust/build/src/lineage_pass.rs
840
853
  - rust/build/src/main.rs
841
- - rust/build/src/optional_pass.rs
842
854
  - rust/build/src/pipeline.rs
843
855
  - rust/build/src/reserved_names.rs
844
856
  - rust/build/src/resolve.rs
845
857
  - rust/build/src/root.rs
846
- - rust/build/src/sidecars.rs
847
858
  - rust/build/src/subprocess.rs
848
859
  - rust/build/src/tmp.rs
849
860
  - rust/codegen/.cargo/config.toml
@@ -867,6 +878,7 @@ files:
867
878
  - rust/codegen/src/manifest.rs
868
879
  - rust/codegen/src/mutations.rs
869
880
  - rust/codegen/src/naming.rs
881
+ - rust/codegen/src/optional_pass.rs
870
882
  - rust/codegen/src/ports.rs
871
883
  - rust/codegen/src/prelude.rs
872
884
  - rust/codegen/src/queries.rs
@@ -876,6 +888,7 @@ files:
876
888
  - rust/codegen/src/registry.rs
877
889
  - rust/codegen/src/reserved_names.rs
878
890
  - rust/codegen/src/shared.rs
891
+ - rust/codegen/src/sidecars.rs
879
892
  - rust/codegen/src/skip_reason.rs
880
893
  - rust/codegen/src/types.rs
881
894
  - rust/host/.cargo/config.toml
@@ -969,6 +982,7 @@ files:
969
982
  - rust/parser/src/parse/hecksagon.rs
970
983
  - rust/parser/src/parse/lifecycle.rs
971
984
  - rust/parser/src/parse/mod.rs
985
+ - rust/parser/src/parse/needs.rs
972
986
  - rust/parser/src/parse/policy.rs
973
987
  - rust/parser/src/parse/process_manager.rs
974
988
  - rust/parser/src/parse/query.rs
@@ -1,79 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Hecks
4
- module RustBuild
5
- # Makes an appended element's field `Option<T>` in the IR when any command sources it from an
6
- # optional argument, since Ruby stores the omitted nil without complaint. `ir.json` records
7
- # the marking, so it runs before the IR is written or generated from.
8
- #
9
- # `rust/build/src/optional_pass.rs` is the Ruby-free build's port of this; it looks only at an
10
- # aggregate's own value objects, where this reads the domain-wide set.
11
- module AppendOptionals
12
- # Marks every aggregate in the IR, in place.
13
- #
14
- # @param tree [Hash] a domain's IR, symbol-keyed
15
- # @return [Hash] the same IR
16
- def self.mark(tree)
17
- domain_wide = by_name(tree[:aggregates].flat_map { |aggregate| aggregate[:value_objects] })
18
- tree[:aggregates].each do |aggregate|
19
- mark_aggregate(aggregate, domain_wide.merge(by_name(aggregate[:value_objects])))
20
- end
21
- tree
22
- end
23
-
24
- # Resolves an `append` target to its entity or value object, or nil if it is neither. A
25
- # local entity wins over a domain-wide value object of the same name: Syntax's local
26
- # `Argument` entity shares a name with Command's `Argument` value object.
27
- #
28
- # @param aggregate [Hash] the aggregate owning the command
29
- # @param target_type [String] the appended-to attribute's element type
30
- # @param value_objects_by_name [Hash{String => Hash}] the value objects in scope
31
- # @return [Hash, nil] the element, or nil
32
- def self.element(aggregate, target_type, value_objects_by_name)
33
- aggregate[:entities].find { |entity| entity[:name] == target_type } ||
34
- value_objects_by_name[target_type]
35
- end
36
-
37
- # @param aggregate [Hash] one aggregate of the IR
38
- # @param value_objects_by_name [Hash{String => Hash}] the value objects in scope
39
- # @return [void]
40
- def self.mark_aggregate(aggregate, value_objects_by_name)
41
- aggregate[:attributes].each do |target_attr|
42
- target = element(aggregate, target_attr[:type], value_objects_by_name)
43
- next unless target
44
-
45
- aggregate[:commands].each do |command|
46
- appends = command[:mutations].select { |m| append_to?(m, target_attr) }
47
- appends.each { |mutation| mark_fields(mutation, command, target) }
48
- end
49
- end
50
- end
51
-
52
- def self.append_to?(mutation, target_attr)
53
- mutation[:op].to_s == "append" && mutation[:target].to_s == target_attr[:name].to_s
54
- end
55
- private_class_method :append_to?
56
-
57
- # A Symbol is an argument name, and an optional one is what this pass wants; anything
58
- # else is a literal.
59
- def self.mark_fields(mutation, command, target)
60
- mutation[:fields].each do |field_name, source|
61
- parsed = Hecks::Bluebook::Assembly::Marks.read(source)
62
- next unless parsed.is_a?(Symbol)
63
-
64
- source_attr = command[:attributes].find { |attr| attr[:name].to_s == parsed.to_s }
65
- next unless source_attr && source_attr[:optional]
66
-
67
- field_attr = target[:attributes].find { |attr| attr[:name].to_s == field_name.to_s }
68
- field_attr[:optional] = true if field_attr
69
- end
70
- end
71
- private_class_method :mark_fields
72
-
73
- def self.by_name(value_objects)
74
- value_objects.to_h { |value_object| [value_object[:name], value_object] }
75
- end
76
- private_class_method :by_name
77
- end
78
- end
79
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Hecks
4
- module RustBuild
5
- # Rust string literals. Ruby's `String#inspect` escapes for Ruby's own read-back (a
6
- # brace-less `\uXXXX`, for one), which is not valid Rust syntax.
7
- module RustLiteral
8
- # Escapes only what Rust's string-literal grammar needs: backslash, double quote and
9
- # control characters.
10
- #
11
- # @param text [#to_s] the text to embed
12
- # @return [String] the quoted literal
13
- def self.string(text)
14
- escaped = text.to_s.each_char.map { |char| escape(char) }.join
15
- "\"#{escaped}\""
16
- end
17
-
18
- def self.escape(char)
19
- case char
20
- when "\\" then "\\\\"
21
- when "\"" then "\\\""
22
- when "\n" then "\\n"
23
- when "\r" then "\\r"
24
- when "\t" then "\\t"
25
- else
26
- code = char.ord
27
- code < 0x20 || code == 0x7F ? format("\\u{%x}", code) : char
28
- end
29
- end
30
- private_class_method :escape
31
- end
32
- end
33
- end
@@ -1,127 +0,0 @@
1
- # Each bin/ script and exe/hecks route, and the hecks 3.0 form that replaces it: the verb spelling the
2
- # 3.0 launcher answers to (ADR 0080, section 7), with QualityControl verbs after `hecks quality_control`.
3
- ---
4
- argument_gate_matrix: hecks conformance_run.argument_gate_matrix [--confirm] (writes only with --confirm)
5
- backfill_era_projections: hecks era.backfill_projections <domain>
6
- behaviors: hecks operation.run_behaviors subject=<path>
7
- bench: hecks fuzz_run.bench [domains=pizzas,banking] [targets=] [iterations=1000] [warmup=200]
8
- [runs=3] [rust_binary=] [format=markdown] [output=]
9
- canonicalise: hecks style_run.canonicalise <file.json>
10
- check_engine_agreement: hecks conformance_run.check_engine_agreement
11
- check_era: hecks host.check_era <url> expected=era-file [timeout=]
12
- codemod_hoist_local_givens: hecks codemod_run.hoist_local_givens --confirm
13
- codemod_implicit_append_fields: hecks codemod_run.drop_implicit_append_fields --confirm
14
- compact: hecks era.compact <domain> [aggregates=A,B] --confirm
15
- console: hecks console [subject=<domain>]
16
- corpus: hecks corpus_run.rust_domains; hecks corpus_run.regen_order; hecks corpus_run.corpus_rust_coverage
17
- deploy_template_diff: hecks deploy template_comparison.diff before=a.yaml after=b.yaml [--json] [--strict]
18
- doc_coverage: hecks conformance_run.measure_doc_coverage
19
- docs: hecks docs [domain-path] [aggregate]
20
- evolve: hecks language_run.word_status; hecks language_run.propose <word> context=X [body=none] [inner=] [opens=]
21
- [fills=]; hecks language_run.admit|deprecate|retire <word> context=X; hecks language_run.rename <word> context=X
22
- new_name=Y; hecks language_run.propose_argument <word> context=X kind=K [required=false] [at=N]
23
- [named=] [fills=] [pairs_shape=]; hecks language_run.admit_argument|deprecate_argument|retire_argument
24
- <word> context=X [at=N] [named=]
25
- expression_projection: hecks language_run.project_expression_tables [--stdout]
26
- follow: hecks operation.follow <domain> [aggregate=Name] [since=N] [interval=0.5] [wait=N] [--from-now] [--stream]
27
- fuzz: hecks fuzz_run.fuzz [domain] [seeds=20] [steps=30] [workers=] [adapter=memory]
28
- generate: hecks fuzz_run.generate_sequence <domain> [seed=1] [steps=30] [adversarial=0.0]
29
- hecks_mcp_door: hecks mcp [--stdio]
30
- hecks_query_ir_mcp: hecks corpus_run.serve_query_ir_mcp
31
- heki_compact: hecks era.compact_heki <domain> [aggregates=A,B] --confirm
32
- history: hecks introspection.history <domain>
33
- ir: hecks ir <domain> [--translations] | hecks ir --meta
34
- lint_deploy_recipes: hecks deploy makefile_check.lint [makefiles=a,b]
35
- merge_tail: hecks era.merge_tail <domain> winners=id:old,id:new --confirm
36
- model_check: hecks model_check [--strict] [--profile client] [<domain> …]
37
- narrate: hecks narrate [domain-path] [aggregate]
38
- pattern-cases: hecks test_suite_run.record_pattern_cases
39
- present: hecks corpus_run.present [port=4567]
40
- project: hecks operation.refresh_projections subject=<domain>
41
- project_bootstrap_table: hecks language_run.project_bootstrap_table
42
- project_cli: hecks project_cli [domain-path …]
43
- project_deploy: hecks deploy recipe.project <domain> [tenant=] [schema=] [out=] [environment=]
44
- project_diagrams: hecks project_diagrams <domain-path> <ChapterName>
45
- project_field_hints: hecks language_run.project_field_hints
46
- project_glossary: hecks introspection.glossary <domain> chapter=Name
47
- project_kernel_capabilities: hecks kernel_run.project_kernel_capabilities
48
- project_model: hecks language_run.project_model
49
- project_oidc: hecks deploy oidc_manifest.project_oidc [domains=a,b]
50
- project_parser_table: hecks language_run.project_parser_table
51
- project_refusal_wording: hecks language_run.project_refusal_wording
52
- project_reserved_names: hecks language_run.project_reserved_names
53
- project_rust: hecks build.project_rust <domain>
54
- project_rust_vocabulary: hecks language_run.project_rust_vocabulary
55
- project_tenant: hecks deploy tenant.provision <domain_dir> slug=s domain= realm= schema=
56
- database= [adapter=PostgresEra]; hecks deploy tenant.reprovision <tenant> directory= database=
57
- [adapter=PostgresEra]
58
- project_vocabulary: hecks language_run.project_vocabulary
59
- project_wasm: hecks build.build_wasm <domain>
60
- project_wasm_browser: hecks build.build_browser_wasm <domain>
61
- qa_concurrency_racer: not user-facing; ProcessPool starts it as hecks quality_control
62
- sweep.race domain= database= schema= verb= step_arguments=
63
- qa_discover_external_domains: hecks quality_control target.discover_external_domains [arguments="--projects-dir
64
- ~/Projects --max-depth 3 --known-path PATH …"]
65
- qa_domain_novelty: hecks quality_control target.judge_novelty <domain> [arguments="--against
66
- PATH …"]
67
- qa_generated_domains: hecks quality_control target.check_generated_domains [arguments="--domains
68
- 3 --start N --forms a,b --seeds 5 --steps 25 --adversarial 0.3 --rust --shrink-budget
69
- 200 --domain-shrink-budget 40 --promote dir --name N --from-dials --blueprint F
70
- --source F"]
71
- qa_log_bug: hecks quality_control bug.log sweep=<sweep id> reference= sequence=N title=
72
- demonstration= symptom= expectation= submitter= [tags=a,b] [name=] [reproduced=yes|no]
73
- qa_mine_combinations: hecks quality_control target.mine_combinations [arguments="--candidates
74
- 3 --rust --seeds 5 --steps 25 --adversarial 0.3 --repair-rounds 1 --agent CMD --from
75
- DIR --against PATH --brief"]
76
- qa_open_pr: hecks quality_control patch.open bug=BUG#n number=N url=… branch=… commit=SHA
77
- title=… [now=]; hecks quality_control improvement.open [angle=ANGLE-n] number=N
78
- url=… branch=… title=… [now=]
79
- qa_postgres_migrate: hecks quality_control sweep.migrate_ledger_from_heki domain=<domain_dir>
80
- data=<heki_dir> [arguments="[aggregate …] --force"] (a dry run without --force)
81
- qa_postgres_role: hecks quality_control sweep.create_ledger_role <database> [role=hecks_qa]
82
- qa_pr_check: hecks quality_control clearance.check_pull_requests
83
- qa_seed_angles: hecks quality_control angle.seed
84
- qa_seed_targets: hecks quality_control target.seed
85
- qa_sweep: hecks quality_control query sweep.run [target=] [arguments="--all --seeds N --steps
86
- N --adversarial F --role-draw F --dry-run F --self-consistency BOOL --modes a,b
87
- --persistence-parity --no-parity"]; hecks quality_control target.release <target> [now=]
88
- [next_streak=] [capabilities=] [yield_score=]
89
- qa_tick: hecks quality_control sweep.tick
90
- query_ir: hecks corpus_run.ir_constructs [names=a,b]; hecks corpus_run.ir_duplicates [domains=a,b] [--meta];
91
- hecks corpus_run.ir_impact name=N field=F
92
- reattest_era: hecks era.reattest <domain> era=N --confirm
93
- reference: hecks language_run.project_reference
94
- refresh_rspec_runtime_baseline: hecks test_suite_run.refresh_runtime_baseline [workers=] [from_run=ID]
95
- regen_codegen_domains: hecks regeneration_run.regenerate_corpus [--check]
96
- regenerate_persistence_legacy_fixtures: hecks test_suite_run.regenerate_legacy_fixtures --confirm
97
- release: hecks publishing_run.publish [--gem-only] [--npm-only] [--npm-local] [--no-wait] --confirm
98
- (without --confirm, the old --dry-run)
99
- release_gem: hecks publishing_run.publish_gem --confirm
100
- rspec_io_parallel_files: hecks test_suite_run.list_io_parallel_specs exclude=REGEX [tags=] [check=file];
101
- hecks test_suite_run.write_io_parallel_spec_list exclude=REGEX [tags=] write=file --confirm
102
- rspec_shard_files: hecks test_suite_run.shard_specs group=1 groups=N [runtime_log=]
103
- run: hecks run [domain] <verb [name=value …] | script.json | - | '{"steps":[…]}'>
104
- rust_conformance: hecks build.check_conformance <domain> script=steps.json [artifact=native]
105
- rust_conformance_fuzz: hecks build.fuzz_conformance <domain> artifact=native [seeds=10]
106
- [steps=25]
107
- rust_coverage: hecks build.rust_coverage <module> [codegen=ruby]; hecks build.check_coverage_allowlist
108
- rust_kernel_coverage: hecks kernel_run.measure_kernel_coverage
109
- scaffold_translation: hecks era.scaffold_translation <domain>
110
- seed_semantics_corpus: hecks test_suite_run.seed_semantics_corpus [fixture=name]
111
- shape: hecks introspection.shape <domain>
112
- smoke_http: hecks operation.smoke_http path=/p [url=] [header=] [scheme=timestamped]
113
- [payload=] [payload_file=] [health_path=] [state_path=]
114
- smoke_test: hecks smoke_test [domain]
115
- spec_example: hecks test_suite_run.run_spec_example file=path example=text
116
- standardize_comments: hecks style_run.report_comments paths=a,b [only=] [--json] [top=20]; hecks
117
- style_run.check_comments paths=…; hecks style_run.fix_comments paths=… --confirm; hecks style_run.write_comment_baseline
118
- --confirm; hecks style_run.check_comments_unchanged ref=REF
119
- standardize_comments_rust: hecks style_run.report_rust_comments paths=a,b [only=] [--json] [top=N];
120
- hecks style_run.check_rust_comments paths=… [only=]; hecks style_run.fix_rust_comments paths=… [only=]
121
- --confirm
122
- statements: hecks introspection.statements <domain> chapter=Name
123
- stores: hecks stores <domain>
124
- stress_concurrency_specs: hecks test_suite_run.stress_concurrency [runs=] [parallel=] [seed_start=]
125
- translation_audit: hecks era.audit_translation <domain>; hecks era.approve_translation <domain>
126
- [snapshot=] [host_version=] [rehearsal=pass|fail] [rehearsed_at=] --confirm
127
- vendor_bluebook: hecks package.vendor <package[@version]> [from=path] [root=path]
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "yaml"
4
-
5
- module Hecks
6
- # The table of what each retired `bin/` script became in hecks 3.0.0 (ADR 0080): a command on
7
- # the Hecks domain, run through `exe/hecks`. `docs/tools.md` is pinned to it.
8
- module ThreeZero
9
- # Each retired script's name mapped to the 3.0 form that replaced it.
10
- FORMS = YAML.safe_load_file(File.join(__dir__, "three_zero/forms.yml")).freeze
11
- end
12
- end