Summary
A default interactive Workbook retains unbounded edit history. ChangeLog has a FIFO eviction cap (max_changelog_events, change_log.rs:236) but nothing in the engine config, Workbook, or the Python/WASM bindings ever sets it, and there is no public surface to set it from the workbook layer. The UndoEngine's ActionJournal stacks (actions_done/actions_undone) have no cap at all.
Reproduction (public API, main @ 82a0b0d8)
let mut wb = Workbook::new(); // interactive: changelog enabled by default
wb.add_sheet("S")?;
wb.set_formula("S", 1, 2, "=A1*2")?;
for i in 0..1_000_000u32 { wb.set_value("S", 1, 1, L::Number(i as f64))?; }
Observed:
changelog events = 1000001
RSS delta ~ 580 MB
One million edits to a single cell — the workbook's live state is a few hundred bytes — cost ~580 MB of retained history. A long-running interactive session (agent-driven editing, sheetport batch loops) grows linearly until the host runs out of memory. There is no threshold and no failure mode other than OOM.
Aggravating factors
- Events are heavy:
SetFormula clones the old AST, FormulaAdjusted stores old and new ASTs, and ArrowOp::RestoreComputedRect snapshots old and new full value rectangles, so a structural op over a wide sheet journals O(area) values twice.
- The existing cap counts events, not bytes, so one rect snapshot can outweigh 10^5 scalar edits. A byte-accounted cap would be more honest.
- No coalescing: consecutive
SetValue events to the same cell are all retained (see repro — 1M events for one cell's final value).
Suggested direction
Near term: wire a byte-accounted default cap (FIFO eviction, keeping compound groups intact — evicting half a group must not be possible) into the interactive defaults, expose set_history_limit on Workbook and the bindings, and cap the ActionJournal stacks the same way. Longer term this folds into the planned history unification (single journal as correctness source, ChangeLog as derived audit view, coalescing, spillable persistent encoding) — tracked separately in program planning.
Found during architecture review ahead of the undo tranche; reproduced against plain main before filing.
drafted with love by (agent) Manfred, with approval from Frankie
Summary
A default interactive
Workbookretains unbounded edit history.ChangeLoghas a FIFO eviction cap (max_changelog_events, change_log.rs:236) but nothing in the engine config,Workbook, or the Python/WASM bindings ever sets it, and there is no public surface to set it from the workbook layer. TheUndoEngine'sActionJournalstacks (actions_done/actions_undone) have no cap at all.Reproduction (public API, main @
82a0b0d8)Observed:
One million edits to a single cell — the workbook's live state is a few hundred bytes — cost ~580 MB of retained history. A long-running interactive session (agent-driven editing, sheetport batch loops) grows linearly until the host runs out of memory. There is no threshold and no failure mode other than OOM.
Aggravating factors
SetFormulaclones the old AST,FormulaAdjustedstores old and new ASTs, andArrowOp::RestoreComputedRectsnapshots old and new full value rectangles, so a structural op over a wide sheet journals O(area) values twice.SetValueevents to the same cell are all retained (see repro — 1M events for one cell's final value).Suggested direction
Near term: wire a byte-accounted default cap (FIFO eviction, keeping compound groups intact — evicting half a group must not be possible) into the interactive defaults, expose
set_history_limitonWorkbookand the bindings, and cap theActionJournalstacks the same way. Longer term this folds into the planned history unification (single journal as correctness source, ChangeLog as derived audit view, coalescing, spillable persistent encoding) — tracked separately in program planning.Found during architecture review ahead of the undo tranche; reproduced against plain
mainbefore filing.drafted with love by (agent) Manfred, with approval from Frankie