diff --git a/context/design-principles.md b/context/design-principles.md index 137b790..660bb13 100644 --- a/context/design-principles.md +++ b/context/design-principles.md @@ -60,12 +60,14 @@ editing) is two commands composed at the binding level, not a monolithic handler - `Binding` is `Cmd | Prefix | Sequence`. The keymap lookup returns one of these three shapes; dispatch pattern-matches exhaustively. -- `CategoryKind` is `Regular | VirtualIndex | VirtualDim | Label`. Business rules - (e.g., the 12-category limit counts only `Regular`) are enforced by matching - on the enum, not by checking name prefixes. Virtual categories (`_Index`, - `_Dim`) exist solely for drill-down mechanics and must never leak into - user-facing logic — use `Model::regular_category_names()` when selecting a - default category for formulas, prompts, or other user-visible choices. +- `CategoryKind` is `Regular | VirtualIndex | VirtualDim | VirtualMeasure | Label`. + Business rules (e.g., the 12-category limit counts only `Regular`) are + enforced by matching on the enum, not by checking name prefixes. Virtual + categories (`_Index`, `_Dim`, `_Measure`) always exist: `_Index` and `_Dim` + support drill-down/records mode; `_Measure` holds numeric data fields and + formula targets (added automatically by `add_formula`). Use + `Model::regular_category_names()` when selecting a default category for + prompts or other user-visible choices. ### When You Add a Variant, the Compiler Finds Every Call Site @@ -157,6 +159,16 @@ A formula is a serializable struct: raw text, target name, category, AST, option filter. It is stored in the model alongside regular data. The evaluator walks the AST at read time. Formulas never become closures or runtime-generated code. +### Formula Evaluation Is Fixed-Point + +`recompute_formulas(none_cats)` iterates formula evaluation until values +stabilize. Each pass evaluates all formula cells using the current cache +(for formula-derived values) and raw data aggregation (for data values). +This avoids recursive evaluation through `evaluate_aggregated` and +naturally handles chained formulas (`Margin = Profit / Revenue` where +`Profit = Revenue - Cost`). Circular references converge to +`CellValue::Error("circular")` after `MAX_EVAL_DEPTH` iterations. + ### Display Rounding Is View-Only Number formatting (`format_f64`) rounds for display. Formula evaluation always