Office is moving to an A3S-owned Rust engine for Word, Spreadsheet, and
Presentation documents. The native engine now includes bounded package
admission, byte-preserving XML, content types, a safe relationship graph,
stable selectors, semantic get, query, text, outline, and stats
reads, bounded issues reports, safe blank-document creation, and
loss-preserving text assignment plus scoped literal/regex replacement for Word,
Spreadsheet, and Presentation. Matches may span rich-text runs without
flattening their formatting. Typed rich-text mutation covers bold, italic,
none/single/double underline, baseline/superscript/subscript, font family,
exact centipoint font size, RGB text color, and horizontal alignment. Word and
Spreadsheet also support an explicit single-strikethrough boolean;
Presentation rejects that property with a typed error instead of silently
dropping it. Word and Presentation additionally share a portable 17-color
highlight palette, display-only text case, and conservative BCP-47 primary
language tags. Word alone supports explicit double strikethrough; Spreadsheet
and Presentation reject it through format-specific typed errors.
Word and Presentation apply character properties to run paths and alignment to
paragraph paths; Spreadsheet applies the same contract to cells or bounded A1
ranges,
creating and deduplicating OOXML font and cell-style records when necessary. It
also exposes a separate typed Spreadsheet cell-presentation contract for
number formats, solid RGB fill or explicit fill removal, cardinal and diagonal
borders, vertical alignment, wrap text, rotation, indentation, shrink-to-fit,
and reading order. These properties apply to a cell or bounded rectangular
range, compose atomically with content and text formatting, preserve unknown
style data, and deduplicate number-format, fill, border, and cell-style
records. Spreadsheet merged cells use a separate typed contract: rectangular
A1 ranges are normalized, exact repeated merges are idempotent, and unmerge
requires one exact existing range. Geometric overlaps and ListObject table
intersections fail closed. Semantic reads expose stable mergeCell nodes and
anchor metadata without materializing every blank covered cell. A third typed
Spreadsheet contract owns data-validation rules for list, whole-number,
decimal, date, time, text-length, and custom-formula constraints. One rule may
target multiple disjoint normalized A1 ranges and carries typed comparison
operators, prompt/error messages, alert style, blank handling, and
list-dropdown state. Inline lists are quoted safely, ISO dates and clock times
are normalized to Spreadsheet serial values, and overlapping rule ranges fail
atomically. Semantic reads expose stable /Sheet/dataValidation[N] nodes,
annotate observed and virtual blank cells, and project inert metadata into
HTML/SVG. Native add/set/remove, batch, exact replay, CLI, Rust, and standard
MCP use the same closed contract while retaining strict/transitional
SpreadsheetML and unknown attributes. It stores validation formulas but does
not evaluate them.
A separate typed Spreadsheet conditional-format contract owns comparison and
formula rules; text, rank, average, duplicate/unique, blank/error, and time
predicates; data bars; two- or three-color scales; and standard three-, four-,
or five-icon sets. Classic rules apply a differential solid fill, font color,
and bold state. Visual rules use typed thresholds, colors, and display flags.
One rule can cover multiple disjoint A1 areas and can stop later rule
evaluation. Semantic reads expose stable /Sheet/cf[N] nodes and selectors
such as conditionalFormatting[type=dataBar]. Rust, versioned batch, CLI,
standard MCP, exact replay, and the Office Skill share the closed value. The
writer deduplicates differential formats, preserves strict or transitional
SpreadsheetML plus unknown attributes, and fails closed when unsupported child
or collection content would be lost. This milestone does not evaluate rule
formulas, reproduce Excel rendering, or cover x14-only data-bar axes/colors,
table/chart/pivot conditional formatting, or complete Spreadsheet parity.
Native Spreadsheet defined names use a separate typed contract for
workbook-global and worksheet-local scopes. Stable selectors include both name
and scope, while compatibility selectors remain available when the name is
unambiguous. worksheet:workbook disambiguates a local scope when the
worksheet itself is literally named workbook. The writer validates Excel identifier and reference limits,
qualifies a bare A1 range only for a local scope, enforces case-insensitive
uniqueness by (name, scope), rejects ListObject table-name collisions, and
protects _xlnm.* and Slicer_* names owned by other Office features.
Semantic get/query, ordinary typed remove, batch, exact replay, CLI, Rust, and
standard MCP share the same value. Strict/transitional SpreadsheetML and
unknown defined-name attributes are retained; unknown collection or child
content fails closed when it cannot be preserved. This contract owns
defined-name lifecycle, not external-link authoring or complete Spreadsheet
parity; supported names participate when an explicit native cell-formula
recalculation references them.
Native Spreadsheet AutoFilters use a closed typed contract shared by worksheet
filters and ListObject tables. One value owns a normalized rectangular A1
range plus at most one criterion for each zero-based range column. Supported
criteria are exact value sets with optional blanks; equality, text, and ordered
comparisons; between/not-between; blanks/non-blanks; top/bottom count or
percentage; and closed dynamic average, relative-date, month, and quarter
families. Worksheet lifecycle uses stable /Sheet/autofilter and
/Sheet/autofilter/filterColumn[N] paths; table filters appear below
/Sheet/table[N]/autofilter. Rust, versioned batch, CLI, standard MCP, exact
replay, and the Office Skill use the same value. The strict/transitional writer
sorts columns deterministically, escapes wildcard literals, rejects duplicate
or out-of-range columns, protects table/merge geometry, and fails closed for
imported date-group items, color/icon filters, embedded sort state,
extensions, comments, or unknown attributes. Physical row sorting is owned by
the separate typed sort contract below; an imported AutoFilter with unsupported
embedded sort state remains readable but non-mutable.
Native Spreadsheet sorting physically reorders a worksheet range through one
stable, ordered multi-key contract. A worksheet path auto-detects its used
range; callers may instead supply /Sheet/A1:D100. Keys are absolute A:XFD
columns inside that range, the first selected row stays fixed only when
header=true, text comparison is case-insensitive by default, numbers sort
before text, blanks always remain last, and records equal on every key retain
their source order. Partial-column sorts move only selected cells; cells outside
the range and destination row properties stay fixed. The editor persists a
worksheet SpreadsheetML sortState, exposed as /Sheet/sort and ordered
/Sheet/sort/key[N] nodes. Removing /Sheet/sort deletes metadata only and
never reverses the physical row order.
Rust, versioned batch/replay, CLI, standard MCP, and the Office Skill share the same sort value. Sorts accept 1–64 unique keys and at most 100,000 selected cells. Exact mutable ListObject or worksheet-AutoFilter ranges, or their exact data ranges, are supported. Table totals rows, formulas anywhere in the workbook, intersecting merges, pivots, unknown existing sort state, and drawing anchors that cannot follow one record losslessly fail closed. Hyperlinks, comments/VML notes, data validation, conditional formatting, protected ranges, ignored errors, and supported drawing anchors move with their records; chart caches are cleared and the worksheet used dimension is recomputed after the physical change.
Native Spreadsheet delimited import accepts bounded UTF-8 CSV or TSV from a regular file or stdin and writes it into an existing worksheet from an explicit A1 start cell. The parser supports a leading BOM, CRLF, quoted delimiters, embedded newlines, and doubled quotes; malformed quote state fails atomically. One request is limited to 8 MiB and a 100,000-cell rectangular extent. Explicit empty fields clear existing target cells, while missing trailing fields in a ragged row leave those cells unchanged.
Typed inference stores formulas, finite numbers, booleans, ISO dates/times, and
text without a Python, Node.js, OfficeCLI, or spreadsheet-application runtime.
Dates honor the workbook's 1900/1904 system and receive a native date number
format. Inferred formulas pass the same bounded native syntax parser as direct
cell writes; malformed expressions fail the complete import. Import does not
implicitly calculate formulas; run the explicit native recalculation command
or mutation when fresh cached results are required. Header mode atomically
installs the worksheet AutoFilter and a canonical frozen pane below the header.
Frozen pane state is readable at /Sheet/freeze, is set through the typed
batch/Rust/MCP contract, and is removed through the ordinary typed remove
mutation. Strict/transitional SpreadsheetML and unknown view content are
preserved; unsupported pane content reports nativeMutable=false and fails
closed on mutation.
Native Spreadsheet formula calculation builds a deterministic bounded
dependency graph across worksheets, ranges, spills, and workbook- or
worksheet-scoped names. The closed built-in registry implements
SUM, AVERAGE, MIN, MAX, COUNT, COUNTA, ABS, SQRT, POWER,
MOD, ROUND, IF, IFERROR, AND, OR, NOT, CONCAT,
CONCATENATE, ROW, COLUMN, SEQUENCE, TRANSPOSE, PI, and NA.
Operators and typed blank, number, text, boolean, and Spreadsheet error values
participate in scalar or rectangular array calculation.
The read-only Rust calculation API leaves package bytes unchanged. The editor,
versioned batch, replay, CLI office native recalculate, and standard MCP
recalculate-spreadsheet-formulas mutation atomically write typed OOXML
caches, canonical array anchors, spill children, and calculated-workbook
metadata. Spill children are read-only; edit or remove their formula anchor.
Exact replay accepts canonical formula storage and natively cached array
anchors. It fails closed for physical distinctions typed mutations cannot
reproduce, including explicit t="normal" storage and uncached or malformed
array anchors.
ListObject structured references resolve table names or display names.
Sales[Qty] and Sales[[Qty]:[Price]] select data rows; #All, #Data,
#Headers, and #Totals select structural rows; and Sales[@Qty],
Sales[[#This Row],[Qty]], or table-local [@Qty] select the current data row.
Table-local forms require the formula cell to be inside the inferred table.
Missing tables, columns, or requested structural rows, disjoint columns,
non-canonical forms, cycles, unsupported or qualified functions, and
external-workbook reads fail with stable typed errors and roll back the whole
batch. The engine never fetches an external workbook or falls back to a shell
or script runtime. One formula is limited to 8,192 characters, depth
128 (including nested named-reference resolution), and 8,192 AST nodes; one
reference value to 100,000 areas; one graph to 100,000 formulas, 1,000,000
edges, and 1,000,000 formula-cell reference visits; one materialized array or
function call to 100,000 cells; and one text result to 1 MiB.
One calculation pass is also limited to 100,000 cumulative spill children and
200,000 OOXML cell writes, plus 8 MiB of cumulative text-result bytes.
Native Spreadsheet tables use a separate closed ListObject contract. Add and
set own the workbook-wide name, optional distinct displayName, final
rectangular A1 range, one exact column identity per range column, header/totals
row state, typed filter criteria, built-in light/medium/dark style identity,
and first/last-column plus row/column-stripe flags; ordinary typed remove
owns deletion. When a header is enabled, its names are stamped into the first
row and the table-owned AutoFilter range excludes an enabled totals row. The
set lifecycle keeps common structured references consistent: changed table
names/display names and position-mapped column identities are rewritten across
cell formulas, defined names, conditional formats, data validations, charts,
and table formulas without touching string literals or external-workbook
references. Table-local references are rewritten only when their ListObject
context is provable. Unsafe local-reference geometry changes fail with
use.office.spreadsheet_table_formula_rewrite_unsupported, and remove
fails with use.office.spreadsheet_table_referenced while a live structured
reference still targets the table. All checks and rewrites share the table
mutation's atomic rollback boundary.
The editor rejects
Excel-identifier and A1/R1C1 name errors,
case-insensitive table/defined-name collisions, duplicate columns, missing data
rows, table/merge/worksheet-AutoFilter overlap, and unsafe relationship graphs.
Semantic reads expose stable /Sheet/table[N] and child column paths. Rust,
versioned batch, CLI, standard MCP, exact replay, and the Office Skill share the
same value while strict/transitional SpreadsheetML and supported unknown root
or style data are retained. Imported calculated columns, totals functions,
date-group/color/icon filters, unsupported embedded sort state, custom styles,
query tables, and external data remain explicit gaps and fail closed when a
lossless typed mutation cannot be proved. Exact mutable table and data ranges
can still be physically sorted through the separate sort contract.
The engine also creates, updates, reads,
queries, and removes typed hyperlinks. Word owns
external HTTP/HTTPS/mailto links and internal bookmark targets in body,
header, and footer paragraphs, with display text and tooltips; Spreadsheet
owns external links and internal workbook locations on cells or bounded
rectangular ranges, with display text and tooltips, auto-creating a missing
single linked cell; Presentation owns external shape-wide links and internal
jumps to existing slides, with optional tooltips. External targets reject
embedded credentials, active or relative schemes, and malformed URIs; semantic rendering
keeps every relationship inert and never fetches it. The same typed engine
creates, updates, reads, queries, and removes classic Office comments. Word
comments anchor to a main-document paragraph or run and expose stable
/comments/comment[N] paths. Spreadsheet comments are classic cell notes with
an author table, VML note drawing, and /SheetName/A1/comment paths, including
notes on otherwise blank cells. Presentation comments use legacy per-slide
comment and shared-author parts, optional EMU coordinates, and
/slide[N]/comment[M] paths. Removing an owning Word node, Spreadsheet cell or
range, or Presentation slide also removes its owned comment resources. This is
plain legacy-comment scope, not complete modern threaded-comment parity:
replies, resolved state, writable dates, rich bodies, Word header/footer
anchors, Spreadsheet threaded comments, and modern PowerPoint threaded
comments remain outside the typed contract. The engine can safely inspect
existing XML parts and replace non-OPC-metadata XML parts while preserving the
root QName and validating the final document. Known chart, header, and footer
part carriers can be created together with their content type and owner
relationship. The typed mutation layer also adds and removes Word paragraphs
and basic table/row/cell structures,
creates real Presentation DrawingML tables, appends grid-conformant rows,
fills underfull rows, edits cell text, and exposes table columns as stable
virtual paths. Columns can be inserted, resized in EMUs, removed, moved,
copied, or swapped while the grid, every row, and the graphic-frame width stay
in sync. Tables and structurally safe rows/cells can also be removed. It
upserts typed Spreadsheet text, number, boolean, and formula cells, removes
cells and bounded cell ranges, structurally inserts or deletes rows and columns,
and adds, removes, renames, reorders, or copies worksheets. It also exposes
typed move, copy, and swap mutations with zero-based or path-relative
placement. Word supports same-parent paragraph, table, row, cell, and run
moves and swaps plus identity-free paragraph, table, row, and run copies.
Spreadsheet supports worksheet arrangement and dense plain-row arrangement with
row/cell renumbering. Presentation supports slide arrangement, same-slide
top-level object movement/swaps, layout-only slide copies, and relationship-free
plain-shape copies with fresh non-visual identities. Cross-parent ownership
migration, formula-bearing or reference-rich row arrangement, identity-bearing
Word copies, table-cell copies, and relationship-owning Presentation copies fail
closed before save. Worksheet copy
clones the owned OPC relationship subgraph while preserving shared workbook
resources; removal garbage-collects only unshared descendants. Structural
Spreadsheet edits rewrite affected A1 formulas, defined names, worksheet
metadata, tables, comments, VML notes, drawing anchors, and chart references;
unsupported pivot and 3D-reference cases fail closed before save. Presentation
slides, text shapes, and basic tables also support native add/remove. PNG, JPEG,
and GIF can be embedded as real Word inline pictures, Spreadsheet one-cell
drawing anchors, and Presentation slide pictures; semantic reads and
reference-aware removal use the same cross-format Picture contract. Saves are
atomic and reject a changed source revision instead of overwriting another
writer.
Cross-format template merge replaces {{key}} text in Word document and
auxiliary text parts, Spreadsheet string cells, and Presentation slides and
notes while preserving split-run formatting and reporting unresolved keys.
The native document engine does not require Microsoft Office, LibreOffice,
OfficeCLI, Python, Node.js, or .NET. LibreOffice may be used only by optional
CI interoperability checks and is never part of document execution. Optional
PNG screenshot output requires the browser feature and a ready A3S Browser
provider because it captures the native semantic HTML through the existing
Browser contract.
The explicit office native CLI exposes in-process blank creation, reads,
typed add/set/remove/move/copy/swap, scoped literal/regex replacement,
rich-text, exact Spreadsheet merged-cell, stable Spreadsheet physical sorting
with persisted sort state, bounded CSV/TSV import with typed inference and
header filter/freeze behavior, explicit Spreadsheet formula recalculation,
worksheet/table AutoFilter, data-validation, conditional-format, defined-name,
ListObject table, hyperlink, and legacy-comment operations, constrained raw
XML access,
known typed part carriers, exact replay artifacts for a constrained canonical
subset, visible PNG/JPEG/GIF pictures, and atomic mutation batches, plus
dependency-free template merge and semantic rendering today. HTML and SVG are
available for Word, Spreadsheet, and Presentation; bounded annotated and issue
views are available for all three formats; and Browser-injected PNG screenshots
are available for all three formats. An authenticated, loopback-only foreground
watch provides full saved-revision refresh for all three formats without a
resident pipe or mutation endpoint. mcp serve office-native exposes the same
editor, annotated/issue analysis, and screenshot composition through typed
standard MCP tools and bounded in-memory sessions.
The packaged a3s-office Skill exposes the same product boundaries to
agents without starting OfficeCLI. Discover its metadata with
office skills list, read only its SKILL.md with
office skills get a3s-office, append its four format/MCP references with
--full, or locate the installed directory with office skills path. The
capability snapshot binds the Skill path and lowercase SHA-256 so a resident
host can verify the bytes before loading them. Resident Code hosts receive the
native engine as canonical route use/office targeting office-native; a ready
OfficeCLI installation is projected separately as use/office-compat targeting
office-compat.
Other 0.1.x commands and the default mcp serve office target still use a
compatibility backend pinned to OfficeCLI 1.0.136. This is a migration
boundary, not a native-promotion claim. The default routes will be promoted
only after mutation, fidelity, rendering, compatibility, and cross-application
interoperability gates pass. The first real compatibility CLI command prepares
that pinned provider when first-use policy allows. In Code, the native Office
worker requests office_install_compat through parent confirmation only when
the requested operation is outside the native surface.
# Inspect without downloading anything.
a3s use office doctor --json
# Create by extension; an existing destination is never overwritten.
a3s-office create report.docx --json
a3s-office create workbook.xlsx --json
a3s-office create deck.pptx --json
# Read without OfficeCLI, Microsoft Office, or LibreOffice.
a3s-office get report.docx /body --depth 2 --json
a3s-office query report.docx 'p[style=Heading1]' --json
a3s-office view report.xlsx stats --json
a3s-office view report.docx issues --json
a3s-office view workbook.xlsx issues --type formula_not_evaluated --limit 20 --json
a3s-office view report.docx html --output report.html --json
a3s-office view workbook.xlsx html --output workbook.html --json
a3s-office view report.docx svg --output report.svg --json
a3s-office view workbook.xlsx svg --output workbook.svg --json
a3s-office view deck.pptx svg --output deck.svg --json
a3s-office view report.docx screenshot --output report.png --timeout-ms 30000 --json
a3s-office watch deck.pptx --port 0
a3s-office validate deck.pptx --json
# Inspect a safely parsed XML part inline or export its original bytes.
a3s-office raw report.docx /word/document.xml --json
a3s-office raw report.docx /word/document.xml --output document.xml --json
# Replace one existing XML part; --output is an optional Office save-as target.
a3s-office raw-set report.docx /word/document.xml --input document.xml --output updated.docx --json
# Create known part carriers and receive their owner relationship IDs.
a3s-office add-part report.docx / --type header --json
a3s-office add-part report.docx / --type chart --json
a3s-office add-part workbook.xlsx /Sheet1 --type chart --json
a3s-office add-part deck.pptx '/slide[1]' --type chart --json
# Replace text in place or save to a separate OOXML document.
a3s-office set report.docx /body/p[1] --text 'Updated' --json
a3s-office set report.xlsx /Sheet1/B2 --text '42' --output updated.xlsx --json
# Find and replace within a semantic scope. Literal matching is the default;
# --regex enables Rust regular expressions and $name/$1 capture expansion.
a3s-office set report.docx /body --find 'Q1 2025' --replace 'Q1 2026' --json
a3s-office set report.docx / --find 'Q([1-4]) 2025' --replace 'Q$1 2026' --regex --json
a3s-office set workbook.xlsx /Sheet1/A1:C20 --find Draft --replace Final --json
a3s-office set deck.pptx '/slide[1]/notes' --find internal --replace confidential --json
# Apply typed text formatting. Word and Presentation character properties use
# run paths; their alignment uses paragraph paths. Word sizes must be exact
# half-point increments. Spreadsheet ranges may combine content and formatting.
# Strikethrough is native for Word and Spreadsheet, but not Presentation.
a3s-office set report.docx '/body/p[1]/r[1]' --bold true --italic false --underline double --script superscript --strikethrough true --double-strikethrough false --text-case small-caps --highlight yellow --language en-US --font-family Aptos --font-size 14 --text-color 123456 --json
a3s-office set report.docx '/body/p[1]' --align center --json
a3s-office set workbook.xlsx /Sheet1/A1:C1 --bold true --underline single --script baseline --strikethrough false --font-size 11.5 --text-color 0066CC --align center --json
a3s-office set deck.pptx '/slide[1]/shape[1]/paragraph[1]/run[1]' --italic true --underline double --script subscript --text-case all-caps --highlight cyan --language zh-CN --font-family 'Aptos Display' --font-size 20 --json
a3s-office set deck.pptx '/slide[1]/shape[1]/paragraph[1]' --align center --json
# Apply Spreadsheet cell presentation independently or in the same atomic set
# as content/text formatting. Fill and border colors accept six-digit RGB.
a3s-office set workbook.xlsx /Sheet1/A1:C3 --number-format currency --fill FFF2CC --border-all thin --border-color 808080 --border-bottom double --border-bottom-color 000000 --vertical-align center --wrap-text true --text-rotation 0 --indent 1 --shrink-to-fit false --reading-order ltr --json
a3s-office set workbook.xlsx /Sheet1/D1 --number 0.125 --bold true --number-format percent --fill 0066CC --json
a3s-office set workbook.xlsx /Sheet1/E1 --border-diagonal slant-dash-dot --border-diagonal-color FF0000 --border-diagonal-up true --border-diagonal-down false --json
# Merge one normalized Spreadsheet range, or unmerge the exact same range.
# Content, text format, cell format, hyperlink, and merge state can share one
# atomic set command.
a3s-office set workbook.xlsx /Sheet1/A1:C1 --text 'Quarter' --bold true --merge-cells true --json
a3s-office set workbook.xlsx /Sheet1/A1:C1 --merge-cells false --json
# Import a bounded UTF-8 CSV/TSV source. Header mode also installs the
# worksheet AutoFilter and canonical frozen pane in the same transaction.
a3s-office import workbook.xlsx /Sheet1 source.csv --header --start-cell A1 --json
a3s-office import workbook.xlsx /Sheet1 --stdin --format tsv --output imported.xlsx --json
a3s-office get workbook.xlsx /Sheet1/freeze --json
# Add, inspect, replace, clear, and remove one worksheet AutoFilter. Each
# --filter is a strict JSON object with a zero-based column and typed criteria.
a3s-office add workbook.xlsx /Sheet1 --type auto-filter --range A1:C20 --filter '{"column":0,"criteria":{"type":"values","values":["Open","Closed"],"includeBlanks":true}}' --filter '{"column":2,"criteria":{"type":"greater-than","value":"100"}}' --json
a3s-office query workbook.xlsx 'filtercolumn[criteriaType=greater-than]' --json
a3s-office get workbook.xlsx /Sheet1/autofilter --depth 2 --json
a3s-office set workbook.xlsx /Sheet1/autofilter --range B2:D30 --filter '{"column":1,"criteria":{"type":"dynamic","kind":"this-month"}}' --json
a3s-office set workbook.xlsx /Sheet1/autofilter --clear-filters --json
a3s-office remove workbook.xlsx /Sheet1/autofilter --json
# Physically sort selected records by ordered absolute columns. A worksheet
# path auto-detects its used range. Remove the resulting semantic sort node to
# clear only persisted metadata; the physical row order remains unchanged.
a3s-office sort workbook.xlsx /Sheet1/A1:D100 --key B:desc --key C:asc --header true --case-sensitive false --json
a3s-office get workbook.xlsx /Sheet1/sort --depth 1 --json
a3s-office remove workbook.xlsx /Sheet1/sort --json
# Add, inspect, replace, and remove one native Spreadsheet ListObject table.
# The range is final and includes enabled header/totals rows. Repeat
# --table-column exactly once for every range column. Table --filter values use
# the same strict zero-based contract as worksheet AutoFilters.
a3s-office add workbook.xlsx /Sheet1 --type table --name Sales --range F1:H4 --table-column Name --table-column Qty --table-column Price --filter '{"column":1,"criteria":{"type":"top","count":10}}' --style medium:4 --json
a3s-office query workbook.xlsx 'table[name=Sales]' --json
a3s-office get workbook.xlsx '/Sheet1/table[1]' --depth 1 --json
a3s-office set workbook.xlsx '/Sheet1/table[1]' --name Inventory --display-name InventoryView --range B2:D6 --table-column Item --table-column Units --table-column Cost --totals-row true --style dark:2 --show-row-stripes false --show-column-stripes true --json
a3s-office remove workbook.xlsx '/Sheet1/table[1]' --json
# Add, inspect, update, and remove typed Spreadsheet data validation. Repeated
# --range values form one rule over disjoint areas; set preserves omitted fields.
a3s-office add workbook.xlsx /Sheet1 --type data-validation --validation-type list --range A2:A20 --range C2:C20 --formula1 'Draft,Review,Approved' --prompt-title Status --prompt 'Choose a workflow state' --error-title 'Invalid status' --error-message 'Choose a listed state' --json
a3s-office query workbook.xlsx 'dataValidation[type=list]' --json
a3s-office get workbook.xlsx /Sheet1/C3 --json
a3s-office set workbook.xlsx '/Sheet1/dataValidation[1]' --validation-type whole --range B2:B50 --operator between --formula1 18 --formula2 120 --allow-blank false --error-style warning --json
a3s-office remove workbook.xlsx '/Sheet1/dataValidation[1]' --json
# Add, query, partially update, and remove native Spreadsheet conditional
# formats. Rule-specific options are closed and validated before atomic save.
a3s-office add workbook.xlsx /Sheet1 --type conditional-format --rule-type cell-is --range A2:A20 --operator greater-than --formula1 80 --fill C6EFCE --text-color 006100 --bold true --json
a3s-office add workbook.xlsx /Sheet1 --type conditional-format --rule-type data-bar --range B2:B20 --color 638EC6 --min min --max number:100 --json
a3s-office add workbook.xlsx /Sheet1 --type conditional-format --rule-type color-scale --range C2:C20 --min-color F8696B --midpoint percentile:50 --mid-color FFEB84 --max-color 63BE7B --json
a3s-office add workbook.xlsx /Sheet1 --type conditional-format --rule-type icon-set --range D2:D20 --icon-set 3-traffic-lights-1 --reverse true --json
a3s-office query workbook.xlsx 'conditionalFormatting[type=iconSet]' --json
a3s-office set workbook.xlsx '/Sheet1/cf[1]' --formula1 90 --fill FFEB9C --stop-if-true true --json
a3s-office remove workbook.xlsx '/Sheet1/cf[2]' --json
# Add, inspect, update, and remove typed Spreadsheet defined names. A sheet
# parent defaults to local scope and qualifies a bare A1 ref automatically.
a3s-office add workbook.xlsx / --type named-range --name Revenue --ref 'Sheet1!$A$2:$A$20' --scope workbook --comment 'Workbook revenue' --json
a3s-office add workbook.xlsx /Sheet1 --type named-range --name Status --ref A2:A20 --json
a3s-office query workbook.xlsx 'namedrange[scope=Sheet1]' --json
a3s-office get workbook.xlsx '/namedrange[@name=Revenue][@scope=workbook]' --json
a3s-office set workbook.xlsx '/namedrange[@name=Status][@scope=Sheet1]' --name WorkflowStatus --ref B2:B20 --volatile false --json
a3s-office remove workbook.xlsx '/namedrange[@name=Revenue][@scope=workbook]' --json
# Add or update inert hyperlinks. External targets accept only absolute
# HTTP/HTTPS/mailto URIs without credentials. Word internal targets are bookmark
# names; Spreadsheet internal targets are workbook locations; Presentation
# internal targets are existing slide[N] paths. Presentation keeps shape text.
a3s-office add report.docx '/body/p[1]' --type hyperlink --url https://example.com/report --display 'Open report' --tooltip 'A3S report' --json
a3s-office set report.docx '/body/p[1]/hyperlink[1]' --location section_1 --display 'Jump to section' --json
a3s-office set report.docx '/header[1]/p[1]' --url https://example.com/header --display 'Header link' --json
a3s-office set workbook.xlsx /Sheet1/A1 --location 'Sheet1!B2' --display B2 --json
a3s-office set workbook.xlsx /Sheet1/B2:C3 --url https://example.com/range --display Range --json
a3s-office set deck.pptx '/slide[1]/shape[1]' --url https://example.com/slides --tooltip 'Open slides' --json
a3s-office set deck.pptx '/slide[1]/shape[1]/hyperlink' --location 'slide[2]' --tooltip 'Next slide' --json
a3s-office query report.docx hyperlink --json
a3s-office remove report.docx '/body/p[1]/hyperlink[1]' --json
# Add, update, discover, and remove classic Office comments. Word uses a body
# paragraph or run anchor; Spreadsheet uses one cell; Presentation uses a slide
# and optionally accepts a complete x/y EMU coordinate pair.
a3s-office add report.docx '/body/p[1]' --type comment --author Alice --initials AL --text 'Please reword this' --json
a3s-office set report.docx '/comments/comment[1]' --author Bob --initials BO --text 'Reviewed' --json
a3s-office add workbook.xlsx /Sheet1/B2 --type comment --author Alice --text 'Check this formula' --json
a3s-office add deck.pptx '/slide[1]' --type comment --author Alice --initials AL --text 'Rework this slide' --x-emu 914400 --y-emu 457200 --json
a3s-office query deck.pptx comment --json
a3s-office remove workbook.xlsx /Sheet1/B2/comment --json
# Preserve Spreadsheet value types. Formula writes validate and store the
# expression; explicit recalculation computes and writes cached values.
a3s-office set workbook.xlsx /Sheet1/A1 --number 42.5 --json
a3s-office set workbook.xlsx /Sheet1/B1 --boolean true --json
a3s-office set workbook.xlsx /Sheet1/C1 --formula 'SUM(A1:B1)' --json
a3s-office recalculate workbook.xlsx --output calculated.xlsx --json
# Set or remove a bounded rectangular range atomically.
a3s-office set workbook.xlsx /Sheet1/A2:C4 --number 0 --json
a3s-office remove workbook.xlsx /Sheet1/B3:C4 --json
# Edit worksheet structure and ordering without invoking OfficeCLI.
a3s-office insert-rows workbook.xlsx /Sheet1 2 --count 3 --json
a3s-office delete-columns workbook.xlsx /Sheet1 B --count 2 --json
a3s-office rename-sheet workbook.xlsx /Sheet1 'Q1 Data' --json
a3s-office move-sheet workbook.xlsx '/Q1 Data' 1 --json
a3s-office copy-sheet workbook.xlsx '/Q1 Data' 'Q1 Copy' --position 2 --json
# Add and remove native document structures.
a3s-office add report.docx /body --type paragraph --text 'Summary' --json
a3s-office add report.docx /body --type table --rows 2 --columns 3 --json
a3s-office add report.docx '/body/tbl[1]' --type row --columns 3 --json
a3s-office add report.docx '/body/tbl[1]/tr[3]' --type cell --text 'Total' --json
a3s-office add workbook.xlsx / --type sheet --name Data --json
a3s-office add deck.pptx / --type slide --text 'Results' --json
a3s-office add deck.pptx '/slide[1]' --type shape --text '42%' --json
a3s-office add deck.pptx '/slide[1]' --type table --rows 3 --columns 2 --json
a3s-office set deck.pptx '/slide[1]/table[1]/tr[1]/tc[1]' --text 'Metric' --json
a3s-office add deck.pptx '/slide[1]/table[1]' --type row --columns 2 --json
a3s-office add deck.pptx '/slide[1]/table[1]' --type column --index 1 --text 'Q2' --json
a3s-office set deck.pptx '/slide[1]/table[1]/col[2]' --width-emu 2000000 --json
a3s-office move deck.pptx '/slide[1]/table[1]/col[1]' --after '/slide[1]/table[1]/col[2]' --json
a3s-office remove deck.pptx '/slide[1]/table[1]/col[3]' --json
a3s-office remove deck.pptx '/slide[1]/table[1]/tr[4]' --json
a3s-office remove workbook.xlsx /Data --json
a3s-office remove deck.pptx '/slide[1]/shape[2]' --json
# Embed bounded PNG, JPEG, or GIF data as a real DrawingML picture. Supplying
# one dimension preserves the source aspect ratio; supplying both is explicit.
a3s-office add report.docx /body --type picture --input logo.png --name Logo --alt 'A3S logo' --width 320 --json
a3s-office add workbook.xlsx /Sheet1/B2 --type picture --input chart.jpeg --width 480 --height 270 --json
a3s-office add deck.pptx '/slide[1]' --type picture --input photo.gif --json
a3s-office remove deck.pptx '/slide[1]/picture[1]' --json
# Arrange supported semantic nodes. --index is zero-based; --before and --after
# resolve stable pre-mutation paths. A copy defaults to immediately after its
# source.
a3s-office move report.docx '/body/p[3]' --before '/body/p[1]' --json
a3s-office copy workbook.xlsx '/Q1 Data' --name 'Q1 Copy' --after '/Q1 Data' --json
a3s-office swap deck.pptx '/slide[1]' '/slide[3]' --json
# Merge a template into a separate output. JSON may be inline, @file, or an
# existing .json path. Existing outputs require an explicit --force.
a3s-office merge template.docx report.docx --data @report.json --json
a3s-office merge template.xlsx report.xlsx --data '{"quarter":"Q3"}' --json
# Apply a bounded, versioned mutation document atomically.
a3s-office batch deck.pptx --input mutations.json --json
# Dump the exactly replayable root subset, then replay it into a native blank.
a3s-office dump report.docx --output report.replay.json --json
a3s-office create restored.docx --json
a3s-office batch restored.docx --input report.replay.json --json
# Optional compatibility pre-warm. The following compatibility commands also
# prepare this pinned provider on first use.
a3s install use/office
a3s use office get report.docx /body --json
a3s use office batch report.xlsx --input updates.json --json
# Launch the explicit native standard MCP preview. No OfficeCLI is consulted.
a3s-office mcp
# Launch the current compatibility standard MCP server.
a3s use mcp serve officeThe native MCP process exposes 12 document tools plus the confirmed
office_install_compat compatibility installer: office_validate,
office_create, office_open, office_list, office_get, office_query,
office_view, office_raw_xml, office_apply_batch,
office_merge_template, office_save, and office_close. It accepts no shell
command string and defines no A3S RPC dialect; stdio carries only standard MCP.
Each process owns at most 64 sessions. Mutation batches are atomic in memory,
limited to 10,000 mutations and 8 MiB of JSON, and remain unsaved until
office_save. Results are limited to 8 MiB, raw XML responses to 1 MiB, and
queries to at most 1,000 returned nodes. office_close rejects dirty sessions
unless the caller saves or explicitly sets discard=true. office_view
accepts html and svg for all three formats in addition to text, annotated,
outline, and statistics. The typed annotated view flattens stable semantic
paths, node types, text, styles, and observed formatting; its limit is 1
through 1,000 and defaults to 200. The issues view accepts an optional typed
issueType and a limit from 1 through 1,000, defaulting to 200. It also
accepts screenshot for all three formats; that mode requires a no-clobber
local output ending in .png and accepts an optional timeoutMs from 1
through 120,000.
Native issue analysis is conservative and read-only. It currently reports
missing picture alternative text, missing or incompatible internal part
relationships, formulas without cached results, formulas that reference a
missing worksheet, cached or explicit formula errors, and low contrast between
explicit RGB run text and its own shape fill. Reports include stable category,
subtype, severity, path, context, and suggestion fields; filtering happens
before the bounded result window, and count, returned, and truncated
remain explicit. It does not infer text overflow, object overlap, theme or
inherited colors, or Microsoft Office layout behavior. A clean report is not a
full fidelity or interoperability certification.
Native render artifacts are deterministic, standalone, and network-free. They
contain no timestamp or source path, escape document text and attributes, carry
stable semantic paths as data-path, and embed only validated internal
PNG/JPEG/GIF parts as data: URLs. HTML declares a restrictive CSP and uses a
sparse observed-cell representation instead of expanding large Spreadsheet
gaps. Each render is bounded to 16 MiB while it is composed. CLI --output
publishes through an atomic no-clobber file operation; inline MCP output remains
subject to the stricter 8 MiB structured-result limit. These are semantic
previews, not a Microsoft Office layout-fidelity claim. Screenshot mode stages
the same deterministic HTML privately, opens its file:// URL through the
existing PageRenderer, and validates one regular PNG plus its size and
SHA-256 receipt before atomic no-clobber publication. It defaults to a 30-second
deadline, caps the deadline at 120 seconds, and caps the PNG at 64 MiB. It does
not fetch external relationships or consult OfficeCLI.
office native watch <file> renders the same bounded all-format HTML, binds
only 127.0.0.1, selects an ephemeral port by default, and prints a URL with a
fresh 256-bit capability token. Every page, status response, and standard SSE
stream requires that token or its HttpOnly same-site cookie and validates the
exact loopback Host. The wrapper runs its own fixed script while the document
preview stays in a sandboxed iframe under the renderer's script-free CSP.
Atomic saves from another office native process trigger a full refresh.
Transient missing or invalid revisions leave the last valid preview visible,
publish a typed error state, and retry until recovery. The server is read-only:
it has no mutation/RPC endpoint, never opens an external relationship, and
does not observe unsaved office-native MCP sessions until office_save.
--timeout-ms bounds automated runs; otherwise Ctrl+C stops the foreground
server. Interactive editing, selection/mark overlays, and layout goldens remain
open.
Native batch input is an ordinary JSON document, not an RPC protocol. The current schema is:
{
"schemaVersion": 1,
"mutations": [
{
"operation": "replace-text",
"path": "/body",
"replacement": {
"find": "Q([1-4]) 2025",
"replace": "Q$1 2026",
"mode": "regex"
}
},
{
"operation": "set-text",
"path": "/body/p[1]",
"text": "Updated"
},
{
"operation": "add-paragraph",
"parent": "/body",
"text": "Summary"
},
{
"operation": "remove",
"path": "/body/p[2]"
},
{
"operation": "move",
"path": "/body/p[3]",
"position": {
"kind": "before",
"path": "/body/p[1]"
}
}
]
}The whole batch rolls back if any mutation fails. Inputs are limited to 8 MiB
and 10,000 mutations. The version 1 mutation set is replace-text, set-text,
set-text-format, set-cell-format, add-data-validation,
set-data-validation, add-conditional-format, set-conditional-format,
add-named-range, set-named-range, add-spreadsheet-table,
set-spreadsheet-table, add-spreadsheet-auto-filter,
set-spreadsheet-auto-filter, sort-spreadsheet-range,
import-spreadsheet-delimited, set-spreadsheet-frozen-pane, merge-cells,
unmerge-cells, recalculate-spreadsheet-formulas,
set-hyperlink, set-comment, set-table-column-width,
set-cell-value, add-paragraph,
add-table, add-table-row, add-table-column, add-table-cell,
add-comment, add-worksheet, insert-rows, delete-rows, insert-columns,
delete-columns, rename-worksheet, move-worksheet, copy-worksheet,
move, copy, swap, replace-xml-part, add-part, add-slide, add-shape,
add-image, and remove.
A data-validation mutation uses the same atomic document and standard MCP
payload. Remove a rule with the ordinary typed remove mutation:
{
"operation": "add-data-validation",
"sheet": "/Sheet1",
"validation": {
"type": "whole",
"ranges": ["B2:B50", "D2:D50"],
"operator": "between",
"formula1": "18",
"formula2": "120",
"allowBlank": false,
"errorStyle": "warning"
}
}Each rule accepts 1–1,024 disjoint ranges and each worksheet accepts at most
65,534 rules. Formulas are limited to 255 characters; prompt/error titles to
32, prompts to 255, and error messages to 225. List and custom rules reject
operators and formula2; comparison rules require an operator and require
formula2 only for between or notBetween. Invalid or overlapping input
rolls back the complete in-memory batch.
A conditional-format mutation uses one complete closed rule value. CLI set
can merge omitted options, while batch and standard MCP set replace the complete
value:
{
"operation": "add-conditional-format",
"sheet": "/Sheet1",
"conditionalFormat": {
"ranges": ["A2:A20"],
"stopIfTrue": true,
"rule": {
"type": "cellIs",
"operator": "greaterThan",
"formula1": "80",
"format": {
"fill": {"red": 198, "green": 239, "blue": 206},
"bold": true
}
}
}
}Closed classic predicates plus data bars, two/three-color scales, and standard 3/4/5-icon sets are supported. Threshold, range, priority, shared-range, and loss-preservation failures roll back the whole batch. Rule formulas are stored, not evaluated, and the semantic preview is not Excel rendering evidence.
A named-range mutation uses one complete scoped value. Deletion reuses the
ordinary typed remove mutation:
{
"operation": "add-named-range",
"namedRange": {
"name": "Revenue",
"ref": "'Sheet1'!$A$2:$A$20",
"scope": "workbook",
"comment": "Workbook revenue",
"volatile": false
}
}Use set-named-range with a stable path and a complete namedRange value.
Names are limited to 255 characters, refs to 8,192, comments to 255, and a
workbook to 65,536 defined names. Workbook-scoped bare A1 refs, leading =,
cross-workbook refs without external-link parts, reserved Office-managed names,
duplicate (name, scope) identities, and ListObject table-name collisions fail
atomically. Use the explicit scope worksheet:workbook for a local name on a
worksheet literally named workbook.
A worksheet AutoFilter mutation uses one complete range-and-columns value.
Table filters use the same column objects:
{
"operation": "add-spreadsheet-auto-filter",
"sheet": "/Sheet1",
"filter": {
"range": "A1:C20",
"columns": [
{
"column": 0,
"criteria": {
"type": "values",
"values": ["Open", "Closed"],
"includeBlanks": true
}
},
{
"column": 2,
"criteria": {"type": "between", "lower": "10", "upper": "100"}
}
]
}
}Use set-spreadsheet-auto-filter with /Sheet/autofilter and a complete
filter; ordinary typed remove deletes it. CLI set preserves an omitted
range, replaces all criteria when one or more --filter objects are supplied,
and uses --clear-filters for an explicit empty criterion list. Column offsets
are zero-based and unique inside the range. Imported date-group/color/icon
filters, embedded sort state, extensions, and unknown content are readable but
nativeMutable=false. Use the separate physical sort mutation for supported
ranges; it does not flatten an unsupported imported AutoFilter.
A Spreadsheet physical sort mutation owns an ordered, stable multi-key value:
{
"operation": "sort-spreadsheet-range",
"path": "/Sheet1/A1:D100",
"sort": {
"keys": [
{"column": "B", "direction": "descending"},
{"column": "C", "direction": "ascending"}
],
"header": true,
"caseSensitive": false
}
}The path may instead be /Sheet1 to auto-detect the used range. Keys are
unique absolute A:XFD columns within that range; 1–64 keys and at most
100,000 selected cells are accepted. Numbers precede text, blanks remain last
in both directions, and rows equal across every key retain source order. A
partial-column range moves only its cells, preserving cells outside the range
and destination row properties. Exact mutable table and worksheet-AutoFilter
ranges, or their exact data ranges, are supported. Formulas, totals rows,
intersecting merges, pivots, unknown sort state, and non-lossless drawing
movement fail the whole batch before save.
Read persisted metadata at /Sheet1/sort and ordered
/Sheet1/sort/key[N]. An ordinary remove of /Sheet1/sort removes only the
SpreadsheetML sort state; it does not restore the prior physical row order.
Supported record-bound hyperlinks, comments/VML notes, validations,
conditional formatting, protected ranges, ignored errors, and drawing anchors
follow the row permutation. The worksheet used dimension is recomputed and
chart caches are cleared. Exact replay emits the same
sort-spreadsheet-range mutation.
A Spreadsheet delimited import embeds bounded content in the typed batch or MCP request; filesystem paths remain a CLI-only concern:
{
"operation": "import-spreadsheet-delimited",
"sheet": "/Sheet1",
"import": {
"content": "Name,Amount,Date\nAlpha,42,2026-07-17",
"format": "csv",
"header": true,
"startCell": "A1"
}
}format is csv or tsv. Header mode replaces the worksheet AutoFilter range
and its canonical frozen pane in the same transaction, so inspect those nodes
before importing into a populated sheet. Set a pane independently with
set-spreadsheet-frozen-pane and remove it through /Sheet/freeze:
{
"operation": "set-spreadsheet-frozen-pane",
"sheet": "/Sheet1",
"pane": {
"frozenRows": 1,
"frozenColumns": 0,
"topLeftCell": "A2"
}
}A Spreadsheet table mutation uses one complete ListObject value. CLI set
preserves omitted fields; batch, Rust, and standard MCP replacements supply the
complete table:
{
"operation": "add-spreadsheet-table",
"sheet": "/Sheet1",
"table": {
"name": "Sales",
"range": "A1:C4",
"columns": [
{"name": "Name"},
{"name": "Qty"},
{"name": "Price"}
],
"filters": [
{"column": 1, "criteria": {"type": "top", "count": 10}}
],
"headerRow": true,
"totalsRow": false,
"style": {"family": "medium", "number": 4},
"showFirstColumn": false,
"showLastColumn": false,
"showRowStripes": true,
"showColumnStripes": false
}
}Use set-spreadsheet-table with a stable path and complete table; use
ordinary typed remove for deletion. Styles are none, light 1–21, medium
1–28, or dark 1–11. none requires all style flags to be false. The final range
must leave at least one data row after enabled header and totals rows and must
not intersect another table, a merge, or a worksheet-level AutoFilter. Table
filters require an enabled header; --clear-filters clears their criteria
without removing the table-owned AutoFilter range.
An image mutation uses the same versioned batch boundary:
{
"operation": "add-image",
"parent": "/Sheet1/B2",
"image": {
"format": "png",
"data": "<base64>",
"name": "Logo",
"altText": "A3S logo",
"widthPx": 320
}
}The native engine validates decoded format signatures and structure, bounds
bytes and pixel dimensions, infers a missing axis from the source aspect ratio,
and inserts a visible OOXML picture object. Word uses an inline DrawingML run,
Spreadsheet uses a one-cell drawing anchor, and Presentation uses a slide
picture. Removal deletes the XML object and an unused image relationship, then
deletes the media part and content-type declaration only when no relationship
anywhere in the package still targets it. Direct CLI image inputs must be
regular, non-symlink files no larger than 64 MiB. Normal CLI output never
contains image data; createdImage and batch createdImages receipts contain
only paths, owner/media parts, relationship ID, format, and final dimensions.
OOXML SVG image embedding is not implemented yet; the all-format SVG semantic preview is an output format and does not alter the package. Correct OOXML SVG image support requires an SVG part plus a raster fallback rather than treating SVG as an ordinary bitmap.
Image replacement, crop, rotation, effects, compression controls, floating Word wrapping, Spreadsheet two-cell sizing, and rich layout rendering also remain outside this bounded add/read/remove milestone.
office native dump produces a stricter versioned batch artifact, also as
ordinary JSON:
{
"format": "a3s.office.native-replay",
"schemaVersion": 1,
"documentKind": "word",
"scope": "/",
"base": "blank",
"baseSha256": "<sha256-of-the-uncompressed-blank-part-map>",
"resultSha256": "<sha256-of-the-uncompressed-result-part-map>",
"mutations": []
}The first dump scope is the complete document (/). It accepts only content
that current typed mutations can reproduce byte-for-byte at the OOXML part-map
level: plain Word paragraphs and rectangular tables, Spreadsheet worksheets,
typed defined names, typed cells, typed worksheet/table AutoFilters, typed
ListObject tables, stable physical row order with supported typed sort state,
canonical frozen panes and import date styles, merged ranges, typed
data-validation rules, canonical typed conditional-format rules, and natively
recalculable formula caches and canonical cached dynamic-array spills;
plus Presentation slides with plain one-run text shapes and canonical basic
tables.
Headers, notes,
media, custom or non-canonical table styling, rich text, non-canonical package resources,
and every other lossy case fail with
use.office.dump_unsupported; nothing is silently flattened or omitted.
Replay requires the exact A3S blank template identified by baseSha256.
batch checks that precondition before mutation and checks resultSha256
afterward. A failed result check restores the original in-memory package before
any save. Dump files are limited to 8 MiB and 10,000 mutations, refuse to
overwrite an existing path, and use a 1 MiB inline-output limit. This is a
portable Office batch artifact, not RPC and not a universal action envelope.
office native merge opens a .docx, .xlsx, or .pptx template, performs a
single-pass replacement, validates the resulting OPC/semantic document, and
atomically writes a separate output. The template and output may not identify
the same file. The output is no-clobber by default; --force is the only way to
replace an existing destination, and it never authorizes modifying the template
in place.
Merge data must be a JSON object. Literal top-level keys take precedence over
flattened nested paths: {"user.name":"literal","user":{"name":"nested"}}
resolves {{user.name}} to literal. Nested objects use dot paths and arrays
use bracket paths such as {{items[0].name}}. Replacement is deliberately
single pass, so a value containing {{another.key}} remains literal and is
reported as unresolved rather than recursively substituted. Results include the
replacement count, sorted used keys, sorted unresolved placeholders, and sorted
changed OOXML parts.
Word merge covers the main document, headers, footers, footnotes, endnotes, and
comments. Presentation merge covers slides and notes. Spreadsheet merge covers
inline strings, direct t="str" values, and referenced shared rich strings;
shared-string replacements are counted per referencing cell and phonetic runs
are left untouched. A resolved placeholder in a numeric, boolean, error, or
otherwise unsupported cell fails closed instead of coercing the cell type.
Data files are limited to 8 MiB and must be regular, non-symlink files. Flattened data is additionally bounded by entry count, nesting depth, key length, and total bytes. XML-forbidden replacement characters fail the whole in-memory transaction before any output is created. This native path does not invoke OfficeCLI, Microsoft Office, LibreOffice, Python, Node.js, or .NET.
General text replacement is separate from template merge. replace-text uses
an explicit semantic path and either case-sensitive, non-overlapping literal
matching or a linear-time Rust regular expression. Word / covers the main
document plus headers, footers, footnotes, endnotes, and comments; narrower
body, header/footer, paragraph, run, table, cell, hyperlink, and comment paths
stay within their source part. Spreadsheet accepts /, a worksheet, or one
cell/rectangular range and edits only string cells. A scoped shared-string edit
clones the rich shared-string item and redirects selected cells when other
cells still reference the original. Presentation accepts /,
slide/object/text paths, and /slide[N]/notes; slide scopes do not implicitly
include notes. Phonetic Spreadsheet text is never changed.
One operation accepts at most 64 KiB of find expression, 1 MiB of replacement
text, 100,000 semantic matches, 64 MiB of expanded replacement text, and a
100,000-cell Spreadsheet scope. Regex matches must consume text. Results report
matchCount, changed, and sorted changedParts; zero matches are a
successful unchanged result. Batch results add these receipts under
textReplacements. All replacements are single pass, preserve split-run
ownership by assigning new text to the first matched run, retain unknown XML,
support strict and transitional OOXML, and participate in normal batch rollback
and post-mutation validation.
Raw replacement is also available inside the same atomic batch:
{
"operation": "replace-xml-part",
"part": "/word/document.xml",
"xml": "<w:document xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\"><w:body><w:p/></w:body></w:document>"
}Only existing XML parts may be replaced. [Content_Types].xml, root and
part-level relationship files, binary parts, DTDs, external entities, non-UTF-8
input, and replacement roots with a different local name or namespace are
rejected. Direct raw-set input is limited to 8 MiB. Inline raw output is
limited to 1 MiB; use its --output option to export larger original part bytes
without modifying the Office package. Raw export refuses to overwrite an
existing destination. Every replacement runs through the normal semantic and
OPC post-mutation validation, and any failure rolls back the whole batch.
Typed part creation is also batchable:
{
"operation": "add-part",
"parent": "/slide[1]",
"type": "chart"
}The batch result keeps the existing ordered paths ledger, adds swaps
receipts containing the post-mutation first and second paths, and adds
createdParts receipts containing part, ownerPart, relationshipId, and
type. Text replacement receipts are reported separately under
textReplacements so a successful zero-match operation remains distinguishable
from a content change. Word supports chart, header, and footer carriers at /;
Spreadsheet supports chart carriers under a worksheet; Presentation supports
chart carriers under a slide. A carrier is a valid blank XML part with content
type and owner relationship. It is not visible in document layout until a typed
operation or explicit XML replacement references the returned relationship ID
from the owner XML.
Typed formatting is batchable through the same public mutation contract:
{
"operation": "set-text-format",
"path": "/body/p[1]/r[1]",
"format": {
"bold": true,
"underline": "double",
"script": "superscript",
"strikethrough": true,
"doubleStrikethrough": false,
"textCase": "small-caps",
"highlight": "yellow",
"language": "en-US",
"fontFamily": "Aptos",
"fontSizeCentipoints": 1150,
"textColor": { "red": 0, "green": 102, "blue": 204 }
}
}fontSizeCentipoints is an integer count of 1/100 point; the CLI accepts the
equivalent point value through --font-size. underline accepts none,
single, or double, and script accepts baseline, superscript, or
subscript. strikethrough is supported by Word and Spreadsheet;
Presentation rejects it before mutation. textCase, highlight, and
language apply to Word and Presentation runs. doubleStrikethrough applies
only to Word. The portable highlight palette is none, the six bright colors,
black/white, and the dark or light gray/color variants exposed by the typed
schema. An empty format object, invalid BCP-47 shape, unknown properties,
invalid RGB components, and unsupported target/property combinations fail the
whole batch. Spreadsheet style records are cloned and deduplicated without
replacing unrelated style children or attributes.
Spreadsheet cell presentation uses its own typed mutation rather than adding
non-text properties to set-text-format:
{
"operation": "set-cell-format",
"path": "/Sheet1/A1:C3",
"format": {
"numberFormat": "currency",
"fill": {
"kind": "solid",
"color": { "red": 255, "green": 242, "blue": 204 }
},
"border": {
"left": {
"kind": "line",
"style": "thin",
"color": { "red": 128, "green": 128, "blue": 128 }
},
"right": { "kind": "line", "style": "thin" },
"top": { "kind": "line", "style": "thin" },
"bottom": { "kind": "line", "style": "double" },
"diagonalUp": false,
"diagonalDown": false
},
"verticalAlignment": "center",
"wrapText": true,
"textRotation": 0,
"indent": 1,
"shrinkToFit": false,
"readingOrder": "left-to-right"
}
}numberFormat accepts an explicit Excel format code or general, number,
currency, accounting, percent, scientific, text, date, time, or
datetime. Format codes are limited to 255 characters, four sections, and
balanced quotes/brackets. Fill is either none or one solid 24-bit RGB color.
Each border side is an explicit none or line value. Lines accept thin,
medium, thick, double, dashed, dotted, dashDot, dashDotDot,
hair, mediumDashed, mediumDashDot, mediumDashDotDot, or
slantDashDot, plus an optional 24-bit RGB color. The shared diagonal line is
controlled independently from diagonalUp and diagonalDown. Vertical
alignment accepts top, center, bottom, justify, or
distributed; rotation accepts 0–180 or 255 for stacked text; indentation is
0–255; and reading order is context, left-to-right, or right-to-left.
Unknown fields, empty format objects, invalid values, and non-Spreadsheet
targets fail the whole batch. Native semantic HTML/SVG previews expose the
observed values as inert data-* attributes; they do not claim Excel layout
fidelity.
Spreadsheet merged cells are batchable through two closed mutations:
{
"operation": "merge-cells",
"path": "/Sheet1/A1:C1"
}Use unmerge-cells with the exact same path to remove the merge. Range order
and case are normalized. An exact repeated merge and an absent exact unmerge
are unchanged successes. A partial overlap, a range intersecting a Spreadsheet
table, or any unmerge range that intersects but does not exactly equal an
existing merge fails the complete batch. The latter error reports
validRanges; callers must unmerge each exact range rather than request a
destructive sweep. Strict and
transitional OOXML are retained, unknown mergeCells data is preserved, and
removing the final merge fails closed if deleting its container would discard
unknown attributes or children. Semantic cell reads expose merge and
mergeAnchor; range reads expose merge=true|false; and mergeCell queries
return stable nodes. HTML/SVG carry the same facts only as inert attributes.
Hyperlinks use the same typed batch contract and remain inert data:
{
"operation": "set-hyperlink",
"path": "/body/p[1]",
"hyperlink": {
"target": {
"kind": "external",
"uri": "https://example.com/report"
},
"display": "Open report",
"tooltip": "A3S report"
}
}Use { "kind": "internal", "location": "section_1" } for a Word
bookmark, Sheet1!B2 for a Spreadsheet location, or /slide[2] for a
Presentation slide jump. Word accepts body, header, and footer paragraph paths
when adding and the returned hyperlink path when updating. Spreadsheet accepts
one cell or a bounded rectangular range; only a missing single cell is created,
and a range link does not rewrite cell contents. Presentation accepts a shape
or its hyperlink path and does not accept separate display text. Removing a
hyperlink or its owning paragraph, cell/range, shape, or slide garbage-collects
only an unused hyperlink or slide relationship. Strict and transitional OOXML
namespaces are preserved.
Legacy comments use their own typed batch variants rather than generic properties:
{
"operation": "add-comment",
"parent": "/slide[1]",
"comment": {
"author": "Alice",
"text": "Review this slide",
"initials": "AL",
"position": { "xEmu": 914400, "yEmu": 457200 }
}
}Use set-comment with a partial update object and the stable returned path.
Word accepts author, initials, and plain text on main-document comments;
Spreadsheet accepts author and plain text for classic cell notes; Presentation
also accepts a complete position. Removing a comment uses the ordinary typed
remove mutation. Unknown OOXML attributes and extension nodes survive these
updates, and strict/transitional relationship and root dialects are retained.
Modern threaded comments and replies, resolved state, writable comment dates,
rich comment bodies, Word header/footer anchors, and Spreadsheet threaded
comments are intentionally not represented yet.
Typed Spreadsheet content values use an explicit nested type, for example:
{
"operation": "set-cell-value",
"path": "/Sheet1/C1",
"value": {
"type": "formula",
"expression": "SUM(A1:B1)"
}
}Formula mutation removes one optional leading =, parses the bounded body into
a source-spanned typed AST, stores the original normalized OOXML formula text,
and marks the workbook for recalculation. It does not implicitly calculate the
workbook. The parser covers scalar and error literals, Excel operator
precedence, function calls and omitted arguments, parentheses and array
constants, names and structured references, A1 cell/row/column references with
quoted, 3D, or external qualifiers, and range/intersection/union operators.
Invalid syntax returns use.office.spreadsheet_formula_invalid with zero-based
UTF-8 byte and character offsets before any package mutation. Structural edits
continue to rewrite supported A1 references without evaluating formulas.
NativeOfficeDocument::formula_dependency_graph and
calculate_spreadsheet_formulas provide read-only graph and calculation
results. NativeOfficeEditor::recalculate_spreadsheet_formulas, the
recalculate-spreadsheet-formulas batch/MCP mutation, exact replay, and the
CLI command above atomically persist supported cached values and spills. Excel
formula breadth beyond the closed native registry, disjoint or non-canonical
structured-reference forms, qualified functions, external-workbook
calculation, and full cross-application conformance remain rich-Spreadsheet
delivery gates.
The native package, semantic, and editor APIs are available directly to Rust callers:
use a3s_office::{
NativeOfficeComment, NativeOfficeCommentUpdate, NativeOfficeDocument,
NativeOfficeEditor, NativeOfficeHighlightColor, NativeOfficeHorizontalAlignment,
NativeOfficeHyperlink,
NativeOfficeInsertPosition, NativeOfficePackage, NativeOfficeRenderFormat,
NativeOfficeReplayArtifact, NativeOfficeRgbColor, NativeOfficeTextCase,
NativeOfficeTextFormat, NativeOfficeTextScript, NativeOfficeUnderline,
NativeSpreadsheetBorder, NativeSpreadsheetBorderLine, NativeSpreadsheetBorderStyle,
NativeSpreadsheetCellFormat, NativeSpreadsheetFill,
NativeSpreadsheetDataValidation, NativeSpreadsheetDataValidationErrorStyle,
NativeSpreadsheetDataValidationOperator, NativeSpreadsheetDataValidationType,
NativeSpreadsheetNamedRange, NativeSpreadsheetNamedRangeScope,
NativeSpreadsheetReadingOrder, NativeSpreadsheetSort,
NativeSpreadsheetSortKey, NativeSpreadsheetTable,
NativeSpreadsheetTableStyle, NativeSpreadsheetVerticalAlignment,
};
# async fn inspect() -> Result<(), Box<dyn std::error::Error>> {
let mut package = NativeOfficePackage::open("report.docx").await?;
let document_xml = package.part("/word/document.xml")?;
println!("{} bytes", document_xml.len());
// Format engines will use this API without dropping unknown OOXML parts.
package.save().await?;
let blank = NativeOfficePackage::create("blank.xlsx").await?;
println!("created {:?}", blank.kind());
let mut workbook = NativeOfficeEditor::create("styled.xlsx").await?;
workbook.set_cell_format(
"/Sheet1/A1:C3",
NativeSpreadsheetCellFormat {
number_format: Some("currency".into()),
fill: Some(NativeSpreadsheetFill::Solid {
color: NativeOfficeRgbColor::new(0xFF, 0xF2, 0xCC),
}),
border: Some(NativeSpreadsheetBorder {
bottom: Some(NativeSpreadsheetBorderLine::Line {
style: NativeSpreadsheetBorderStyle::Double,
color: Some(NativeOfficeRgbColor::new(0x80, 0x80, 0x80)),
}),
..NativeSpreadsheetBorder::default()
}),
vertical_alignment: Some(NativeSpreadsheetVerticalAlignment::Center),
wrap_text: Some(true),
reading_order: Some(NativeSpreadsheetReadingOrder::LeftToRight),
..NativeSpreadsheetCellFormat::default()
},
)?;
workbook.merge_cells("/Sheet1/A1:C3")?;
let validation_path = workbook.add_data_validation(
"/Sheet1",
NativeSpreadsheetDataValidation::new(
NativeSpreadsheetDataValidationType::Whole,
"D2:D50",
"18",
)
.with_operator(NativeSpreadsheetDataValidationOperator::Between)
.with_formula2("120")
.with_allow_blank(false)
.with_error_message(
NativeSpreadsheetDataValidationErrorStyle::Warning,
"Age outside range",
"Enter an age from 18 through 120",
),
)?;
println!("created {validation_path}");
let named_range_path = workbook.add_named_range(
NativeSpreadsheetNamedRange::new("Revenue", "'Sheet1'!$A$2:$A$20")
.with_scope(NativeSpreadsheetNamedRangeScope::Workbook)
.with_comment("Workbook revenue"),
)?;
println!("created {named_range_path}");
let table_path = workbook.add_spreadsheet_table(
"/Sheet1",
NativeSpreadsheetTable::new("Sales", "G1:I4", ["Name", "Qty", "Price"])
.with_style(NativeSpreadsheetTableStyle::Medium { number: 4 }),
)?;
println!("created {table_path}");
let sort_path = workbook.sort_spreadsheet_range(
"/Sheet1/G1:I4",
NativeSpreadsheetSort::new(vec![
NativeSpreadsheetSortKey::descending("H"),
NativeSpreadsheetSortKey::ascending("G"),
])
.with_header(true),
)?;
println!("sorted records at {sort_path}");
workbook.save().await?;
let document = NativeOfficeDocument::open("report.docx").await?;
println!("{}", document.text_view().text);
let html = document.render(NativeOfficeRenderFormat::Html)?;
println!("{} {} bytes", html.sha256, html.byte_length);
let headings = document.query("p[style=Heading1]")?;
println!("{} heading(s)", headings.len());
let mut editor = NativeOfficeEditor::open("report.docx").await?;
let raw = editor.raw_xml_part("/word/document.xml")?;
println!("{} {}", raw.part, raw.sha256);
let header = editor.add_part("/", a3s_office::NativeOfficePartType::Header)?;
println!("{} {}", header.part, header.relationship_id);
editor.set_text("/body/p[1]", "Updated")?;
editor.set_text_format(
"/body/p[1]/r[1]",
NativeOfficeTextFormat {
bold: Some(true),
underline: Some(NativeOfficeUnderline::Double),
script: Some(NativeOfficeTextScript::Superscript),
strikethrough: Some(true),
double_strikethrough: Some(false),
text_case: Some(NativeOfficeTextCase::SmallCaps),
highlight: Some(NativeOfficeHighlightColor::Yellow),
language: Some("en-US".into()),
font_family: Some("Aptos".into()),
font_size_centipoints: Some(1400),
text_color: Some(NativeOfficeRgbColor::new(0x12, 0x34, 0x56)),
..NativeOfficeTextFormat::default()
},
)?;
editor.set_text_format(
"/body/p[1]",
NativeOfficeTextFormat {
alignment: Some(NativeOfficeHorizontalAlignment::Center),
..NativeOfficeTextFormat::default()
},
)?;
let hyperlink = NativeOfficeHyperlink::external("https://example.com/report")?
.with_display("Open report")
.with_tooltip("A3S report");
let hyperlink_path = editor.set_hyperlink("/body/p[1]", hyperlink)?;
println!("created {hyperlink_path}");
let comment_path = editor.add_comment(
"/body/p[1]",
NativeOfficeComment::new("Alice", "Please review this paragraph")?
.with_initials("AL"),
)?;
editor.set_comment(
&comment_path,
NativeOfficeCommentUpdate {
text: Some("Reviewed".into()),
..NativeOfficeCommentUpdate::default()
},
)?;
let added = editor.add_paragraph("/body", "Summary")?;
let moved = editor.move_node(
added,
None,
Some(NativeOfficeInsertPosition::at_index(0)),
)?;
let copied = editor.copy_node(&moved, None, None, None)?;
let swapped = editor.swap_nodes(moved, copied)?;
editor.remove(swapped.second)?;
let table = editor.add_table("/body", 2, 3)?;
editor.set_text(format!("{table}/tr[1]/tc[1]"), "Name")?;
editor.save().await?;
let mut template = NativeOfficeEditor::open("template.docx").await?;
let merge = template.merge_template(&serde_json::json!({
"customer": {"name": "A3S Lab"}
}))?;
println!("{} replacement(s)", merge.replaced_count);
template.save_as_new("merged.docx").await?;
let replay = NativeOfficeReplayArtifact::dump(&editor.snapshot()?, "/")?;
let mut restored = NativeOfficeEditor::create("restored.docx").await?;
restored.apply_replay(&replay)?;
restored.save().await?;
# Ok(())
# }Managed compatibility installation accepts only approved HTTPS release
origins, bounds the download, verifies the publisher SHA-256, stages outside
the active version, and activates atomically. Compatibility execution sets
OFFICECLI_SKIP_UPDATE=1 so upgrades remain explicit A3S operations.
The native engine does not copy OfficeCLI's private resident protocol. The
explicit office-native target now provides typed in-process sessions over its
own standard MCP surface. Until the default-route promotion, a lost
compatibility response can return
use.office.outcome_unknown; callers must not retry it automatically.
See Native Office Engine for the complete requirements, compatibility scope, safety invariants, delivery gates, and migration plan.