# The check tools' contract > Generated by `npm run gen:ai-pack` (scripts/ai-pack.ts) — do not edit. The published tools are `tool/axmo-check.mjs` and `tool/axmo-shape-check.mjs`, built from > the product's own code (`npm run build:check-bundle`); the repository's `npm run axmo:check` runs the same code and > answers the same (`check:check-bundle`). So does `tool/axmo-workbook.mjs` (`npm run axmo:workbook`, AGL9-23). This > contract is public (owner, AGL9-20). ``` node axmo-check.mjs [--depth definition|bindings|complete] [--values] [--series [--at "=[;=]..."]]... [--strict] [--verbose] node axmo-shape-check.mjs node axmo-workbook.mjs extract [--out ] [--depth bindings|definition|complete] [--verbose] node axmo-workbook.mjs apply -o [--strict] [--force] [--verbose] ``` - **What `axmo-check` runs:** the Import dialog's own handler, over an in-memory stand-in for Excel: the real read, build, compile and update. It prints exactly the report the dialog shows (`message-forms.md`). - **Depth:** as the dialog: `complete`, unless the file declares no data source, axis definition or output range (then `definition`). - **Exit code:** `0` the report holds no error; `1` it holds one; `2` the tool failed (a file it cannot read, a usage error, an unknown `--series` name, an `--at` the series cannot answer). `--values` and `--series` never change `0` or `1`. **`--strict`:** a warning in the report makes the exit code `1` too — a source that loaded nothing, a row skipped — and the report's last line is `(checked with --strict: a warning counts as an error)`. Without it, a warning never changes the exit code. - **`--at`** follows the `--series` it narrows: `;` separates the pairs (it is reserved in every name), and each pair splits at its first `=`. The element is matched as a source label is — trimmed, case-insensitive. A dimension not named prints all of its elements. A dimension the series is not on, or an element not in it: exit `2`. Each `--series` is its own request: a series named twice, each with its own `--at`, prints two blocks, in order. A second `--at` after one `--series` is a usage error (exit `2`). - **The stand-in never pretends:** what it cannot do as Excel would is an Update line `error[CHECK_HOST_SHORTFALL]: …`, so such a report is never clean. Formulas written to cells are not calculated. - **Standard output** is the report (then the results asked for), written whole before the tool exits, through a pipe or to a file; logs go to standard error with `--verbose`, else nowhere. - **`axmo-shape-check`** validates the file against the JSON Schema only — shape: unknown and missing keys, wrong types and values, wrong structure. It does not check names, references, formulas, loaders, data or values, and gives no line numbers. Exit `0` / `1` / `2` as above. ## `axmo-workbook`: the model of an Axmo workbook, out and back in (AGL9-23) - **`extract `** writes `model.yaml` — the workbook's model at `bindings` depth (or `--depth definition`), byte for byte what the add-in's Export writes from the same workbook — and `dsdata.yaml`, every `DS.DATA` call in the workbook's cells (`message-forms.md`), into `--out ` (default: the current folder). It reads the workbook as last saved. **`--depth complete`** (AGL9-25) also writes the `data:` section the add-in's Export writes at Complete — every input range's cells, partitioned, with the tables' and names' recipe, as the add-in writes them from the same cells (`check:workbook-tools`) — from the cells as the file holds them: a formula as its saved value, a date as its serial number. The next Update in Excel calculates from the cells as they are then. A formula cell the file holds no saved value for is written empty, and a line says so; a source whose reference the file does not resolve leaves the section empty, as the add-in's Export does, with a line that says why. **The partition** is the add-in's own (`partitionRects`, AGL9-27): on each sheet, every source's top row and the row after its bottom cut the covered cells into bands, and a band whose covered columns differ from the band above starts new rectangles. So of two sources on one sheet, the taller is split where the shorter ends: `A1:F21` beside `I1:J4` is written `A1:F4`, `A5:F21` and `I1:J4`. Each cell is written once, and an import writes the cells back as they were. - **`apply -o `** writes a NEW workbook whose model is the file's, and never changes its input. **`-o`** naming one of its inputs is refused, with or without `--force`; naming a file that exists is refused unless `--force` is given, so the user's original is never replaced by mistake (AGL9-27). Each is one line on standard error, exit `2`, and nothing is read or written: `axmo-workbook: -o names an input file ({book.xlsx}); apply never writes over its input — name a new file.` · `axmo-workbook: {new.xlsx} already exists; apply replaces a file only with --force — name a new file, or add --force.` In order: the file is checked as `axmo-check` checks it, at `bindings` — an error in its **Import** or **Compile** section refuses; its **Update** section is printed in full and never refuses, because the check's stand-in holds none of the workbook's cells, tables or names (sources there read as missing or empty). Then every data source's reference is checked against the workbook itself — its sheets, tables and defined names, no cell read — and one the workbook does not hold refuses. The model is written as the definition Import would store, with a higher revision; the saved values are removed (the next Update calculates every value); the names of objects the new model does not have are removed, as Import removes them (every Input Range tracker; every `AXMO_` name no new Output Range uses). An Output Range whose sheet and anchor are unchanged keeps what it last wrote, so the next Update clears it as usual; a moved, removed or renamed range's old cells stay, and the report names them. Every `DS.DATA` call is compiled against the new model. Every loader is run against its source's cells as the workbook holds them (no cell is written), and a column it names that the workbook's source does not have is a warning at the loader; a source whose header row holds a formatted number or date is not read, since its displayed text is Excel's to compute. An Output Range anchored on a sheet the workbook does not hold is a warning at the range: Update does not create a sheet (measured, AGL9-27), so the range is not written until the sheet is added in Excel. - **`apply` writes no cell, and rewrites no formula.** A file's `data` section is not applied. Every part of the workbook but its Axmo parts, `xl/workbook.xml`'s names and the package's own content-type and relationship entries is copied as it was read, byte for byte. - **`apply`'s report:** the check's report, then the line `Apply`, then `apply`'s own lines (`message-forms.md`), then what was removed, then the closing line: open the new workbook in Excel and press **Update**. - **Exit code:** `0` written; `1` refused — a coded error line, or, with `--strict`, a warning among `apply`'s own lines (a broken `DS.DATA` call, a range whose old cells stay, a loader column, a range's missing sheet, data not applied) — and nothing written; `2` the tool failed (a usage error, a file it cannot read or write). `--strict` does not read the check's Update section. With `--strict`, written or refused, the report's last line is `(checked with --strict: a warning counts as an error)`. - **Never save an Axmo workbook with a general library** (openpyxl, pandas, ExcelJS, SheetJS): each removes the model. Change it through Axmo, or through `apply`.