XLSX workbench
Spreadsheets are where agents do the most damage by being clever: pasted values where formulas
belong, dates written as text, whole-number floats displayed as 3.0000000001. Follow the
contracts below.
Step 0 - Check the toolchain
python -c "import defusedxml, openpyxl; print(openpyxl.__version__, defusedxml.__version__)"
.csv/.tsv -> standard library csv module is fine and often better (streaming).
.xlsx/.xlsm/.xltx -> openpyxl.
- Safe package preflight requires
defusedxml==0.7.1; do not fall back to the standard XML
parser for untrusted OOXML parts.
- Macro preservation: openpyxl keeps VBA in
.xlsm only with keep_vba=True on load and save.
- Before any
openpyxl.load_workbook() of an existing package, copy and use the bounded
same-handle loader in references/package.md. read_only=True does
not bound shared strings, styles, or other package parts parsed during initialization.
Step 1 - Classify the task
| Request |
Route |
| Open, inspect, profile a workbook |
references/read.md |
| Edit cells, add sheets, fix formatting |
references/edit.md |
| Build a new workbook (data + formulas + chart) |
references/create.md |
| CSV/TSV in or out, messy data cleanup |
references/csv.md |
| Conditional formatting, structured tables, pivot-style aggregation |
references/formatting.md |
| Safely open an existing OOXML package |
references/package.md |
Step 2 - Contracts that always apply
- Formulas are formulas. If the user asks for a total/average/lookup, write
=SUM(B2:B10)
in the cell - never the computed number - unless the user explicitly asked to freeze values.
openpyxl writes the formula; Excel/WPS/LibreOffice calculate on open.
data_only=True reads cached values (last calculated by a real app) and loses
formulas on save. Use it only for reading values; never load, edit, and save with it.
- Types: write
int/float/datetime/bool, never formatted strings. Dates go in as
datetime with number_format='yyyy-mm-dd'; currency as float plus
number_format='#,##0.00' (or the locale-appropriate currency format string).
- Formulas are not recalculated by openpyxl. After writing formulas, set
wb.calculation.fullCalcOnLoad = True before saving so Excel/WPS/LibreOffice recalculate
on open even when the workbook (typically one you loaded, which can carry
fullCalcOnLoad=False) uses calculation mode manual - check
wb.calculation.calcMode. You still cannot read results back without opening the file in
a real spreadsheet app; verify formula strings and ranges structurally instead (see
postcheck).
- Dimensions: treat
<dimension>, ws.max_row, and ws.max_column as untrusted hints.
Discover logical cells with the sparse worksheet-XML scan in the read route; never expand an
unknown rectangular range merely to find its bounds.
- Save to a new path first; overwrite only on explicit request.
Step 3 - Postcheck (mandatory)
Save this as postcheck.py, copy load_validated_workbook() and its dependencies from
references/package.md, then pass the output path followed by every
sheet the task should produce, for example python postcheck.py output.xlsx Sales Summary:
import openpyxl
import sys
if len(sys.argv) < 3:
raise SystemExit("usage: python postcheck.py OUTPUT.xlsx EXPECTED_SHEET [...]")
output_path, *expected_sheets = sys.argv[1:]
# Populate this whenever the task requested specific display formats.
expected_number_formats = {
# "Sales": {"D2": "#,##0.00", "E2": "yyyy-mm-dd"},
}
# Populate every formula the task intends to create or preserve.
expected_formulas = {
# "Sales": {"D2": "=C2*1.08"},
}
# Populate every expected sheet with the exact used range required by the task.
expected_dimensions = {
# "Sales": "A1:E20",
# "Summary": "A1:C8",
}
wb = load_validated_workbook(output_path)
def require(condition, message):
if not condition:
raise ValueError(message)
def formula_text(value):
if isinstance(value, str):
return value
if text := getattr(value, "text", None):
return text
fields = ("ref", "r1", "r2", "dt2D", "dtr", "ca", "del1", "del2")
details = ", ".join(
f"{name}={getattr(value, name)!r}" for name in fields if hasattr(value, name)
)
return f"{type(value).__name__}({details})"
print("sheets:", wb.sheetnames)
missing = set(expected_sheets) - set(wb.sheetnames)
require(not missing, f"missing expected sheets: {sorted(missing)}")
require(
set(expected_dimensions) == set(expected_sheets),
"expected_dimensions must declare the exact used range for every expected sheet",
)
calc = wb.calculation
print("calcMode:", getattr(calc, "calcMode", None),
"fullCalcOnLoad:", getattr(calc, "fullCalcOnLoad", None))
if any(expected_formulas.values()):
require(
getattr(calc, "fullCalcOnLoad", False) is True
or getattr(calc, "calcMode", None) == "auto",
"formula output is not configured to recalculate in spreadsheet viewers",
)
for ws in wb.worksheets:
print(f"{ws.title} dims:", ws.dimensions)
if ws.title in expected_dimensions:
require(
ws.dimensions == expected_dimensions[ws.title],
f"{ws.title}: expected used range {expected_dimensions[ws.title]!r}, "
f"got {ws.dimensions!r}",
)
# `expected_formulas` is the task contract, so verify those coordinates directly.
# Never call unbounded iter_rows(): one styled extreme cell can make the rectangle huge.
actual_formulas = {}
for coordinate, expected_formula in expected_formulas.get(ws.title, {}).items():
cell = ws[coordinate]
actual_formula = formula_text(cell.value) if cell.data_type == "f" else None
actual_formulas[coordinate] = actual_formula
require(
actual_formula == expected_formula,
f"{ws.title}!{coordinate}: expected formula {expected_formula!r}, "
f"got {actual_formula!r}",
)
print(f"{ws.title} expected formula cells:", list(actual_formulas.items())[:10])
for coordinate, expected_format in expected_number_formats.get(ws.title, {}).items():
actual_format = ws[coordinate].number_format
require(actual_format == expected_format, (
f"{ws.title}!{coordinate}: expected format {expected_format!r}, got {actual_format!r}"
))
wb.close()
Confirm: expected sheet names exist; used range matches expectations; intended formula cells
contain formula strings; when the task wrote formulas, the printout shows fullCalcOnLoad: True
(or calcMode: auto) so viewers will recalculate — otherwise set it and re-save; every
task-specific formatted cell is listed in expected_number_formats and matches. Report what
was verified and note that final rendered values require opening in a spreadsheet application.
1---2name: document-skills-xlsx3description: XLSX workbench4---56# XLSX workbench78Spreadsheets are where agents do the most damage by being clever: pasted values where formulas9belong, dates written as text, whole-number floats displayed as `3.0000000001`. Follow the10contracts below.1112## Step 0 - Check the toolchain1314```bash15python -c "import defusedxml, openpyxl; print(openpyxl.__version__, defusedxml.__version__)"16```1718- `.csv`/`.tsv` -> standard library `csv` module is fine and often better (streaming).19- `.xlsx`/`.xlsm`/`.xltx` -> openpyxl.20- Safe package preflight requires `defusedxml==0.7.1`; do not fall back to the standard XML21 parser for untrusted OOXML parts.22- Macro preservation: openpyxl keeps VBA in `.xlsm` only with `keep_vba=True` on load and save.23- Before **any** `openpyxl.load_workbook()` of an existing package, copy and use the bounded24 same-handle loader in [references/package.md](references/package.md). `read_only=True` does25 not bound shared strings, styles, or other package parts parsed during initialization.2627## Step 1 - Classify the task2829| Request | Route |30|---|---|31| Open, inspect, profile a workbook | [references/read.md](references/read.md) |32| Edit cells, add sheets, fix formatting | [references/edit.md](references/edit.md) |33| Build a new workbook (data + formulas + chart) | [references/create.md](references/create.md) |34| CSV/TSV in or out, messy data cleanup | [references/csv.md](references/csv.md) |35| Conditional formatting, structured tables, pivot-style aggregation | [references/formatting.md](references/formatting.md) |36| Safely open an existing OOXML package | [references/package.md](references/package.md) |3738## Step 2 - Contracts that always apply39401. **Formulas are formulas.** If the user asks for a total/average/lookup, write `=SUM(B2:B10)`41 in the cell - never the computed number - unless the user explicitly asked to freeze values.42 openpyxl writes the formula; Excel/WPS/LibreOffice calculate on open.432. **`data_only=True` reads cached values** (last calculated by a real app) and **loses44 formulas on save**. Use it only for reading values; never load, edit, and save with it.453. **Types**: write `int`/`float`/`datetime`/`bool`, never formatted strings. Dates go in as46 `datetime` with `number_format='yyyy-mm-dd'`; currency as float plus47 `number_format='#,##0.00'` (or the locale-appropriate currency format string).484. **Formulas are not recalculated by openpyxl.** After writing formulas, set49 `wb.calculation.fullCalcOnLoad = True` before saving so Excel/WPS/LibreOffice recalculate50 on open even when the workbook (typically one you loaded, which can carry51 `fullCalcOnLoad=False`) uses calculation mode `manual` - check52 `wb.calculation.calcMode`. You still cannot read results back without opening the file in53 a real spreadsheet app; verify formula strings and ranges structurally instead (see54 postcheck).555. **Dimensions**: treat `<dimension>`, `ws.max_row`, and `ws.max_column` as untrusted hints.56 Discover logical cells with the sparse worksheet-XML scan in the read route; never expand an57 unknown rectangular range merely to find its bounds.586. Save to a new path first; overwrite only on explicit request.5960## Step 3 - Postcheck (mandatory)6162Save this as `postcheck.py`, copy `load_validated_workbook()` and its dependencies from63[references/package.md](references/package.md), then pass the output path followed by every64sheet the task should produce, for example `python postcheck.py output.xlsx Sales Summary`:6566```python67import openpyxl68import sys6970if len(sys.argv) < 3:71 raise SystemExit("usage: python postcheck.py OUTPUT.xlsx EXPECTED_SHEET [...]")72output_path, *expected_sheets = sys.argv[1:]73# Populate this whenever the task requested specific display formats.74expected_number_formats = {75 # "Sales": {"D2": "#,##0.00", "E2": "yyyy-mm-dd"},76}77# Populate every formula the task intends to create or preserve.78expected_formulas = {79 # "Sales": {"D2": "=C2*1.08"},80}81# Populate every expected sheet with the exact used range required by the task.82expected_dimensions = {83 # "Sales": "A1:E20",84 # "Summary": "A1:C8",85}86wb = load_validated_workbook(output_path)8788def require(condition, message):89 if not condition:90 raise ValueError(message)9192def formula_text(value):93 if isinstance(value, str):94 return value95 if text := getattr(value, "text", None):96 return text97 fields = ("ref", "r1", "r2", "dt2D", "dtr", "ca", "del1", "del2")98 details = ", ".join(99 f"{name}={getattr(value, name)!r}" for name in fields if hasattr(value, name)100 )101 return f"{type(value).__name__}({details})"102103print("sheets:", wb.sheetnames)104missing = set(expected_sheets) - set(wb.sheetnames)105require(not missing, f"missing expected sheets: {sorted(missing)}")106require(107 set(expected_dimensions) == set(expected_sheets),108 "expected_dimensions must declare the exact used range for every expected sheet",109)110calc = wb.calculation111print("calcMode:", getattr(calc, "calcMode", None),112 "fullCalcOnLoad:", getattr(calc, "fullCalcOnLoad", None))113if any(expected_formulas.values()):114 require(115 getattr(calc, "fullCalcOnLoad", False) is True116 or getattr(calc, "calcMode", None) == "auto",117 "formula output is not configured to recalculate in spreadsheet viewers",118 )119for ws in wb.worksheets:120 print(f"{ws.title} dims:", ws.dimensions)121 if ws.title in expected_dimensions:122 require(123 ws.dimensions == expected_dimensions[ws.title],124 f"{ws.title}: expected used range {expected_dimensions[ws.title]!r}, "125 f"got {ws.dimensions!r}",126 )127 # `expected_formulas` is the task contract, so verify those coordinates directly.128 # Never call unbounded iter_rows(): one styled extreme cell can make the rectangle huge.129 actual_formulas = {}130 for coordinate, expected_formula in expected_formulas.get(ws.title, {}).items():131 cell = ws[coordinate]132 actual_formula = formula_text(cell.value) if cell.data_type == "f" else None133 actual_formulas[coordinate] = actual_formula134 require(135 actual_formula == expected_formula,136 f"{ws.title}!{coordinate}: expected formula {expected_formula!r}, "137 f"got {actual_formula!r}",138 )139 print(f"{ws.title} expected formula cells:", list(actual_formulas.items())[:10])140 for coordinate, expected_format in expected_number_formats.get(ws.title, {}).items():141 actual_format = ws[coordinate].number_format142 require(actual_format == expected_format, (143 f"{ws.title}!{coordinate}: expected format {expected_format!r}, got {actual_format!r}"144 ))145wb.close()146```147148Confirm: expected sheet names exist; used range matches expectations; intended formula cells149contain formula strings; when the task wrote formulas, the printout shows `fullCalcOnLoad: True`150(or `calcMode: auto`) so viewers will recalculate — otherwise set it and re-save; every151task-specific formatted cell is listed in `expected_number_formats` and matches. Report what152was verified and note that final rendered values require opening in a spreadsheet application.