gridcraft · diff

git:20261022.ca0d7e4 to git:20261023.8287b5e

406 added, 279 removed. Audit A to A.

---
name: gridcraft
description: >-
- GridCraft is an open-source, clean-room Rust reimplementation of Microsoft Excel: a fast spreadsheet
- app for macOS (also Windows, Linux, BSD and the web) that reads and writes real Excel .xlsx files,
- and that can be driven headlessly by agents through a CLI, an MCP server and a live JSON control
- channel. Use this skill whenever the user mentions GridCraft by name; asks what it can do or which
- formats, keyboard shortcuts, commands or worksheet functions it has; wants to create, edit, convert,
- calculate, print or export spreadsheets with it; wants to automate or script it (MCP, CLI, control
- channel, recorded scripts); needs assets or data prepared for it (make/convert .xlsx, CSV, TSV, JSON,
- or convert .xls/.xlsb/.ods into .xlsx); wants its output verified, repaired or round-tripped
- (gridcraft-cli check); or reports a symptom (crash, wrong formula result, formatting lost on save,
- Excel saying "we found a problem with some content"). Trigger it even when the app is not named but
- the job is Excel-style spreadsheet work — "make me an Excel file", "add a formula column", "sort and
- filter this sheet", "pivot this data", "export this workbook to PDF", "read the values out of this
- spreadsheet". Do not assume Excel feature parity from Excel knowledge; check this skill first.
+ GridCraft is an open-source, clean-room Rust reimplementation of Microsoft Excel: a spreadsheet
+ app for macOS (also Windows, Linux, BSD and the web) that reads and writes real .xlsx files and
+ can be driven headlessly through a CLI, an MCP server and a live JSON control channel. Use this
+ skill whenever the user mentions GridCraft by name; asks what it can do or which formats,
+ keyboard shortcuts, commands or worksheet functions it has; wants to create, edit, convert,
+ calculate, print or export spreadsheets with it; wants to automate or script it (MCP, CLI,
+ control channel, recorded scripts); needs inputs prepared for it (build/convert .xlsx, CSV, TSV,
+ JSON, or convert .xls/.xlsb/.ods into .xlsx); wants its output verified, repaired or
+ round-tripped (gridcraft-cli check); or reports a symptom (crash, wrong formula result,
+ formatting lost on save, Excel saying "we found a problem with some content"). Trigger it even
+ when the app is not named but the job is Excel-style spreadsheet work — "make me an Excel file",
+ "add a formula column", "sort and filter this sheet", "pivot this data", "export this workbook to
+ PDF", "read the values out of this spreadsheet". Do not assume Excel parity from Excel knowledge;
+ check this skill first.
---
# GridCraft
+ **Audit basis:** this checkout is tag `v0.11.1`, commit `ac8da7d49cbeb4be6aa1ad5869f3d29fe2da8cd9` (release
+ commit, 2026-10-22). The installed `/Applications/GridCraft.app` reports version `0.11.1`
+ (`CFBundleShortVersionString`), its `GridCraftBuildCommit` is the same SHA, and its binary prints `GridCraft
+ 0.11.1`, so the bundle matches this source exactly. **Verify against your installed version** if the ArtCraft
+ Launcher has auto-updated since. Counts and formats below were re-derived from this tag; every claim carries a
+ repo-relative pointer.
+
## Overview
- GridCraft is an open-source, clean-room reimplementation of Microsoft Excel's workflow, written in pure
- Rust by the ArtCraft team; the source lives at `github.com/storytold/gridcraft` (README.md). XLSX is the
- native format, and an engine-first architecture makes every ribbon button, menu item, dialog, keystroke
- and mouse gesture a *command* that agents can run over a JSON control channel, an MCP server, or the
- headless CLI (`README.md` §"For agents"; `AGENTS.md`). Maturity is **alpha**: the core workflow (open
- XLSX → edit/format → sort/filter → chart/pivot → print to PDF → save XLSX) works end to end, and the
- project estimates feature breadth ~75% but only ~50% "ready for real work" (ROADMAP.md headline
- numbers). It runs on macOS 11+, Windows, Linux, BSD and the browser via WebAssembly; this skill was
- written against release **v0.10.0**, the exact version the installed app reports (`GridCraft 0.10.0`).
+ GridCraft is an open-source, clean-room reimplementation of Microsoft Excel's workflow in pure Rust
+ (README.md), by the ArtCraft team; source: `github.com/storytold/gridcraft`. XLSX is the native document
+ format, and the architecture is engine-first: every ribbon button, menu item, dialog OK, keystroke and mouse
+ gesture is a *command* (`crates/engine/src/cmd/`), reachable over a JSON control channel, an MCP server and a
+ headless CLI (README.md §"For agents"; `docs/architecture.md`). Maturity is **alpha**: the core workflow (open
+ XLSX → edit/format → sort/filter → chart/pivot → print to PDF → save XLSX) works end to end, but the project's
+ own estimate is ~75% feature breadth and ~50% "ready for real work" (ROADMAP.md headline;
+ `docs/target-app-parity.md`). It runs natively on macOS 11+, Windows, Linux and BSD, and in the browser via
+ WebAssembly (`apps/gridcraft-web`).
## Capabilities (verified)
### Core workbook and editing
- - Multiple workbooks open in one OS window ("documents"), plus sheet tabs, names, freeze panes,
- outlining; three built-in samples `sales`, `budget`, `grades` — `crates/engine/src/sample.rs`,
- `crates/engine/src/cmd/file.rs` (`file.new`), `docs/ui-parity.md` (no multiple windows per workbook).
- - Full command surface: 274 of the 290 Excel ribbon/menu ids in the catalog are implemented (94.5%) —
- `crates/engine/src/catalog.rs`, generated `docs/parity-checklist.md`. The CLI prints the live list:
- `gridcraft-cli commands`. Examples: `cell.set`, `home.bold`, `insert.chart`, `data.sortAscending`.
- - Formatting: fonts, borders, fills, alignment, merges, Excel's number-format language
- (`crates/numfmt`), styles/themes, conditional formatting (`crates/engine/src/cf.rs`), data validation,
- and pass-through of unmodelled workbook parts — `crates/xlsx/src/passthrough.rs`.
- - Tables, notes and threaded comments (replies, resolved state, shown/hidden), hyperlinks, pictures
- (floating and in-cell rich-value pictures: `docs/cell-pictures.md`, PNG/JPEG only; no `IMAGE()`
- function), shapes/text boxes (8 shape kinds vs Excel's ~170), checkboxes (`insert.checkbox`,
- `cell.toggleCheckbox`), freehand ink on the Draw tab with Ink to Shape (`draw.pen`, `draw.stroke`,
- `draw.lasso`, `draw.eraser`, `draw.inkToShape` — `crates/engine/src/cmd/draw.rs`), sparklines
- (line/column/win-loss: `crates/model/src/features.rs` `SparklineKind`).
- - AutoSave toggle in the title bar (saves after each change when the workbook has a file) —
- `crates/ui-egui/src/ribbon.rs` `title_bar`.
+ - Several workbooks open at once inside one OS window ("documents"); the Window menu lists them
+ (`view.switchWindows`) and `window.activate {index}` switches. Sheet tabs, defined names, freeze panes,
+ outline groups, hidden sheets, tab colours (implemented — `crates/engine/src/cmd/{file,sheet,extra}.rs`).
+ One window only; see limitations.
+ - Three built-in samples `budget`, `sales`, `grades`: `file.new {sample}`, CLI `--sample`, URL `?sample=`
+ (`crates/engine/src/sample.rs`).
+ - 346 engine commands (341 at v0.10.0); 275 of the 291 Excel ribbon/menu ids implemented, 94.5% (274/290
+ before) — `crates/engine/src/catalog.rs`, generated `docs/parity-checklist.md`. Added since v0.10.0:
+ `automate.trustScripts`, `formulas.convertToNumber`, `formulas.selectPrecedents`,
+ `formulas.selectDependents`, `view.showObjects`.
+ - Formatting: fonts, fills, borders, alignment, merges, Excel's number-format language (`crates/numfmt`), cell
+ styles and themes, conditional formatting (cell-is, expression, text, top-10, above-average, time period,
+ duplicate/unique, colour scale, data bar, icon set) and data validation (whole, decimal, list, date, time,
+ text length, custom) — `crates/engine/src/cmd/{format,insert}.rs`, `crates/model/src/features.rs`.
+ - Tables (structured references, header/total rows, banded rows, style gallery, Resize Table, remove
+ duplicates), notes and threaded comments with replies/resolution/timestamps, hyperlinks, pictures floating
+ or inside a cell (PNG/JPEG only), shapes and text boxes (9 kinds: rectangle, rounded rectangle, ellipse,
+ triangle, line, arrow, text box, icon, ink), checkboxes, freehand ink with Ink to Shape, sparklines
+ (line/column/win-loss) — `crates/engine/src/cmd/{insert,extra,draw}.rs`, `crates/model/src/features.rs`,
+ `docs/cell-pictures.md`.
+ - Editing: fill handle (drag; right-drag opens Copy Cells / Fill Series / Fill Formatting Only / Fill Without
+ Formatting / Series…), AutoFill, Flash Fill, Paste Special (values/formats/formulas/comments, transpose,
+ Paste Link), Format Painter, Find/Replace, Go To and Go To Special, Watch Window, undo/redo with bounded
+ history — `crates/ui-egui/src/grid.rs`, `crates/engine/src/cmd/{edit,whatif}.rs`.
+ - Background error checking: a constant whose text would be a number gets Excel's green corner, a warning
+ button naming the problem, and Convert to Number (`formulas.convertToNumber`) — new at v0.11.0 (#1994);
+ `crates/ui-egui/src/grid.rs`, `crates/engine/src/cmd/formulas.rs`.
+ - AutoSave switch in the title bar, now persisted across restarts; it saves only workbooks that already have a
+ file. Closing the window with unsaved work asks first (`crates/ui-egui/src/{lib,ribbon}.rs`).
+ - Slicers exist as in-sheet filter widgets over one table column (`insert.slicer`,
+ `slicer.toggle|selectAll|delete`), but are not written to XLSX (`crates/engine/src/cmd/slicer.rs`).
### Formulas and calculation
- - 513 of Excel's 523 worksheet functions by name (504 working, 96.4%), including dynamic arrays and
- spilling, LET, LAMBDA, MAP/REDUCE/SCAN/BYROW/BYCOL/MAKEARRAY, structured table references,
- iterative calculation for circular references — `docs/function-parity.md` ("Measured" line),
- `crates/functions/src/`, `crates/calc/src/`.
- - Evaluate without editing: `gridcraft-cli eval '=SUM(A1:A9)' --in book.xlsx` or the `formulas.evaluate`
- command (`apps/gridcraft-cli/src/main.rs::eval`).
- - The CLI is a calculation oracle/tester: `gridcraft-cli check <files-or-dir> [--tolerance 1e-9]
- [--max-report N]` recalculates every formula, compares against the file's cached values (skipping
- volatile formulas and files saved with `fullCalcOnLoad`), then round-trips the workbook through
- GridCraft's own XLSX writer comparing 14 categories (package, sheets, values, formulas, numFmts,
- merges, tables, condFormats, validations, charts, comments, hyperlinks, pivots, names) and validates
- the package the way Excel's repair would — `docs/cli.md` §`check`,
- `apps/gridcraft-cli/src/check.rs` (`Diffs::new` list). Exit 1 on any difference; `--json` emits
- schema `gridcraft-check/1`; mismatches roll up by function and by signature (`number → #SPILL!`).
+ - 513 of Excel's 523 worksheet functions by name, 504 working (96.4%), including dynamic arrays and spilling,
+ LET/LAMBDA, MAP/REDUCE/SCAN/BYROW/BYCOL/MAKEARRAY, GROUPBY/PIVOTBY, structured table references, iterative
+ calculation for circular references — `docs/function-parity.md` ("Measured" line), `crates/functions/`,
+ `crates/calc/`.
+ - Evaluate without editing: `gridcraft-cli eval '=SUM(A1:A9)' --in book.xlsx` or `formulas.evaluate`. A
+ formula that could not be typed into a cell (parse error, wrong argument count) is an error that says why,
+ not a silent `#VALUE!` (`crates/engine/src/cmd/formulas.rs::evaluate`;
+ `apps/gridcraft-cli/src/main.rs::eval`).
+ - Calculation settings travel with the workbook: automatic / automaticExceptTables / manual, iterative on/off,
+ max iterations and max change (`formulas.calculationOptions`; `crates/model/src/workbook.rs`).
### Files it reads and writes
- - **XLSX is native** (read/write): cells, formulas (shared/array/dynamic), styles, themes, merges,
- conditional formats, validation, tables, notes/threaded comments, hyperlinks, names, freeze panes,
- outlines, print settings, pictures, shapes (incl. ink as DrawingML polylines), sparklines,
- PivotTables (caches and tables), Allow-Edit-Ranges protection, calc settings —
- `crates/engine/src/io.rs`, `crates/xlsx/src/{read,write,sheet_read,sheet_write}.rs`,
- `docs/file-format-parity.md`. Strict Open XML files open but always save as transitional.
- - `.xlsm`/`.xltm` keep their VBA project byte-for-byte (macros are **never run**); `.xltx`/`.xltm`
- save with the template content type — `crates/xlsx/src/lib.rs` (`XlsxKind`), `write.rs`, `io.rs`.
- - **Import-only**: `.xls` (BIFF8; values, formulas, number formats, merges, sizes, names, calc and
- sheet-display settings — no fonts/fills/borders/charts/pictures/comments/CF/validation) with chart,
- macro and dialog sheets skipped — `crates/xlsx/src/xls/`; `.xlsb` and `.ods` (worksheet **values
- only**, cached formula results) `crates/xlsx/src/{xlsb,ods}.rs`. `.xlt` opens as `.xls`.
- - **Text/data**: CSV and TSV/TXT/TAB (delimiter detection, UTF-8/UTF-16/Windows-1252; writes the
- *displayed* text, General keeps full numeric precision; a workbook opened as CSV keeps its
- separator on save; output refused over 512 MiB) `crates/xlsx/src/csv.rs`, `io.rs::csv`;
- `file.open {csvAsText: true}` keeps every field as text (`io.rs`). JSON workbook (debug serde of
- the internal model) `io.rs`. HTML export (one sheet as a table; refuses >100,001 rows) `io.rs::to_html`.
- - **PDF export** from a sheet (own PDF 1.7 writer, 14 standard fonts, no embedding) —
- `crates/pdf/src/lib.rs`, `crates/engine/src/cmd/print.rs::export_pdf`.
- - **Password-encrypted `.xlsx` open** (Agile AES + Standard encryption, [MS-OFFCRYPTO]) via a
- `password` parameter; the workbook opens without a save path and is saved **without** the password;
- RC4/Extensible/encrypted-`.xls` refused — `crates/xlsx/src/crypto.rs`, `io.rs`,
- `docs/control-protocol.md`.
- - **Save is refused** for `.xls`, `.xlsb`, `.ods` (import-only) — `io.rs::save_bytes_as`.
+ - **`.xlsx` is native read/write**: cells, shared/array/dynamic formulas, styles, themes (including per-script
+ fonts), merges, conditional formats, validation, tables, notes (with box position and size) and threaded
+ comments, hyperlinks, names, freeze panes, outlines, print settings, pictures (shared media written once),
+ shapes incl. ink, sparklines, PivotTables, sheet/workbook protection and Allow Edit Ranges, calc settings,
+ `<workbookPr showObjects>` — `crates/engine/src/io.rs`,
+ `crates/xlsx/src/{read,write,sheet_read,sheet_write}.rs`, `docs/file-format-parity.md`.
+ - **Pass-through on save** (kept byte-for-byte when their owner survives): custom XML, external workbook links
+ (never followed or refreshed), data connections, sheet query tables, printer settings, form/ActiveX
+ controls, OLE objects, sheet backgrounds with their VML, header/footer pictures, and chart sheets —
+ `crates/xlsx/src/passthrough.rs`.
+ - `.xlsm` / `.xltm` keep their VBA project byte-for-byte; macros are **never executed**. `.xltx`/`.xltm` save
+ with the template content type — `crates/xlsx/src/lib.rs` (`XlsxKind`), `crates/xlsx/src/write.rs`, test
+ `macros_are_saved_only_into_xlsm` in `crates/engine/src/io.rs`.
+ - **Import-only**: `.xls`/`.xlt` (BIFF8: values, formulas, number formats, merges, sizes, names, calc and
+ sheet-display settings; chart/macro/dialog sheets skipped), `.xlsb` and `.ods` (worksheet values and cached
+ formula results only) — `crates/xlsx/src/xls/`, `crates/xlsx/src/{xlsb,ods}.rs`,
+ `docs/{xlsb,ods}-import.md`.
+ - **Text/data**: CSV and TSV/TXT/TAB with delimiter/encoding detection (UTF-8/UTF-16, likely Windows-1256
+ Arabic, otherwise Windows-1252); writes the *displayed* text, keeps the separator a file was opened with,
+ refuses output over 512 MiB; `file.open {csvAsText:true}` keeps every field as text —
+ `crates/xlsx/src/csv.rs`, `crates/engine/src/io.rs::csv`. `.json` is a debug serde dump of the internal
+ model (unstable, not an interchange format). HTML exports one sheet as a table (pictures as data-URI images,
+ charts as inline SVG; refuses more than 100,001 rows) — `crates/engine/src/io.rs::to_html`
+ (`HTML_EXPORT_MAX_ROWS`).
+ - **`.pdf` export**: own PDF 1.7 writer (no dependencies); `file.exportPdf {path?, sheets?: active|all,
+ range?, fitToPage?}` returns `{pages, bytes, path}` or base64; `file.print` returns the same PDF; the CLI
+ `convert` now accepts `.pdf` as an output extension. `http(s)` hyperlinks and internal cell destinations
+ become real PDF link annotations; `file:` links are omitted — `crates/pdf/src/lib.rs`,
+ `crates/engine/src/cmd/print.rs`, `apps/gridcraft-cli/src/main.rs::convert`.
+ - **Password-encrypted `.xlsx` open** (Agile AES and Standard encryption, [MS-OFFCRYPTO]) via a `password`
+ param; the workbook opens with no save path and always saves **unencrypted**. RC4/Extensible and encrypted
+ `.xls` are refused — `crates/xlsx/src/crypto.rs`, `crates/engine/src/io.rs`.
+ - **Save is refused** for `.xls`, `.xlsb`, `.ods` with an explanatory message
+ (`crates/engine/src/io.rs::save_bytes_as`). Content wins over the extension: an ODS/XLSB package is imported
+ as what it is; an XLSX package is never misread as ODS/XLSB.
+ - Hard caps: 512 MB per decompressed XLSX part and 1 GB per package (`crates/xlsx/src/package.rs`), 2 GB per
+ file read, 100,001 HTML rows, 512 MiB CSV/TSV output, 16,000,000 cells per array result
+ (`crates/calc/src/eval.rs`).
- ### Charts, pivots, analysis
- - 25 chart kinds (column/bar/line/pie/doughnut/area/scatter/bubble/radar/histogram/waterfall/funnel/
- treemap/sunburst/box-whisker/stock/combo…) with a Chart Design tab, Format pane, Recommended Charts
- (own heuristic, live previews), trendlines (six types, read/written/drawn), PivotCharts
- (`insert.pivotChart`, bound to a PivotTable's report) — `crates/model/src/features.rs` `ChartKind`
- (25 variants), `crates/chart/src/`, `docs/chart-parity.md`.
+ ### Charts, PivotTables and analysis
+ - 25 `ChartKind`s
+ (column/bar/line/pie/doughnut/area/scatter/bubble/radar/histogram/waterfall/funnel/treemap/sunburst/box-whisker/stock/combo
+ and subtypes), chartex parts for histogram/box-whisker/waterfall/funnel/treemap/sunburst, six trendline
+ types, PivotCharts (`insert.pivotChart`), Recommended Charts, Select Data, Switch Row/Column, Change
+ Type/Colours/Layout — `crates/model/src/features.rs` (`ChartKind`), `crates/xlsx/src/{chart,chartex}.rs`,
+ `crates/engine/src/cmd/{insert,extra}.rs`, `docs/chart-parity.md`.
+ - Chart objects move and resize: drag the bottom-right corner or use `chart.set {at, width, height}` (resize
+ landed after v0.10.0; `crates/ui-egui/src/grid.rs` `Drag::Object`, `crates/ui-egui/src/panes.rs`). All 2-D
+ kinds survive the XLSX round trip.
- PivotTables: field list, Filters/Rows/Columns/Values areas, report filters, layouts, date grouping,
- show-values-as, calculated fields, values on rows, grand totals; round-trip to/from XLSX —
- `crates/engine/src/cmd/pivot.rs`, `crates/xlsx/src/pivot.rs`, `docs/parity-checklist.md`
- (PivotTable Analyze 13/13, Design 4/4).
- - Data tools: sort, AutoFilter (by value/colour, top-10, custom), advanced filter, remove duplicates,
- text-to-columns, Flash Fill, group/ungroup/subtotals, consolidate, scenario manager
- (`data.scenario*`), Goal Seek (`data.goalSeek`), data table — `crates/engine/src/cmd/{data,whatif}.rs`.
+ show-values-as, calculated fields, values on rows, grand totals, styles, refresh, change source; round-trip
+ to/from XLSX — `crates/engine/src/cmd/pivot.rs`, `crates/xlsx/src/pivot.rs`.
+ - Data tools: sort (multi-key), AutoFilter (values/colour/top-10/custom), Advanced Filter (criteria ranges,
+ copy-to, unique), remove duplicates, text-to-columns, Flash Fill, group/ungroup/subtotals, consolidate,
+ scenario manager, Goal Seek, data table — `crates/engine/src/cmd/{data,whatif}.rs`,
+ `docs/parity-checklist.md` Data 23/28.
- Solver (`data.solver`: Simplex LP with integer/binary branch-and-bound, augmented-Lagrangian local
- nonlinear, Evolutionary; Answer and Limits reports), Forecast Sheet with hindcast
- (`data.forecastSheet`), Analysis ToolPak (13 of 19 tools) —
- `crates/engine/src/cmd/{solver,forecast,analysis,whatif}.rs`, `docs/gaps.md` §13.
- - Native printing and print preview via the shared `craft-print` library (pinned git rev in
- `apps/gridcraft/Cargo.toml`; printer choice, copies, duplex, grayscale), Page Setup, Page Break
- Preview and Page Layout view (paper view, rulers with draggable margins, in-place header/footer
- editing) — `crates/engine/src/cmd/print.rs`, `docs/control-protocol.md` §Native printing,
- `docs/ui-parity.md`.
+ nonlinear, Evolutionary; Answer and Limits reports), Forecast Sheet with hindcast (`data.forecastSheet`),
+ Analysis ToolPak (13 of Excel's 19 tools) — `crates/engine/src/cmd/{solver,forecast,analysis}.rs`.
+ - Printing: native macOS/Windows/Linux printing through the pinned shared `craft-print` library (printer
+ choice, copies, duplex, grayscale), Page Setup, Page Break Preview, Page Layout view with rulers, draggable
+ margins and in-place header/footer editing — `apps/gridcraft/Cargo.toml`, `apps/gridcraft/src/print.rs`,
+ `crates/engine/src/cmd/print.rs`, `docs/ui-parity.md`, `docs/hardware-parity.md`.
### Automation surface (the reason to read this skill)
- - **CLI** `gridcraft-cli` (headless; *not* inside the app bundle — install separately): `info`,
- `convert` (to `.xlsx`/`.csv`/`.tsv`/`.json`/`.html`), `eval`, `cat`, `run` (scripted `--cmd`/
- `--script` steps over a workbook, `--out`, `--print`, `--sheet`, `--csv`, `--formulas`, `--quiet`),
- `check`, `commands`, `functions` (`--search`, `--category`), `mcp`, `send`, `version` —
- `apps/gridcraft-cli/src/main.rs`, `docs/cli.md`.
- - **MCP server** over stdio (`gridcraft-cli mcp [--in FILE | --sample NAME]`, or `--connect PORT` to
- drive a running app) with 21 tools: `execute_command`, `read_range`, `write_range`, `set_cell`,
- `get_cell`, `evaluate_formula`, `format_range`, `insert_chart`, `create_table`, `sort_range`,
- `filter`, `open_workbook`/`save_workbook`/`new_workbook`, `undo`/`redo`, `screenshot` (remote only),
- `list_commands`, `list_functions`, `inspect_workbook`, `select` — `crates/mcp/src/tools.rs`,
- `docs/mcp.md`. Resources: `gridcraft://workbook`, `gridcraft://commands`, `gridcraft://functions`.
- - **Control channel**: the desktop app started with `--control <port>` (or `GRIDCRAFT_CONTROL_PORT`)
- accepts newline-delimited JSON on `127.0.0.1`: `engine.execute`, `engine.commands`, `engine.journal`
- (replayable; passwords excluded), `document.inspect`, `sheet.read`, `cell.get`, real pointer and
- keyboard input (`ui.click`, `ui.move`, `ui.drag`, `ui.cell.click`, `ui.cell.drag`, `ui.key`,
- `ui.text`), editor driving (`ui.edit.*`), dialogs (`ui.dialog`, `ui.dialog.set/confirm/cancel`),
- ribbon/backstage (`ui.ribbon`, `ui.backstage` via `engine.execute`), `ui.screenshot` (PNG;
- animations settle first), `ui.set`, `ui.resize`, `ui.focus`, `app.open`/`app.save`/`app.quit` —
- `crates/ui-egui/src/control.rs`, `docs/control-protocol.md`.
- - **Recorded scripts** on the Automate tab: record → save → run/delete (`automate.recordActions`,
- `automate.newScript`, `automate.allScripts`, `automate.runScript`, `automate.deleteScript`) —
- `crates/engine/src/cmd/extra.rs`.
- - **Web (WebAssembly)**: same UI crate via eframe web runner, `trunk build --release` → `dist/web`;
- URL flags `?sample=`, `?webgl`, `?host=parent` (iframe integration with an `&author=` name); no TCP
- control channel in the browser — `apps/gridcraft-web/src/main.rs`.
- - Programmatic calls **never open dialogs**: a command with missing file params returns an error like
- "programmatic calls never open dialogs" instead (`crates/engine/src/lib.rs::execute_headless`).
- - Interface languages: 11 built in (`en es zh ja ko ru pt-BR de cs fr sr`), but only chrome is
- translated (~12% of ~2,200 strings each; pt-BR ~42%); formula names/separators stay en-US canonical;
- `app.language.set` switches at runtime — `crates/ui-egui/src/i18n.rs` (`Language::ALL`),
- `docs/localization-parity.md`, `docs/control-protocol.md`. Spanish input mode is a formula-entry aid
- only (`docs/formula-input.md`).
+ Counts at this tag, with v0.10.0 in brackets: **346 engine commands** (341); **275/291 catalog ids**
+ (274/290); **21 MCP tools** (21, unchanged); **11 CLI subcommands** (11; `convert` gained `.pdf`); **31
+ documented control-channel methods** plus `ui.menu.invoke` and `ui.message.dismiss` in code (set unchanged);
+ **68 shortcut bindings on command specs** (65; added `Ctrl+6`, `Ctrl+[`, `Ctrl+]`); **365 key-tip sequences**
+ (364); **19 interface languages** (11); **25 chart kinds** (25); **513/523 functions** (513/523).
+ - **CLI `gridcraft-cli`** (headless; the app bundle ships no CLI): `info`, `convert`, `eval`, `cat`, `check`,
+ `run`, `commands`, `functions`, `mcp`, `send`, `version` — `apps/gridcraft-cli/src/main.rs` (USAGE),
+ `docs/cli.md`. `check` is the important one: it recalculates every formula against the file's Excel-cached
+ values (skipping volatile formulas and `fullCalcOnLoad` files), then writes and re-reads the workbook
+ through GridCraft's own XLSX writer, comparing 14 categories — package, sheets, values, formulas, numFmts,
+ merges, names, tables, condFormats, validations, charts, comments, hyperlinks, pivots — and validating the
+ package (content types, relationship targets/ids). Exit 1 on any difference, 2 on a usage error; `--json`
+ emits schema `gridcraft-check/1`; discovery caps at depth 32 and 100,000 files and reports omissions as
+ failures (`apps/gridcraft-cli/src/check.rs`).
+ - **MCP server** over stdio, protocol `2025-06-18` (older `2025-03-26`/`2024-11-05` clients are answered in
+ their own version); headless (`--in FILE | --sample NAME`) or remote (`--connect PORT`). 21 tools:
+
+ | Tool | Does |
+ |---|---|
+ | `execute_command` | any command id + params |
+ | `list_commands` | ids, labels, ribbon paths, shortcuts, param docs; `search?` |
+ | `inspect_workbook` | sheets, used ranges, tables, charts, names, selection, history |
+ | `read_range` | `range?`, `sheet?`, `formulas?`, `formatted?` |
+ | `write_range` | `range`, 2-D `values`; strings starting `=` are formulas |
+ | `set_cell` / `get_cell` | enter one cell / value, formula, display text, style, merge, comment, link |
+ | `evaluate_formula` | `formula`, optional `cell`; never changes the sheet |
+ | `select` | `range`, `active?` (comma-separated areas allowed) |
+ | `format_range` | bold, italic, underline, font name/size/colour, fill, number format, alignment, wrap, border presets |
+ | `insert_chart` | `range`, `type`, `subtype?`, `title?`, `at?` |
+ | `create_table` | `range`, `style?`, `header?`, `name?` |
+ | `sort_range` | `keys` (`"B"`, `"B desc"` or `{column, order}`), `range?`, `header?` |
+ | `filter` | `column`, `range?`, `values?`, `custom?`, `top?`, `blanks?`, `clear?` |
+ | `open_workbook` / `save_workbook` / `new_workbook` | path / path? / sample? |
+ | `undo` / `redo` | `steps?` |
+ | `screenshot` | `path`; remote/desktop only |
+ | `list_functions` | `search?`, `category?` |
+
+ Resources: `gridcraft://workbook`, `gridcraft://commands`, `gridcraft://functions`; `prompts/list` is empty.
+ `gridcraft-mcp` is also a library (`Server::new(Box::new(Headless::new()))`, `Remote::connect`) —
+ `crates/mcp/src/{tools,server,headless,backend}.rs`, `docs/mcp.md`.
+ - **Control channel**: the app started with `--control <port>` (or `GRIDCRAFT_CONTROL_PORT`) accepts
+ newline-delimited JSON on `127.0.0.1`; each line gets one reply line. Read state: `engine.execute`,
+ `engine.commands` (also lists UI-only ids), `engine.journal` (10,000 entries / 64 MB, passwords stripped),
+ `document.inspect`, `sheet.read`, `cell.get`, `ui.inspect`. Drive: `ui.click|drag|move`,
+ `ui.cell.click|drag`, `ui.key|text`, `ui.edit.begin|text|commit|cancel`, `ui.ribbon`,
+ `ui.dialog|set|confirm|cancel`, `ui.message.dismiss`, `ui.menu.invoke`. Window/app: `ui.screenshot` (PNG,
+ animations settled), `ui.set`, `ui.resize`, `ui.focus`, `app.open|save|quit`. `ui.backstage {page:
+ home|new|open|info|saveAs|print|export|options|account|close}` and `ui.ribbonTab` are commands. Any other
+ method that is a command id runs that command — `crates/ui-egui/src/control.rs`, `docs/control-protocol.md`.
+ - **UI-only commands** reachable over `engine.execute`/MCP: `view.theme {mode: system|light|dark}`,
+ `view.darkMode`, `view.animations`, `view.interfaceScale {percent: 75–200|"auto"}`, `view.collapseRibbon`,
+ `options.autoComplete`, `app.language.set`, `ui.printSettings`, `ui.printSubmit` —
+ `crates/ui-egui/src/{lib,control}.rs`.
+ - **Recorded scripts** (Automate tab): `automate.recordActions {stop?, name?}` records the command journal,
+ `automate.newScript {name, commands}`, `automate.allScripts` (also reports a preserved `(VBA)` project as
+ never executed), `automate.runScript {name, trusted?}`, `automate.deleteScript {name}`. New in v0.11.0:
+ `automate.trustScripts {trusted?}` plus a trust gate — a workbook opened from a file that carries scripts
+ starts untrusted and `runScript` refuses until trusted for the session; steps may not call `file.*`/`link.*`
+ or pass a `path`, and they run inside a filesystem sandbox. Scripts are stored in the workbook as a hidden
+ defined name `_sc_scripts`, so they round-trip XLSX — `crates/engine/src/cmd/extra.rs`,
+ `crates/engine/src/io.rs::ScriptSandbox`.
+ - **Web build**: same UI crate via eframe web runner, `trunk build --release` → `dist/web`; URL flags
+ `?sample=`, `?webgl`, `?host=parent&author=`; preferences in `localStorage`; no TCP control channel (Open
+ uses the browser picker, Save downloads) — `apps/gridcraft-web/src/main.rs`.
+ - **Programmatic calls never open dialogs**: `file.open`/`file.save`/`data.getData`/`data.fromTextCsv` without
+ a file path return "missing `path`…: programmatic calls never open dialogs"
+ (`crates/engine/src/lib.rs::execute_headless`).
+ - **Interface languages**: 19 (`Language::ALL`): en, es, el, zh, ja, ko, ru, pt, de, cs, fr, sr, tr, pl, it,
+ uk, vi, nl, ro — up from 11. Seven catalogs are near-complete (ja ~963, ro ~976, el/it/pl/uk ~955, pt ~949
+ entries); cs ~524, nl ~505; the rest 390–450. Untranslated strings fall back to English; command ids,
+ document text, formulas and number formats stay English/en-US. `app.language.set {language|code}` switches
+ at runtime; the desktop follows the system language until a choice is saved (`crates/ui-egui/src/i18n.rs`,
+ `crates/ui-egui/src/i18n/*.rs`).
+
## Known limitations and not-implemented
- - **No write support** for `.xls`, `.xlsb`, `.ods` (import only); `.xls` import lacks fonts, fills,
- borders, charts, pictures, comments, CF, validation, freeze panes; `.xlsb`/`.ods` import values only,
- formula cells use their last saved values — `crates/engine/src/io.rs`, `docs/file-format-parity.md`.
- - **Encryption is read-only**: GridCraft cannot save a password-protected file, and encrypted `.xls`
- (FILEPASS) or RC4 files are refused — `docs/gaps.md` §7.
- - **Macros never run** and VBA is dropped on open unless saving as `.xlsm`/`.xltm` (kept byte-for-byte,
- not executed) — `docs/gaps.md` §21.
- - **Real-Excel fidelity is unproven**: nobody has confirmed Excel opens GridCraft's output without a
- repair prompt across a real corpus; some unmodelled XLSX content (slicers and slicer caches,
- timelines, the data model, query tables attached to tables, unmodelled `extLst`) is dropped on save.
- Slicers can be created in-app (thin front-end over filters: `crates/engine/src/cmd/slicer.rs`) but
- are not written to XLSX (`crates/xlsx/src/passthrough.rs` lists `slicer`/`slicerCache` as dropped).
- Chart sheets are preserved unchanged (written back in place) but cannot be viewed/edited/created —
- `docs/gaps.md` §1/§3, `docs/file-format-parity.md`, `docs/chart-parity.md`.
- - **Charts are shallow**: no 3-D types, axis scaling (min/max/log), error bars, data tables under
- charts, per-point formatting, full data labels; several kinds (stock, combo, treemap, sunburst,
- box-whisker, waterfall, funnel) "lose their kind in XLSX" (open PRs #158/#161); charts cannot be
- resized (#190; move and delete work) — `docs/chart-parity.md`, `docs/gaps.md` §4.
- - **Scale limits**: ~100 MB XLSX files fail to open (#175); recalculation is single-threaded;
- whole-sheet operations on huge ranges are bounded/refused (HTML export caps at 100,001 rows; CSV/TSV
- at 512 MiB — refused, not truncated) — `docs/gaps.md` §2, `crates/engine/src/io.rs`,
- `crates/xlsx/src/csv.rs` (`MAX_CSV_BYTES`).
- - **No Power Query editor** (only `data.getData` from CSV/TSV/JSON arrays of objects), no connections
- refresh, no data model, no `data.fromHtml`; no Solver Sensitivity/Population reports, AllDifferent
- constraints, Options dialog or Load/Save Model — `docs/gaps.md` §12/§13, `crates/engine/src/cmd/extra.rs`.
- - **HTML input**: a `.html` path opened via `file.open` is treated as delimited text, not parsed HTML
- (`io.rs` falls through to `read_csv`); the HTML-table import `docs/file-format-parity.md` mentions
- under Get Data is not backed by a reader (uncertain — see `crates/xlsx/src/`, no HTML reader exists).
- - **Non-Latin text**: PDF export can't render CJK/Arabic/Devanagari/Thai (standard PDF fonts only, no
- embedding); Arabic/Devanagari shaping missing in the grid; no bidi (right-to-left sheets mirror the
- grid, text stays LTR inside its box) — `docs/gaps.md` §5, `docs/localization-parity.md`,
- `crates/pdf/src/lib.rs`.
- - **One OS window**: `view.newWindow` opens a *copy of the workbook* as another document, not a
- second window; `view.arrangeAll` is a no-op — `crates/engine/src/cmd/extra.rs`, `docs/gaps.md` §20.
- - No VBA/Office Scripts/plugin or custom-function API; no collaboration/co-authoring; no mobile build;
- no accessible tree for the grid; no in-app AI (`docs/gaps.md` §16/§18/§21-23). Inserted icons lose
- their artwork on save (#551); no Office InkML import; 18 functions missing, 9 stubs (all CUBE,
- WEBSERVICE, FILTERXML) — `docs/gaps.md` §15/§17.
- - Repo docs contain stale duplicate lines (e.g. `docs/gaps.md` §19 still says CSV writes raw values
- and templates save with the workbook content type; both were fixed — `io.rs::csv` writes displayed
- text, `crates/xlsx/src/tests/roundtrip.rs` tests the template content types). Prefer generated docs
- (`docs/parity-checklist.md`) and code over prose.
+ - **Partial:** `.xls` import covers values, formulas, number formats, merges, sizes, names, calc and
+ sheet-display settings; no fonts, fills, borders, charts, pictures, comments, conditional formats,
+ validation, hyperlinks or freeze panes; chart/macro/dialog sheets are skipped, BIFF5 and FILEPASS files
+ refused. `.xlsb`/`.ods` import values and cached formula results only. None of the three is ever written
+ (`crates/xlsx/src/xls/mod.rs`, `crates/xlsx/src/{xlsb,ods}.rs`, `crates/engine/src/io.rs`).
+ - **Encryption is read-only**: Agile and Standard `.xlsx` open with a password; saves are always unencrypted
+ and have no save path, so Save asks where to go. RC4/Extensible and encrypted `.xls` are refused
+ (`crates/xlsx/src/crypto.rs`, `docs/gaps.md` §7).
+ - **Macros never run.** VBA is carried byte-for-byte only into `.xlsm`/`.xltm`; any other save drops it.
+ GridCraft's recorded scripts are not VBA (`crates/engine/src/io.rs` test `macros_are_saved_only_into_xlsm`).
+ - **Excel-repair-free output is unproven**: no real corpus and no Excel verification. Unmodelled content is
+ dropped when its owner is rewritten — slicers and slicer caches, timelines and caches, the data model, query
+ tables attached to tables, unmodelled `extLst`; chart sheets are passed through unchanged but cannot be
+ viewed, edited or created (`docs/gaps.md` §1/§3, `crates/xlsx/src/passthrough.rs`).
+ - **Slicers/timelines:** slicers can be created and clicked in-sheet but are not saved to XLSX; timelines are
+ catalogued (`insert.timeline`) but not implemented (`crates/engine/src/cmd/slicer.rs`,
+ `docs/parity-checklist.md`).
+ - **Charts are shallow:** no 3-D types, no axis scaling (min/max/log), no error bars, no per-point formatting;
+ data labels and gridlines are just booleans; chart sheets are not editable. Move and resize do work; all 2-D
+ kinds round-trip (`docs/chart-parity.md`, `crates/model/src/features.rs`, `crates/xlsx/src/chartex.rs`).
+ - **PivotTables:** no calculated items, `GETPIVOTDATA`, timelines, value/label filters, number grouping or
+ data-model pivots; XLSX round-trip has not been checked in Excel (`crates/engine/src/cmd/pivot.rs`,
+ `docs/function-parity.md`).
+ - **No Power Query or data model:** Get & Transform is only From Text/CSV (`data.fromTextCsv`), Get Data
+ (`data.getData`: CSV/TSV or a JSON array of objects) and From Table/Range; Queries & Connections lists
+ imported tables; Refresh All refreshes PivotTables only. No From Web, Stocks, Geography, Relationships or
+ Analyze Data (`crates/engine/src/cmd/extra.rs`, `docs/parity-checklist.md`).
+ - **Scale:** recalculation is single-threaded; the project reports ~100 MB workbooks failing to open (#175).
+ Whole-range work is capped and refuses with an error instead of hanging (see the file caps above;
+ `crates/calc/src/eval.rs` `MAX_CELLS = 16_000_000`).
+ - **Text:** no complex shaping (Arabic, Devanagari, Thai) and no bidi; a right-to-left sheet mirrors the grid,
+ but text stays LTR inside its box and printing/PDF are not mirrored. PDF export sets characters outside
+ WinAnsi in a predefined, **non-embedded** Adobe-GB1 CID font (STSong-Light / UniGB-UTF16-H): Chinese is
+ tested and no longer becomes `?`; Japanese/Korean coverage depends on that font (uncertain — inspect the
+ PDF); Arabic/Devanagari/Thai have no glyphs there (`crates/pdf/src/lib.rs`, `crates/pdf/src/tests.rs`,
+ `docs/localization-parity.md`, `docs/hardware-parity.md`).
+ - **One OS window:** `view.newWindow` clones the workbook into another document (title suffix `:2`);
+ `view.arrangeAll` does nothing (`crates/engine/src/cmd/extra.rs`).
+ - **Accessibility:** AccessKit is enabled but grid cells and the ribbon are not exposed as an accessible tree;
+ Check Accessibility (`review.checkAccessibility`) is a basic scan (missing alt text, merged cells, default
+ sheet names); no high-contrast mode (`docs/gaps.md` §18, `crates/engine/src/cmd/review.rs`).
+ - **No VBA/Office Scripts/add-in or custom-function API, no collaboration/co-authoring, no mobile build, no
+ in-app AI** (`docs/gaps.md` §16/§21–23).
+ - **Functions:** 18 of Excel's 523 names are absent (including GETPIVOTDATA, IMAGE, STOCKHISTORY, TRANSLATE,
+ DETECTLANGUAGE, CALL, REGISTER.ID, RTD, COPILOT, PY) and 9 are error stubs (7 CUBE functions, WEBSERVICE,
+ FILTERXML) — `docs/function-parity.md`, `crates/functions/src/info.rs`.
+ - **HTML is export-only:** opening `.html`/`.htm` with `file.open` falls through to the CSV/TSV reader, so a
+ saved web page opens as delimited text, not as a table (`crates/engine/src/io.rs::open_plain`).
+ - **Objects:** inserted icons lose their artwork on XLSX save (#551); Office InkML is not imported; pictures
+ must be PNG/JPEG (`docs/gaps.md` §17, `crates/engine/src/cmd/insert.rs`).
+ - **Save semantics:** only `.xlsx` and `.json` saves clear the dirty flag and become the document's file;
+ `.csv/.tsv/.html/.pdf` are exports that leave the workbook dirty. `file.close {force:true}` and the
+ control-channel `app.quit` discard unsaved changes without asking (`crates/engine/src/cmd/file.rs::save`,
+ `crates/ui-egui/src/control.rs`, `crates/ui-egui/src/lib.rs::guard_window_close`).
+ - **Stale repo prose:** README's key-tip paragraph still says "initial Home-only"; `docs/gaps.md` §5 (PDF
+ fonts), §8 (PivotCharts/slicers), §19 (CSV raw values, template content type) and
+ `docs/control-protocol.md`'s language list (11) lag the code; `docs/ui-parity.md` still says 59
+ shortcuts/364 keytips. Prefer code and the generated `docs/parity-checklist.md`.
## How to operate
- ### macOS launch and flags
- - Launch: `open -a GridCraft`; open a document: `open -a GridCraft /path/book.xlsx` (Finder
- double-click and `open` arrive as Apple events — `apps/gridcraft/src/apple_events.rs`); files dropped
- on the window open too (`apps/gridcraft/src/main.rs`).
- - Binary (for flags/environment): `/Applications/GridCraft.app/Contents/MacOS/GridCraft [--control PORT]
- [--sample budget|sales|grades] [files…]` — `apps/gridcraft/src/main.rs` (usage doc-comment).
- `--sample` without a name opens `sales`; `--version` prints `GridCraft 0.10.0`.
- - Environment: `GRIDCRAFT_CONTROL_PORT=<port>` (same as `--control`), `GRIDCRAFT_NO_PREFS=1` (no
- settings/logs written — the mode for agents' test runs), `RUST_LOG` for log directives,
- `GRIDCRAFT_NO_NATIVE_MENU` to skip the native menu.
- - Logs: `~/Library/Application Support/GridCraft/logs/gridcraft.log` (rotated `.1`/`.2`, 16 MiB cap)
- next to `ui.json`/`prefs.json` — README.md §Logs.
+ ### macOS launch, flags, environment
+ - `open -a GridCraft`; `open -a GridCraft book.xlsx` — Finder double-clicks and `open` arrive as Apple events,
+ and files dropped on the window open too (`apps/gridcraft/src/apple_events.rs`,
+ `apps/gridcraft/src/main.rs`).
+ - The binary takes only these flags: `/Applications/GridCraft.app/Contents/MacOS/GridCraft [--control PORT]
+ [--sample budget|sales|grades] [files…]`; `--version` prints `GridCraft 0.11.1`. There is no other CLI
+ surface in the bundle (`apps/gridcraft/src/main.rs`).
+ - Environment: `GRIDCRAFT_CONTROL_PORT=<port>` (same as `--control`), `GRIDCRAFT_NO_PREFS=1` (reads/writes no
+ `ui.json`/`prefs.json` and keeps no log file — the mode for agent test runs), `GRIDCRAFT_NO_NATIVE_MENU`,
+ `RUST_LOG` (env_logger-style directives; default gridcraft crates `info`, everything else `warn`).
+ - Settings and logs live under `Library/Application Support/GridCraft/` in the user's home: `ui.json`,
+ `prefs.json`, and `logs/gridcraft.log`, rotated to `.1`/`.2` at each start, 16 MiB cap
+ (`apps/gridcraft/src/{main,logging}.rs`, README.md §Logs).
- ### The CLI (install separately)
- The app bundle ships no CLI; install with `cargo install --path apps/gridcraft-cli` from a source
- checkout, or the release zip `gridcraft-cli-<ver>-macos-universal.zip` (README.md Downloads,
- `docs/cli.md`). Common calls:
+ ### The CLI (installed separately)
+ The bundle ships no CLI. Install with `cargo install --path apps/gridcraft-cli` from a source checkout, or the
+ release zip `gridcraft-cli-<ver>-macos-universal.zip` (README.md Downloads; `docs/cli.md`).
```sh
gridcraft-cli info book.xlsx [--json]
- gridcraft-cli convert in.xls out.xlsx [--sheet Sales] # xlsx/csv/tsv/json/html by extension
+ gridcraft-cli convert in.xls out.xlsx [--sheet Sales] # .xlsx/.csv/.tsv/.json/.html/.pdf by extension
gridcraft-cli eval '=XLOOKUP("b",{"a","b"},{1,2})' # → 2
gridcraft-cli cat book.xlsx --range A1:F20 [--formulas|--csv]
gridcraft-cli run --in book.xlsx --cmd 'cell.set={"cell":"D2","input":"=B2*C2"}' \
--cmd 'edit.fillDown={"range":"D2:D20"}' --out book.xlsx --print A1:D5
gridcraft-cli check corpus/ --json # calc oracle + XLSX round-trip fidelity
gridcraft-cli commands --search chart # every command and its params doc
gridcraft-cli functions --category statistical # function list with signatures
gridcraft-cli send 7979 engine.execute '{"command":"home.bold","params":{"range":"A1:C1"}}'
```
- A `--cmd` is `id`, `id={json}` or `id {json}`; `--script` reads one step per line in the same forms or
- as `{"command":…, "params":…}` JSON objects (blank/# lines skipped). The first failing step stops the
- run with a non-zero exit (`docs/cli.md` §`run`).
+ - `run` flags: `--in FILE | --sample NAME`, repeated `--cmd`, `--script FILE`, `--out FILE`, repeated `--print
+ RANGE|used`, `--sheet`, `--quiet`, `--csv`, `--formulas`. A `--cmd` is `id`, `id={json}` or `id {json}`; a
+ script has one step per line in the same forms or as `{"command":…,"params":…}` objects; blank and `#` lines
+ are skipped. The first failing step stops the run with a non-zero exit
+ (`apps/gridcraft-cli/src/main.rs::run`).
+ - `convert` refuses an unknown output extension; `.pdf` goes through `file.exportPdf` and reports bytes/pages.
+ ### MCP
+ ```sh
+ claude mcp add gridcraft -- gridcraft-cli mcp # headless in-process session
+ claude mcp add gridcraft-app -- gridcraft-cli mcp --connect 7979 # drive the visible app
+ ```
+ Results are one text block holding pretty JSON; failures set `isError: true`. Unknown tools, missing/wrong
+ arguments are JSON-RPC `-32602`; malformed JSON `-32700`; unknown methods `-32601`
+ (`crates/mcp/src/server.rs`, `docs/mcp.md`). The remote backend uses a 30 s socket timeout: after a timeout,
+ re-read the state instead of retrying blind (`crates/mcp/src/backend.rs`).
+
### Control channel (drive the running app)
```sh
/Applications/GridCraft.app/Contents/MacOS/GridCraft --sample sales --control 7979
# then, newline-delimited JSON to 127.0.0.1:7979:
{"id":1,"method":"engine.execute","params":{"command":"cell.set","params":{"cell":"B2","input":"=SUM(A1:A9)"}}}
{"id":2,"method":"ui.screenshot","params":{"path":"shot.png"}}
```
- `ui.inspect` first (ribbon tab, editor, open dialog, Backstage page, message, grid rect) then act with
- `ui.click`/`ui.drag`/`ui.key`/`ui.text`/`ui.edit.*`/`ui.dialog*`; `ui.screenshot` returns PNG (needs a
- visible window). `app.open {path, password?}` opens files; `file.open` on a password-protected
- workbook returns an error without the password, never a dialog (`docs/control-protocol.md`).
-
- ### MCP (for agent tool-calling)
- ```sh
- claude mcp add gridcraft -- gridcraft-cli mcp # headless in-process session
- claude mcp add gridcraft-app -- gridcraft-cli mcp --connect 7979 # drive the visible app
- ```
- Protocol `2025-06-18` (older clients answered in their own version). Results are one pretty-JSON text
- block; failures set `isError: true` (`docs/mcp.md`).
+ - Run `ui.inspect` first (ribbon tab, editor, open dialog, Backstage page, message, grid rect, scroll, perf)
+ and act on what it shows; `ui.inspect` also reports `documents` and the active index. `ui.screenshot` needs
+ a presented frame: if the window is hidden/minimised it answers "no frame was presented before the
+ screenshot timeout; make the window visible, use `ui.focus`, then retry" (`crates/ui-egui/src/control.rs`).
+ - Limits: 64 MiB per request line; 10,000 queued synthetic pointer events; the server waits 60 s for the UI,
+ then reports a timeout. A request the client abandoned is dropped unrun; if the app had already started it,
+ the reply says the outcome is unknown — read the cell/state before retrying
+ (`apps/gridcraft/src/control_server.rs`, `crates/ui-egui/src/control.rs`).
+ - The port is unauthenticated and binds localhost only. Do not expose it; any local process can run every
+ command.
+ - `ui.dialog.confirm` sends exactly what the dialog's OK sends (fixed in v0.11.0); `ui.dialog.cancel` closes
+ it; dialogs without an OK command return "this dialog has no OK command; drive it with its own commands".
- ### Document model, save/export flow
- - One window, multiple documents; switch with the Window menu / `window.activate {index}`; hide with
- `view.hideWindow`. Sheet tabs at the bottom. File opens the Backstage (pages: home, new with the
- three samples, open with recents, info, saveAs, print, export, options, close — `docs/ui-parity.md`).
- - Save (`file.save`/`file.saveAs`/⌘S/⇧⌘S): `.xlsx` by default; the extension picks the format
- (`.csv`, `.tsv`, `.json`, `.html`, `.pdf`). `.pdf` saves a print of the sheet (Page Setup applies)
- and does not mark the workbook saved. `file.saveBytes {format}` returns base64 without writing.
- - Exports: `file.exportPdf {path?}` (no path → base64 + page count), `file.exportCsv {path, sheet?}`,
- `file.exportHtml {path}`, `file.printPreview`, and on the desktop `ui.printSettings` /
- `ui.printSubmit` (native printer jobs via craft-print; poll `ui.printSettings` until not busy).
+ ### Document model and save/export flow
+ - One window, several documents; the Window menu (`view.switchWindows`, `window.activate`, `view.hideWindow`)
+ switches/hides them. Sheet tabs at the bottom. File opens the Backstage with 9 pages: home, new (three
+ samples), open (recents), info, saveAs, print, export, options, account (`crates/ui-egui/src/backstage.rs`).
+ - Save (`file.save`, `file.saveAs`, ⌘S, ⇧⌘S): the extension picks the format — `.xlsx` (default), `.csv`,
+ `.tsv`, `.json`, `.html`, `.pdf`; a `.pdf` save exports the sheet and leaves the document dirty, unsaved and
+ pointing at its old file. `file.saveBytes {format}` returns base64 without writing (formats:
+ xlsx/csv/tsv/json/html) (`crates/engine/src/cmd/file.rs`).
+ - Exports: `file.exportPdf {path?, sheets?: active|all}` (no path → base64 + page count), `file.exportCsv
+ {path, sheet?}`, `file.exportHtml {path}`, `file.printPreview {page?, sheets?}`, and on the desktop
+ `ui.printSettings` / `ui.printSubmit` (native jobs via craft-print; poll `ui.printSettings` until not busy)
+ (`crates/engine/src/cmd/{file,print}.rs`, `docs/control-protocol.md`).
+ - Opening a `.xls`/`.xlsb`/`.ods` produces a document titled `<name> (imported).xlsx` with no save path;
+ opening an already-open path just switches to it; a password-decrypted workbook also has no save path
+ (`crates/engine/src/cmd/file.rs::open`).
### Keyboard essentials (verified)
- - ⌘S save, ⇧⌘S Save As, ⌘O open, ⌘N new, ⌘W close, ⌘Z/⇧⌘Z undo/redo, ⌘Y redo — `crates/ui-egui/src/ribbon.rs::shortcut`.
- - ⌘B/⌘I/⌘U bold/italic/underline; Ctrl+1 Format Cells; ⌘D fill down; ⌘R fill right; ⌘E Flash Fill;
- ⌘T table; ⌘K insert link; Ctrl+Shift+L filter; F2 edit; Ctrl+Shift+number formats; Ctrl+Shift+&
- outline border — `ribbon.rs`, `keymap.rs`, `grid.rs`. 59 command shortcuts plus grid/editor keys;
- Excel-for-Mac variants mapped in `keymap.rs`.
- - Key tips: press **F10** (macOS; Fn+F10 if needed; letters also work with left Alt held), then
- sequence letters; a 364-sequence table covers all tabs (per-control badges) —
- `crates/ui-egui/src/keytips/keytips.json` (364 entries), `docs/ui-parity.md` §Keyboard.
- (README's "initial Home-only key tips" prose is stale.)
+ - 68 shortcut bindings on command specs (65 at v0.10.0), plus a wider keymap table for number formats,
+ borders, F-keys and Alt/Option combinations. Essentials: ⌘S save, ⇧⌘S Save As, ⌘O open, ⌘N new, ⌘W close, ⌘Z
+ undo, ⌘Y redo; ⌘B/⌘I/⌘U; ⌘1 Format Cells (Ctrl+1 on Windows); ⌘D fill down, ⌘R fill right; ⌃E Flash Fill (⌘E
+ centres); ⌘T table; ⌘K link; Ctrl+Shift+L filter; F2 edit; Ctrl+Shift+4 currency; Ctrl+Shift+7 outline
+ border; Ctrl+` show formulas; F9 calculate; Ctrl+[ / Ctrl+] select precedents/dependents (add Shift for
+ every level); Ctrl+6 hide/show objects — `crates/ui-egui/src/{ribbon,keymap,grid}.rs`, `docs/ui-parity.md`
+ §Shortcut audit.
+ - Key tips: F10 on any platform (Fn+F10 if needed) or left Alt on Windows/Linux/BSD, then sequence letters;
+ 365 sequences cover every ribbon tab, including `F` → File view. README's "initial Home-only" sentence is
+ stale (`crates/ui-egui/src/keytips/keytips.json`, `crates/ui-egui/src/keytips.rs`).
+ - The macOS native menu has Tools ▸ Command Palette… (⌘⇧P), which opens the `commandSearch` dialog
+ (`apps/gridcraft/src/native_menu.rs`, `crates/ui-egui/src/dialogs.rs`).
## Working with an AI agent
- **Prefer headless over GUI.** For "read/write/convert/verify" jobs use `gridcraft-cli` (or MCP) on
- files — no window, no prefs, deterministic. Drive the GUI over the control channel only when the task
- is genuinely about the UI (dialogs with no command equivalent, visual state, screenshots, printing).
- Never drive the GUI blind: `ui.inspect` / `ui.screenshot` before and after, and act on what they show.
+ **Prefer headless over GUI.** For read/write/convert/verify jobs use `gridcraft-cli` (or MCP) on files: no
+ window, no preferences (set `GRIDCRAFT_NO_PREFS=1` if you launch the app), deterministic. Drive the GUI over
+ the control channel only when the task is genuinely about the UI: dialogs with no command equivalent, visual
+ state, screenshots, native printing. Never drive blind — `ui.inspect`/`ui.screenshot` before and after, and
+ act on what they show.
Legitimate agent work:
- - **Prepare inputs**: build `.xlsx` from commands (`gridcraft-cli run --script steps.jsonl`), convert
- `.xls`/`.xlsb`/`.ods`/CSV/TSV into `.xlsx` (`convert`), or generate CSV/TSV/JSON and import with
- `data.getData`. All GridCraft inputs must end up in a supported read format (`io.rs::FileKind`).
- Use `csvAsText` when leading zeros and digit-only identifiers must survive import.
- - **Interpret/repair project files**: XLSX is the native format and is documented in code
- (`crates/xlsx/src/`); the `.json` workbook is a debug serialization of the internal model — treat it
- as unstable, never a long-lived interchange format. "Repair" means: open in GridCraft, inspect
- (`info`, `check`), fix with commands, save as `.xlsx`. Do not promise recovery of parts GridCraft
- doesn't model (slicers, timelines, data model, chart-sheet editing).
- - **Verify exports honestly**: run `gridcraft-cli check` on round-tripped files, compare `cat`/`info`
- output against expectations, check PDF page counts. A zero exit from `check` means *GridCraft*
- agrees with itself and the cached values — it does not prove Excel will open the file cleanly
- (explicitly unverified, `docs/gaps.md` §1).
- - **Scripts**: recorded scripts (`automate.*`) capture and replay commands; they are a convenience
- layer over the same command ids, not a separate API.
+ - **Prepare inputs.** Build `.xlsx` from commands (`gridcraft-cli run --script steps.jsonl`), convert
+ `.xls`/`.xlsb`/`.ods`/CSV/TSV into `.xlsx`, or generate CSV/TSV/JSON and import with
+ `data.fromTextCsv`/`data.getData`. Use `file.open {csvAsText:true}` when leading zeros and digit-only
+ identifiers must survive. Remember an import opens as an unsaved "(imported).xlsx".
+ - **Interpret/repair files.** XLSX is native and documented in code (`crates/xlsx/src/`); the `.json` workbook
+ is a debug serialization, not a long-lived interchange format. "Repair" means: open in GridCraft, inspect
+ (`info`, `check`), fix with commands or formulas, save as `.xlsx`. Do not promise recovery of parts
+ GridCraft does not model (slicers, timelines, data model, chart-sheet editing).
+ - **Verify exports honestly.** Run `gridcraft-cli check` on round-tripped files, compare `cat`/`info` output
+ against expectations, check PDF page counts and links. A zero exit from `check` means GridCraft agrees with
+ itself and with the file's cached values; it does **not** prove Excel will open the file without a repair
+ prompt (explicitly unverified, `docs/gaps.md` §1).
+ - **Scripts.** Recorded scripts are a convenience layer over the same command ids, not a separate API. A
+ workbook received from elsewhere carries its scripts as a hidden name; nothing runs until the workbook is
+ trusted (`automate.trustScripts`, or `runScript {trusted:true}` once), and script steps cannot touch files
+ or open links — run those commands directly.
Must NOT attempt:
- - Writing `.xls`/`.xlsb`/`.ods` or saving password protection; expecting macros to run; expecting
+ - Writing `.xls`/`.xlsb`/`.ods`, or saving password protection; expecting macros to run; expecting
slicers/timelines to survive a save; assuming HTML import; assuming multiple windows per workbook.
- - Editing `.xlsx` with raw XML surgery on files that contain unmodelled parts (slicer caches, VML,
- custom XML) — GridCraft's own writer is the only supported path, and even it drops parts it doesn't
- model once it rewrites them; prefer read-only analysis of such files.
- - Treating formula input as locale-independent without checking `docs/formula-input.md` (canonical
- English syntax is what files and APIs use).
- - Exporting non-Latin text to PDF and expecting it to appear (the 14 standard PDF fonts cannot).
+ - Raw XML surgery on `.xlsx` files that contain unmodelled parts (slicer caches, VML, custom XML): GridCraft's
+ own writer is the supported path, and even it drops parts it does not model once it rewrites them. Prefer
+ read-only analysis of such files.
+ - Treating formulas as locale-independent without checking `docs/formula-input.md`: Spanish input/display
+ exists, but files, commands and APIs use canonical English names and `,` separators.
+ - Promising PDF output for Arabic, Devanagari or Thai (no glyphs in the CID font, no shaping); verify CJK
+ output visually first.
+ - Calling `app.quit` or `file.close {force:true}` while a workbook has unsaved changes: both discard them
+ silently. Save first.
## Verification protocol (alpha discipline)
- 1. **Throwaway round-trip before any real work**: new workbook → the exact edit → save `.xlsx` →
- reopen in GridCraft → `gridcraft-cli check file.xlsx`. Add the source file itself to `check` when
- testing formula fidelity (it recalculates and compares against Excel-cached values).
- 2. **Never trust a silent export**: CSV/HTML/PDF exports are one-shot (`file.exportCsv` etc.) with no
- diff UI. Re-open or re-import the artifact, or inspect it (`cat`, PDF page count, open the PDF in
- another reader). Remember PDF export cannot render non-Latin text.
- 3. **Version and back up**: keep the previous version of any workbook you edit; GridCraft is alpha and
- autosave/recovery has not been soaked (`docs/gaps.md` §10). For agent runs, files are safer than
- GUI state — save after each logical step (`--out`) rather than at the end.
- 4. **Update between projects, not during**: the app auto-updates via the ArtCraft Launcher; update,
- then re-run your round-trip before a new task (`README.md` Downloads; repo is the source of truth
- for what a given version can do).
+ 1. **Throwaway round-trip before real work**: new workbook → the exact edit → save `.xlsx` → reopen (or
+ `gridcraft-cli info`/`cat`) → `gridcraft-cli check file.xlsx`. Add the source file itself to `check` when
+ testing formula fidelity: it recalculates and compares against Excel-cached values.
+ 2. **Never trust a silent export.** CSV/HTML/PDF are one-shot exports with no diff UI. Re-read or re-import
+ the artifact (`cat`, `--json`, PDF page count/links, open the PDF elsewhere). Remember PDF export cannot be
+ assumed to render Arabic/Devanagari/Thai.
+ 3. **Version and back up.** Keep the previous copy of any workbook you edit; autosave covers only workbooks
+ that already have a file, and there is no crash-recovery soak (`docs/gaps.md` §10). For agent runs, save
+ after each logical step (`--out`) rather than at the end.
+ 4. **Update between projects, not during.** The app auto-updates through the ArtCraft Launcher; update, then
+ re-run your round-trip before a new task. The repo at a tag is the source of truth for what a version can
+ do.
+ 5. **After a control/MCP timeout, re-read state** (`cell.get`, `sheet.read`, `document.inspect`) before
+ retrying: a queued request is dropped, but one already taken may still finish.
+ 6. **Trust deliberately.** Do not call `automate.trustScripts` on a workbook you did not create unless the
+ user asked you to run its scripts.
## Sources & evidence
- - Repo: https://github.com/storytold/gridcraft — inspected at tag **v0.10.0**, commit
- `2ebdf164c2c9c536122e0a2cffe79078b5c818ac` ("Release 0.10.0", 2026-10-21). The installed app reports
- `GridCraft 0.10.0` (the bundle's `CFBundleShortVersionString` is stale — trust the app/CLI version).
- If your installed version differs, verify against your installed version.
- - Date of inspection: 2026-10-22. Repo treated read-only.
- - Key files read: `README.md`, `ROADMAP.md`, `AGENTS.md`, `Cargo.toml`, `docs/{cli,mcp,control-protocol,
- parity-checklist,function-parity,chart-parity,file-format-parity,gaps,ui-parity,localization-parity,
- hardware-parity,roadmap,cell-pictures,formula-input}.md`, `apps/gridcraft-cli/src/{main.rs,check.rs}`,
- `apps/gridcraft/src/main.rs`, `apps/gridcraft-web/src/main.rs`, `crates/engine/src/{io.rs,lib.rs,
- sample.rs,catalog.rs}`, `crates/engine/src/cmd/*.rs`, `crates/xlsx/src/lib.rs`, `crates/xlsx/src/csv.rs`,
- `crates/xlsx/src/crypto.rs`, `crates/xlsx/src/passthrough.rs`, `crates/mcp/src/tools.rs`,
- `crates/ui-egui/src/{control.rs,keymap.rs,ribbon.rs,i18n.rs,keytips/keytips.json}`,
- `crates/model/src/features.rs`, `crates/pdf/src/lib.rs`.
- - Shared library: `craft-print` is pinned from `storytold/craft-libs`
- (`rev d27646a5f7c8b4fc85d9dd3213b8c88b58e0088d`, verified present in the local checkout) — used for
- native print submission; PDF generation stays in GridCraft (`apps/gridcraft/Cargo.toml`,
- `craft-libs/crates/print`).
- - Deeper per-topic reference: `docs/target-app-parity.md` (authoritative assessment),
- `docs/gaps.md` (ranked work list), `docs/architecture.md` (layering), `docs/roadmap.md` (beta gates).
+ - Repo: https://github.com/storytold/gridcraft — inspected at tag **v0.11.1**, commit
+ `ac8da7d49cbeb4be6aa1ad5869f3d29fe2da8cd9` ("Release 0.11.1", 2026-10-22). The installed
+ `/Applications/GridCraft.app` reports `0.11.1` and its binary prints `GridCraft 0.11.1`;
+ `GridCraftBuildCommit` matches the tag. If your installed version differs, verify against it.
+ - Date of inspection: 2026-10-23. Repo treated read-only (not built or modified).
+ - Key files read: `README.md`, `ROADMAP.md`, `AGENTS.md`, `Cargo.toml`,
+ `docs/{cli,mcp,control-protocol,parity-checklist,function-parity,chart-parity,file-format-parity,gaps,ui-parity,localization-parity,hardware-parity,target-app-parity,cell-pictures,formula-input,xlsb-import,ods-import}.md`,
+ `apps/gridcraft-cli/src/{main.rs,check.rs}`,
+ `apps/gridcraft/src/{main.rs,control_server.rs,logging.rs,apple_events.rs,native_menu.rs}`,
+ `apps/gridcraft-web/src/main.rs`, `crates/engine/src/{io.rs,lib.rs,sample.rs,catalog.rs}`,
+ `crates/engine/src/cmd/*.rs`,
+ `crates/xlsx/src/{lib.rs,read.rs,write.rs,csv.rs,crypto.rs,passthrough.rs,pivot.rs,chart.rs,chartex.rs,package.rs}`,
+ `crates/xlsx/src/{xls,xlsb.rs,ods.rs}`, `crates/pdf/src/{lib.rs,tests.rs}`,
+ `crates/mcp/src/{tools.rs,server.rs,backend.rs}`,
+ `crates/ui-egui/src/{control.rs,keymap.rs,ribbon.rs,lib.rs,dialogs.rs,grid.rs,panes.rs,i18n.rs,backstage.rs}`,
+ `crates/ui-egui/src/i18n/*.rs`, `crates/ui-egui/src/keytips/keytips.json`,
+ `crates/model/src/{features.rs,workbook.rs,sheet.rs}`, `crates/functions/src/`, `crates/calc/src/eval.rs`,
+ `apps/gridcraft/Cargo.toml`.
+ - Shared library: `craft-print` pinned from `storytold/craft-libs` at revision
+ `d27646a5f7c8b4fc85d9dd3213b8c88b58e0088d` (`apps/gridcraft/Cargo.toml`) for native print submission; PDF
+ generation stays in GridCraft (`crates/pdf`).
+ - Deeper per-topic reference (repo docs): `docs/target-app-parity.md` (authoritative assessment),
+ `docs/gaps.md` (ranked work list, partly stale), `docs/architecture.md` (layering), `docs/roadmap.md` (beta
+ gates), `docs/parity-checklist.md` (generated from the catalog).