How OfficeCLI Batch Command Ensures Atomic Transactions and Handles Rollbacks
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:
// 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:
// 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.Replaceoperations
In-Memory Execution and Deferred Save
With the temporary copy in place, the handler executes the batch:
// 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:
// 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
# 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
# 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
# Fail fast for non-atomic scenarios
officecli batch mydoc.docx --stop-on-error --commands '[...]'
Summary
- Atomic detection: Automatically triggered by mutating verbs unless
--best-effortis specified - Isolation via temp files: Original document protected from partial writes
- In-memory processing: All changes deferred until batch completion
- Binary rollback decision:
File.Replaceon success,File.Deleteon failure - Automatic cleanup: 15-minute TTL on orphaned temporary files
- Granular controls:
--stop-on-errorand--forcefor edge case handling
The implementation in 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 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.
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 →