diff --git a/context/repo-map.md b/context/repo-map.md index 8945a48b..cec3bd8e 100644 --- a/context/repo-map.md +++ b/context/repo-map.md @@ -154,7 +154,7 @@ Region=West, Measure=Revenue = |pending| # pipe-quoted text 4. **Commands never mutate.** `&CmdContext` is read-only; return `Vec>`. If you want to touch `&mut App`, add or extend an Effect. 5. **`CellKey` is always sorted.** Use the constructors; equality and hashing rely on canonical form. 6. **No `Model::add_item`.** With a `Workbook` in hand use the facade: `wb.add_item("Region", "East")?` / `add_item_in_group` / `remove_item` (improvise-tvt). With only a `Model`, go through the category: `m.category_mut("Region").unwrap().add_item("East")`. -7. **Virtual categories.** `_Measure` items = explicit items ∪ formula targets. Use `measure_item_names()` / `effective_item_names("_Measure")`. `add_formula` does *not* add items to `_Measure`. Formula-target lookup also falls back into `_Measure` via `Model::find_item_category`. Cell aggregation goes through `apply_agg`, formula `SUM(…)`/`AVG(…)` through `eval_agg` — they differ on empty input (absent vs. `0` for Sum/Count, error for Avg/Min/Max). +7. **Virtual categories.** `_Measure` items = explicit items ∪ formula targets. Use `measure_item_names()` / `effective_item_names("_Measure")`. `add_formula` does *not* add items to `_Measure`. Formula-target lookup also falls back into `_Measure` via `Model::find_item_category`. Cell aggregation goes through `apply_agg`, formula `SUM(…)`/`AVG(…)` through `eval_agg` — they differ on empty input (absent vs. A third rule joins them for **non-numeric** values: `aggregate_cell_values` reduces text by **unanimity** — one matching value, or several copies of the same value, is that value; differing values, or text among numbers, is `CellValue::Error("mixed")` (improvise-l4qx). The **all-numeric branch is checked first**, so `Count` of a lone number is still `1` and not the number — that ordering is the whole difference between this rule and "a single value passes through". What the rule forbids is *dropping*: `filter_map(as_f64)` used to, and since `_Index`/`_Dim` are on `Axis::None` in every default view that made **every text cell in every document render blank**, including the pipe-quoted text `.improv` carries as a first-class value. `evaluate_aggregated` and `aggregate_raw` share the helper, so a formula ref over mixed values now errors `"mixed"` rather than silently summing the numeric ones. `0` for Sum/Count, error for Avg/Min/Max). 8. **Display rounding is view-only.** `format_f64` (half-away-from-zero) is only called in rendering. Formula eval uses full `f64`. Never feed a rounded value back into eval. 9. **Formula eval is fixed-point.** `recompute_formulas(none_cats)` iterates until stable, bounded by `MAX_EVAL_DEPTH`; circular refs converge to `CellValue::Error("circular")`. `App::new` runs it before the first frame so formula cells render on startup. 10. **Drill into formula cells** strips the `_Measure` coordinate when it names a formula target, so `matching_cells` returns the raw records that feed the formula instead of returning empty. @@ -217,11 +217,11 @@ Line counts are static; test counts are informational — run `cargo test --work ### `improvise-core` (`crates/improvise-core/src/`) ``` -model/types.rs 2062 / 70t Model, formula eval (fixed-point), CRUD +model/types.rs 2185 / 76t Model, formula eval (fixed-point), CRUD model/cell.rs 650 / 28t CellKey (sorted), CellValue, DataStore (interned + index) model/category.rs 222 / 6t Category, Item, Group, CategoryKind model/symbol.rs 79 / 3t Symbol interning -view/layout.rs 1194 / 29t GridLayout, drill, records mode +view/layout.rs 1174 / 25t GridLayout, drill, records mode view/types.rs 627 / 33t View config (axes, pages, hidden, collapsed, format) view/axis.rs 205 / 5t Axis enum + label/color/next methods, AxisColor workbook.rs 345 / 17t Workbook: Model + cross-view ops, item facade