From 7d04cb313a7536a3e973623fad134cdcfb7b4988 Mon Sep 17 00:00:00 2001 From: Brummel Date: Thu, 11 Jun 2026 18:15:37 +0200 Subject: [PATCH] spec: 0032 correct test 5 fixture nesting (boss-signed) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test 5's asymmetric-collision fixture asserted a root-level param_space ["sma.length"] while also being "test 2's fixture with one leg swapped" (nested) — a contradiction: check_fan_in_distinguishability only descends into composites, so a root-level collision is not rejected today (not RED-first). Caught at plan time while writing the verbatim fixture body. Corrected: test 5 nests the asymmetric collision in an inner composite `asym` (param-bearing Sma "sma" + paramless Pass1 .named("sma") + Sub, both leaves on role price fanning into the Sub) under a source-bound root, asserting param_space() == ["asym.sma.length"] (the single injective entry — the paramless leg contributes no path) and that compile_with_params(&[I64(2)]) is Ok after. Re-grounded PASS, re-panel unanimous SOUND. Forward correction on top of 7f059d0 (main is forward-only — no rewind of the boss-signed spec). refs #59 --- docs/specs/0032-param-namespace-injectivity.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/specs/0032-param-namespace-injectivity.md b/docs/specs/0032-param-namespace-injectivity.md index 02e00d7..33bb772 100644 --- a/docs/specs/0032-param-namespace-injectivity.md +++ b/docs/specs/0032-param-namespace-injectivity.md @@ -377,13 +377,19 @@ positional filter per invocation). 4. **Unchanged green** `interchangeable_fan_in_allowed` (~1531): two **param-less** `Pass1` into a `Join2` → still `compile().is_ok()`. Paramless nodes contribute no `param_space()` path, so no duplicate; the C9 interchangeability survives. -5. **New** `asymmetric_node_name_collision_compiles` — pins the intentional drop: - a param-bearing `Sma` (default name `"sma"`) and a **paramless** `Pass1` - `.named("sma")`, both on role `price`, fanning into one `Sub`. Equal node-name +5. **New** `asymmetric_node_name_collision_compiles` — pins the intentional drop. + The asymmetric collision must sit **inside a composite** for the old check to + reach it (`check_fan_in_distinguishability` only descends into composites; a + root-level fan-in is never checked), so it reuses test 2's nested shape: an inner + composite `asym` holding a param-bearing `Sma` (default name `"sma"`), a + **paramless** `Pass1` `.named("sma")`, and a `Sub`, with both leaves on role + `price` fanning into the `Sub`; `asym` sits under a source-bound `root` + (`Some(ScalarKind::F64)`), exactly like `sma_cross_under_root`. Equal node-name signatures + one param → **rejected today** (`IndistinguishableFanIn`); after the - change `param_space()` is a single entry `["sma.length"]` (injective), so - `compile_with_params(&[Scalar::I64(2)])` is `Ok`. RED-green flip is the *reverse* - of test 3 — what the old check rejected, the new one admits, by design. + change `param_space()` is the single entry `["asym.sma.length"]` (injective — the + paramless leg contributes no path), so `compile_with_params(&[Scalar::I64(2)])` + is `Ok`. The RED-green flip is the *reverse* of test 3 — what the old check + rejected, the new one admits, by design. 6. **The #59 test** `by_name_bootstrap_of_unnamed_cross_reports_duplicate_path`: `bp.with("sma_cross.sma.length", 2).bootstrap()` on an unnamed cross → `Err(BindError::Compile(CompileError::DuplicateParamPath("sma_cross.sma.length".into())))`,