THE declaration the engine cares about: what unit does a change
invalidate? Everything else a combinator declares — group_by, into,
key derivation — is mapping data into shape once you already know what to
recompute.
recompute_by :category, from: :expense_catRead: "recompute by category, from the input's expense_cat" — a change to
a row's expense_cat invalidates my category unit, so redo it whole.
That one fact supplies the input edge, the grouping, the claim resolution
and the read scope, so it replaces the key_rule vocabulary entirely.
Four answers to the one question:
recompute_by :cell— the whole cell; any change re-does everything (a fold whose every output depends on every input).recompute_by :cat, from: :child_field— per unit, resolved by READING the changed rows and evaluating the field. A key the lookup can't find (a deleted row) degrades to whole-cell: vanish must reprice everything it might have left.recompute_by :month, from_key: true— per unit, resolved PURELY from the changed key's leading|-segments. No query and deletion-safe, at the price of the key-grammar contract.- (omitted) — key-for-key: a changed input key maps to the same output key.
NOTE this is the RECOMPUTE unit, not the output's grain. They coincide for a
plain rollup, and diverge the moment one unit emits many rows: percentiles
per day recompute_by :day (touch one reading, redo that day) while the
rows are keyed day+percentile via expand:.
The unit is consumed at COMPILE time — it lowers to the combinator's over:
group_by:pair and is never traversed at recompute. One declaration per node, so a combinator reads exactly one input: one unit, one claim translation.