plan: 0040 add derive_signature bounds-guard step

Implement-time discovery: the new check (and the existing edge/role checks) compute
signature() on every interior node before the recursion validates that node's
interior. For a structurally-invalid composite (an out-of-range OutField or role
target), derive_signature indexed unguarded and PANICKED — exposed by
output_port_out_of_range_rejected the moment its covering root role forces
c.signature(). Added Task 1 Step 2b: make derive_signature and interior_slot_kind
bounds-total (a placeholder kind for an invalid index; the real fault is still
reported by validate_wiring's guarded OutputPortOutOfRange / BadInteriorIndex). Fix
verified empirically before re-dispatch. Not a design change to the check itself.

refs #65
This commit is contained in:
2026-06-14 16:33:00 +02:00
parent 669541c6f6
commit 277f8714d4
+53
View File
@@ -136,6 +136,54 @@ fn check_ports_connected(
}
```
- [ ] **Step 2b: Make `derive_signature` bounds-total (the check exposes a latent panic)**
`check_ports_connected` (and the existing edge/role checks) compute `signature()` on
every interior node *before* the recursion validates that node's interior. For a
structurally-invalid composite (an out-of-range `OutField` or role target), the
current `derive_signature` indexes unguarded and **panics** instead of letting
`validate_wiring`'s guarded loops report `OutputPortOutOfRange` / `BadInteriorIndex`.
Make the two indexers bounds-total (a placeholder kind for an invalid index; the real
fault is still reported by `validate_wiring`).
In `crates/aura-engine/src/blueprint.rs`, replace the `output` map in `derive_signature`:
```rust
let kind = c.nodes()[of.node].signature().output[of.field].kind;
```
with:
```rust
// bounds-total: a structurally-invalid OutField (out-of-range node/field)
// yields a placeholder kind, never a panic — the real fault is reported by
// validate_wiring's guarded output check (OutputPortOutOfRange). signature()
// is computed speculatively by the parent's edge/role/connectivity checks
// before the recursion validates this composite's interior (cycle 0040).
let kind = c
.nodes()
.get(of.node)
.and_then(|n| n.signature().output.get(of.field).map(|f| f.kind))
.unwrap_or(ScalarKind::F64);
```
and replace `interior_slot_kind`'s body:
```rust
nodes[t.node].signature().inputs[t.slot].kind
```
with:
```rust
// bounds-total: a target into a missing node/slot yields a placeholder kind, never a
// panic — the real fault is reported by validate_wiring's guarded index checks.
nodes
.get(t.node)
.and_then(|n| n.signature().inputs.get(t.slot).map(|p| p.kind))
.unwrap_or(ScalarKind::F64)
```
- [ ] **Step 3: Build and confirm exactly the four expected flips**
Run: `cargo build -p aura-engine`
@@ -226,6 +274,11 @@ with:
- [ ] **Step 3: Re-wire `output_port_out_of_range_rejected`**
(This is the test whose deep fault is an out-of-range OUTPUT field. The covering role
forces the root's role-kind loop to compute `c.signature()`, which only stays
panic-free because of the `derive_signature` guard in Task 1 Step 2b; the recursion
then reports `OutputPortOutOfRange` as before.)
Replace:
```rust