Cell becomes the hot-path value carrier; Scalar narrows to dynamic boundaries (C7/C8 realization) #74

Closed
opened 2026-06-16 12:33:09 +02:00 by Brummel · 0 comments
Owner

Goal

Complete the deferred carrier swap flagged by the C7 realization note in
docs/design/INDEX.md (added 2026-06, commits cd3d1ca / 049f22a): make the
tag-free Cell the value carrier on the hot path, and keep Scalar
(= { kind, cell }) only where a value must be self-describing.

Background

The Cell/Scalar split already landed (cd3d1ca): Scalar is now
{ kind: ScalarKind, cell: Cell }, and Cell (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: "Cell is not yet the hot-path carrier —
eval -> Option<&[Scalar]> and the edges still flow Scalar; replacing the
carrier with Cell is a larger step that will get its own note (and touch
C8's eval contract)." 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 travels
detached 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's
    kind is declared in NodeSchema.output[i].kind, which the harness holds.
  • Every node's output buffer 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).
  • The harness forward path: scratch: Vec<Cell>; the inter-node edge push
    forwards a raw Cell into the consumer column.
  • AnyColumn::push_cell(Cell) — a new branch-free, infallible push (no
    KindMismatch): bootstrap already verifies edge from_field kind ==
    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") already
    treats this as infallible; push_cell makes 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.
  • The whole param path: PrimitiveBuilder::build(&[Scalar]),
    bind(slot, Scalar) (kind-checks the value), BoundParam.value,
    zip_params, Blueprint::compile_with_params(&[Scalar]), the
    sweep / mc / walkforward param-point args, and aura-cli run_oos /
    scalar_as_param_f64. A param point routinely travels detached from its
    co-indexed param-space — serialized into the RunManifest, passed to
    run_one(seed, point) — so self-description is load-bearing there.
  • Source ingestion: Source::next -> Option<(Timestamp, Scalar)> and the
    source-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 the
    boundary 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 eval test, which asserts the exact output value), consistent
with C7 (type at the schema) and with the existing output-width check being a
debug_assert only.

Acceptance

  • Node::eval signature and every node out-buffer use Cell; Scalar stays
    on the param / get / source boundaries.
  • All existing tests green (eval-output assertions migrated to the Cell
    carrier; get-side / param-side assertions stay Scalar).
  • cargo build / clippy / test / doc --workspace all clean.
  • A new C8/C7 realization note records the carrier swap in the ledger.

Spec: docs/specs/0047-cell-hot-path-carrier.md (in progress).

## Goal Complete the deferred carrier swap flagged by the C7 realization note in `docs/design/INDEX.md` (added 2026-06, commits cd3d1ca / 049f22a): make the tag-free `Cell` the value carrier on the hot path, and keep `Scalar` (= `{ kind, cell }`) only where a value must be self-describing. ## Background The Cell/Scalar split already landed (cd3d1ca): `Scalar` is now `{ kind: ScalarKind, cell: Cell }`, and `Cell` (`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: "`Cell` is not yet the hot-path carrier — `eval -> Option<&[Scalar]>` and the edges still flow `Scalar`; replacing the carrier with `Cell` is a larger step that will get its own note (and touch C8's `eval` contract)." 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 travels detached 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's kind is declared in `NodeSchema.output[i].kind`, which the harness holds. - Every node's output buffer `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`). - The harness forward path: `scratch: Vec<Cell>`; the inter-node edge push forwards a raw `Cell` into the consumer column. - `AnyColumn::push_cell(Cell)` — a new branch-free, **infallible** push (no `KindMismatch`): bootstrap already verifies edge `from_field` kind == 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")` already treats this as infallible; `push_cell` makes 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. - The whole param path: `PrimitiveBuilder::build(&[Scalar])`, `bind(slot, Scalar)` (kind-checks the value), `BoundParam.value`, `zip_params`, `Blueprint::compile_with_params(&[Scalar])`, the sweep / mc / walkforward param-point args, and aura-cli `run_oos` / `scalar_as_param_f64`. A param point routinely travels **detached** from its co-indexed param-space — serialized into the RunManifest, passed to `run_one(seed, point)` — so self-description is load-bearing there. - Source ingestion: `Source::next -> Option<(Timestamp, Scalar)>` and the source-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 the boundary 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 `eval` test, which asserts the exact output value), consistent with C7 (type at the schema) and with the existing output-width check being a `debug_assert` only. ## Acceptance - `Node::eval` signature and every node out-buffer use `Cell`; `Scalar` stays on the param / `get` / source boundaries. - All existing tests green (eval-output assertions migrated to the `Cell` carrier; get-side / param-side assertions stay `Scalar`). - `cargo build / clippy / test / doc --workspace` all clean. - A new C8/C7 realization note records the carrier swap in the ledger. Spec: `docs/specs/0047-cell-hot-path-carrier.md` (in progress).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Brummel/Aura#74