# Buffered transact ops are the only transaction model

All three adapters (dynamodb, sqlite, idb) expose exactly one transaction surface: entity services pre-build validated op descriptors (`insertOp`/`updateOp`/`deleteOp`/`restoreOp` — the latter two toggle the `_d` tombstone) outside any transaction, and `table.transact(ops)` applies the batch atomically, broadcasting only after commit. Ops are deferred writes: each one is concretized by a pure `apply(u)` when `transact` assigns the commit-time write cursor, so an unstamped write is unrepresentable and `transact` accepts nothing but entity ops (raw table-level writes are not transaction currency). Optimistic concurrency (`expectedU`, the analog of DynamoDB's `ConditionExpression`) replaces read-inside-transaction logic; `updateOp`, `deleteOp`, and `restoreOp` can opt out via `lastWriteWins: true`.

The model is forced by the weakest engines and adopted everywhere for consistency. DynamoDB's `TransactWriteItems` is inherently a one-shot batch. IndexedDB transactions auto-commit the moment control returns to the event loop, so an interactive `begin`/`commit`/`rollback` surface is impossible to implement honestly there. SQLite _could_ support interactive transactions — and originally did, as `transaction(effect)` — but that gave it a private vocabulary no other adapter could conform to. We deliberately dropped the capability so that all adapters share one contract, enforced by the shared conformance suite.

Rejected: keeping sqlite's `transaction(effect)` alongside `transact(ops)` (two transaction vocabularies on one adapter, and code written against the richer one is unportable); sequential best-effort writes (API parity without the atomicity guarantee callers would assume).

Superseded in part by ADR 0006: `expectedU` no longer replaces read-inside-transaction logic at the _caller_; `transact` performs the reads itself at commit time. The one-shot batch model and the absence of interactive transactions are unchanged, but an op is no longer "concretized by a pure `apply(u)`" — `apply` now also takes the item `transact` read, and may answer that no write is needed.
