# OfficeCLI Batch Operations with Atomic Transactions: Complete Implementation Guide

> Implement OfficeCLI batch operations with atomic transactions for reliable document edits. Learn how temporary file staging and automatic rollback ensure data integrity.

- Repository: [OfficeAI/OfficeCLI](https://github.com/iofficeai/OfficeCLI)
- Tags: how-to-guide
- Published: 2026-08-04

---

**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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/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`.

```bash

# 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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/BatchTypes.cs)

```csharp
// 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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/CommandBuilder.Batch.cs) (line 36).

### Mode Selection Logic

```csharp
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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/CommandBuilder.Batch.cs), lines 90-104).

**Resident present:** The batch is forwarded as a single `batch` request to [`ResidentServer.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/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).

```bash

# 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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/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:**

```json
{
  "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

```bash
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

```bash
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

```json
// 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"}}
]

```

```bash

# 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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/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.