hecks 3.2.0 → 3.2.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/lib/hecks/bluebook/dsl/hecksagon_builder.rb +3 -3
- data/lib/hecks/cli/check_era.rb +3 -39
- data/lib/hecks/doors/mcp_door.rb +16 -2
- data/lib/hecks/doors/mcp_door_scope.rb +43 -4
- data/lib/hecks/doors/mcp_guide_cache.rb +75 -0
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +17 -1
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +10 -2
- data/lib/hecks/hecks/adapters/rust_workspace.rb +1 -1
- data/lib/hecks/hecks/adapters/site_toolchain.rb +21 -11
- data/lib/hecks/language/bluebook/vocabulary.bluebook +7 -7
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +1 -1
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +3 -3
- data/lib/hecks/projections/deploy/box/settings.rb +23 -2
- data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +2 -0
- data/lib/hecks/projections/deploy/box/templates/render-compose-taskdef.sh.tmpl +17 -0
- data/lib/hecks/projections/deploy/box.rb +13 -2
- data/lib/hecks/projections/site/edge/behaviours.rb +5 -3
- data/lib/hecks/projections/site/edge/checks.rb +31 -5
- data/lib/hecks/projections/site/edge.rb +25 -11
- data/lib/hecks/projections/site/routes_ts.rb +50 -27
- data/lib/hecks/projections/site/site_cdn.rb +7 -4
- data/lib/hecks/projections/site/table/checks.rb +53 -7
- data/lib/hecks/projections/site/table/link_builder.rb +64 -0
- data/lib/hecks/projections/site/table/row_builder.rb +21 -6
- data/lib/hecks/projections/site/table.rb +16 -5
- data/lib/hecks/rust_build/kernel_input.rb +18 -6
- data/lib/hecks/rust_build/project_rust.rb +6 -132
- data/lib/hecks/site/bluebook/site.bluebook +27 -13
- data/lib/hecks/storehouse.rb +35 -1
- data/lib/hecks/tools/comment_style.rb +4 -2
- data/lib/hecks/tools/rust_comment_style.rb +1 -1
- data/lib/hecks/tools/site_routes.rb +69 -28
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +1 -1
- data/lib/hecks.rb +1 -0
- data/rust/build/src/lineage_pass.rs +1 -1
- data/rust/build/src/pipeline.rs +1 -1
- data/rust/codegen/src/attr.rs +1 -1
- data/rust/codegen/src/bridging.rs +1 -1
- data/rust/codegen/src/commands.rs +1 -1
- data/rust/codegen/src/constraints.rs +1 -1
- data/rust/codegen/src/dependency_planning.rs +1 -1
- data/rust/codegen/src/domain_generator.rs +2 -2
- data/rust/codegen/src/exemplar.rs +1 -1
- data/rust/codegen/src/expr_emitter.rs +1 -1
- data/rust/codegen/src/fielded.rs +1 -1
- data/rust/codegen/src/json_codec.rs +1 -1
- data/rust/codegen/src/main.rs +3 -4
- data/rust/codegen/src/mutations.rs +1 -1
- data/rust/codegen/src/naming.rs +1 -1
- data/rust/codegen/src/ports.rs +1 -1
- data/rust/codegen/src/queries.rs +3 -3
- data/rust/codegen/src/reactions.rs +1 -1
- data/rust/codegen/src/read_models.rs +2 -2
- data/rust/codegen/src/reference_specs.rs +1 -1
- data/rust/codegen/src/registry.rs +4 -4
- data/rust/codegen/src/shared.rs +1 -1
- data/rust/codegen/src/skip_reason.rs +1 -1
- data/rust/codegen/src/types.rs +4 -4
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/src/needs.rs +94 -27
- data/rust/src/exemplar/commands.rs +1 -1
- data/rust/src/exemplar/constraints.rs +1 -1
- data/rust/src/exemplar/fielded.rs +1 -1
- data/rust/src/exemplar/json.rs +1 -1
- data/rust/src/exemplar/mod.rs +1 -1
- data/rust/src/exemplar/mutations.rs +1 -1
- data/rust/src/exemplar/queries.rs +1 -1
- data/rust/src/exemplar/reactions.rs +1 -1
- data/rust/src/exemplar/read_models.rs +1 -1
- data/rust/src/exemplar/registry.rs +1 -1
- data/rust/src/exemplar/types.rs +1 -1
- data/rust/src/kernel/cli.rs +2 -2
- data/rust/src/kernel/read_model.rs +1 -1
- data/rust/src/lib.rs +1 -1
- metadata +3 -23
- data/rust/project/bridging.rb +0 -257
- data/rust/project/commands.rb +0 -877
- data/rust/project/constraints.rb +0 -86
- data/rust/project/dependency_planning.rb +0 -178
- data/rust/project/domain_generator.rb +0 -983
- data/rust/project/exemplar.rb +0 -148
- data/rust/project/expr_emitter.rb +0 -72
- data/rust/project/fielded.rb +0 -141
- data/rust/project/json_codec.rb +0 -641
- data/rust/project/mutations.rb +0 -695
- data/rust/project/naming.rb +0 -223
- data/rust/project/ports.rb +0 -65
- data/rust/project/queries.rb +0 -493
- data/rust/project/reactions.rb +0 -317
- data/rust/project/read_models.rb +0 -690
- data/rust/project/reference_specs.rb +0 -27
- data/rust/project/registry.rb +0 -396
- data/rust/project/skip_reason.rb +0 -29
- data/rust/project/types.rb +0 -225
- data/rust/project/write_if_changed.rb +0 -7
- data/rust/project.rb +0 -30
- data/rust/project_rust_pipeline.rb +0 -391
data/rust/codegen/src/fielded.rs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! Port of `
|
|
1
|
+
//! Port of the retired Ruby generator's `json_codec.rb`, the JSON boundary generator: `to_json`/`from_json`
|
|
2
2
|
//! codecs, closed-set codecs, and `extract_id`/`extract_wants`/`self_identity`.
|
|
3
3
|
|
|
4
4
|
use crate::exemplar::Exemplar;
|
data/rust/codegen/src/main.rs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! `hecks-codegen` —
|
|
1
|
+
//! `hecks-codegen` — the IR-to-Rust-source generator, the only one (ADR 0086).
|
|
2
2
|
//! Usage: `hecks-codegen <prelude|domain|full> ...`, one subcommand per function below.
|
|
3
3
|
// Some ported `naming.rs` helpers are not called yet.
|
|
4
4
|
#![allow(dead_code)]
|
|
@@ -110,7 +110,7 @@ fn write_domain(
|
|
|
110
110
|
mod_name: &str,
|
|
111
111
|
out_dir: &str,
|
|
112
112
|
) -> Result<domain_generator::GeneratedDomain, String> {
|
|
113
|
-
// Refuse before writing anything
|
|
113
|
+
// Refuse before writing anything, so a name collision is reported before any file exists.
|
|
114
114
|
let aggregate_names: Vec<&str> = ir
|
|
115
115
|
.get("aggregates")
|
|
116
116
|
.map(Json::each)
|
|
@@ -257,8 +257,7 @@ fn run_full(args: &[String]) -> Result<(), String> {
|
|
|
257
257
|
puts_blank(&mut merged_rs);
|
|
258
258
|
puts_str(&mut merged_rs, &reactions::emit_identity_head_table(&ex, &merged_aggregates));
|
|
259
259
|
puts_blank(&mut merged_rs);
|
|
260
|
-
// Section order
|
|
261
|
-
// this file byte for byte.
|
|
260
|
+
// Section order is part of the output: `hecks regenerate_corpus --check` diffs this file byte for byte.
|
|
262
261
|
puts_str(&mut merged_rs, &reactions::emit_entity_identity_head_table(&ex, &merged_aggregates));
|
|
263
262
|
puts_blank(&mut merged_rs);
|
|
264
263
|
puts_str(&mut merged_rs, &reactions::emit_command_attributes_table(&ex, &merged_aggregates));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! Port of `
|
|
1
|
+
//! Port of the retired Ruby generator's `mutations.rb`, function for function.
|
|
2
2
|
//! `mark_append_optional_fields!` is not ported: `Json` has no mutation API and the corpus
|
|
3
3
|
//! already declares `optional: true` on every field that pass would touch.
|
|
4
4
|
|
data/rust/codegen/src/naming.rs
CHANGED
data/rust/codegen/src/ports.rs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! Port of `
|
|
1
|
+
//! Port of the retired Ruby generator's `ports.rb` — read that file's own header comments
|
|
2
2
|
//! in full; this mirrors its algorithm directly, function for function.
|
|
3
3
|
|
|
4
4
|
use crate::exemplar::Exemplar;
|
data/rust/codegen/src/queries.rs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! Port of `
|
|
1
|
+
//! Port of the retired Ruby generator's `queries.rb`, mirrored function for function.
|
|
2
2
|
|
|
3
3
|
use crate::exemplar::Exemplar;
|
|
4
4
|
use crate::json::Json;
|
|
@@ -302,7 +302,7 @@ pub fn query_skip_reason(query: &Json, aggregate: &Json, value_objects_by_name:
|
|
|
302
302
|
let extra_keys = ["cursor", "consistency", "freshness", "inspection"];
|
|
303
303
|
let extras: Vec<&str> = extra_keys.iter().filter(|k| query.get(k).is_some()).copied().collect();
|
|
304
304
|
if !extras.is_empty() {
|
|
305
|
-
return Some(skip(extras[0], format!("declares {} — out of scope for this generator (rust/
|
|
305
|
+
return Some(skip(extras[0], format!("declares {} — out of scope for this generator (rust/codegen/src/queries.rs's own header has the full argument)", extras.join(", "))));
|
|
306
306
|
}
|
|
307
307
|
if query.get("index_hints").map(Json::each).unwrap_or(&[]).iter().any(|_| true) {
|
|
308
308
|
return Some(skip("index_hints", "declares use_index, out of scope for the same reason the extras above are"));
|
|
@@ -780,7 +780,7 @@ pub fn emit_query_arg_check_table(query_defs: &[QueryDef]) -> String {
|
|
|
780
780
|
})
|
|
781
781
|
.collect();
|
|
782
782
|
format!(
|
|
783
|
-
"/// C3.7 for a named query's own arguments — `query_arg_checks`\n/// (rust/
|
|
783
|
+
"/// C3.7 for a named query's own arguments — `query_arg_checks`\n/// (rust/codegen/src/queries.rs) has the full story.\npub fn check_query_args(verb: &str, args: &crate::kernel::Json) -> Result<(), crate::kernel::Refusal> {{\n match verb {{\n{}\n _ => Ok(()),\n }}\n}}\n",
|
|
784
784
|
arms.join("\n")
|
|
785
785
|
)
|
|
786
786
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! Port of `
|
|
1
|
+
//! Port of the retired Ruby generator's `read_models.rb`, mirrored function for function —
|
|
2
2
|
//! read that file's own header for the full algorithm.
|
|
3
3
|
|
|
4
4
|
use crate::exemplar::Exemplar;
|
|
@@ -267,7 +267,7 @@ fn aggregation_skip_reason(read_model: &Json, aggregates_by_name: &HashMap<Strin
|
|
|
267
267
|
None
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
-
// Mirrors `
|
|
270
|
+
// Mirrors the retired Ruby generator's `read_models.rb`'s `group_by_skip_reason`: the one
|
|
271
271
|
// shape the corpus declares — a single rootless head, group_by alone.
|
|
272
272
|
fn group_by_skip_reason(read_model: &Json, aggregates_by_name: &HashMap<String, &Json>, unsupported_names: &[String]) -> Option<SkipReason> {
|
|
273
273
|
let heads = read_model.get("aggregate_heads").map(Json::each).unwrap_or(&[]);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! Command router emitter, ported from `
|
|
1
|
+
//! Command router emitter, ported from the retired Ruby generator's `registry.rb`.
|
|
2
2
|
//! The entry structs mirror the Hashes `domain_generator.rb` accumulates while walking the IR.
|
|
3
3
|
|
|
4
4
|
use crate::exemplar::Exemplar;
|
|
@@ -144,7 +144,7 @@ pub fn emit_role_check(
|
|
|
144
144
|
))
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
/// Port of `
|
|
147
|
+
/// Port of the retired Ruby generator's `registry.rb#emit_tenant_boundary_check`.
|
|
148
148
|
/// Hand-built: the target accessor differs for a single-attribute value object and a bare scalar.
|
|
149
149
|
pub fn emit_tenant_boundary_check(check: &TenantBoundaryCheck) -> String {
|
|
150
150
|
let ref_ident = naming::rust_ident_field(&check.reference_field);
|
|
@@ -740,7 +740,7 @@ pub fn emit_registry(exemplar: &Exemplar, aggregates: &[AggregateEntry]) -> Stri
|
|
|
740
740
|
format!("{header}{body}")
|
|
741
741
|
}
|
|
742
742
|
|
|
743
|
-
/// Port of `
|
|
743
|
+
/// Port of the retired Ruby generator's `registry.rb#emit_reference_table`.
|
|
744
744
|
pub fn emit_reference_table(aggregates: &[AggregateEntry]) -> String {
|
|
745
745
|
let rows: Vec<String> = aggregates
|
|
746
746
|
.iter()
|
|
@@ -760,7 +760,7 @@ pub fn emit_reference_table(aggregates: &[AggregateEntry]) -> String {
|
|
|
760
760
|
)
|
|
761
761
|
}
|
|
762
762
|
|
|
763
|
-
/// Port of `
|
|
763
|
+
/// Port of the retired Ruby generator's `registry.rb#emit_reference_lookup`.
|
|
764
764
|
pub fn emit_reference_lookup(aggregates: &[AggregateEntry]) -> String {
|
|
765
765
|
let arms: Vec<String> = aggregates
|
|
766
766
|
.iter()
|
data/rust/codegen/src/shared.rs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! Predicates ported from `
|
|
1
|
+
//! Predicates ported from the retired Ruby generator's `mutations.rb` that `types.rs` and `json_codec.rs` share.
|
|
2
2
|
|
|
3
3
|
use crate::json::Json;
|
|
4
4
|
use std::collections::HashMap;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! A skip decision plus the construct family that forced it (`
|
|
1
|
+
//! A skip decision plus the construct family that forced it (the retired Ruby generator's `skip_reason.rb`).
|
|
2
2
|
//! `construct` goes to `manifest.json` beside the reason so tools read the family, not the prose.
|
|
3
3
|
|
|
4
4
|
use std::fmt;
|
data/rust/codegen/src/types.rs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! Type emitters ported from `
|
|
1
|
+
//! Type emitters ported from the retired Ruby generator's `types.rb`: records, entities, value objects and
|
|
2
2
|
//! closed-set tables.
|
|
3
3
|
|
|
4
4
|
use crate::exemplar::Exemplar;
|
|
@@ -286,7 +286,7 @@ pub fn emit_record(exemplar: &Exemplar, aggregate: &Json, value_objects_by_name:
|
|
|
286
286
|
format!("{struct_part}\n\n{fielded_part}")
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
-
/// Port of `
|
|
289
|
+
/// Port of the retired Ruby generator's `types.rb#projected_field_pseudo_attributes` (ADR 0025).
|
|
290
290
|
pub fn projected_field_pseudo_attributes(aggregate: &Json) -> Vec<Json> {
|
|
291
291
|
aggregate
|
|
292
292
|
.get("projected_fields")
|
|
@@ -318,7 +318,7 @@ pub fn with_projected_field_pseudo_attributes(aggregate: &Json) -> Json {
|
|
|
318
318
|
Json::Object(pairs)
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
-
/// Port of `
|
|
321
|
+
/// Port of the retired Ruby generator's `types.rb#emit_set_projected_field`.
|
|
322
322
|
pub fn emit_set_projected_field(aggregate: &Json) -> String {
|
|
323
323
|
let name = naming::rust_ident(aggregate.get("name").and_then(Json::as_str).unwrap_or(""));
|
|
324
324
|
let arms: Vec<String> = aggregate
|
|
@@ -338,7 +338,7 @@ pub fn emit_set_projected_field(aggregate: &Json) -> String {
|
|
|
338
338
|
)
|
|
339
339
|
}
|
|
340
340
|
|
|
341
|
-
/// Port of `
|
|
341
|
+
/// Port of the retired Ruby generator's `types.rb#emit_projected_field_table`.
|
|
342
342
|
pub fn emit_projected_field_table(aggregate: &Json) -> String {
|
|
343
343
|
let name = naming::screaming_snake(aggregate.get("name").and_then(Json::as_str).unwrap_or(""));
|
|
344
344
|
let rows: Vec<String> = aggregate
|
data/rust/host/HECKS_RELEASE
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.2.
|
|
1
|
+
3.2.1
|
data/rust/host/src/needs.rs
CHANGED
|
@@ -110,21 +110,34 @@ pub fn table(domain_ir: &Value) -> Value {
|
|
|
110
110
|
Value::Object(verbs)
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
fn list<'a>(value: &'a Value, key: &str) -> &'a [Value] {
|
|
114
|
+
value.get(key).and_then(Value::as_array).map(Vec::as_slice).unwrap_or(&[])
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
fn named(value: &Value, name: &str) -> bool {
|
|
118
|
+
value.get("name").and_then(Value::as_str) == Some(name)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/// The command a verb names: `Domain::Aggregate.Command` on the aggregate itself, or
|
|
122
|
+
/// `Domain::Aggregate.Entity[.Entity].Command` on an entity nested inside it.
|
|
123
|
+
fn command_for<'a>(domain_ir: &'a Value, verb: &str) -> Option<&'a Value> {
|
|
124
|
+
let mut segments = verb.split('.');
|
|
125
|
+
let aggregate = segments.next()?.rsplit("::").next()?;
|
|
126
|
+
let mut path: Vec<&str> = segments.collect();
|
|
127
|
+
let command_name = path.pop()?;
|
|
128
|
+
let mut node = list(domain_ir, "aggregates").iter().find(|a| named(a, aggregate))?;
|
|
129
|
+
for entity in path {
|
|
130
|
+
node = list(node, "entities").iter().find(|e| named(e, entity))?;
|
|
131
|
+
}
|
|
132
|
+
list(node, "commands").iter().find(|c| named(c, command_name))
|
|
133
|
+
}
|
|
134
|
+
|
|
113
135
|
/// The command's attributes that declare a default (`attribute :runs, Count, default: 30`), each
|
|
114
136
|
/// with the declared value. An attribute whose default is null declares none.
|
|
115
137
|
fn declared_defaults<'a>(domain_ir: &'a Value, verb: &str) -> Vec<(&'a str, &'a Value)> {
|
|
116
|
-
let Some(
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
let list = |value: &'a Value, key: &str| -> Vec<&'a Value> {
|
|
120
|
-
value.get(key).and_then(Value::as_array).map(|a| a.iter().collect()).unwrap_or_default()
|
|
121
|
-
};
|
|
122
|
-
list(domain_ir, "aggregates")
|
|
123
|
-
.into_iter()
|
|
124
|
-
.filter(|a| named(a, aggregate))
|
|
125
|
-
.flat_map(|a| list(a, "commands"))
|
|
126
|
-
.filter(|c| named(c, command_name))
|
|
127
|
-
.flat_map(|c| list(c, "attributes"))
|
|
138
|
+
let Some(command) = command_for(domain_ir, verb) else { return Vec::new() };
|
|
139
|
+
list(command, "attributes")
|
|
140
|
+
.iter()
|
|
128
141
|
.filter_map(|a| {
|
|
129
142
|
let default = a.get("default").filter(|d| !d.is_null())?;
|
|
130
143
|
Some((a.get("name").and_then(Value::as_str)?, default))
|
|
@@ -133,29 +146,35 @@ fn declared_defaults<'a>(domain_ir: &'a Value, verb: &str) -> Vec<(&'a str, &'a
|
|
|
133
146
|
}
|
|
134
147
|
|
|
135
148
|
/// Every command's declared defaults as the kernel's input `"defaults"` reads them:
|
|
136
|
-
/// `{ verb: { attribute: value } }
|
|
137
|
-
/// the host fills the outermost one.
|
|
149
|
+
/// `{ verb: { attribute: value } }`, entity commands included. The kernel fills a reaction's
|
|
150
|
+
/// command from this the same way the host fills the outermost one.
|
|
138
151
|
pub fn defaults_table(domain_ir: &Value) -> Value {
|
|
139
152
|
let domain = domain_ir.get("name").and_then(Value::as_str).unwrap_or_default();
|
|
140
153
|
let mut verbs = serde_json::Map::new();
|
|
141
|
-
|
|
142
|
-
for aggregate in aggregates {
|
|
154
|
+
for aggregate in list(domain_ir, "aggregates") {
|
|
143
155
|
let aggregate_name = aggregate.get("name").and_then(Value::as_str).unwrap_or_default();
|
|
144
|
-
|
|
145
|
-
let command_name = command.get("name").and_then(Value::as_str).unwrap_or_default();
|
|
146
|
-
let verb = format!("{domain}::{aggregate_name}.{command_name}");
|
|
147
|
-
let defaults: serde_json::Map<String, Value> = declared_defaults(domain_ir, &verb)
|
|
148
|
-
.into_iter()
|
|
149
|
-
.map(|(name, value)| (name.to_string(), value.clone()))
|
|
150
|
-
.collect();
|
|
151
|
-
if !defaults.is_empty() {
|
|
152
|
-
verbs.insert(verb, Value::Object(defaults));
|
|
153
|
-
}
|
|
154
|
-
}
|
|
156
|
+
collect_defaults(domain_ir, aggregate, &format!("{domain}::{aggregate_name}"), &mut verbs);
|
|
155
157
|
}
|
|
156
158
|
Value::Object(verbs)
|
|
157
159
|
}
|
|
158
160
|
|
|
161
|
+
/// Adds the defaults of `node`'s commands, then of each entity nested in it, under `prefix`.
|
|
162
|
+
fn collect_defaults(domain_ir: &Value, node: &Value, prefix: &str, verbs: &mut serde_json::Map<String, Value>) {
|
|
163
|
+
for command in list(node, "commands") {
|
|
164
|
+
let command_name = command.get("name").and_then(Value::as_str).unwrap_or_default();
|
|
165
|
+
let verb = format!("{prefix}.{command_name}");
|
|
166
|
+
let defaults: serde_json::Map<String, Value> =
|
|
167
|
+
declared_defaults(domain_ir, &verb).into_iter().map(|(name, value)| (name.to_string(), value.clone())).collect();
|
|
168
|
+
if !defaults.is_empty() {
|
|
169
|
+
verbs.insert(verb, Value::Object(defaults));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
for entity in list(node, "entities") {
|
|
173
|
+
let entity_name = entity.get("name").and_then(Value::as_str).unwrap_or_default();
|
|
174
|
+
collect_defaults(domain_ir, entity, &format!("{prefix}.{entity_name}"), verbs);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
159
178
|
/// The facts of a command invocation: the `with` object when the call carries one, else the
|
|
160
179
|
/// flat object itself (the kernel's `CommandInvocation` reads both shapes).
|
|
161
180
|
fn facts_mut(args: &mut Value) -> Option<&mut serde_json::Map<String, Value>> {
|
|
@@ -351,4 +370,52 @@ mod tests {
|
|
|
351
370
|
assert_eq!(defaults_table(&defaulted_ir()), json!({"Qc::Check.Start": {"runs": 30}}));
|
|
352
371
|
assert_eq!(defaults_table(&integer_ir()), json!({}));
|
|
353
372
|
}
|
|
373
|
+
|
|
374
|
+
// A board entity with a defaulted command, and a card nested inside it with another.
|
|
375
|
+
fn nested_ir() -> Value {
|
|
376
|
+
json!({"name": "Np", "aggregates": [{"name": "Workspace", "commands": [], "entities": [
|
|
377
|
+
{"name": "Board", "commands": [
|
|
378
|
+
{"name": "Retitle", "attributes": [
|
|
379
|
+
{"name": "label", "type": "BoardLabel", "default": {"value": "untitled"}}], "needs": []}
|
|
380
|
+
], "entities": [
|
|
381
|
+
{"name": "Card", "commands": [
|
|
382
|
+
{"name": "Remark", "attributes": [
|
|
383
|
+
{"name": "note", "type": "CardNote", "default": {"text": "none"}}], "needs": []}
|
|
384
|
+
]}
|
|
385
|
+
]}
|
|
386
|
+
]}]})
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
#[test]
|
|
390
|
+
fn an_entity_command_is_filled_from_its_own_declared_default() {
|
|
391
|
+
let mut args = json!({"reference": {"value": "W1"}, "number": {"value": 1}});
|
|
392
|
+
fill_defaults(&nested_ir(), "Np::Workspace.Board.Retitle", &mut args);
|
|
393
|
+
assert_eq!(args["label"], json!({"value": "untitled"}));
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
#[test]
|
|
397
|
+
fn a_command_on_an_entity_nested_in_an_entity_is_filled_too() {
|
|
398
|
+
let mut args = json!({"with": {"reference": {"value": "W1"}}});
|
|
399
|
+
fill_defaults(&nested_ir(), "Np::Workspace.Board.Card.Remark", &mut args);
|
|
400
|
+
assert_eq!(args["with"]["note"], json!({"text": "none"}));
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
#[test]
|
|
404
|
+
fn an_entity_path_that_names_nothing_is_left_untouched() {
|
|
405
|
+
let mut args = json!({"number": 1});
|
|
406
|
+
fill_defaults(&nested_ir(), "Np::Workspace.Shelf.Retitle", &mut args);
|
|
407
|
+
fill_defaults(&nested_ir(), "Np::Workspace.Board.Remark", &mut args);
|
|
408
|
+
assert_eq!(args, json!({"number": 1}));
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
#[test]
|
|
412
|
+
fn the_kernel_table_lists_entity_commands_under_their_full_path() {
|
|
413
|
+
assert_eq!(
|
|
414
|
+
defaults_table(&nested_ir()),
|
|
415
|
+
json!({
|
|
416
|
+
"Np::Workspace.Board.Retitle": {"label": {"value": "untitled"}},
|
|
417
|
+
"Np::Workspace.Board.Card.Remark": {"note": {"text": "none"}}
|
|
418
|
+
})
|
|
419
|
+
);
|
|
420
|
+
}
|
|
354
421
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! Exemplar shapes for rust/
|
|
1
|
+
//! Exemplar shapes for rust/codegen/src/commands.rs (see mod.rs).
|
|
2
2
|
//!
|
|
3
3
|
//! `dispatch_fn` proves the outer `pub fn .. -> DispatchResult { .. dispatch(..) .. }` wrapper.
|
|
4
4
|
#![allow(dead_code, unused_variables)]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! Exemplar shapes for rust/
|
|
1
|
+
//! Exemplar shapes for rust/codegen/src/constraints.rs (see mod.rs).
|
|
2
2
|
//!
|
|
3
3
|
//! Both shapes are single-line refusal checks spliced into a `Result`-returning body.
|
|
4
4
|
#![allow(dead_code, unused_variables)]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! Exemplar shapes for rust/
|
|
1
|
+
//! Exemplar shapes for rust/codegen/src/fielded.rs (see mod.rs).
|
|
2
2
|
//!
|
|
3
3
|
//! Each `Fielded::field` arm shape is a standalone leaf so both outer skeletons can reuse it.
|
|
4
4
|
#![allow(dead_code, unused_variables)]
|
data/rust/src/exemplar/json.rs
CHANGED
data/rust/src/exemplar/mod.rs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Compile-checked Rust for every shape rust/
|
|
1
|
+
// Compile-checked Rust for every shape rust/codegen/src/*.rs emits; exemplar.rs slices the fenced
|
|
2
2
|
// `// TMPL:<id> BEGIN` / `END` regions and substitutes real names for the `tmpl_` vocabulary.
|
|
3
3
|
//
|
|
4
4
|
// `tmpl_*_host` functions only give fenced fragments real types and are never called, hence
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Exemplar shape for rust/
|
|
1
|
+
// Exemplar shape for rust/codegen/src/registry.rs's `emit_query_table`; see mod.rs.
|
|
2
2
|
#![allow(dead_code, unused_variables)]
|
|
3
3
|
|
|
4
4
|
// A `const` cannot call a function, so the row is a real literal substituted wholesale.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Exemplar shapes for rust/
|
|
1
|
+
// Exemplar shapes for rust/codegen/src/read_models.rs's `emit_read_model_table`
|
|
2
2
|
// — see mod.rs's own header. `ReadModelDef`/`ReadModelHead`/`FilteredHead`/
|
|
3
3
|
// `ReferenceField` are real kernel types (`rust/src/kernel/read_model.rs`).
|
|
4
4
|
#![allow(dead_code, unused_variables)]
|
data/rust/src/exemplar/types.rs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Exemplar shapes for rust/
|
|
1
|
+
// Exemplar shapes for rust/codegen/src/types.rs; see mod.rs.
|
|
2
2
|
// `closed_set_enum` is a one-field closed set (one variant per member); a multi-field closed
|
|
3
3
|
// set is a fixed data table instead.
|
|
4
4
|
#![allow(dead_code, unused_variables)]
|
data/rust/src/kernel/cli.rs
CHANGED
|
@@ -159,7 +159,7 @@ pub fn run(input: &str) -> String {
|
|
|
159
159
|
real declared query whose shape this generator's codegen doesn't cover yet (order_by/limit/\
|
|
160
160
|
cursor/consistency/freshness/authorization/null_semantics/inspection/index_hints, a where \
|
|
161
161
|
clause hopping through a reference, or a literal comparator value whose true JSON type \
|
|
162
|
-
can't be recovered from the exported IR — rust/
|
|
162
|
+
can't be recovered from the exported IR — rust/codegen/src/queries.rs's own header has the \
|
|
163
163
|
full argument); the wheres-only, single-aggregate field-comparator subset and the ad hoc \
|
|
164
164
|
filter shape ({{\"aggregate\",\"field\",\"op\",\"value\"}}) both execute for real"
|
|
165
165
|
)),
|
|
@@ -195,7 +195,7 @@ pub fn run(input: &str) -> String {
|
|
|
195
195
|
or a real declared read model whose shape this generator's codegen doesn't cover yet \
|
|
196
196
|
(anything beyond a root aggregate fetched by reference id plus reference-matched sibling \
|
|
197
197
|
heads — where/order_by/limit/offset/cursor/consistency/freshness/authorize(TenantScope)/\
|
|
198
|
-
nulls/inspect_query/use_index — rust/
|
|
198
|
+
nulls/inspect_query/use_index — rust/codegen/src/read_models.rs's own header has the full \
|
|
199
199
|
argument, including why where/order_by/limit specifically can never be recovered from the \
|
|
200
200
|
canonical IR at all); the named/declared AGGREGATE query form (\"Domain::Aggregate.Query\") \
|
|
201
201
|
and the ad hoc filter shape ({{\"aggregate\",\"field\",\"op\",\"value\"}}) both execute for \
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//! Interprets a compiled `report` block (`ReadModelDef`) against a domain's generated
|
|
2
|
-
//! `READ_MODELS` table — the hand-written counterpart to `
|
|
2
|
+
//! `READ_MODELS` table — the hand-written counterpart to the retired Ruby generator's `read_models.rb`.
|
|
3
3
|
|
|
4
4
|
use super::refusal_wording::{InvariantViolationGroupByCollisionArgs, NotFoundReadModelReferenceMissingArgs, UnauthorizedTenantRequiredArgs};
|
|
5
5
|
use super::{named_query, query_comparators, query_ordering, repository, AggregateScan, Json, QueryCondition, QueryConditionValue, Refusal};
|
data/rust/src/lib.rs
CHANGED
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.
|
|
4
|
+
version: 3.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chris Young
|
|
@@ -273,6 +273,7 @@ files:
|
|
|
273
273
|
- lib/hecks/doors/launcher_options.rb
|
|
274
274
|
- lib/hecks/doors/mcp_door.rb
|
|
275
275
|
- lib/hecks/doors/mcp_door_scope.rb
|
|
276
|
+
- lib/hecks/doors/mcp_guide_cache.rb
|
|
276
277
|
- lib/hecks/doors/ruby_door.rb
|
|
277
278
|
- lib/hecks/doors/ruby_door/aggregate_door.rb
|
|
278
279
|
- lib/hecks/doors/ruby_door/chapter.rb
|
|
@@ -616,6 +617,7 @@ files:
|
|
|
616
617
|
- lib/hecks/projections/site/site_cdn.rb
|
|
617
618
|
- lib/hecks/projections/site/table.rb
|
|
618
619
|
- lib/hecks/projections/site/table/checks.rb
|
|
620
|
+
- lib/hecks/projections/site/table/link_builder.rb
|
|
619
621
|
- lib/hecks/projections/site/table/row_builder.rb
|
|
620
622
|
- lib/hecks/projections/statements.rb
|
|
621
623
|
- lib/hecks/projections/vocabulary.rb
|
|
@@ -973,28 +975,6 @@ files:
|
|
|
973
975
|
- rust/parser/src/parse/read_model.rs
|
|
974
976
|
- rust/parser/src/parse/value_object.rs
|
|
975
977
|
- rust/parser/src/ruby_value.rs
|
|
976
|
-
- rust/project.rb
|
|
977
|
-
- rust/project/bridging.rb
|
|
978
|
-
- rust/project/commands.rb
|
|
979
|
-
- rust/project/constraints.rb
|
|
980
|
-
- rust/project/dependency_planning.rb
|
|
981
|
-
- rust/project/domain_generator.rb
|
|
982
|
-
- rust/project/exemplar.rb
|
|
983
|
-
- rust/project/expr_emitter.rb
|
|
984
|
-
- rust/project/fielded.rb
|
|
985
|
-
- rust/project/json_codec.rb
|
|
986
|
-
- rust/project/mutations.rb
|
|
987
|
-
- rust/project/naming.rb
|
|
988
|
-
- rust/project/ports.rb
|
|
989
|
-
- rust/project/queries.rb
|
|
990
|
-
- rust/project/reactions.rb
|
|
991
|
-
- rust/project/read_models.rb
|
|
992
|
-
- rust/project/reference_specs.rb
|
|
993
|
-
- rust/project/registry.rb
|
|
994
|
-
- rust/project/skip_reason.rb
|
|
995
|
-
- rust/project/types.rb
|
|
996
|
-
- rust/project/write_if_changed.rb
|
|
997
|
-
- rust/project_rust_pipeline.rb
|
|
998
978
|
- rust/src/exemplar/commands.rs
|
|
999
979
|
- rust/src/exemplar/constraints.rs
|
|
1000
980
|
- rust/src/exemplar/fielded.rs
|