When an AI Agent Edits a Spreadsheet, What Happens to the Workbook?
I asked a deliberately narrow question: when an AI agent opens an Excel workbook and saves it without changing the workbook's content, what changes inside the file?
The answer matters because an .xlsx or .xlsm file is a ZIP package of interdependent parts. A workbook can contain worksheet XML, styles, validations, comments, external links, charts, pivot caches, slicers, macros, custom XML, and relationship graphs. A one-cell edit should not silently rewrite unrelated structures.
Why test against LibreOffice? When AI coding agents inspect, update, or convert spreadsheets, standard Python libraries like openpyxl or pandas hit a wall: they cannot evaluate formula graphs, populate cached values (<v>), or render sheets. To give agents formula execution and export capabilities, runtimes such as OpenAI's local Codex environment in ChatGPT Desktop bundle a 410 MB headless build of LibreOffice into their sandbox.
I built WolfXL, a Rust-backed Excel engine for Python, around a different constraint: in-process execution with surgical byte preservation. To pressure-test the design, I compared its guarded modify path against that bundled LibreOffice route.
The short result:
- Across five source-identified workbooks, a no-op LibreOffice round trip changed 8 to 151 unrelated OOXML parts.
- WolfXL changed zero unrelated parts on the same five fixtures.
- In a source-matched local benchmark, WolfXL was 2.2x to 3.9x faster on the paired tasks and inputs reported below.
- LibreOffice still covers workflows WolfXL does not, and an isolated idle-footprint probe favored LibreOffice on sampled resident memory.
Every number below points to a compact receipt. The receipts retain unsupported and unfavorable rows.
The preservation test
The test is intentionally boring:
- Open a valid workbook.
- Request no content change.
- Save to a new file.
- Compare every OOXML package part against the source.
A changed package part is called package drift here. Package drift is not automatically corruption. Office export filters can legitimately normalize namespaces, relationship identifiers, calculation metadata, and drawing geometry. A large drift count does show that the save operation reached far beyond the requested no-op.
Results
| Workbook fixture | WolfXL unrelated drift | Bundled LibreOffice unrelated drift |
|---|---|---|
| Average by region and quarter | 0 | 8 parts |
| Validation, protection, and custom XML | 0 | 19 parts |
| External links | 0 | 14 parts |
| Pivot chart and slicers | 0 | 31 parts |
| PowerPivot model | 0 | 151 parts |
These are bounded observations for five files and one operation. They do not prove that every changed part causes a visible failure, or that every workbook will behave the same way.
The preservation result is still operationally important. Agent workflows often open and save the same workbook several times. A broad rewrite on every turn compounds risk, makes reviews noisy, and makes it harder to prove that the agent changed only what the user requested.
The design response: inspect, patch, verify
WolfXL's agent-facing write path uses a guarded transaction:
wolfxl-ops inspect 'input.xlsx' --request-id 'agent-mutation-001' > 'inspect-receipt.json'
wolfxl-ops plan 'mutation-request.json' > 'plan-receipt.json'
wolfxl-ops apply 'mutation-request.json' > 'mutation-receipt.json'
The request names the input, output, allowed operations, and allowed ranges. The apply step writes an isolated temporary workbook, verifies the requested change, checks unrelated package drift, emits an attestation, and atomically publishes the destination.
Unsupported operations, non-ready plans, failed verification, and unrelated drift are stop conditions. The route does not silently switch to another engine.
For ordinary Python code, the API keeps the openpyxl shape:
-from openpyxl import load_workbook, Workbook
+from wolfxl import load_workbook, Workbook
wb = load_workbook("model.xlsx", modify=True)
ws = wb["Inputs"]
ws["B7"] = 0.045
wb.save("model.updated.xlsx")
WolfXL Community is MIT-licensed and available from public PyPI for supported workbook I/O and API fit. Commercial 2.1+ adds current production operations, including the guarded mutation route used in this audit.
Source-matched runtime measurements
Preservation is the primary result. I also measured the process cost of calling a bundled office suite beside a spreadsheet-specific engine.
The local benchmark used:
- macOS arm64 on the same machine
- the LibreOffice binary bundled in the inspected Codex runtime
- WolfXL built from commit
35de4e6914854a2dd3e66f5ee69e71729f606ff4 - the resulting CPython 3.13 arm64 wheel installed into a clean environment
- identical fixture inputs per paired task
- three recorded observations per row
- separate cold and warm scenarios
Selected paired medians
| Task | Input | WolfXL | Bundled LibreOffice | Ratio |
|---|---|---|---|---|
| CSV extraction, agent-task cold | 10x4 XLSX | 0.303 s | 0.667 s | 2.20x |
| Recalculate and extract, cold | 10x4 formula XLSX | 0.293 s | 0.721 s | 2.46x |
| No-op round trip, cold | 10x4 XLSX | 0.297 s | 0.762 s | 2.56x |
| CSV extraction, cold-start scenario | 10x4 XLSX | 0.333 s | 1.111 s | 3.34x |
| CSV extraction, cold | 20,000x8 XLSX | 0.421 s | 1.503 s | 3.57x |
| No-op round trip, cold | 20,000x8 XLSX | 0.371 s | 1.446 s | 3.90x |
Those ratios describe these adapters, builds, fixtures, and modes. They are not a universal performance ranking.
Installation and memory
The inspected runtime footprint was:
| Runtime | Installed bytes |
|---|---|
| WolfXL benchmark environment | 37,524,645 |
| Bundled LibreOffice | 423,048,828 |
Workload peak process-tree RSS favored WolfXL in the rows above, at roughly 56 to 61 MiB versus 104 to 124 MiB for LibreOffice. The isolated footprint probe went the other direction: 53.1 MiB for WolfXL versus 34.5 MiB for LibreOffice. That probe is not a workload-memory ranking, so I am retaining it as a red result rather than hiding it.
The full benchmark contained 70 observations: 67 completed successfully and three recorded an explicit unsupported result for LibreOffice on the WolfXL-specific surgical-edit task. No unavailable result was converted into an estimate.
Why keep LibreOffice at all?
At first glance, comparing WolfXL with LibreOffice might look like comparing apples to oranges. LibreOffice is a complete office suite with decades of engineering across word processing, presentations, vector drawing, and spreadsheet calculation. WolfXL is a specialized Python-facing spreadsheet engine.
That contrast is precisely the architectural point.
AI agent runtimes never needed a full office suite to update a financial model. They bundled LibreOffice because the Python ecosystem lacked an in-process, headless spreadsheet engine capable of formula evaluation and byte preservation. When an agent invokes a desktop office suite headlessly to modify a single cell, the suite executes a full import and re-export cycle rather than applying a surgical patch. It normalizes namespaces, recalculates internal layout identifiers, and rewrites dozens of untouched OOXML parts on save.
LibreOffice remains a capable office suite for broad document conversion, legacy formats, and workflows outside WolfXL's scope. The product decision centers on intelligent routing rather than replacement:
| Need | Preferred route |
|---|---|
| Supported cell, style, formula, or structural edit where preservation matters | Guarded WolfXL transaction |
| Broad office conversion or unsupported legacy format | LibreOffice fallback |
| Unsupported operation with no approved fallback | Fail visibly |
An agent should know which engine handles the file, what that engine supports, and whether unrelated state changed before the output replaces the source.
Beyond spreadsheets: the document execution architecture
Spreadsheets were the first target because they present the most fragile execution challenge in document automation. Unlike static document trees, workbooks combine hierarchical XML with reactive calculation graphs, dynamic array formulas, volatile dependencies, and binary pivot caches. A single cell edit can trigger cascade recalculations or risk corrupting binary cache streams if the engine cannot model the underlying specification.
Solving byte preservation and in-process formula evaluation in spreadsheets establishes the foundation of reliable document manipulation for autonomous agents.
The same architectural principles (in-process execution, surgical XML patching, and zero-drift package writes) are extending to other Microsoft Office formats under the WolfSuite federation:
- Word documents (
.docx) through wolfdocx - Presentations (
.pptx) through wolfppt
The design goal remains consistent across all three: give Python-based AI agents fast, deterministic, byte-preserving document engines without forcing them to bootstrap a desktop office suite. If your agent pipelines encounter document drift or heavy office dependencies in Word or PowerPoint workflows, follow our progress on GitHub.
What this study does not claim
- No universal corruption claim. Changed OOXML parts are package drift. User-visible damage requires feature-specific validation.
- No Microsoft Excel parity claim. The comparison does not establish Excel correctness.
- No universal speed claim. The timing results apply to the recorded source, runtime, tasks, inputs, and machine.
- No Walnut benchmark claim. The inspected runtime also contains
@oai/artifact-tooland@oai/walnut, but those packages are outside this published comparison. - No office-suite replacement claim. WolfXL is a Python-facing spreadsheet engine for supported automation workflows.
Evidence and producer snapshots
The source-bound public receipts and the exact producer snapshots are:
- Preservation receipt, SHA-256
781fc7730468a1cde78998c265a7e02940caf525a439651427033a7d734ece53 - Benchmark receipt, SHA-256
9874b2fbe1d5b1be08628975535ad43f3549b44728dc0239a62a3e9588cf7231 - Exact-source wheel build receipt, wheel SHA-256
9197ed89779e7992b45ae3b5bffef85f0abbe52da1a61ac96d5390550b960bf2 - Preservation producer snapshot, SHA-256
692e374d6e646c7af4966bee23a68ca29b0e4f93ee72c3eff92986611a00e6c7 - Benchmark producer snapshot, SHA-256
83f85eec2b6c5b8b695040f68ef04aa05f5417fc2ccdc18558a5f491a258ab95
The measured commit was clean before and after the wheel build, and the wheel was the one used for the published local benchmark. The producer snapshots expose the measured harness code, but they are repository-bound rather than standalone downloads: their source identity derives from their location under scripts/ in the matching WolfXL evaluation checkout.
Try WolfXL
If you have a workbook where a one-cell edit must preserve charts, pivots, links, macros, or custom XML, I would like the hardest sanitized fixture you can share.
Enjoyed this post?
Get in touch →