Cell becomes the hot-path value carrier; Scalar narrows to dynamic boundaries (C7/C8 realization) #74
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Goal
Complete the deferred carrier swap flagged by the C7 realization note in
docs/design/INDEX.md(added 2026-06, commitscd3d1ca/049f22a): make thetag-free
Cellthe value carrier on the hot path, and keepScalar(=
{ kind, cell }) only where a value must be self-describing.Background
The Cell/Scalar split already landed (
cd3d1ca):Scalaris now{ kind: ScalarKind, cell: Cell }, andCell(crates/aura-core/src/cell.rs)is a tag-free 64-bit word with branch-free typed accessors. The C7 note
explicitly defers the carrier swap: "
Cellis not yet the hot-path carrier —eval -> Option<&[Scalar]>and the edges still flowScalar; replacing thecarrier with
Cellis a larger step that will get its own note (and touchC8's
evalcontract)." This issue is that step.Settled principle
C7: the scalar kind lives at the schema/column/port, not in the value. So any
value site whose kind is already known from co-present context must NOT
re-carry a per-value tag — it uses a bare
Cell. A value site that travelsdetached from its kind (must self-describe) keeps
Scalar.Convert-set (kind structurally co-present →
Cell)Node::eval -> Option<&[Cell]>— the per-cycle output record; each field'skind is declared in
NodeSchema.output[i].kind, which the harness holds.out: [Scalar; N]→[Cell; N](add, sub, sma,ema, exposure, lincomb, sim_broker; recorder is a pure sink returning
None;plus all test / fixture nodes that
impl Node).scratch: Vec<Cell>; the inter-node edge pushforwards a raw
Cellinto the consumer column.AnyColumn::push_cell(Cell)— a new branch-free, infallible push (noKindMismatch): bootstrap already verifies edgefrom_fieldkind ==consumer slot kind (harness.rs:306-314, green tests
bootstrap_rejects_a_kind_mismatch/..._per_field_kind_mismatch/..._recorder_edge), so the runtime forward needs no per-value kind check.The existing
.push(...).expect("edge kind checked at wiring")alreadytreats this as infallible;
push_cellmakes that explicit in the type.Keep-set (self-describing dynamic boundary → stays
Scalar)AnyColumn::get -> Option<Scalar>— the type-erased read used by sinks /recorder / serialization / playground, where the caller has no static kind.
PrimitiveBuilder::build(&[Scalar]),bind(slot, Scalar)(kind-checks the value),BoundParam.value,zip_params,Blueprint::compile_with_params(&[Scalar]), thesweep / mc / walkforward param-point args, and aura-cli
run_oos/scalar_as_param_f64. A param point routinely travels detached from itsco-indexed param-space — serialized into the RunManifest, passed to
run_one(seed, point)— so self-description is load-bearing there.Source::next -> Option<(Timestamp, Scalar)>and thesource-target push stay
Scalar. The k-way-merged ingestion boundary (C3)is heterogeneous and self-describing by nature; the source produces a
Scalar, so stripping it to a cell at the push would be churn at exactly theboundary whose job is to carry heterogeneous values.
Invariant
Behaviour-preserving (C1 determinism): same input → same run, byte-identical
metrics / manifests. Only the carrier type narrows; the output-record values
are unchanged. The per-value runtime kind check on node output is removed —
node-output-kind correctness becomes a node-authoring responsibility (honored
by each node's
evaltest, which asserts the exact output value), consistentwith C7 (type at the schema) and with the existing output-width check being a
debug_assertonly.Acceptance
Node::evalsignature and every node out-buffer useCell;Scalarstayson the param /
get/ source boundaries.Cellcarrier; get-side / param-side assertions stay
Scalar).cargo build / clippy / test / doc --workspaceall clean.Spec:
docs/specs/0047-cell-hot-path-carrier.md(in progress).