# Markdown pipe-table formulas (agy gap 15, roadmap 40) Pure calculation in existing `crates/blocks`; engine command and current markdown table UI. No new crate/dependency. Bases computed columns stay separate. ## Research (2026-10-21; agy claims checked) agy's Advanced Tables URL is WRONG (404). Correct project: https://github.com/tgrosinger/advanced-tables-obsidian . Its own help says formulas are on the line following a table, evaluated explicitly: https://raw.githubusercontent.com/tgrosinger/advanced-tables-obsidian/main/docs/help.md . Thus agy's `=SUM(B1:B5)` cells are a requested design, not verified Advanced Tables syntax. Drop Notion spreadsheet-equivalence claim; a database formula property is a different model. No claim of full Excel/Org/Advanced Tables compatibility. ## Problem and goal Small budgets in markdown pipe tables need simple arithmetic without moving into a database. Formula cells remain literal markdown source; Visual mode shows calculated values and exposes formula source on edit. Source/Obsidian retain formulas legibly. ## Design decisions ### D1 Opt-in syntax and coordinates Enable only a table immediately followed by `` on next line, so existing `=...` prose is unchanged. Numeric data-row coordinates: A1 is first body row, first column; header and delimiter rows are excluded. Formula cell starts `=` after whitespace trim; literal text starting `=` uses inline code. Numbers are finite signed decimal with optional exponent, decimal dot only. Empty referenced cell is 0; nonnumeric text is a typed error, never silently parsed from formatted markup. Grammar: arithmetic `+ - * /`, unary sign, parentheses, cell refs and rectangular ranges only as aggregate args; `SUM`, `AVG`, `MIN`, `MAX`, `COUNT`. COUNT counts numeric cells, ignores empty cells, errors on text. AVG/MIN/MAX empty range errors. No dates, strings, scripts, network, external/cross-table refs, implicit locale conversions or volatile functions. ### D2 Evaluation and safety New `crates/blocks/src/table_formulas/{mod,parse,eval}.rs` accepts cell-source grid, emits per-cell `Number` or `{code,span}` error. Dependency graph evaluates once per source change, detects cycles and propagates dependency errors; no recursive traversal without bounds. Caps: 10,000 cells, 1 KiB per formula, 128 AST depth, 100,000 dependency edges/expanded-range visits and 1,000,000 evaluation operations per table. Exceeding cap is explicit error, never truncation. Out-of-grid refs, division by zero, nonfinite arithmetic, unknown function and cycles have stable codes. Display finite numbers with Rust shortest-roundtrip formatting; no promise of exact decimal accounting. Row/column insert/delete preserves formula text verbatim: references are positional and may change result; UI warns before structural edit of enabled table, no hidden Excel-style rewrite. ### D3 Commands/UI and source integrity `table.formulas.evaluate {markdown,table_index}` -> `{cells:[{row,column,value?,error?}],diagnostics}` read-only; zero-based table_index, one-based result coordinates. Engine uses shared pure table extraction based on markdown source ranges; UI parser at `views/note_view/blocks/table.rs` delegates cell splitting to pure module where necessary, preserving existing rendering. No second table interpretation for engine/UI. Read-only Visual evaluation cached by exact table source plus opt-in marker; formula editor always shows original literal. Editing goes through existing `NoteEdit`, never writes computed values. Header/body rendering exposes localized error tooltip with source span. Marker absent -> exact existing behavior. CSV/export remains raw formula source in v1, visibly documented; no automatic spreadsheet execution export. Engine registry and assistant gate classify evaluate READ. ## Acceptance criteria 1. Opted-in table `A1=2`, `B1=3`, `C1 = A1*B1`, aggregate over a numeric range render correct values while source stays byte-identical. 2. Marker absent and inline-code `=literal` cells keep existing rendering and never evaluate. 3. Cycle, unknown function, text operand, out-of-bounds, division by zero, overflow and every cap return bounded diagnostics without panic. 4. Unicode text, escaped pipes, code-span pipes, CRLF and short rows produce identical coordinates in command and Visual view. 5. Changing one literal updates dependent results; unchanged frame performs no reparse/evaluation (counter test). 6. Editing/undo retains formula syntax, table marker and unrelated bytes; structural edit requires confirmation and preserves refs verbatim. ## Open owner questions - Q1: approve opt-in comment and A1 body-only coordinates? Proposal yes; explicitly NotesCraft syntax, not Advanced Tables compatibility. - Q2: approve finite f64 v1 rather than accounting-grade decimal? New decimal dependency needs owner approval before addition. - Q3: approve positional references unchanged on structural edits, or require reference rewrite before release? Proposal warning plus unchanged formulas. ## Feature ideas (researched, ranked) 1. Separate explicit materialize-results command with preview, inspired by Advanced Tables explicit evaluation: https://raw.githubusercontent.com/tgrosinger/advanced-tables-obsidian/main/docs/help.md . 2. Import Advanced Tables trailing formulas only after a compatibility spec against upstream syntax: https://github.com/tgrosinger/md-advanced-tables/blob/main/docs/formulas.md . URL is linked from verified help; syntax has not been validated for this v1. 3. Offer Bases computed properties when table grows into a note database: https://obsidian.md/help/bases/syntax .