OfficeCLI Batch Operations with Atomic Transactions: Complete Implementation Guide

OfficeCLI executes multiple document edits as atomic transactions by default, using temporary file staging and automatic rollback if any batch item fails.

The OfficeCLI batch command provides a robust pipeline for applying complex document transformations through JSON-defined operations. According to the iOfficeAI/OfficeCLI source code, the implementation guarantees data integrity through a three-layer architecture that separates command parsing, execution replay, and transactional file handling.

JSON Input Sources and Parsing

OfficeCLI accepts batch commands through three mutually exclusive channels, enforced in CommandBuilder.Batch.cs (lines 110-113).

Supported Input Methods

  • --commands — inline JSON array string
  • --input <file> — path to JSON file (use - for stdin)
  • stdin — automatic fallback when no flags provided

The parser automatically unwraps {"data":[…]} envelopes produced by dump --json (lines 66-74), enabling seamless pipelines without external tools like jq.


# Pipeline from dump to batch without jq manipulation

officecli dump mydoc.pptx --json | officecli batch newdoc.pptx --commands "$(cat)"

BatchItem Validation and Strict Schema

Before execution, every batch item undergoes strict validation in CommandBuilder.Batch.cs (lines 96-112).

Validation rules enforced:

  1. Known fields only — unknown properties trigger an error listing valid BatchItem.KnownFields
  2. Null rejection[null] entries are blocked (lines 123-128) to prevent NullReferenceException
  3. Shape validation — each object must conform to the expected structure defined in BatchTypes.cs
// From CommandBuilder.Batch.cs lines 96-112
foreach (var item in items)
{
    var unknown = item.Properties.Keys.Except(BatchItem.KnownFields);
    if (unknown.Any())
        throw new ArgumentException($"Unknown fields: {string.Join(", ", unknown)}");
}

Atomic vs. Best-Effort Execution Modes

The CLI determines transactional behavior through logic in CommandBuilder.Batch.cs (line 36).

Mode Selection Logic

var atomic = !bestEffort && items.Any(it => !ReadOnlyBatchVerbs.Contains(it.Command ?? ""));
Mode Trigger Behavior
Atomic (default) No --best-effort flag + contains mutating verbs All-or-nothing transaction with temp file staging
Best-effort --best-effort flag present Apply successful items, skip failures
Read-only pass-through Only get, query, dump verbs No temp file created, executes directly

The ReadOnlyBatchVerbs set is defined in BatchTypes.cs (lines 125-133) and includes operations that never modify the document DOM.

Temporary File Architecture for Atomic Guarantees

When atomic mode is active, CommandBuilder.Batch.cs implements a multi-stage file handling protocol (lines 29-38, 45-52, 115-131).

Staging Process

  1. Symlink resolution — follows symlinks to the true target path (lines 38-41)
  2. Unique temp name generation — truncates stems to stay under 255-byte limits (TruncateStemForTempName, lines 96-112)
  3. Stream-wise copy — creates batchprep-<guid> then renames to batch-<guid> (lines 115-131)
  4. Orphan sweep — deletes stale temp files older than 15 minutes (lines 53-84)

Permission Preservation

  • Unix: SetUnixFileMode preserves original permissions
  • Windows: File.Replace maintains destination ACLs (lines 126-130)

Resident Server Integration

OfficeCLI detects running resident processes and adapts its atomic strategy accordingly (CommandBuilder.Batch.cs, lines 90-104).

Resident present: The batch is forwarded as a single batch request to ResidentServer.cs. The resident applies items in-memory and defers physical writes until save, close, or idle-autosave triggers.

Resident absent: The CLI opens the document once, executes RunNonResidentBatch (lines 137-148) with DeferSave = true for Word documents, and promotes the temp file only after all items succeed (lines 174-186).


# Resident handles atomicity in-memory; disk write deferred

officecli batch mydoc.docx --input ops.json

# Follow with explicit save or let idle-autosave complete

officecli save mydoc.docx

Exit Codes and Result Formatting

The CLI produces structured output through BatchResult objects with hierarchical exit codes (CommandBuilder.Batch.cs, lines 158-176).

Exit Code Condition
0 Complete success
1 One or more batch items failed
2 Success with warnings (e.g., LaTeX diagnostics)

JSON envelope structure:

{
  "success": false,
  "results": [
    {"index": 0, "command": "add", "status": "ok"},
    {"index": 1, "command": "set", "status": "error", "message": "Path not found"}
  ],
  "warnings": ["LaTeX: undefined command \\foo"]
}

Plain-text mode prints human-readable summaries to stdout and warnings to stderr.

Practical Batch Operation Examples

Default Atomic Transaction

officecli batch report.docx --commands '
[
  {"command":"add","parent":"/body","type":"table","props":{"rows":3,"cols":2}},
  {"command":"set","path":"/body/table[1]/cell[1,1]","props":{"text":"Revenue"}},
  {"command":"set","path":"/body/table[1]/cell[2,1]","props":{"text":"$1.2M"}}
]' --json

If the third set fails, the table addition is rolled back and report.docx remains unchanged.

Best-Effort Partial Application

officecli batch legacy.docx --input migrations.json --best-effort

# Successfully applied: 147/150 items

# Failed items logged to stderr with index numbers

Reusable Batch File Pattern

// standard-ops.json
[
  {"command":"query","selector":"shape[type=\"placeholder\"]"},
  {"command":"remove","path":"/slide[1]/shape[type=\"placeholder\"]"},
  {"command":"add","parent":"/slide[1]","type":"shape","props":{"text":"Generated content"}}
]

# Apply to multiple documents

for f in *.pptx; do
  officecli batch "$f" --input standard-ops.json
done

Summary

  • Atomic by default: OfficeCLI treats mutating batches as transactions unless --best-effort is specified
  • Three-layer architecture: Parsing (CommandBuilder.Batch.cs lines 62-94), replay (lines 81-90), and transaction handling (lines 29-38, 45-52, 124-144) operate independently
  • Temp file safety: Stream-wise copies, orphan sweeps, and permission preservation protect document integrity
  • Resident awareness: Automatic fallback to in-memory atomic processing when servers are active
  • Strict validation: Unknown fields and null entries are rejected before execution begins

Frequently Asked Questions

How does OfficeCLI handle batch failures in atomic mode?

The CLI abandons the temporary staging file and leaves the original document untouched. In CommandBuilder.Batch.cs (lines 174-186), the replacement operation File.Replace(tempPath, originalPath, backupPath) only executes when BatchResult.All(r => r.Success) returns true. Failed batches return exit code 1 with detailed per-item error messages.

What is the maximum safe batch size for atomic operations?

The source code imposes no explicit limit. Practical constraints derive from memory usage during JSON parsing and the 15-minute orphan sweep threshold for temp files. Extremely large batches (thousands of items) may benefit from chunking to avoid resident server timeout issues, though the resident's in-memory processing in ResidentServer.cs handles typical workloads efficiently.

Can atomic batches include read-only operations mixed with mutations?

Yes. The atomic decision (line 36) evaluates the presence of any mutating verb, not the ratio. A batch containing 99 query commands and 1 set command triggers full temp-file staging because set is not in ReadOnlyBatchVerbs. This conservative approach ensures consistency regardless of operation ordering.

Why does my pipeline from dump to batch work without JSON manipulation?

CommandBuilder.Batch.cs (lines 66-74) automatically detects and unwraps the {"data":[…]} envelope structure emitted by dump --json. This design eliminates friction in common workflows where dumped document state feeds directly into batch modifications.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →