OfficeCLI Batch Operations: Atomic Multi-Command Execution Explained
OfficeCLI batch operations execute multiple document commands as a single atomic transaction, automatically rolling back all changes if any step fails.
The iOfficeAI/OfficeCLI repository provides a powerful batch verb that transforms scattered document mutations into reliable, all-or-nothing operations. Whether you're automating slide creation, bulk cell updates, or complex document restructuring, understanding the atomic multi-command execution model ensures your scripts never leave documents in an inconsistent state.
How OfficeCLI Batch Operations Achieve Atomicity
Atomicity in OfficeCLI centers on the ResidentServer component, a persistent in-process server that mediates all document operations. When you submit a batch, the server orchestrates three protective mechanisms that guarantee transactional integrity.
Pre-Batch Snapshotting and Automatic Rollback
Before executing any command, ResidentServer captures the on-disk document state. If a single batch item fails, the entire sequence is discarded and the original snapshot reloads. This pre-batch snapshot mechanism ensures no partial changes survive a failed operation.
The rollback logic is enforced in src/officecli/ResidentServer.cs around lines 1312-1316:
// Snapshot captured before batch begins
var preBatchSnapshot = CreateDocumentSnapshot(document);
// On failure: restore original state
if (batchHadFailure) {
RestoreFromSnapshot(document, preBatchSnapshot);
return BatchResult.RolledBack;
}
This approach eliminates the risk of corrupted documents during automation workflows.
Deferred Saving for Performance Optimization
Inside an active batch, every mutation executes with the DeferSave flag enabled. Rather than serializing the document after each command, ResidentServer accumulates changes in memory and performs exactly one save after successful batch completion.
This optimization—documented at lines 1271-1274 of ResidentServer.cs—reduces I/O from N individual saves to a single write:
// Each batch item executes with deferred persistence
ExecuteMutation(command, flags: MutationFlags.DeferSave);
// Single commit point after all items succeed
if (!batchHadFailure) {
PersistDocument(document); // One write, not N
}
For large batches containing dozens or hundreds of operations, this yields substantial performance gains.
Command Flow: From JSON to Execution
The CommandBuilder.Batch class drives batch execution. It parses incoming JSON, validates each item, and delegates to ExecuteBatchItem while tracking overall success.
Batch Entry Point and Item Iteration
Processing begins at line 82 of src/officecli/CommandBuilder.Batch.cs:
public BatchResult ExecuteBatch(BatchRequest request) {
foreach (var item in request.Items) {
var result = ExecuteBatchItem(item);
if (!result.Success) _lastBatchHadFailure = true;
}
return FinalizeBatch();
}
The high-level coordination—including watch notifications and atomic commit/rollback decisions—resides at lines 727-730.
Error Handling and Exit Codes
After processing all items, the server writes a single envelope indicating batch success or failure (lines 810-815). Crucially, any failure forces a non-zero exit code at lines 894-896, enabling shell scripts and CI pipelines to detect problems immediately:
// Exit code propagation for shell integration
if (_lastBatchHadFailure) {
Environment.ExitCode = 1; // Signals failure to calling process
}
Batch JSON Format and Supported Commands
Each batch item requires a command field identifying the operation, a path specifying the target location, and optional props for additional parameters.
| Command | Purpose | Example Use Case |
|---|---|---|
add |
Insert new elements | Add shapes, charts, paragraphs |
set |
Modify existing properties | Update values, styles, formatting |
remove |
Delete elements | Remove slides, cells, text ranges |
move |
Relocate elements | Reorder slides, reposition shapes |
swap |
Exchange two elements | Swap table rows, slide positions |
raw-set |
Direct property injection | Set low-level OOXML properties |
The Python and Node SDKs expose identical schemas, enabling programmatic batch construction with full IDE support.
Practical Code Examples
CLI: JSON Batch via Standard Input
Pipe a JSON array directly to the batch verb:
cat <<'EOF' | officecli batch my-presentation.pptx
[
{"command":"add","path":"/slide[1]","type":"shape","props":{"name":"Title","text":"Quarterly Report"}},
{"command":"set","path":"/slide[1]/shape[Title]","props":{"font.size":28}},
{"command":"add","path":"/slide[1]","type":"chart","props":{"type":"bar"}}
]
EOF
Note: The single-quoted heredoc (
<<'EOF') prevents shell expansion, ensuring JSON special characters pass through unchanged.
Python SDK: Object-Based Batches
from officecli import Document
doc = Document.open("budget.xlsx")
batch_items = [
{"command": "set", "path": "/Sheet1/A1", "props": {"value": 12345}},
{"command": "set", "path": "/Sheet1/B1", "props": {"value": 67890}},
{"command": "add", "path": "/Sheet1", "type": "chart", "props": {"type": "pie", "data_range": "A1:B1"}}
]
# Atomic: all three operations succeed or none persist
doc.batch(batch_items)
doc.save()
doc.close()
If the chart creation fails (invalid range, for example), cells A1 and B1 remain unchanged thanks to automatic rollback.
Node.js SDK: Async Error Handling
const { Document } = require("officecli");
(async () => {
const doc = await Document.open("report.docx");
try {
await doc.batch([
{ command: "add", path: "/body/p[1]", props: { text: "Executive Summary" } },
{ command: "set", path: "/body/p[2]", props: { style: "Heading1" } },
{ command: "swap", path: "/body/p[2]", props: { with": "/body/p[3]" } }
]);
await doc.save();
console.log("Batch committed successfully");
} catch (e) {
// Document automatically restored to pre-batch state
console.error("Batch failed, no changes applied:", e.message);
process.exit(1);
} finally {
await doc.close();
}
})();
The catch block receives structured error information while the document remains pristine.
Key Source Files for Deep Dives
| File | Responsibility | Critical Lines |
|---|---|---|
src/officecli/ResidentServer.cs |
Snapshot management, deferred saves, rollback logic | 1271-1274 (defer), 1312-1316 (rollback), 810-896 (finalization) |
src/officecli/CommandBuilder.Batch.cs |
JSON parsing, item iteration, watch notifications | 82-85 (entry), 727-730 (coordination) |
sdk/python/officecli.py |
Python SDK batch API surface | Document.batch() method |
sdk/node/index.js |
Node.js SDK batch API surface | Document.prototype.batch |
plugins/plugin-protocol.md |
Protocol specification for plugin-authored batches | Batch envelope schema |
These implementations collectively define the atomic multi-command execution guarantee that distinguishes OfficeCLI from simpler append-only automation tools.
Summary
OfficeCLI batch operations deliver true atomicity for document automation through three core mechanisms:
- Pre-batch snapshots enable complete rollback on any failure
- Deferred saving minimizes I/O by persisting only once per successful batch
- Structured error propagation ensures calling processes detect failures reliably
The ResidentServer architecture in src/officecli/ResidentServer.cs and the CommandBuilder.Batch orchestration layer make these guarantees transparent across CLI, Python, and Node.js interfaces.
Frequently Asked Questions
What happens if one command in a batch fails?
The entire batch rolls back automatically. ResidentServer restores the pre-batch snapshot, discarding all changes from every command in the sequence. The document remains exactly as it was before the batch started, and the process exits with code 1.
Can I mix different command types in a single batch?
Yes. Batches freely combine add, set, remove, move, swap, and raw-set operations targeting any document regions. The atomic guarantee applies uniformly regardless of command diversity or complexity.
How does deferred saving affect rollback reliability?
Deferred saving operates entirely in memory until final commit. Since no partial state reaches disk, rollback simply abandons the in-memory mutations and reloads the original snapshot. This design makes atomicity robust even for batches containing hundreds of operations.
Are batch operations available in all OfficeCLI SDKs?
Yes. The Python SDK (officecli.py), Node.js SDK (index.js), and native CLI all implement identical batch semantics using the same underlying ResidentServer. Batches constructed programmatically or via JSON stdin receive identical atomicity guarantees.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →