hecks 3.6.0 → 3.9.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 (146) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/bluebook/meta_validator/modes.rb +14 -0
  3. data/lib/hecks/bluebook/meta_validator/syntax_boot/disk_cache.rb +28 -3
  4. data/lib/hecks/bluebook/meta_validator/verdict_cache/prebuilt.rb +44 -0
  5. data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +3 -17
  6. data/lib/hecks/bluebook/meta_validator.rb +4 -2
  7. data/lib/hecks/cache_dir.rb +10 -0
  8. data/lib/hecks/corpus/routes.rb +1 -1
  9. data/lib/hecks/deploy/bluebook/deploy.bluebook +109 -0
  10. data/lib/hecks/deploy/oidc.json +10 -0
  11. data/lib/hecks/doors/usage_cache.rb +19 -9
  12. data/lib/hecks/fuzzing/coverage_campaign.rb +42 -17
  13. data/lib/hecks/fuzzing/failure_triage.rb +80 -0
  14. data/lib/hecks/fuzzing/form_census/chapter_forms.rb +38 -0
  15. data/lib/hecks/fuzzing/form_census/reference_forms.rb +49 -0
  16. data/lib/hecks/fuzzing/form_census.rb +28 -52
  17. data/lib/hecks/fuzzing/isolated_boot/postgres_scratch.rb +22 -7
  18. data/lib/hecks/fuzzing/isolated_boot/scratch_schema.rb +27 -0
  19. data/lib/hecks/fuzzing/isolated_boot.rb +6 -2
  20. data/lib/hecks/fuzzing/mutation/checks.rb +76 -0
  21. data/lib/hecks/fuzzing/mutation/finders.rb +136 -0
  22. data/lib/hecks/fuzzing/mutation/judge.rb +94 -0
  23. data/lib/hecks/fuzzing/mutation/observation.rb +38 -0
  24. data/lib/hecks/fuzzing/mutation/operators.rb +52 -0
  25. data/lib/hecks/fuzzing/mutation/report.rb +78 -0
  26. data/lib/hecks/fuzzing/mutation/run.rb +99 -0
  27. data/lib/hecks/fuzzing/mutation/selection.rb +32 -0
  28. data/lib/hecks/fuzzing/mutation/site.rb +21 -0
  29. data/lib/hecks/fuzzing/mutation/source_map.rb +59 -0
  30. data/lib/hecks/fuzzing/mutation.rb +12 -0
  31. data/lib/hecks/fuzzing/properties/catalog.rb +10 -59
  32. data/lib/hecks/fuzzing/properties/guaranteed_by_construction.rb +63 -0
  33. data/lib/hecks/fuzzing/properties/read_model_names.rb +50 -0
  34. data/lib/hecks/fuzzing/properties/read_models.rb +138 -0
  35. data/lib/hecks/fuzzing/properties/role_checks.rb +50 -0
  36. data/lib/hecks/fuzzing/properties.rb +7 -0
  37. data/lib/hecks/fuzzing/replay/observation.rb +1 -1
  38. data/lib/hecks/fuzzing/replay/role_check.rb +49 -0
  39. data/lib/hecks/fuzzing/replay/session.rb +22 -6
  40. data/lib/hecks/fuzzing/replay.rb +1 -0
  41. data/lib/hecks/fuzzing/runtime_coverage.rb +70 -0
  42. data/lib/hecks/fuzzing/shrinker/value_simplification.rb +64 -0
  43. data/lib/hecks/fuzzing/shrinker.rb +3 -0
  44. data/lib/hecks/fuzzing.rb +3 -0
  45. data/lib/hecks/gate/stages.yml +12 -2
  46. data/lib/hecks/hecks/adapters/deploy_toolchain/answers.rb +2 -1
  47. data/lib/hecks/hecks/adapters/github_rulesets.rb +7 -5
  48. data/lib/hecks/hecks/adapters/process_pool.rb +26 -4
  49. data/lib/hecks/hecks/adapters/site_toolchain/prober.rb +64 -0
  50. data/lib/hecks/hecks/adapters/site_toolchain.rb +41 -5
  51. data/lib/hecks/hecks/custodian.behaviors +6 -0
  52. data/lib/hecks/hecks/custodian.bluebook +37 -0
  53. data/lib/hecks/hecks/hecks.hecksagon +6 -0
  54. data/lib/hecks/language/bluebook/vocabulary.bluebook +4 -1
  55. data/lib/hecks/projections/deploy/box/roll_scripts.rb +6 -4
  56. data/lib/hecks/projections/deploy/box/settings/patterns.rb +4 -0
  57. data/lib/hecks/projections/deploy/box/settings/types.rb +7 -3
  58. data/lib/hecks/projections/deploy/box/settings.rb +18 -0
  59. data/lib/hecks/projections/deploy/box/shared_database.rb +69 -0
  60. data/lib/hecks/projections/deploy/box/templates/Makefile.tmpl +6 -7
  61. data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +1 -1
  62. data/lib/hecks/projections/deploy/box/templates/provision-database.sh.tmpl +97 -0
  63. data/lib/hecks/projections/deploy/box/templates/restore-to-rds.sh.tmpl +4 -2
  64. data/lib/hecks/projections/deploy/box/templates/verify-copy.sh.tmpl +2 -1
  65. data/lib/hecks/projections/deploy/box.rb +14 -2
  66. data/lib/hecks/projections/deploy/shared_database_instance/templates/Makefile.tmpl +11 -0
  67. data/lib/hecks/projections/deploy/shared_database_instance/templates/README.md.tmpl +25 -0
  68. data/lib/hecks/projections/deploy/shared_database_instance/templates/rds.yaml.tmpl +180 -0
  69. data/lib/hecks/projections/deploy/shared_database_instance.rb +106 -0
  70. data/lib/hecks/projections/deploy/vercel/settings.rb +83 -0
  71. data/lib/hecks/projections/deploy/vercel.rb +134 -0
  72. data/lib/hecks/projections/site/probe.rb +131 -0
  73. data/lib/hecks/projections/site/site_host/dockerfile.rb +1 -1
  74. data/lib/hecks/projections/site/site_host/templates.rb +12 -4
  75. data/lib/hecks/projections.rb +2 -0
  76. data/lib/hecks/quality_control/adapters/github_checks.rb +9 -2
  77. data/lib/hecks/quality_control/cli/qa_sweep/dials.rb +1 -0
  78. data/lib/hecks/quality_control/cli/qa_sweep/seed_loop.rb +10 -2
  79. data/lib/hecks/runtime/value/validation.rb +34 -3
  80. data/lib/hecks/rust_build/native_build.rb +29 -1
  81. data/lib/hecks/rust_build/wasm.rb +16 -3
  82. data/lib/hecks/site/bluebook/site.bluebook +31 -0
  83. data/lib/hecks/site/bluebook/site.ports.hecksagon +6 -0
  84. data/lib/hecks/tools/deploy_recipe/generation.rb +59 -0
  85. data/lib/hecks/tools/deploy_recipe/targets.rb +56 -2
  86. data/lib/hecks/tools/deploy_recipe/worlds.rb +2 -1
  87. data/lib/hecks/tools/deploy_recipe.rb +20 -10
  88. data/lib/hecks/tools/fuzz_sweep/options.rb +21 -10
  89. data/lib/hecks/tools/fuzz_sweep/reporting.rb +25 -3
  90. data/lib/hecks/tools/fuzz_sweep.rb +26 -9
  91. data/lib/hecks/tools/lanes/files.rb +2 -4
  92. data/lib/hecks/tools/lanes/live.rb +3 -4
  93. data/lib/hecks/tools/lanes/ruleset.rb +23 -1
  94. data/lib/hecks/tools/lanes.rb +12 -1
  95. data/lib/hecks/tools/mutation_run.rb +73 -0
  96. data/lib/hecks/tools.rb +1 -0
  97. data/lib/hecks/version.rb +1 -1
  98. data/lib/hecks/vocabulary.rb +3 -1
  99. data/prebuilt/4e6c3cd1f578a38732ea5454dae52562d03d8e9886216077a5c34cbf589f3463.marshal +0 -0
  100. data/prebuilt/911cdcc56b5a094ed26583a4c76401dba6455b1f434722ed22b850bc1b1bdd28.marshal +0 -0
  101. data/prebuilt/verdicts-3230a251570aa03666708d16f92e646feab6a3f8cc2a3540c017d748e41d41a1.json +1 -0
  102. data/qa/settings.yml +1 -0
  103. data/rust/codegen/src/fielded.rs +3 -3
  104. data/rust/codegen/src/json_codec.rs +2 -2
  105. data/rust/codegen/src/naming.rs +20 -0
  106. data/rust/host/Cargo.toml +6 -2
  107. data/rust/host/HECKS_RELEASE +1 -1
  108. data/rust/host/src/api.rs +28 -5
  109. data/rust/host/src/auth.rs +11 -1
  110. data/rust/host/src/boundary_fuzz/api.rs +260 -0
  111. data/rust/host/src/boundary_fuzz/auth.rs +119 -0
  112. data/rust/host/src/boundary_fuzz/expr_json.rs +158 -0
  113. data/rust/host/src/boundary_fuzz/ir.rs +79 -0
  114. data/rust/host/src/boundary_fuzz/server.rs +227 -0
  115. data/rust/host/src/boundary_fuzz/web.rs +125 -0
  116. data/rust/host/src/commerce_ir.rs +459 -0
  117. data/rust/host/src/extension.rs +100 -0
  118. data/rust/host/src/fuzz_support.rs +165 -0
  119. data/rust/host/src/ir.rs +4 -448
  120. data/rust/host/src/journal.rs +1 -1
  121. data/rust/host/src/lib.rs +39 -0
  122. data/rust/host/src/main.rs +10 -44
  123. data/rust/host/src/mint.rs +1 -1
  124. data/rust/host/src/payments/tests.rs +8 -8
  125. data/rust/host/src/payments.rs +10 -10
  126. data/rust/host/src/rate_limit.rs +27 -41
  127. data/rust/host/src/secrets.rs +44 -0
  128. data/rust/host/src/server.rs +6 -1
  129. data/rust/host/src/web/commerce.rs +113 -0
  130. data/rust/host/src/web/newsletter.rs +2 -1
  131. data/rust/host/src/web/newsletter_send.rs +1 -1
  132. data/rust/host/src/web/registration_receipt.rs +2 -2
  133. data/rust/host/src/web/registrations.rs +49 -49
  134. data/rust/host/src/web/resend_webhook.rs +299 -0
  135. data/rust/host/src/web.rs +55 -49
  136. data/rust/parser/fuzz/.gitignore +4 -0
  137. data/rust/parser/fuzz/Cargo.lock +89 -0
  138. data/rust/parser/fuzz/Cargo.toml +27 -0
  139. data/rust/parser/fuzz/README.md +19 -0
  140. data/rust/parser/fuzz/fuzz_targets/chapter.rs +50 -0
  141. data/rust/parser/fuzz/fuzz_targets/resolve.rs +35 -0
  142. data/rust/parser/src/parse/command.rs +14 -2
  143. data/rust/parser/src/parse/mod.rs +25 -0
  144. data/rust/parser/src/ruby_value.rs +95 -6
  145. data/rust/src/kernel/json.rs +34 -3
  146. metadata +58 -2
@@ -0,0 +1,35 @@
1
+ //! Coverage-guided target for `parse::chapter::resolve_hecksagon_dependencies`.
2
+ //!
3
+ //! Run from `rust/parser` with a nightly toolchain: `cargo +nightly fuzz run resolve`.
4
+ #![no_main]
5
+ #![allow(dead_code)]
6
+
7
+ #[path = "../../src/build/mod.rs"]
8
+ mod build;
9
+ #[path = "../../src/canonical.rs"]
10
+ mod canonical;
11
+ #[path = "../../src/diag.rs"]
12
+ mod diag;
13
+ #[path = "../../src/emit.rs"]
14
+ mod emit;
15
+ #[path = "../../src/expr/mod.rs"]
16
+ mod expr;
17
+ #[path = "../../src/ir.rs"]
18
+ mod ir;
19
+ #[path = "../../src/keywords.rs"]
20
+ mod keywords;
21
+ #[path = "../../src/lex.rs"]
22
+ mod lex;
23
+ #[path = "../../src/parse/mod.rs"]
24
+ mod parse;
25
+ #[path = "../../src/ruby_value.rs"]
26
+ mod ruby_value;
27
+
28
+ use libfuzzer_sys::fuzz_target;
29
+
30
+ fuzz_target!(|data: &[u8]| {
31
+ let Ok(source) = std::str::from_utf8(data) else {
32
+ return;
33
+ };
34
+ let _ = parse::chapter::resolve_hecksagon_dependencies("Fuzzed", "fuzz.hecksagon", source);
35
+ });
@@ -171,10 +171,22 @@ pub fn parse_body(
171
171
 
172
172
  match gated.row.word {
173
173
  "role" => {
174
- command.role = Some(super::positional_text(file, line, "role", &gated.args, 1)?)
174
+ command.role = Some(super::positional_literal_text(
175
+ file,
176
+ line,
177
+ "role",
178
+ &gated.args,
179
+ 1,
180
+ )?)
175
181
  }
176
182
  "goal" => {
177
- command.goal = Some(super::positional_text(file, line, "goal", &gated.args, 1)?)
183
+ command.goal = Some(super::positional_literal_text(
184
+ file,
185
+ line,
186
+ "goal",
187
+ &gated.args,
188
+ 1,
189
+ )?)
178
190
  }
179
191
  // A synthesized inline `one_of(...)` closed set is dropped; `CommandBuilder#build`
180
192
  // ignores it.
@@ -743,6 +743,31 @@ pub(crate) fn positional_text(
743
743
  })
744
744
  }
745
745
 
746
+ /// Like `positional_text`, but the argument must be a literal. Ruby evaluates what follows
747
+ /// `role` or `goal`, so a bare word or a call there (`role sets :name`) is an expression this
748
+ /// parser cannot evaluate, and reading its source text as the value would invent one.
749
+ pub(crate) fn positional_literal_text(
750
+ file: &str,
751
+ line: usize,
752
+ word: &str,
753
+ args: &ArgumentGateResult,
754
+ at: usize,
755
+ ) -> ParseResult<String> {
756
+ let raw = positional_raw(file, line, word, args, at)?;
757
+ match ruby_value::read(raw) {
758
+ ruby_value::Value::Bare(_) => Err(Diagnostic::new(
759
+ file,
760
+ line,
761
+ format!(
762
+ "'{word}'s positional argument {at} ('{}') is not a literal",
763
+ raw.trim()
764
+ ),
765
+ )),
766
+ ruby_value::Value::Str(s) => Ok(s),
767
+ literal => Ok(ruby_value::to_s(&literal)),
768
+ }
769
+ }
770
+
746
771
  /// A required symbol positional without its colon (`attribute :name`), bare or quoted (`:"a.b"`).
747
772
  /// The quoted form spells dotted paths, e.g. `correlates_by :"reference.value"`.
748
773
  pub(crate) fn positional_symbol(
@@ -192,21 +192,97 @@ fn unquote(raw: &str) -> String {
192
192
  unescape_double_quoted_inner(&raw[1..raw.len() - 1])
193
193
  }
194
194
 
195
+ /// Unescapes a Ruby double-quoted body: `\n`, `\t`, `\e` and the other single-letter escapes,
196
+ /// octal `\NNN`, `\xHH`, `\uHHHH` and `\u{H H ...}`, and `\<newline>` as nothing. Any other
197
+ /// escaped character stands for itself, as in Ruby. `\c`, `\C-` and `\M-` control and meta
198
+ /// escapes are left as the bare letter, since no bluebook spells them.
195
199
  fn unescape_double_quoted_inner(inner: &str) -> String {
196
200
  let mut out = String::with_capacity(inner.len());
197
- let mut chars = inner.chars();
201
+ let mut chars = inner.chars().peekable();
198
202
  while let Some(ch) = chars.next() {
199
- if ch == '\\' {
200
- if let Some(next) = chars.next() {
201
- out.push(next);
202
- }
203
- } else {
203
+ if ch != '\\' {
204
204
  out.push(ch);
205
+ continue;
206
+ }
207
+ let Some(next) = chars.next() else {
208
+ continue;
209
+ };
210
+ match next {
211
+ 'a' => out.push('\u{7}'),
212
+ 'b' => out.push('\u{8}'),
213
+ 'e' => out.push('\u{1b}'),
214
+ 'f' => out.push('\u{c}'),
215
+ 'n' => out.push('\n'),
216
+ 'r' => out.push('\r'),
217
+ 's' => out.push(' '),
218
+ 't' => out.push('\t'),
219
+ 'v' => out.push('\u{b}'),
220
+ '\n' => {}
221
+ '0'..='7' => out.push(read_octal_escape(next, &mut chars)),
222
+ 'x' => match read_hex_digits(&mut chars, 2) {
223
+ Some(code) => out.push(char::from_u32(code).unwrap_or('\u{fffd}')),
224
+ None => out.push('x'),
225
+ },
226
+ 'u' => read_unicode_escape(&mut chars, &mut out),
227
+ other => out.push(other),
205
228
  }
206
229
  }
207
230
  out
208
231
  }
209
232
 
233
+ /// Up to three octal digits starting with `first`, as one character.
234
+ fn read_octal_escape(first: char, chars: &mut std::iter::Peekable<std::str::Chars>) -> char {
235
+ let mut code = first.to_digit(8).unwrap_or(0);
236
+ for _ in 0..2 {
237
+ match chars.peek().and_then(|c| c.to_digit(8)) {
238
+ Some(digit) => {
239
+ code = code * 8 + digit;
240
+ chars.next();
241
+ }
242
+ None => break,
243
+ }
244
+ }
245
+ char::from_u32(code & 0xff).unwrap_or('\u{fffd}')
246
+ }
247
+
248
+ /// Between one and `max` hex digits, or `None` when the next character is not one.
249
+ fn read_hex_digits(chars: &mut std::iter::Peekable<std::str::Chars>, max: usize) -> Option<u32> {
250
+ let mut code: Option<u32> = None;
251
+ for _ in 0..max {
252
+ match chars.peek().and_then(|c| c.to_digit(16)) {
253
+ Some(digit) => {
254
+ code = Some(code.unwrap_or(0) * 16 + digit);
255
+ chars.next();
256
+ }
257
+ None => break,
258
+ }
259
+ }
260
+ code
261
+ }
262
+
263
+ /// `\uHHHH` (exactly four digits) or `\u{H H ...}` (one or more codepoints), after the `u`.
264
+ fn read_unicode_escape(chars: &mut std::iter::Peekable<std::str::Chars>, out: &mut String) {
265
+ if chars.peek() == Some(&'{') {
266
+ chars.next();
267
+ loop {
268
+ while chars.peek().is_some_and(|c| *c == ' ') {
269
+ chars.next();
270
+ }
271
+ match read_hex_digits(chars, 6) {
272
+ Some(code) => out.push(char::from_u32(code).unwrap_or('\u{fffd}')),
273
+ None => break,
274
+ }
275
+ }
276
+ if chars.peek() == Some(&'}') {
277
+ chars.next();
278
+ }
279
+ } else if let Some(code) = read_hex_digits(chars, 4) {
280
+ out.push(char::from_u32(code).unwrap_or('\u{fffd}'));
281
+ } else {
282
+ out.push('u');
283
+ }
284
+ }
285
+
210
286
  /// Scans adjacent quoted literals (either quote style, whitespace-separated) from index 0.
211
287
  /// Returns their unescaped concatenation and the chars consumed, or `None` if `raw` does not
212
288
  /// start with a closed literal. Callers compare `consumed` to the full length to tell a whole
@@ -408,6 +484,19 @@ mod tests {
408
484
  );
409
485
  }
410
486
 
487
+ #[test]
488
+ fn double_quoted_escapes_read_as_ruby_reads_them() {
489
+ assert_eq!(read(r#""a\tb\nc""#), Value::Str("a\tb\nc".to_string()));
490
+ assert_eq!(read(r#""\e\s\a\0""#), Value::Str("\u{1b} \u{7}\0".to_string()));
491
+ assert_eq!(
492
+ read(r#""\x41\101é\u{1F4A5 41}""#),
493
+ Value::Str("AA\u{e9}\u{1F4A5}A".to_string())
494
+ );
495
+ assert_eq!(read(r#""q\q\"\\""#), Value::Str("qq\"\\".to_string()));
496
+ assert_eq!(read("\"a\\\nb\""), Value::Str("ab".to_string()));
497
+ assert_eq!(read(r#""\xZ""#), Value::Str("xZ".to_string()));
498
+ }
499
+
411
500
  #[test]
412
501
  fn concatenates_three_adjacent_literals_mixing_quote_styles() {
413
502
  assert_eq!(read("\"a\" 'b' \"c\""), Value::Str("abc".to_string()));
@@ -346,7 +346,7 @@ impl Json {
346
346
  }
347
347
 
348
348
  pub fn parse(input: &str) -> Result<Json, String> {
349
- let mut parser = Parser { chars: input.chars().peekable() };
349
+ let mut parser = Parser { chars: input.chars().peekable(), depth: 0 };
350
350
  let value = parser.parse_value()?;
351
351
  parser.skip_ws();
352
352
  Ok(value)
@@ -611,8 +611,14 @@ fn write_escaped_string(s: &str, out: &mut String) {
611
611
  // Recursive descent over chars; the input is a valid `&str`, so no UTF-8 byte handling is needed.
612
612
  struct Parser<'a> {
613
613
  chars: std::iter::Peekable<std::str::Chars<'a>>,
614
+ depth: usize,
614
615
  }
615
616
 
617
+ /// How deeply arrays and objects may nest before `parse` refuses. Each level is a recursive call,
618
+ /// so an unbounded document (`[[[[...`) overflows the stack, which aborts the process rather than
619
+ /// refusing. 128 is `serde_json`'s own recursion limit, so the host and this parser refuse alike.
620
+ const MAX_DEPTH: usize = 128;
621
+
616
622
  impl<'a> Parser<'a> {
617
623
  fn skip_ws(&mut self) {
618
624
  while matches!(self.chars.peek(), Some(c) if c.is_whitespace()) {
@@ -651,7 +657,26 @@ impl<'a> Parser<'a> {
651
657
  }
652
658
  }
653
659
 
660
+ // Counts one level of nesting for the duration of `body`; refuses past `MAX_DEPTH`.
661
+ fn nested(&mut self, body: impl FnOnce(&mut Self) -> Result<Json, String>) -> Result<Json, String> {
662
+ if self.depth >= MAX_DEPTH {
663
+ return Err(format!("JSON nests deeper than {MAX_DEPTH} levels"));
664
+ }
665
+ self.depth += 1;
666
+ let result = body(self);
667
+ self.depth -= 1;
668
+ result
669
+ }
670
+
654
671
  fn parse_object(&mut self) -> Result<Json, String> {
672
+ self.nested(Self::parse_object_body)
673
+ }
674
+
675
+ fn parse_array(&mut self) -> Result<Json, String> {
676
+ self.nested(Self::parse_array_body)
677
+ }
678
+
679
+ fn parse_object_body(&mut self) -> Result<Json, String> {
655
680
  self.expect('{')?;
656
681
  let mut fields = Vec::new();
657
682
  self.skip_ws();
@@ -676,7 +701,7 @@ impl<'a> Parser<'a> {
676
701
  Ok(Json::Object(fields))
677
702
  }
678
703
 
679
- fn parse_array(&mut self) -> Result<Json, String> {
704
+ fn parse_array_body(&mut self) -> Result<Json, String> {
680
705
  self.expect('[')?;
681
706
  let mut items = Vec::new();
682
707
  self.skip_ws();
@@ -773,7 +798,13 @@ impl<'a> Parser<'a> {
773
798
  // A literal with a `.`/exponent is a genuine `Float`, and Ruby's `Float` is the same
774
799
  // `f64` Rust's is, so there is nothing to preserve there.
775
800
  let is_plain_integer = !s.contains('.') && !s.contains(['e', 'E']);
776
- let raw = if is_plain_integer && canonical_integer_digits(value) != s { Some(s) } else { None };
801
+ let keeps_source = is_plain_integer && canonical_integer_digits(value) != s;
802
+ // `1e999` parses to infinity with no source text kept, and `write` would print it as `inf`:
803
+ // not JSON, so refuse it. A plain integer keeps its digits, which `write` re-emits as-is.
804
+ if !value.is_finite() && !keeps_source {
805
+ return Err(format!("number {s:?} is out of range"));
806
+ }
807
+ let raw = keeps_source.then_some(s);
777
808
  Ok(Json::Num(value, raw))
778
809
  }
779
810
  }
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.6.0
4
+ version: 3.9.0
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-06 00:00:00.000000000 Z
11
+ date: 2026-10-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: prism
@@ -271,6 +271,7 @@ files:
271
271
  - lib/hecks/bluebook/meta_validator/translation_judge.rb
272
272
  - lib/hecks/bluebook/meta_validator/translation_judge/rules.rb
273
273
  - lib/hecks/bluebook/meta_validator/verdict_cache.rb
274
+ - lib/hecks/bluebook/meta_validator/verdict_cache/prebuilt.rb
274
275
  - lib/hecks/bluebook/meta_validator/verdict_cache/tagging.rb
275
276
  - lib/hecks/bluebook/meta_validator/world_judge.rb
276
277
  - lib/hecks/bluebook/model_check.rb
@@ -473,13 +474,28 @@ files:
473
474
  - lib/hecks/fuzzing/domain_generator/rendering.rb
474
475
  - lib/hecks/fuzzing/domain_generator/tokens.rb
475
476
  - lib/hecks/fuzzing/era_boundary.rb
477
+ - lib/hecks/fuzzing/failure_triage.rb
476
478
  - lib/hecks/fuzzing/form_census.rb
479
+ - lib/hecks/fuzzing/form_census/chapter_forms.rb
480
+ - lib/hecks/fuzzing/form_census/reference_forms.rb
477
481
  - lib/hecks/fuzzing/generated_domain_check.rb
478
482
  - lib/hecks/fuzzing/invalid_value_generator.rb
479
483
  - lib/hecks/fuzzing/isolated_boot.rb
480
484
  - lib/hecks/fuzzing/isolated_boot/copying.rb
481
485
  - lib/hecks/fuzzing/isolated_boot/postgres_scratch.rb
486
+ - lib/hecks/fuzzing/isolated_boot/scratch_schema.rb
482
487
  - lib/hecks/fuzzing/isolated_boot/worlds.rb
488
+ - lib/hecks/fuzzing/mutation.rb
489
+ - lib/hecks/fuzzing/mutation/checks.rb
490
+ - lib/hecks/fuzzing/mutation/finders.rb
491
+ - lib/hecks/fuzzing/mutation/judge.rb
492
+ - lib/hecks/fuzzing/mutation/observation.rb
493
+ - lib/hecks/fuzzing/mutation/operators.rb
494
+ - lib/hecks/fuzzing/mutation/report.rb
495
+ - lib/hecks/fuzzing/mutation/run.rb
496
+ - lib/hecks/fuzzing/mutation/selection.rb
497
+ - lib/hecks/fuzzing/mutation/site.rb
498
+ - lib/hecks/fuzzing/mutation/source_map.rb
483
499
  - lib/hecks/fuzzing/nondeterministic.rb
484
500
  - lib/hecks/fuzzing/outside_world.rb
485
501
  - lib/hecks/fuzzing/persistence_parity.rb
@@ -489,6 +505,7 @@ files:
489
505
  - lib/hecks/fuzzing/properties/corrections.rb
490
506
  - lib/hecks/fuzzing/properties/dispatch_and_mutations.rb
491
507
  - lib/hecks/fuzzing/properties/group_by_recompute.rb
508
+ - lib/hecks/fuzzing/properties/guaranteed_by_construction.rb
492
509
  - lib/hecks/fuzzing/properties/guards.rb
493
510
  - lib/hecks/fuzzing/properties/invariants_and_aggregation.rb
494
511
  - lib/hecks/fuzzing/properties/lifecycle_and_replay.rb
@@ -498,7 +515,10 @@ files:
498
515
  - lib/hecks/fuzzing/properties/policy_wiring.rb
499
516
  - lib/hecks/fuzzing/properties/query_rows.rb
500
517
  - lib/hecks/fuzzing/properties/querying.rb
518
+ - lib/hecks/fuzzing/properties/read_model_names.rb
519
+ - lib/hecks/fuzzing/properties/read_models.rb
501
520
  - lib/hecks/fuzzing/properties/references.rb
521
+ - lib/hecks/fuzzing/properties/role_checks.rb
502
522
  - lib/hecks/fuzzing/properties/tenant_scope.rb
503
523
  - lib/hecks/fuzzing/qa_settings.rb
504
524
  - lib/hecks/fuzzing/replay.rb
@@ -508,8 +528,10 @@ files:
508
528
  - lib/hecks/fuzzing/replay/mutation_trace.rb
509
529
  - lib/hecks/fuzzing/replay/observation.rb
510
530
  - lib/hecks/fuzzing/replay/read_only_steps.rb
531
+ - lib/hecks/fuzzing/replay/role_check.rb
511
532
  - lib/hecks/fuzzing/replay/session.rb
512
533
  - lib/hecks/fuzzing/rotation_priority.rb
534
+ - lib/hecks/fuzzing/runtime_coverage.rb
513
535
  - lib/hecks/fuzzing/rust_gap_manifest.rb
514
536
  - lib/hecks/fuzzing/self_consistency.rb
515
537
  - lib/hecks/fuzzing/self_consistency/saga_redelivery.rb
@@ -530,6 +552,7 @@ files:
530
552
  - lib/hecks/fuzzing/sequence_generator/query_binding.rb
531
553
  - lib/hecks/fuzzing/sequence_generator/step_builder.rb
532
554
  - lib/hecks/fuzzing/shrinker.rb
555
+ - lib/hecks/fuzzing/shrinker/value_simplification.rb
533
556
  - lib/hecks/fuzzing/structural_skips.rb
534
557
  - lib/hecks/fuzzing/sweep_depth.rb
535
558
  - lib/hecks/fuzzing/target_capabilities.rb
@@ -621,6 +644,7 @@ files:
621
644
  - lib/hecks/hecks/adapters/shell.rb
622
645
  - lib/hecks/hecks/adapters/site_toolchain.adapter
623
646
  - lib/hecks/hecks/adapters/site_toolchain.rb
647
+ - lib/hecks/hecks/adapters/site_toolchain/prober.rb
624
648
  - lib/hecks/hecks/adapters/terminal.adapter
625
649
  - lib/hecks/hecks/adapters/terminal.rb
626
650
  - lib/hecks/hecks/codebase.behaviors
@@ -793,6 +817,7 @@ files:
793
817
  - lib/hecks/projections/deploy/box/settings/origin.rb
794
818
  - lib/hecks/projections/deploy/box/settings/patterns.rb
795
819
  - lib/hecks/projections/deploy/box/settings/types.rb
820
+ - lib/hecks/projections/deploy/box/shared_database.rb
796
821
  - lib/hecks/projections/deploy/box/stack_yaml.rb
797
822
  - lib/hecks/projections/deploy/box/templates/MIGRATION.md.tmpl
798
823
  - lib/hecks/projections/deploy/box/templates/Makefile.tmpl
@@ -809,6 +834,7 @@ files:
809
834
  - lib/hecks/projections/deploy/box/templates/hosting.mk.tmpl
810
835
  - lib/hecks/projections/deploy/box/templates/mode-summary-services.tmpl
811
836
  - lib/hecks/projections/deploy/box/templates/mode-summary-taskdef.tmpl
837
+ - lib/hecks/projections/deploy/box/templates/provision-database.sh.tmpl
812
838
  - lib/hecks/projections/deploy/box/templates/rds.yaml.tmpl
813
839
  - lib/hecks/projections/deploy/box/templates/render-compose-taskdef.sh.tmpl
814
840
  - lib/hecks/projections/deploy/box/templates/render-compose.sh.tmpl
@@ -879,6 +905,10 @@ files:
879
905
  - lib/hecks/projections/deploy/scripts/smoke-after-deploy.sh.tmpl
880
906
  - lib/hecks/projections/deploy/shared.rb
881
907
  - lib/hecks/projections/deploy/shared/rows.rb
908
+ - lib/hecks/projections/deploy/shared_database_instance.rb
909
+ - lib/hecks/projections/deploy/shared_database_instance/templates/Makefile.tmpl
910
+ - lib/hecks/projections/deploy/shared_database_instance/templates/README.md.tmpl
911
+ - lib/hecks/projections/deploy/shared_database_instance/templates/rds.yaml.tmpl
882
912
  - lib/hecks/projections/deploy/smoke.rb
883
913
  - lib/hecks/projections/deploy/smoke/harness.js
884
914
  - lib/hecks/projections/deploy/smoke/workflow.yml.tmpl
@@ -971,6 +1001,8 @@ files:
971
1001
  - lib/hecks/projections/deploy/templates/shared/plain_db.tmpl
972
1002
  - lib/hecks/projections/deploy/templates/shared/vpc_and_database.tmpl
973
1003
  - lib/hecks/projections/deploy/text_template.rb
1004
+ - lib/hecks/projections/deploy/vercel.rb
1005
+ - lib/hecks/projections/deploy/vercel/settings.rb
974
1006
  - lib/hecks/projections/diagrams.rb
975
1007
  - lib/hecks/projections/diagrams/dispatch.rb
976
1008
  - lib/hecks/projections/diagrams/statecharts.rb
@@ -1030,6 +1062,7 @@ files:
1030
1062
  - lib/hecks/projections/site/payload/specs.rb
1031
1063
  - lib/hecks/projections/site/payload/specs/wire.rb
1032
1064
  - lib/hecks/projections/site/payload_driver.rb
1065
+ - lib/hecks/projections/site/probe.rb
1033
1066
  - lib/hecks/projections/site/regions.rb
1034
1067
  - lib/hecks/projections/site/root_rows.rb
1035
1068
  - lib/hecks/projections/site/routes_ts.rb
@@ -1375,6 +1408,7 @@ files:
1375
1408
  - lib/hecks/tools/comment_style/walker.rb
1376
1409
  - lib/hecks/tools/corpus_report.rb
1377
1410
  - lib/hecks/tools/deploy_recipe.rb
1411
+ - lib/hecks/tools/deploy_recipe/generation.rb
1378
1412
  - lib/hecks/tools/deploy_recipe/targets.rb
1379
1413
  - lib/hecks/tools/deploy_recipe/worlds.rb
1380
1414
  - lib/hecks/tools/deploy_recipe_lint.rb
@@ -1405,6 +1439,7 @@ files:
1405
1439
  - lib/hecks/tools/lanes/ruleset.rb
1406
1440
  - lib/hecks/tools/lanes/watch_workflow.rb
1407
1441
  - lib/hecks/tools/lanes/workflow.rb
1442
+ - lib/hecks/tools/mutation_run.rb
1408
1443
  - lib/hecks/tools/oidc_manifests.rb
1409
1444
  - lib/hecks/tools/query_ir_run.rb
1410
1445
  - lib/hecks/tools/regeneration_run.rb
@@ -1439,6 +1474,9 @@ files:
1439
1474
  - lib/rubocop/cop/hecks/fallback_hash_lookup.rb
1440
1475
  - lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb
1441
1476
  - lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb
1477
+ - prebuilt/4e6c3cd1f578a38732ea5454dae52562d03d8e9886216077a5c34cbf589f3463.marshal
1478
+ - prebuilt/911cdcc56b5a094ed26583a4c76401dba6455b1f434722ed22b850bc1b1bdd28.marshal
1479
+ - prebuilt/verdicts-3230a251570aa03666708d16f92e646feab6a3f8cc2a3540c017d748e41d41a1.json
1442
1480
  - qa/settings.yml
1443
1481
  - rust/.cargo/config.toml
1444
1482
  - rust/.cargo/mutants.toml
@@ -1505,13 +1543,23 @@ files:
1505
1543
  - rust/host/src/bin/expr_harness.rs
1506
1544
  - rust/host/src/bin/lineage_harness.rs
1507
1545
  - rust/host/src/bin/mint_harness.rs
1546
+ - rust/host/src/boundary_fuzz/api.rs
1547
+ - rust/host/src/boundary_fuzz/auth.rs
1548
+ - rust/host/src/boundary_fuzz/expr_json.rs
1549
+ - rust/host/src/boundary_fuzz/ir.rs
1550
+ - rust/host/src/boundary_fuzz/server.rs
1551
+ - rust/host/src/boundary_fuzz/web.rs
1508
1552
  - rust/host/src/checkout.rs
1553
+ - rust/host/src/commerce_ir.rs
1509
1554
  - rust/host/src/dispatch.rs
1510
1555
  - rust/host/src/expr_json.rs
1556
+ - rust/host/src/extension.rs
1511
1557
  - rust/host/src/field_hints.rs
1558
+ - rust/host/src/fuzz_support.rs
1512
1559
  - rust/host/src/ir.rs
1513
1560
  - rust/host/src/journal.rs
1514
1561
  - rust/host/src/lambda_client.rs
1562
+ - rust/host/src/lib.rs
1515
1563
  - rust/host/src/log.rs
1516
1564
  - rust/host/src/main.rs
1517
1565
  - rust/host/src/mint.rs
@@ -1533,6 +1581,7 @@ files:
1533
1581
  - rust/host/src/ui_schema.rs
1534
1582
  - rust/host/src/wasm_runner.rs
1535
1583
  - rust/host/src/web.rs
1584
+ - rust/host/src/web/commerce.rs
1536
1585
  - rust/host/src/web/newsletter.rs
1537
1586
  - rust/host/src/web/newsletter/cooldown.rs
1538
1587
  - rust/host/src/web/newsletter/template.rs
@@ -1541,6 +1590,7 @@ files:
1541
1590
  - rust/host/src/web/registration_receipt.rs
1542
1591
  - rust/host/src/web/registration_receipt/tests.rs
1543
1592
  - rust/host/src/web/registrations.rs
1593
+ - rust/host/src/web/resend_webhook.rs
1544
1594
  - rust/lsp/.gitignore
1545
1595
  - rust/lsp/Cargo.lock
1546
1596
  - rust/lsp/Cargo.toml
@@ -1554,6 +1604,12 @@ files:
1554
1604
  - rust/parser/.gitignore
1555
1605
  - rust/parser/Cargo.lock
1556
1606
  - rust/parser/Cargo.toml
1607
+ - rust/parser/fuzz/.gitignore
1608
+ - rust/parser/fuzz/Cargo.lock
1609
+ - rust/parser/fuzz/Cargo.toml
1610
+ - rust/parser/fuzz/README.md
1611
+ - rust/parser/fuzz/fuzz_targets/chapter.rs
1612
+ - rust/parser/fuzz/fuzz_targets/resolve.rs
1557
1613
  - rust/parser/src/build/closed_sets.rs
1558
1614
  - rust/parser/src/build/identity.rs
1559
1615
  - rust/parser/src/build/mod.rs