feat(engine): gang data model + validation gate (#61 task 1)
One public knob fanning into >=2 sibling params: Gang/GangMember types
(the Role name/targets split + BoundParam pos/name dual), Composite.gangs
behind the single fallible Composite::with_gangs gate, check_gangs as the
shared $A predicate (all three future minting boundaries route here), and
CompileError::BadGang(GangFault) as the typed refusal.
PrimitiveBuilder::original_pos (aura-core) extracts bind's pos-reconstruction
loop verbatim so gang members and BoundParam share one coordinate space.
Deviation ratified vs the plan: GangFault::KindMismatch carries structural
{node, name} coordinates instead of a stringly member label, consistent with
the sibling arms (plan amended). A redundant test-local fixture wrapper was
dropped after a fresh quality re-review of the blocked task diff.
Verified: cargo test -p aura-engine --lib 249/0, -p aura-core 53/0, clippy
-D warnings clean on both crates.
refs #61
This commit is contained in:
@@ -162,6 +162,22 @@ impl PrimitiveBuilder {
|
||||
pub fn bound_params(&self) -> &[BoundParam] {
|
||||
&self.bound
|
||||
}
|
||||
/// Map an index into the CURRENT (shrunk) open-param list back to the
|
||||
/// slot's position in the ORIGINAL pre-bind param list — the coordinate
|
||||
/// space `BoundParam.pos` uses: add one for each already-bound slot at an
|
||||
/// original position <= it (the same reconstruction `bind` performs when
|
||||
/// recording a `BoundParam`).
|
||||
pub fn original_pos(&self, open_idx: usize) -> usize {
|
||||
let mut orig = open_idx;
|
||||
let mut prior: Vec<usize> = self.bound.iter().map(|b| b.pos).collect();
|
||||
prior.sort_unstable();
|
||||
for p in prior {
|
||||
if p <= orig {
|
||||
orig += 1;
|
||||
}
|
||||
}
|
||||
orig
|
||||
}
|
||||
/// The full declared signature (read pre-build by `Composite::param_space`,
|
||||
/// `BlueprintNode::signature`, and the renderer).
|
||||
pub fn schema(&self) -> &NodeSchema {
|
||||
@@ -220,14 +236,7 @@ impl PrimitiveBuilder {
|
||||
// the closure capture below is what bootstrap actually reads.
|
||||
let name = self.schema.params[pos].name.clone();
|
||||
let kind = self.schema.params[pos].kind;
|
||||
let mut orig = pos;
|
||||
let mut prior: Vec<usize> = self.bound.iter().map(|b| b.pos).collect();
|
||||
prior.sort_unstable();
|
||||
for p in prior {
|
||||
if p <= orig {
|
||||
orig += 1;
|
||||
}
|
||||
}
|
||||
let orig = self.original_pos(pos);
|
||||
self.bound.push(BoundParam { pos: orig, name, kind, value });
|
||||
self.schema.params.remove(pos); // [param_space side] shrink the declared surface
|
||||
let inner = self.build; // [value side] wrap the build closure
|
||||
@@ -265,14 +274,7 @@ impl PrimitiveBuilder {
|
||||
}
|
||||
let name = self.schema.params[pos].name.clone();
|
||||
let kind = self.schema.params[pos].kind;
|
||||
let mut orig = pos;
|
||||
let mut prior: Vec<usize> = self.bound.iter().map(|b| b.pos).collect();
|
||||
prior.sort_unstable();
|
||||
for p in prior {
|
||||
if p <= orig {
|
||||
orig += 1;
|
||||
}
|
||||
}
|
||||
let orig = self.original_pos(pos);
|
||||
self.bound.push(BoundParam { pos: orig, name, kind, value });
|
||||
self.schema.params.remove(pos);
|
||||
let inner = self.build;
|
||||
@@ -549,6 +551,18 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// original_pos maps a shrunk-schema index back to the pre-bind slot,
|
||||
/// mirroring bind's own BoundParam.pos recording.
|
||||
#[test]
|
||||
fn original_pos_reconstructs_prebind_slots() {
|
||||
let b = probe3(); // params a,b,c at 0,1,2
|
||||
assert_eq!(b.original_pos(0), 0);
|
||||
assert_eq!(b.original_pos(2), 2);
|
||||
let b = b.bind("b", Scalar::i64(1)); // shrunk: [a, c]
|
||||
assert_eq!(b.original_pos(0), 0); // a
|
||||
assert_eq!(b.original_pos(1), 2); // c sat at 2 originally
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bind_records_bound_param_at_original_position() {
|
||||
// middle-of-three: binding `b` records its ORIGINAL slot position (1).
|
||||
|
||||
Reference in New Issue
Block a user