hecks 3.12.0 → 3.13.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/script_runner.adapter +3 -0
  3. data/lib/hecks/adapters/driven/script_runner.rb +93 -0
  4. data/lib/hecks/adapters/driven.rb +1 -0
  5. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +16 -12
  6. data/lib/hecks/bluebook/meta_validator.rb +28 -21
  7. data/lib/hecks/deploy/bluebook/deploy.bluebook +1 -1
  8. data/lib/hecks/framework/bluebook/change.behaviors +30 -0
  9. data/lib/hecks/framework/bluebook/change.bluebook +162 -0
  10. data/lib/hecks/framework/bluebook/change.hecksagon +21 -0
  11. data/lib/hecks/framework/change.oidc.json +34 -0
  12. data/lib/hecks/framework/console_settings.oidc.json +103 -0
  13. data/lib/hecks/framework/governance.oidc.json +28 -0
  14. data/lib/hecks/framework/identity.oidc.json +18 -0
  15. data/lib/hecks/framework/privacy.oidc.json +30 -0
  16. data/lib/hecks/gate/stages.yml +3 -0
  17. data/lib/hecks/grammar/translation.oidc.json +48 -0
  18. data/lib/hecks/hecks/adapters/codebase/test_runner.rb +12 -1
  19. data/lib/hecks/hecks/adapters/deploy_toolchain.rb +2 -1
  20. data/lib/hecks/projections/deploy/fargate/cdn/lines.rb +12 -1
  21. data/lib/hecks/projections/site/admin_cms/templates/membership.rb +10 -7
  22. data/lib/hecks/projections/site/edge/behaviours.rb +10 -6
  23. data/lib/hecks/projections/site/edge/checks.rb +2 -0
  24. data/lib/hecks/projections/site/edge/function_checks.rb +104 -0
  25. data/lib/hecks/projections/site/edge/function_code.rb +86 -0
  26. data/lib/hecks/projections/site/edge/redirect_host.js.tmpl +65 -0
  27. data/lib/hecks/projections/site/edge.rb +35 -9
  28. data/lib/hecks/projections/site/site_cdn.rb +21 -7
  29. data/lib/hecks/projections/site/table/row_builder.rb +12 -2
  30. data/lib/hecks/projections/site/table.rb +4 -2
  31. data/lib/hecks/site/bluebook/site.bluebook +16 -1
  32. data/lib/hecks/tools/oidc_manifests.rb +44 -6
  33. data/lib/hecks/tools/site_routes/templates.rb +13 -9
  34. data/lib/hecks/version.rb +1 -1
  35. data/prebuilt/{verdicts-df19f35868d3284a70f55e42dcef87ef4e65e6a678e5135f72e1f6cff4c1357b.json → verdicts-48318ca172edd21443b1ad02801c697fff100b746e828f09db987647641a5df2.json} +1 -1
  36. data/rust/host/HECKS_RELEASE +1 -1
  37. data/rust/host/src/auth.rs +58 -0
  38. data/rust/host/src/platform_signin/session.rs +1 -1
  39. metadata +21 -7
  40. /data/lib/hecks/framework/{oidc.json → compliance.oidc.json} +0 -0
  41. /data/lib/hecks/grammar/{oidc.json → expression.oidc.json} +0 -0
  42. /data/prebuilt/{d74a00e996222c1d8b676d5959092effc46b4dc59e0555f3bf9554ac58e3094a.marshal → 2b59737fbdbd5f44edda0fddce2611f45d76087b3a7c782d1f1a17ea58c77948.marshal} +0 -0
  43. /data/prebuilt/{9ee54e2f0d4d6d3fe046827779b221e02de665fba911a048ae5dd8b8fbfb7d90.marshal → 58208399bada10b0db99928c48e38789288fc58f7b71a72b21bfa386680e32ed.marshal} +0 -0
@@ -1 +1 @@
1
- 3.12.0
1
+ 3.13.0
@@ -260,6 +260,43 @@ fn identity_id_from_state(state: &Value) -> Option<String> {
260
260
  })
261
261
  }
262
262
 
263
+ /// Resolves an external identity the way the Google callback does: the PostgresEra head first,
264
+ /// then the flat journal's `instances`. A domain whose Identity chapter keeps no head of its own
265
+ /// (its heads cover its own aggregates and the membership aggregate) holds the link only there.
266
+ ///
267
+ /// # Errors
268
+ ///
269
+ /// Returns the flat journal's read error, except that a missing flat journal means no link there. A
270
+ /// head that cannot be read is logged and skipped, as the Google callback does, so the flat journal
271
+ /// still gets its turn.
272
+ pub async fn resolve_identity_anywhere(
273
+ client: &Mutex<Client>,
274
+ wasm_path: &Path,
275
+ domain_ir: &Value,
276
+ issuer: &str,
277
+ subject: &str,
278
+ ) -> anyhow::Result<Option<String>> {
279
+ match resolve_identity_from_head(client, domain_ir, issuer, subject).await {
280
+ Ok(Some(found)) => return Ok(Some(found)),
281
+ Ok(None) => {}
282
+ Err(error) => eprintln!("resolve_identity: identity head: {error:#}"),
283
+ }
284
+ let read = match dispatch::read(client, wasm_path).await {
285
+ Ok(read) => read,
286
+ Err(error) if flat_journal_is_absent(&error) => return Ok(None),
287
+ Err(error) => return Err(error),
288
+ };
289
+ Ok(resolve_identity(read.get("instances").unwrap_or(&Value::Null), issuer, subject))
290
+ }
291
+
292
+ // A store with heads only has no flat journal to read; that is "no link there", not an outage.
293
+ fn flat_journal_is_absent(error: &anyhow::Error) -> bool {
294
+ error
295
+ .chain()
296
+ .filter_map(|cause| cause.downcast_ref::<tokio_postgres::Error>())
297
+ .any(|db| db.code() == Some(&tokio_postgres::error::SqlState::UNDEFINED_TABLE))
298
+ }
299
+
263
300
  /// Identity lives in the PostgresEra head, not `dispatch::read`'s
264
301
  /// `instances` — tries both the domain-qualified and the bare relation name.
265
302
  pub async fn resolve_identity_from_head(
@@ -1102,6 +1139,27 @@ mod tests {
1102
1139
  assert!(resolve_identity(&instances, "google", "nope").is_none());
1103
1140
  }
1104
1141
 
1142
+ #[test]
1143
+ fn resolve_identity_finds_a_link_that_only_the_flat_journal_holds() {
1144
+ // The shape `ExternalIdentifier.Link` leaves in the flat snapshot of a domain whose Identity
1145
+ // chapter has no head of its own; the key carries the issuer URL and the subject.
1146
+ let instances = json!({
1147
+ "Identity::ExternalIdentifier#https://accounts.google.com:111": {
1148
+ "identity": "471ef402-8609-4fa4-9288-6372fc74650d",
1149
+ "key": {"value": "https://accounts.google.com:111"},
1150
+ "issuer": {"value": "https://accounts.google.com"},
1151
+ "subject": {"value": "111"},
1152
+ },
1153
+ });
1154
+ let read = json!({ "instances": instances });
1155
+ assert_eq!(
1156
+ resolve_identity(read.get("instances").unwrap_or(&Value::Null), "https://accounts.google.com", "111").as_deref(),
1157
+ Some("471ef402-8609-4fa4-9288-6372fc74650d")
1158
+ );
1159
+ assert!(resolve_identity(read.get("instances").unwrap_or(&Value::Null), "https://accounts.google.com", "112").is_none());
1160
+ assert!(resolve_identity(&Value::Null, "https://accounts.google.com", "111").is_none());
1161
+ }
1162
+
1105
1163
  #[test]
1106
1164
  fn resolve_identity_reads_the_2_0_identity_field() {
1107
1165
  let instances = json!({
@@ -123,7 +123,7 @@ pub struct HostMembers<'a> {
123
123
  #[async_trait]
124
124
  impl MemberDirectory for HostMembers<'_> {
125
125
  async fn identity_for(&self, issuer: &str, subject: &str) -> anyhow::Result<Option<String>> {
126
- auth::resolve_identity_from_head(self.client, self.domain_ir, issuer, subject).await
126
+ auth::resolve_identity_anywhere(self.client, self.wasm_path, self.domain_ir, issuer, subject).await
127
127
  }
128
128
 
129
129
  async fn session_for_identity(&self, identity_id: &str) -> anyhow::Result<Option<Session>> {
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.12.0
4
+ version: 3.13.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-16 00:00:00.000000000 Z
11
+ date: 2026-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: prism
@@ -97,6 +97,8 @@ files:
97
97
  - lib/hecks/adapters/driven/postgres_era.adapter
98
98
  - lib/hecks/adapters/driven/prism.adapter
99
99
  - lib/hecks/adapters/driven/prism.rb
100
+ - lib/hecks/adapters/driven/script_runner.adapter
101
+ - lib/hecks/adapters/driven/script_runner.rb
100
102
  - lib/hecks/adapters/driven/secure_random_identity.adapter
101
103
  - lib/hecks/adapters/driven/secure_random_identity.rb
102
104
  - lib/hecks/adapters/driven/sql_query_builder.rb
@@ -454,13 +456,21 @@ files:
454
456
  - lib/hecks/forms/value_object_shape.rb
455
457
  - lib/hecks/fqn.rb
456
458
  - lib/hecks/framework.rb
459
+ - lib/hecks/framework/bluebook/change.behaviors
460
+ - lib/hecks/framework/bluebook/change.bluebook
461
+ - lib/hecks/framework/bluebook/change.hecksagon
457
462
  - lib/hecks/framework/bluebook/compliance.bluebook
458
463
  - lib/hecks/framework/bluebook/console_settings.bluebook
459
464
  - lib/hecks/framework/bluebook/framework.hecksagon
460
465
  - lib/hecks/framework/bluebook/governance.bluebook
461
466
  - lib/hecks/framework/bluebook/identity.bluebook
462
467
  - lib/hecks/framework/bluebook/privacy.bluebook
463
- - lib/hecks/framework/oidc.json
468
+ - lib/hecks/framework/change.oidc.json
469
+ - lib/hecks/framework/compliance.oidc.json
470
+ - lib/hecks/framework/console_settings.oidc.json
471
+ - lib/hecks/framework/governance.oidc.json
472
+ - lib/hecks/framework/identity.oidc.json
473
+ - lib/hecks/framework/privacy.oidc.json
464
474
  - lib/hecks/freezer.rb
465
475
  - lib/hecks/fuzzing.rb
466
476
  - lib/hecks/fuzzing/bounded_exhaustive_expressions.rb
@@ -574,11 +584,12 @@ files:
574
584
  - lib/hecks/grammar/evolve/keywords.rb
575
585
  - lib/hecks/grammar/evolve/seeds.rb
576
586
  - lib/hecks/grammar/expression.bluebook
587
+ - lib/hecks/grammar/expression.oidc.json
577
588
  - lib/hecks/grammar/expression_operators.json
578
589
  - lib/hecks/grammar/grammar.hecksagon
579
- - lib/hecks/grammar/oidc.json
580
590
  - lib/hecks/grammar/operators.rb
581
591
  - lib/hecks/grammar/translation.bluebook
592
+ - lib/hecks/grammar/translation.oidc.json
582
593
  - lib/hecks/hecks/adapters/bluebook_report.rb
583
594
  - lib/hecks/hecks/adapters/codebase/codemods.rb
584
595
  - lib/hecks/hecks/adapters/codebase/conformance.rb
@@ -1177,7 +1188,10 @@ files:
1177
1188
  - lib/hecks/projections/site/edge/behaviours.rb
1178
1189
  - lib/hecks/projections/site/edge/checks.rb
1179
1190
  - lib/hecks/projections/site/edge/findings.rb
1191
+ - lib/hecks/projections/site/edge/function_checks.rb
1192
+ - lib/hecks/projections/site/edge/function_code.rb
1180
1193
  - lib/hecks/projections/site/edge/pattern.rb
1194
+ - lib/hecks/projections/site/edge/redirect_host.js.tmpl
1181
1195
  - lib/hecks/projections/site/edge/route_checks.rb
1182
1196
  - lib/hecks/projections/site/edge/row_reader.rb
1183
1197
  - lib/hecks/projections/site/edge/rules.rb
@@ -1622,9 +1636,9 @@ files:
1622
1636
  - lib/rubocop/cop/hecks/symbol_string_key_mix.rb
1623
1637
  - lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb
1624
1638
  - lib/rubocop/cop/hecks/untyped_raise.rb
1625
- - prebuilt/9ee54e2f0d4d6d3fe046827779b221e02de665fba911a048ae5dd8b8fbfb7d90.marshal
1626
- - prebuilt/d74a00e996222c1d8b676d5959092effc46b4dc59e0555f3bf9554ac58e3094a.marshal
1627
- - prebuilt/verdicts-df19f35868d3284a70f55e42dcef87ef4e65e6a678e5135f72e1f6cff4c1357b.json
1639
+ - prebuilt/2b59737fbdbd5f44edda0fddce2611f45d76087b3a7c782d1f1a17ea58c77948.marshal
1640
+ - prebuilt/58208399bada10b0db99928c48e38789288fc58f7b71a72b21bfa386680e32ed.marshal
1641
+ - prebuilt/verdicts-48318ca172edd21443b1ad02801c697fff100b746e828f09db987647641a5df2.json
1628
1642
  - qa/settings.yml
1629
1643
  - rust/.cargo/config.toml
1630
1644
  - rust/.cargo/mutants.toml