# How OfficeCLI Batch Command Ensures Atomic Transactions and Handles Rollbacks

> Learn how the OfficeCLI batch command ensures atomic transactions and handles rollbacks by using temporary files, guaranteeing data integrity for your operations.

- Repository: [OfficeAI/OfficeCLI](https://github.com/iofficeai/OfficeCLI)
- Tags: internals
- Published: 2026-08-10

---

**The OfficeCLI `batch` command guarantees atomicity by executing all operations on a temporary copy and only promoting changes to the original document if every command succeeds; on any failure, the temporary file is deleted and the original remains untouched.**

OfficeCLI's `batch` command is designed for reliable document automation, treating collections of document-modifying operations as **single atomic transactions**. This article examines the implementation details in the iOfficeAI/OfficeCLI repository, showing how the tool prevents partial writes and automatically rolls back on errors.

## Atomic vs. Best-Effort Mode

The batch command determines its transaction behavior based on two factors: the `--best-effort` flag and the composition of commands in the batch.

By default, the batch runs **atomically** when any mutating operation is present. The code checks whether items contain verbs outside the `ReadOnlyBatchVerbs` set:

```csharp
// src/officecli/CommandBuilder.Batch.cs#L29-L37
var atomic = !bestEffort && items.Any(it => !ReadOnlyBatchVerbs.Contains(it.Command ?? ""));

```

- **Atomic mode (default)**: All-or-nothing execution; any failure triggers rollback
- **Best-effort mode (`--best-effort`)**: Applies successful commands, leaves partial changes in place

## Temporary File Creation and Isolation

When atomic execution is required, OfficeCLI creates a **same-directory temporary copy** before touching the original document. This isolation strategy protects the source file from corruption during the batch operation.

The temporary filename is carefully constructed to avoid length limits:

```csharp
// src/officecli/CommandBuilder.Batch.cs#L44-L52
var tmpStem = TruncateStemForTempName(Path.GetFileNameWithoutExtension(targetPath));
var tmpPath = Path.Combine(tmpDir, $".{tmpStem}.batch-{Guid.NewGuid():N}{tmpExt}");

```

Key characteristics of this approach:

- The original file is **never opened for write** during batch execution
- The GUID ensures unique temporary names for concurrent operations
- Same-directory placement enables atomic `File.Replace` operations

## In-Memory Execution and Deferred Save

With the temporary copy in place, the handler executes the batch:

```csharp
// Handled via DocumentHandlerFactory.Open(workPath, editable:true)
// followed by RunNonResidentBatch for each BatchItem

```

All modifications occur **in-memory** until the handler disposes. The `DocumentHandlerFactory` opens the temporary path with editable permissions, and each `BatchItem` is processed through `RunNonResidentBatch`. Results aggregate into `BatchResult` objects that track per-command success.

## Rollback Mechanism on Failure

The rollback logic executes after batch completion, evaluating whether to commit or discard changes:

```csharp
// src/officecli/CommandBuilder.Batch.cs#L74-L82
if (tmpPath != null)
{
    if (batchSuccessLocal)
        File.Replace(tmpPath, targetPath, null);
    else
        File.Delete(tmpPath);
}

```

The decision path is straightforward:

| Condition | Action |
|-----------|--------|
| `batchSuccessLocal == true` | Atomically replace original with temp copy |
| `batchSuccessLocal == false` | Delete temporary file, original unchanged |

This ensures **true atomicity**: either all changes persist, or none do.

## Atomic Promotion on Success

When execution succeeds, `File.Replace` performs an **atomic swap** of the temporary file over the original. This Windows API call:

- Preserves the original file's permissions and attributes
- Eliminates the window for corruption between delete and recreate
- Handles cross-filesystem edge cases gracefully

## Error Handling Flags

OfficeCLI provides additional controls for fine-tuning batch behavior:

- **`--stop-on-error`**: Forces immediate abort on first failure (relevant for non-atomic batches; atomic mode inherently stops)
- **`--force`**: Bypasses document protection checks while maintaining atomic semantics

These flags modify execution flow without compromising the core transaction guarantees.

## Cleanup of Orphaned Temporary Files

To prevent disk pollution from crashed or interrupted batches, the implementation performs **pre-execution cleanup**:

- Scans for stale `.batch-*` files
- Removes temporary files older than **15 minutes**
- Executes before creating new temporary copies

This defensive measure ensures long-running CLI processes don't accumulate garbage across failures.

## Practical Usage Examples

### Default Atomic Batch

```bash

# Aborts and rolls back on any error

officecli batch mydoc.docx --commands '[{"command":"add","parent":"/slide[1]","type":"shape","props":{"text":"Hi"}},{"command":"set","path":"/slide[2]/shape[5]","props":{"color":"red"}}]'

```

### Best-Effort Batch

```bash

# Applies successful items, leaves partial changes

officecli batch mydoc.docx --best-effort --commands '[{"command":"add","parent":"/slide[1]","type":"shape","props":{"text":"Hi"}},{"command":"set","path":"/slide[2]/shape[5]","props":{"color":"red"}}]'

```

### Explicit Early Abort

```bash

# Fail fast for non-atomic scenarios

officecli batch mydoc.docx --stop-on-error --commands '[...]'

```

## Summary

- **Atomic detection**: Automatically triggered by mutating verbs unless `--best-effort` is specified
- **Isolation via temp files**: Original document protected from partial writes
- **In-memory processing**: All changes deferred until batch completion
- **Binary rollback decision**: `File.Replace` on success, `File.Delete` on failure
- **Automatic cleanup**: 15-minute TTL on orphaned temporary files
- **Granular controls**: `--stop-on-error` and `--force` for edge case handling

The implementation in [`CommandBuilder.Batch.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/CommandBuilder.Batch.cs) delivers production-grade transaction semantics for document automation workflows.

## Frequently Asked Questions

### How does OfficeCLI determine whether to use atomic mode or best-effort mode?

The batch command checks the `--best-effort` flag and scans command verbs against `ReadOnlyBatchVerbs`. If `--best-effort` is absent and at least one command is mutating, atomic mode activates. This logic resides in [`CommandBuilder.Batch.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/CommandBuilder.Batch.cs) lines 29-37.

### What happens to my original document if the batch fails halfway through?

The original document remains completely unchanged. The batch operates on a temporary copy created in the same directory. On any failure, the code executes `File.Delete(tmpPath)` and abandons the temporary file without touching the source.

### Can I recover a batch that was interrupted by a system crash?

Partial batches cannot be recovered. However, the original document is never at risk during crashes because all writes target the temporary copy first. Orphaned temporary files are automatically cleaned up on subsequent batch runs if they exceed 15 minutes of age.

### Does `--stop-on-error` change the atomic rollback behavior?

No. `--stop-on-error` controls early exit for **non-atomic** batches. In atomic mode (the default), the batch inherently stops on first failure and rolls back. The flag becomes relevant when you explicitly use `--best-effort` but still want fail-fast semantics.