OmniGraph does not have `BEGIN` / `COMMIT` / `ROLLBACK`. Branches do that job. This page explains the model, when to use which primitive, and shows worked examples for the patterns that come up most.
If you need to coordinate multiple queries atomically, you fork a branch, run mutations on it, and merge when you're satisfied. If something goes wrong, you delete the branch.
## The atomicity model
Two primitives, two scopes:
| Scope | Primitive | Atomic? | Failure mode |
|---|---|---|---|
| **One `.gq` query** (any number of statements inside) | The query itself — handled by the publisher's atomic manifest commit | Yes — all statements land together or none of them do | The publisher never publishes; target unchanged |
| **Many queries that must succeed together** | Branches: `branch_create` → run N queries on the branch → `branch_merge` | Yes — the merge is a single atomic publish | Drop the branch (`branch_delete`); main is unaffected |
Snapshot isolation is per-query — every read inside one query sees one consistent manifest version. Two concurrent queries on the same branch see independent snapshots; the publisher's CAS catches racing writes.
| Connection-bound session state | Branch-scoped lineage on disk |
| Locks (or MVCC + abort on conflict) | Snapshot isolation per query + three-way merge at branch-join |
| Transaction is invisible to ops | Branch is a durable artifact (visible in `branch_list`, queryable, time-travelable) |
The trade-off: branches are heavier than a connection-scoped transaction (they exist on disk, have a name, show up in `branch_list`), but they fit the agent-as-user model — agents naturally fork branches to plan, batch, and review work. And they're durable: if your process crashes mid-workflow, the branch survives and you can pick up where you left off.
## Worked examples
### 1. Single query, multi-statement (atomic by default)
A `.gq` query with multiple `insert` / `update` statements is one transaction. Either all statements land together at publish time, or none do.
- Each query on the branch is its own publisher commit — so they're individually atomic. Per-query CAS works on branches just like on main.
- The branch lives on disk. Process crash mid-workflow? Re-open and resume.
- Multiple agents can work on different branches in parallel without blocking each other.
- The merge is a three-way merge at the row level. Conflicts surface as `OmniError::MergeConflicts(Vec<MergeConflict>)`, with structured kinds (`DivergentInsert`, `DivergentUpdate`, `DeleteVsUpdate`, …) so callers can handle them programmatically.
### 4. Coordinating multiple agents
Two agents writing to the same graph independently:
Each agent sees a consistent snapshot of `main` at the time it forked. The first merge to `main` lands as a fast-forward (or a no-op if no concurrent change). The second merge runs three-way: rows touched by both branches surface as `MergeConflict`s for the caller to resolve.
This is the workflow MR-797 / agentic loops are designed around: **branches are the unit of "an agent's working set."**
## Failure modes
| Scenario | What happens | Caller action |
|---|---|---|
| Single query fails mid-flight | Publisher never publishes; target unchanged | Read the error, decide whether to retry |
| Concurrent writers race the same `(table, branch)` | Publisher CAS rejects the loser with `ManifestConflictDetails::ExpectedVersionMismatch` | Refresh handle, retry the query |
| Branch with N successful mutations, then merge fails (three-way conflict) | Each individual mutation already committed on the branch; merge surfaces `MergeConflicts` | Inspect, decide whether to keep working on the branch, abandon it (`branch_delete`), or resolve and re-merge |
| One conceptual change, multiple statements | One `.gq` query with multiple statements |
| Bulk import of a related set of records | One `omnigraph load` (the loader is one atomic query under the hood) |
| Many independent changes, no coordination needed | Many separate queries on `main`. Each is its own atomic unit. |
| "Do these N things, all together or not at all" | Branch → run N queries → merge |
| "Try things, evaluate, then commit" | Branch → mutate → read/inspect → merge or delete |
| "Multiple agents writing concurrently" | One branch per agent, merge to `main` at end of agent task |
| "Long-running workflow that may span sessions or process restarts" | Branch (durable on disk) |
## What this model can't do
- **Cross-query atomicity on `main` without a branch.** If you don't want to fork a branch, multiple queries on `main` publish independently. There is no implicit transaction.
- **Long-running interactive transactions.** No `BEGIN` over a connection. Branches are the durable equivalent.
- **"Pessimistic" locks** that serialize writers before they reach the storage layer. Snapshot-MVCC + publisher CAS handles concurrency optimistically; the loser retries.