audit: cycle B (0016) — ledger 0016 realization note (C8/C19), debt #36 filed

Architect drift review of the #31 cycle (2a9a141..4b64409) against the design
ledger. The feature is clean: C19/C23 realized literally (LeafFactory recipe;
compile_with_params builds-then-wires slot-by-slot in the same walk param_space()
projects — #34's dual-traversal hazard genuinely subsumed, one traversal, both
mirror tests green incl. nesting); C12 "no recompile per param-set" holds
(value-empty blueprint, vector-bound, bit-identity green); the removed
Composite::schema/BlueprintNode::schema confirmed caller-free.

ledger (fixed here):
- C8: the 0015 note's "binding a value to a slot (#31) ... deferred" line was stale
  (shipped). Updated to record #31 landed in 0016, and that the param declaration's
  authoring home moved to LeafFactory.params() (read pre-build) alongside the built
  node's schema().params (the duplication is debt #36).
- C19: added a "Realization (cycle 0016 — param-set injection)" note (the per-cycle
  pattern C8/C10 already carry for 0005/0006/0007/0015): the value-empty leaf,
  build-then-wire bootstrap_with_params/compile_with_params, the typed kind/arity
  errors, the bit-identical compilat, and the C22 blueprint-view bare-type label
  (value-bearing SMA(2) now only in the compiled view).

debt (filed, carry-on):
- Every param-bearing node declares its ParamSpecs twice (factory() and schema()),
  kept in lockstep only by a per-node test; drift compiles. Filed as #36 (sibling of
  #34's traversal-duplication concern). Not active drift — the agreement holds and
  is tested per node.

regression: profile declares no regression scripts and no architect_sweeps — the
architect review is the sole gate. No baseline to update.

Cycle B is drift-clean after these amendments. NOT a milestone close — that needs
the end-to-end milestone fieldtest, deferred until the sweep root (#32-#33) lands.
This commit is contained in:
2026-06-07 21:08:49 +02:00
parent 4b64409036
commit a70c5faab7
+27 -2
View File
@@ -260,8 +260,12 @@ symbol (like `FieldSpec.name`) — same-type siblings in one composite share a n
uniqueness is at the slot. A vector knob (`LinComb.weights`) expands to `N` flat
`weights[i]` entries, `N` topology-fixed (C19). Permitted kinds are `i64`/`f64`/
`bool` (a `timestamp` knob is a structural axis, C20, never a numeric sweep param).
Binding a value to a slot (#31) and enumerating a sweep (#32) are the deferred next
layers.
Binding a value to a slot landed in cycle 0016 (#31, see C19/C23); enumerating a
sweep (#32) is the deferred next layer. The 0016 binding also moved the param
declaration's *authoring* home: a value-empty blueprint leaf is a `LeafFactory`
recipe whose `params()` is read pre-build, while the built node's `schema().params`
still reports the same slots — the two are kept in lockstep by a per-node test
(a duplication filed as debt, #36).
### C9 — Fractal, acyclic composition
**Guarantee.** A composite is itself a `Node` that wires a sub-graph and exposes
@@ -544,6 +548,27 @@ cdylib** rebuild (C12/C13: the cdylib loads once); re-deriving an instance per
param-set is a cheap **graph re-compilation**, not a code recompile. Naming the
build phase a compilation makes its successor explicit: the compilat is the target
of behaviour-preserving optimisation (C23).
**Realization (cycle 0016 — param-set injection).** The bootstrap now binds an
injected param-set, realizing C12's "params injected at graph build (the optimizer
sees a generic vector of typed ranges)" and C19's "factory `params → sized node`"
*literally*: a blueprint leaf is **value-empty**`BlueprintNode::Leaf` holds a
`LeafFactory { name, params, build }` recipe, not a built node — and the value lives
only in the injected vector (no baked default), so the blueprint stays a pure
param-generic recipe. `Blueprint::bootstrap_with_params(Vec<Scalar>)` /
`compile_with_params` **build each leaf through its own constructor** (the single
sizing/validation gate) from its kind-checked slice **while lowering** (build-then-
wire), consuming the vector slot-by-slot in the *same* depth-first walk
`param_space()` projects — so the two share one traversal (subsuming the #34 dual-
traversal hazard) and the value reaches the node at the slot the sweep enumerates.
Arity is checked up front (`param_space().len()`); a wrong-kind or wrong-length
vector is a typed `CompileError::{ParamKindMismatch, ParamArity}` (the typed-value
check C8 deferred). The lowering/edge/source rewrite is structurally unchanged, so
the **compilat stays bit-identical** for a given point (C23, the correctness
invariant). The value *domain* (e.g. `length ≥ 1`) stays the constructor's own
`assert`; the search-range is still the run's (#32/C20). One value-empty leaf
detail for C22: the blueprint view (pre-run, param-generic) labels a leaf by **bare
type** (`LeafFactory::label``[SMA]`) — the value-bearing `SMA(2)` of C8's render-
label refinement now appears only in the *compiled* view (built nodes, `Node::label`).
### C20 — Strategy ↔ harness; the harness is the root sim graph
**Guarantee.** A **strategy** is a reusable composite-node blueprint (C9):