# Cell as the hot-path value carrier — Implementation Plan

> **Parent spec:** `docs/specs/0047-cell-hot-path-carrier.md`
>
> **For agentic workers:** REQUIRED SUB-SKILL: use the `implement` skill to run
> this plan. Steps use `- [ ]` checkboxes for tracking.

**Goal:** Narrow the hot-path value carrier from `Scalar` to the tag-free `Cell`
(`Node::eval -> Option<&[Cell]>`, every node out-buffer, the inter-node forward),
keeping `Scalar` on the self-describing dynamic boundaries (params, `get`, source
ingestion). Behaviour-preserving (C1).

**Architecture:** The `Node::eval` trait return type is the single atomic
compile cut: flipping it breaks all 21 `impl Node` bodies at once, so the
lib-code flip (trait + 8 aura-std nodes + recorder + harness forward loop) lands
as one task gated by a **lib-only** build; the `#[cfg(test)]` fixtures and the
eval-output assertions (which only compile under `--all-targets`) migrate in a
second task gated by the full four-gate run. A new branch-free `AnyColumn::push_cell`
(added first, additively) is the infallible inter-node push; the fallible
`push(Scalar)` and `get -> Option<Scalar>` stay for ingestion / type-erased reads.

**Tech Stack:** `aura-core` (`node.rs`, `any.rs`), `aura-std` (8 nodes),
`aura-engine` (`harness.rs` forward loop + fixtures, `blueprint.rs`/`graph_model.rs`
fixtures), the design ledger.

---

## Files this plan creates or modifies

- Modify: `crates/aura-core/src/node.rs:13,264,286,410` — `use` adds `Cell`;
  `Node::eval` trait sig → `Option<&[Cell]>`; `Bare`/`Probe` eval sigs.
- Modify: `crates/aura-core/src/any.rs:7,62-92` — add `use crate::cell::Cell;`;
  add `push_cell(Cell)`; add a `push_cell` round-trip test in the `tests` mod.
- Modify: `crates/aura-std/src/add.rs:5,20,26,58,64,92`
- Modify: `crates/aura-std/src/sub.rs:6,11,17,49,55,83`
- Modify: `crates/aura-std/src/sma.rs:6-9,14,21,45,54,86,97,100`
- Modify: `crates/aura-std/src/ema.rs:20-23,37,52,79,99,131,144,147`
- Modify: `crates/aura-std/src/exposure.rs:6-9,15,22,46,51,81`
- Modify: `crates/aura-std/src/lincomb.rs:9-12,30,40,68,77,105,118,136,168,183`
- Modify: `crates/aura-std/src/sim_broker.rs:7,41,54,82,94,124`
- Modify: `crates/aura-std/src/recorder.rs:10,60` — `use` adds `Cell` (the return
  type names it); eval sig → `Option<&[Cell]>` (body still returns `None`).
- Modify: `crates/aura-engine/src/harness.rs:10,24,373,416,426-428` (forward path)
  + fixtures `AsOfSum`/`BarrierSum`/`MixedSum`/`Ohlcv`/`TwoField`/`TapForward`
  (out-buffers, eval sigs, assignments) + their ctor literals
  (`:898,944,993,1044,1156,1209,1255,1380,1307,1614,1475,1781`).
- Modify: `crates/aura-engine/src/blueprint.rs:816` (test `use` adds `Cell`) +
  fixtures `Join2`/`Pass1`/`SinkF64`/`SinkI64` (`:1057,1063,1069,1076,1082,1087,1098,1110`)
  + ctor literals (`:1119,1127,2283`).
- Modify: `crates/aura-engine/src/graph_model.rs:274-276,286` (test `use` adds
  `Cell`; `Bare` eval sig).
- Modify: `docs/design/INDEX.md` (C7 realization note `:195-210`; C8 prose `:221,242`).
- KEEP (no change — param plane / type-erased read / ingestion / channel rows):
  `node.rs` param path, `any.rs::push`/`get`, `sweep.rs`/`mc.rs`/`walkforward.rs`
  param args + `scalar_as_f64`, `aura-cli/src/main.rs` `run_oos`/`scalar_as_param_f64`,
  `report.rs::f64_field`, `recorder.rs` channel row, `graph_model.rs::scalar_str`,
  `blueprint.rs::compile_with_params`, `test_fixtures.rs`, `builder.rs`,
  `aura-ingest`, `aura-registry`, `ctx.rs`, `column.rs`, `error.rs`, `scalar.rs`,
  `cell.rs`.

---

## Task 1: Add `AnyColumn::push_cell` + round-trip test (additive)

**Files:**
- Modify: `crates/aura-core/src/any.rs`

This task is additive: `push_cell` is new `pub` code, the test exercises it, and
nothing existing changes — the workspace stays green throughout.

- [ ] **Step 1: Import `Cell` in any.rs**

In `crates/aura-core/src/any.rs`, after the existing import line
`use crate::error::KindMismatch;` (line ~6), and the line
`use crate::scalar::{Scalar, ScalarKind, Timestamp};` (line 7), add:

```rust
use crate::cell::Cell;
```

- [ ] **Step 2: Add the `push_cell` method**

In `impl AnyColumn`, immediately after the existing `push` method (ends ~line 82),
add:

```rust
    /// Branch-free, infallible hot-path push of a bare [`Cell`] into this column's
    /// concrete `Column<T>`, reinterpreting the cell by *this* column's kind. The
    /// inter-node forward uses this: the edge's `from_field`→slot kind match is
    /// verified once at bootstrap (C7), so no per-value kind check is needed here
    /// — unlike [`push`](Self::push), which keeps the kind guard for the
    /// ingestion boundary.
    pub fn push_cell(&mut self, c: Cell) {
        match self {
            AnyColumn::I64(col) => col.push(c.i64()),
            AnyColumn::F64(col) => col.push(c.f64()),
            AnyColumn::Bool(col) => col.push(c.bool()),
            AnyColumn::Ts(col) => col.push(c.ts()),
        }
    }
```

- [ ] **Step 3: Add the round-trip test**

In the `#[cfg(test)] mod tests` block (after `same_kind_push_round_trips`, ~line
171), add:

```rust
    #[test]
    fn push_cell_round_trips_by_column_kind() {
        // push_cell reinterprets the bare cell by the column's own kind; reading
        // back through `get` reconstructs the self-describing Scalar.
        let mut f = AnyColumn::with_capacity(ScalarKind::F64, 2);
        f.push_cell(Cell::from_f64(1.5));
        assert_eq!(f.get(0), Some(Scalar::f64(1.5)));
        assert_eq!(f.run_count(), 1);

        let mut i = AnyColumn::with_capacity(ScalarKind::I64, 2);
        i.push_cell(Cell::from_i64(42));
        assert_eq!(i.get(0), Some(Scalar::i64(42)));

        let mut b = AnyColumn::with_capacity(ScalarKind::Bool, 2);
        b.push_cell(Cell::from_bool(true));
        assert_eq!(b.get(0), Some(Scalar::bool(true)));

        let mut t = AnyColumn::with_capacity(ScalarKind::Timestamp, 2);
        t.push_cell(Cell::from_ts(Timestamp(7)));
        assert_eq!(t.get(0), Some(Scalar::ts(Timestamp(7))));
    }
```

- [ ] **Step 4: Verify it compiles and passes**

Run: `cargo test -p aura-core push_cell_round_trips_by_column_kind`
Expected: PASS (1 passed). The rest of the workspace is untouched and still green.

---

## Task 2: Atomic lib-code carrier flip

**Files:**
- Modify: `crates/aura-core/src/node.rs`
- Modify: `crates/aura-std/src/{add,sub,sma,ema,exposure,lincomb,sim_broker,recorder}.rs`
- Modify: `crates/aura-engine/src/harness.rs`

The trait-sig flip breaks every `impl Node` simultaneously; all lib-code impls +
the harness forward loop must change together. Fixtures (`#[cfg(test)]`) and
eval-output assertions are deferred to Task 3, so the gate here is a **lib-only**
build (`cargo build --workspace`, NOT `--all-targets`) — the cfg(test) code does
not compile until Task 3.

- [ ] **Step 1: Flip the `Node::eval` trait signature (node.rs)**

In `crates/aura-core/src/node.rs`:

Line 13 — add `Cell` to the import:
```rust
use crate::{Cell, Ctx, Scalar, ScalarKind};
```

Line 264 — the trait method:
```rust
    fn eval(&mut self, ctx: Ctx<'_>) -> Option<&[Cell]>;
```

Update the trait/struct doc prose that names the old carrier: in the `Node`
doc-comment (~lines 248-255) and the C8 reference, replace the phrase
`returning a borrowed slice into a buffer the node owns` context — specifically
any `Option<&[Scalar]>` mention in rustdoc becomes `Option<&[Cell]>`. (Search the
file for `&[Scalar]` in doc comments; the param-path doc mentions of `Scalar`
stay.)

- [ ] **Step 2: Migrate `add.rs`**

`crates/aura-std/src/add.rs`:

Line 5 — `use aura_core::{Cell, Ctx, FieldSpec, Firing, Node, NodeSchema, PortSpec, PrimitiveBuilder, Scalar, ScalarKind};`
Line 20 — `out: [Cell; 1],`
Line 26 — `Self { out: [Cell::from_f64(0.0)] }`
Line 58 — `fn eval(&mut self, ctx: Ctx<'_>) -> Option<&[Cell]> {`
Line 64 — `self.out[0] = Cell::from_f64(a[0] + b[0]);`

(`Scalar` stays in the `use` — the test column writes at lines 87, 91 keep it.)

- [ ] **Step 3: Migrate `sub.rs`**

`crates/aura-std/src/sub.rs`:

Line 6 — add `Cell,` to the import (→ `use aura_core::{Cell, Ctx, FieldSpec, Firing, Node, NodeSchema, PortSpec, PrimitiveBuilder, Scalar, ScalarKind};`)
Line 11 — `out: [Cell; 1],`
Line 17 — `Self { out: [Cell::from_f64(0.0)] }`
Line 49 — `fn eval(&mut self, ctx: Ctx<'_>) -> Option<&[Cell]> {`
Line 55 — `self.out[0] = Cell::from_f64(a[0] - b[0]);`

- [ ] **Step 4: Migrate `sma.rs`**

`crates/aura-std/src/sma.rs`:

Lines 6-9 — add `Cell,` to the multi-line import:
```rust
use aura_core::{
    Cell, Ctx, FieldSpec, Firing, Node, NodeSchema, ParamSpec, PortSpec, PrimitiveBuilder, Scalar,
    ScalarKind,
};
```
Line 14 — `out: [Cell; 1],`
Line 21 — `Self { length, out: [Cell::from_f64(0.0)] }`
Line 45 — `fn eval(&mut self, ctx: Ctx<'_>) -> Option<&[Cell]> {`
Line 54 — `self.out[0] = Cell::from_f64(sum / self.length as f64);`

(`Scalar` stays — column writes + `bind(..., Scalar::i64(2))` keep it.)

- [ ] **Step 5: Migrate `ema.rs`**

`crates/aura-std/src/ema.rs`:

Lines 20-23 — add `Cell,` to the multi-line import:
```rust
use aura_core::{
    Cell, Ctx, FieldSpec, Firing, Node, NodeSchema, ParamSpec, PortSpec, PrimitiveBuilder, Scalar,
    ScalarKind,
};
```
Line 37 — `out: [Cell; 1],`
Line 52 — `out: [Cell::from_f64(0.0)],`
Line 79 — `fn eval(&mut self, ctx: Ctx<'_>) -> Option<&[Cell]> {`
Line 99 — `self.out[0] = Cell::from_f64(self.ema);`

- [ ] **Step 6: Migrate `exposure.rs`**

`crates/aura-std/src/exposure.rs`:

Lines 6-9 — add `Cell,` to the multi-line import:
```rust
use aura_core::{
    Cell, Ctx, FieldSpec, Firing, Node, NodeSchema, ParamSpec, PortSpec, PrimitiveBuilder, Scalar,
    ScalarKind,
};
```
Line 15 — `out: [Cell; 1],`
Line 22 — `Self { scale, out: [Cell::from_f64(0.0)] }`
Line 46 — `fn eval(&mut self, ctx: Ctx<'_>) -> Option<&[Cell]> {`
Line 51 — `self.out[0] = Cell::from_f64((w[0] / self.scale).clamp(-1.0, 1.0));`

- [ ] **Step 7: Migrate `lincomb.rs`**

`crates/aura-std/src/lincomb.rs`:

Lines 9-12 — add `Cell,` to the multi-line import:
```rust
use aura_core::{
    Cell, Ctx, FieldSpec, Firing, Node, NodeSchema, ParamSpec, PortSpec, PrimitiveBuilder, Scalar,
    ScalarKind,
};
```
Line 30 — `out: [Cell; 1],`
Line 40 — `Self { weights, out: [Cell::from_f64(0.0)] }`
Line 68 — `fn eval(&mut self, ctx: Ctx<'_>) -> Option<&[Cell]> {`
Line 77 — `self.out[0] = Cell::from_f64(acc);`

- [ ] **Step 8: Migrate `sim_broker.rs` (eval body; test helper is Task 3)**

`crates/aura-std/src/sim_broker.rs`:

Line 7 — add `Cell,` to the import.
Line 41 — `out: [Cell; 1],`
Line 54 — `out: [Cell::from_f64(0.0)],`
Line 82 — `fn eval(&mut self, ctx: Ctx<'_>) -> Option<&[Cell]> {`
Line 94 — `self.out[0] = Cell::from_f64(self.cum);`

(The test helper at line 124 migrates in Task 3.)

- [ ] **Step 9: Migrate `recorder.rs` (eval sig only)**

`crates/aura-std/src/recorder.rs`:

Line 10 — add `Cell` to the import (the return type `Option<&[Cell]>` names it,
even though the body returns `None`):
```rust
use aura_core::{Cell, Ctx, Firing, Node, NodeSchema, PortSpec, PrimitiveBuilder, Scalar, ScalarKind, Timestamp};
```
Line 60 — `fn eval(&mut self, ctx: Ctx<'_>) -> Option<&[Cell]> {`

The body is unchanged: it builds the `Vec<Scalar>` channel row from `get(0)?`
and returns `None` (pure sink). `Scalar` stays in the `use`.

- [ ] **Step 10: Migrate the harness forward path (harness.rs)**

`crates/aura-engine/src/harness.rs`:

Line 24 — add `Cell` to the import:
```rust
use aura_core::{AnyColumn, Cell, Ctx, Firing, Node, NodeSchema, Scalar, ScalarKind, Timestamp};
```
Line 10 — the module-doc prose `borrowed record (`Option<&[Scalar]>`)` →
`borrowed record (`Option<&[Cell]>`)`.
Line 373 — `let mut scratch: Vec<Cell> = Vec::new();`
Line 416 — `let result: Option<&[Cell]> = {`
Lines 426-428 — replace the fallible inter-node push with the infallible cell push:
```rust
                        nb.inputs[e.slot].push_cell(scratch[e.from_field]);
                        nb.slots[e.slot] = SlotState { fresh_at: cycle_id, last_ts: ts };
```
(i.e. drop the `.push(...).expect("edge kind checked at wiring")` and call
`push_cell`. The `nb.slots[...] = ...` line that follows is unchanged.)

**Do NOT change line 402** — the source-target forward
`nb.inputs[t.slot].push(value).expect("source kind checked at wiring")` stays on
the fallible `push(Scalar)` (ingestion, keep-set).

- [ ] **Step 11: Lib-only build gate**

Run: `cargo build --workspace`
Expected: `Finished` with 0 errors. (This builds lib + bin targets only; the
`#[cfg(test)]` fixtures and eval-output assertions do NOT compile yet — that is
Task 3. Do **not** run `--all-targets` here; it will fail by design until Task 3.)

---

## Task 3: Migrate `#[cfg(test)]` fixtures + eval-output assertions

**Files:**
- Modify: `crates/aura-core/src/node.rs` (Bare, Probe)
- Modify: `crates/aura-engine/src/harness.rs` (6 fixtures + ctor literals)
- Modify: `crates/aura-engine/src/blueprint.rs` (4 fixtures + ctor literals + use)
- Modify: `crates/aura-engine/src/graph_model.rs` (Bare + use)
- Modify: `crates/aura-std/src/{add,sub,sma,ema,exposure,lincomb,sim_broker}.rs` (assertions)

Per the spec's three-role test-migration rule: **eval-output** sites → `Cell`;
**column writes** (`inputs[i].push(Scalar::f64(x))`) → stay `Scalar`;
**out-of-graph channel sends** (`vec![Scalar::f64(v)]`) → stay `Scalar`.

- [ ] **Step 1: node.rs fixtures Bare + Probe**

`crates/aura-core/src/node.rs`:

Line 288 (`Bare::eval`) — `fn eval(&mut self, _ctx: Ctx<'_>) -> Option<&[Cell]> {`
Line 410 (`Probe::eval`) — `fn eval(&mut self, _ctx: Ctx<'_>) -> Option<&[Cell]> {`

Both bodies return `None` — unchanged. `Probe`'s field `Probe(Vec<Scalar>)`
(line 405) stays `Vec<Scalar>` (a stored param point). All `Scalar::i64/f64` in
the `tests` / `zip_params_tests` modules are param/bind sites — stay `Scalar`.

- [ ] **Step 2: harness.rs fixtures (6) + ctor literals**

`crates/aura-engine/src/harness.rs` — for each fixture, change the `out` field
type, the `eval` signature, and the `Scalar::*` assignment(s) to the `Cell`
carrier:

- `AsOfSum`: line 621 `out: [Cell; 1],`; line 636 eval sig; line 642
  `self.out[0] = Cell::from_f64(a[0] + b[0]);`
- `BarrierSum`: line 650 `out: [Cell; 1],`; line 665 eval sig; line 666
  `self.out[0] = Cell::from_f64(ctx.f64_in(0)[0] + ctx.f64_in(1)[0]);`
- `MixedSum`: line 675 `out: [Cell; 1],`; line 694 eval sig; line 701
  `self.out[0] = Cell::from_f64(a[0] + b[0] + c[0]);`
- `Ohlcv`: line 711 `out: [Cell; 5],`; line 732 eval sig; line 738
  `self.out[i] = Cell::from_f64(w[0]);`
- `TwoField`: line 749 `out: [Cell; 2],`; line 767 eval sig; line 768
  `self.out[0] = Cell::from_f64(0.0);`; line 769 `self.out[1] = Cell::from_i64(0);`
- `TapForward` (**dual-role**): line 778 `out: [Cell; 1],`; line 794 eval sig;
  line 801 `self.out[0] = Cell::from_f64(v);`. **Line 779 keeps**
  `tx: mpsc::Sender<(Timestamp, Vec<Scalar>)>` and **line 800 keeps**
  `let _ = self.tx.send((ctx.now(), vec![Scalar::f64(v)]));` (out-of-graph row).

Then the fixture **constructor literals** — change each `out:` init to the `Cell`
carrier (the type must match the migrated field):
- Lines 898, 944, 993, 1044, 1781 (AsOfSum/BarrierSum/MixedSum/BarrierSum):
  `out: [Cell::from_f64(0.0)]`
- Lines 1156, 1209, 1255, 1380 (`Ohlcv { out: [Scalar::f64(0.0); 5] }`):
  `out: [Cell::from_f64(0.0); 5]`
- Lines 1307, 1614 (`TwoField { out: [Scalar::f64(0.0), Scalar::i64(0)] }`):
  `out: [Cell::from_f64(0.0), Cell::from_i64(0)]`
- Line 1475 (`TapForward { out: [Scalar::f64(0.0)], tx: ... }`):
  `out: [Cell::from_f64(0.0)]` (the `tx:` field unchanged)

**Keep `Scalar`** on every recorded-row / channel-row literal (recorder + Tap
channel sends, `Vec<Scalar>` assertions) at lines 830-832, 877-879, 924-927,
970-971, 1025-1027, 1070-1071, 1235-1236, 1278-1279, 1358-1361, 1367-1368,
1490-1492, 1562-1563, 1598-1601, 1682-1684, 1746-1756, 1813-1821, 1873-1874,
1935-1947, 2022-2036, 2108 — these are out-of-graph rows, not eval output.

- [ ] **Step 3: blueprint.rs fixtures (4) + ctor literals + use**

`crates/aura-engine/src/blueprint.rs`:

Line 816 — add `Cell` to the test-mod import:
```rust
    use aura_core::{Cell, Ctx, FieldSpec, Firing, NodeSchema, Timestamp};
```
- `Join2`: line 1057 `out: [Cell; 1],`; line 1063 eval sig; line 1069
  `self.out[0] = Cell::from_f64(a[0] + b[0]);`
- `Pass1`: line 1076 `out: [Cell; 1],`; line 1082 eval sig; line 1087
  `self.out[0] = Cell::from_f64(w[0]);`
- `SinkF64`: line 1098 eval sig → `Option<&[Cell]>` (body returns `None`).
- `SinkI64`: line 1110 eval sig → `Option<&[Cell]>` (body returns `None`).

Constructor literals:
- Line 1119 (`Pass1 { out: [Scalar::f64(0.0)] }`) → `out: [Cell::from_f64(0.0)]`
- Line 1127 (`Join2 { out: [Scalar::f64(0.0)] }`) → `out: [Cell::from_f64(0.0)]`
- Line 2283 (`Pass1 { out: [Scalar::f64(0.0)] }`) → `out: [Cell::from_f64(0.0)]`

**Keep `Scalar`** on `compile_with_params(&[Scalar::...])` calls (param plane):
lines 1239, 1899, 2058, 2193, 2212, 2265, 2315.

- [ ] **Step 4: graph_model.rs fixture + use**

`crates/aura-engine/src/graph_model.rs`:

Lines 274-276 — add `Cell` to the test-mod import:
```rust
    use aura_core::{
        Cell, FieldSpec, Node, NodeSchema, PortSpec, PrimitiveBuilder, Scalar,
    };
```
Line 286 — `Bare::eval` sig → `fn eval(&mut self, _c: aura_core::Ctx<'_>) -> Option<&[Cell]> {`
(body returns `None`). `scalar_str(s: aura_core::Scalar)` at line 27 is untouched
(renders a `BoundParam.value`). If clippy later flags `Scalar` as unused in this
test module, remove it from the line-274 import — the clippy gate (Step 8) is the
backstop.

- [ ] **Step 5: aura-std eval-output assertions + sim_broker helper**

Migrate ONLY the eval-output assertion lines (`Some([Scalar::f64(x)].as_slice())`
→ `Some([Cell::from_f64(x)].as_slice())`); leave every `inputs[i].push(Scalar::f64(x))`
column-write untouched.

- `add.rs:92` — `Some([Cell::from_f64(14.0)].as_slice())`
- `sub.rs:83` — `Some([Cell::from_f64(6.0)].as_slice())`
- `sma.rs:86,97,100` — wrap each expected mean with `Cell::from_f64(...)`
  (line 86 is inside the `Some(m) => assert_eq!(got, Some([Cell::from_f64(m)].as_slice()))`
  match arm; lines 97, 100 are `Some([Cell::from_f64(7.0)]...)` / `Some([Cell::from_f64(9.0)]...)`).
- `ema.rs:131,144,147` — same: line 131 match arm `Some([Cell::from_f64(m)]...)`;
  lines 144, 147 `Some([Cell::from_f64(7.0)]...)` / `Some([Cell::from_f64(9.0)]...)`.
- `exposure.rs:81` — `Some([Cell::from_f64(want)].as_slice())`
- `lincomb.rs:105,118,136,168,183` — each `Some([Cell::from_f64(x)].as_slice())`
  (the expected weighted sums: 11.0, 12.0, 6.0, 11.0, 11.0 respectively).
- `sim_broker.rs:124` — the test helper match arm:
  `Some([c]) => c.f64(),` (was `Some([s]) => s.as_f64(),`). The `panic!` arm and
  the column-write seeds (lines 120, 122) are unchanged.

- [ ] **Step 6: Full-target build gate**

Run: `cargo build --workspace --all-targets`
Expected: `Finished` with 0 errors. (Now the test code compiles against the
`Cell` carrier.)

- [ ] **Step 7: Test gate**

Run: `cargo test --workspace`
Expected: all tests pass, 0 failed. (Behaviour-preserving: the only changed
test expectations are eval-output re-spellings; values are identical.)

- [ ] **Step 8: Clippy gate**

Run: `cargo clippy --workspace --all-targets -- -D warnings`
Expected: 0 warnings. (Catches any now-unused `Scalar` import left in a fully
migrated file.)

- [ ] **Step 9: Doc gate**

Run: `cargo doc --workspace --no-deps 2>&1`
Expected: builds with no warnings (the migrated rustdoc comments + intra-doc
links to `Cell` resolve).

---

## Task 4: Record the carrier swap in the design ledger

**Files:**
- Modify: `docs/design/INDEX.md`

The C7 realization note promised "its own note (and touch C8's eval contract)";
this task delivers it so the ledger no longer asserts a now-false "not yet".

- [ ] **Step 1: Update the C7 realization note**

In `docs/design/INDEX.md`, the C7 `Realization (Cell carrier split, 2026-06)`
note (lines ~195-210) currently ends:

```
the `scalar_eq_is_value_not_bitwise` fixture. Behaviour-preserving. `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).
```

Replace the "not yet" sentence with the realization (keep the value-PartialEq
sentence before it):

```
the `scalar_eq_is_value_not_bitwise` fixture. Behaviour-preserving.

**Realization (`Cell` becomes the hot-path carrier, 2026-06, #74).** The carrier
swap deferred above has landed: `Node::eval` now returns `Option<&[Cell]>` and
every node out-buffer is `[Cell; N]`, so the inter-node forward carries tag-free
8-byte words. The kind lives only at the schema/column: the harness forwards each
field via the new branch-free `AnyColumn::push_cell` (infallible — the edge
kind match is verified once at bootstrap, the surviving guard
`bootstrap_rejects_*_kind_mismatch`). `Scalar` remains on the self-describing
dynamic boundaries: the param plane (`build`/`bind`/`compile_with_params`/sweep
points/`RunManifest`), `AnyColumn::get` (the type-erased read for sinks/serde),
and source ingestion (`Source::next`, the heterogeneous C3 merge). The removed
per-value runtime kind check on node output is the same authoring-bug class C8
already leaves to a `debug_assert` (output width); node-output-kind correctness
is the node's declared `FieldSpec` contract, caught by each node's own test.
Behaviour-preserving (C1).
```

- [ ] **Step 2: Update the C8 prose mentions of the old carrier**

In `docs/design/INDEX.md`, the C8 guarantee (line ~221) and the C8 cycle-0005
realization (line ~242) state `eval(ctx) -> Option<&[Scalar]>` /
`eval` returns `Option<&[Scalar]>`. Update both `Option<&[Scalar]>` → 
`Option<&[Cell]>`, and in the 0005 realization append a brief pointer: append to
that sentence ` (the carrier is now a tag-free `Cell` — see the C7 carrier note)`.

- [ ] **Step 3: Glossary check (no write expected)**

Run: `grep -n "not yet" docs/glossary.md; grep -n -A2 "^### cell" docs/glossary.md`
Expected: the `cell` entry already says cell "is what the SoA hot path reads
without a per-value branch" — forward-compatible, no "not yet" claim. If the grep
shows a "not yet"-style claim about cell being the carrier, fix it; otherwise no
glossary change.

- [ ] **Step 4: Verify no stale "not yet the hot-path carrier" remains**

Run: `grep -rn "not yet.*hot-path carrier\|not yet.*carrier" docs/`
Expected: no matches (the C7 note's "not yet" line is gone).

Run: `cargo doc --workspace --no-deps 2>&1`
Expected: no new warnings (sanity re-check after the ledger edit; the ledger is
markdown, but this confirms nothing in code regressed).
