ADR 0018: Acknowledged durability and isolation contract
Status: Accepted Date: 2026-08-15 Build target: v0.5.x (M6 foundations) Related: ADR 0013 (publication protocol), ADR 0014 (checkpoints), ADR 0015 (write modes), ADR 0020 (NTFS write-through namespace durability amendment), issues #747–#756, adjacent M5 interchange #738/#742/#745
Context
Section titled “Context”ADR 0013 already freezes flush ordering and CURRENT authority. ADR 0015
already names three embedded write modes. Public callers still lacked one
versioned place that answers:
- when a successful write is acknowledged-durable;
- which platforms and filesystems are in scope;
- which isolation and conflict outcomes each write mode provides; and
- which transaction anomalies are prevented versus merely documented.
Without that contract, docs risk either understating immutable-snapshot guarantees or claiming generic ACID / serializability that the engine does not provide. M6 recovery, delta, transaction, and certification work must freeze this vocabulary before changing behavior.
Decision
Section titled “Decision”Normative surface
Section titled “Normative surface”This ADR is the public acknowledged-durability and isolation contract for local
project writes. Machine-readable coverage lives in
tests/contracts/durability-isolation-matrix.json
(graphforge-durability-isolation/1). Narrative architecture lives in
concurrency and recovery.
Semantic changes to acknowledgement, recovery authority, filesystem scope, or isolation outcomes require a new ADR that amends or supersedes this one. Silent doc or code drift is forbidden. ADR 0020 is that explicit amendment for the Windows filesystem scope and platform-native namespace durability barrier.
Acknowledgement boundary
Section titled “Acknowledgement boundary”A caller-visible success means the write is acknowledged-durable only after all of the following have completed on a supported filesystem:
- every staged participant file has been written, closed, and file-flushed;
manifest.jsonhas been written and file-flushed, and the generation tree has completed its platform-native namespace durability barriers;- for optimistic attempts, the private attempt directory has been atomically
promoted into
generations/<generation-uuid>/with the required parent namespace durability barriers; - the exact new
CURRENTbytes have been written to a sibling, file-flushed, atomically replaced or created through the supported platform primitive, and the project-root platform-native namespace durability barrier has completed.
Step 4’s platform-native namespace durability barrier is part of
acknowledgement. Atomic CURRENT replacement alone is the visibility
linearization point for new readers. POSIX additionally requires project-root
directory fsync(2); Windows NTFS performs the rename through the flushed
FILE_FLAG_WRITE_THROUGH staging handle and does not claim a directory-handle
FlushFileBuffers barrier. Journals are never acknowledgement authority.
If the process dies after CURRENT replacement but before the applicable
barrier completes, reopen accepts whichever exact valid CURRENT the
filesystem presents. It does not infer intent from journals, directory scans,
timestamps, or UUID order.
Platform and filesystem scope
Section titled “Platform and filesystem scope”Durable projects may be created or mutated only after fail-closed preflight of:
- exclusive and shared advisory locks released by the OS on process exit;
- same-directory atomic file creation and replacement;
- file data-and-metadata flush;
- a platform-native namespace durability barrier for every changed entry; and
- stable file identity while an open handle is locked.
Supported implementations, as amended by ADR 0020, are POSIX local filesystems
with fcntl/flock, same-filesystem rename(2), and file plus directory
fsync(2); and fixed writable Windows local NTFS with LockFileEx, flushed
FILE_FLAG_WRITE_THROUGH staging handles, and same-handle
SetFileInformationByHandle rename. ReFS is unsupported/unproven.
Network, userspace, removable, cross-device, symlink-mediated, or unknown
filesystems are rejected with GF_UNSUPPORTED_FILESYSTEM before the project
root or CURRENT changes. There is no best-effort durability mode.
Recovery authority
Section titled “Recovery authority”Recovery and reopen resolve authority exactly as ADR 0013:
- the sole commit authority is an exact, valid
CURRENTnaming an existing generation whose manifest digest matches; - journals and directory scans are advisory cleanup/diagnostics only;
- malformed, missing, or digest-mismatched pointers fail closed as
GF_PROJECT_CORRUPTwithout electing a “newest” generation.
Reader isolation
Section titled “Reader isolation”Every opened facade pins one immutable generation. Long-lived readers do not
follow later commits. Fresh opens resolve the generation named by durable
CURRENT. Graph, provenance, knowledge, and epistemic participants become
visible together; mixed generations are corruption.
Writer isolation by mode
Section titled “Writer isolation by mode”| Mode | Reader view | Writer admission | Commit order | Conflict outcomes |
|---|---|---|---|---|
single_writer |
pinned immutable snapshot | competing writers fail with GF_WRITER_BUSY before staging |
one serial writer | no concurrent writer conflicts; busy is pre-publication |
queued_writer |
pinned immutable snapshot; snapshot reads bypass the queue | bounded FIFO per facade; cancel only unstarted work | one serial writer after dequeue | queue-full / cancel structured errors; no concurrent publish races |
optimistic_multi_writer |
pinned immutable snapshot per attempt | distinct composite transaction identities may stage concurrently | CURRENT commit point is serialized; compatible work may rebase |
closed matrix in ADR 0015: merge, GF_WRITE_CONFLICT, GF_IDEMPOTENCY_CONFLICT, GF_REBASE_EXHAUSTED |
Only publish_composite_transaction and the uniform GraphTransaction
lifecycle have optimistic replay in v0.5.x. Other one-shot mutation APIs retain
single-writer behavior even when the facade selects optimistic mode, unless
their work is staged through that shared lifecycle.
These modes do not claim generic ACID, serializable isolation, or SSI.
Write-skew witness (optimistic is not SSI)
Section titled “Write-skew witness (optimistic is not SSI)”Optimistic mode may merge concurrent changes to different properties of the same object. That admits write-skew histories that are legal under snapshot isolation but illegal under serializability.
Minimal witness:
- Start from one committed object
Accountwith propertiescredit=0anddebit=0, plus invariant “credit + debit <= 1” maintained only by application logic. - Transaction T1 reads both properties, observes
debit=0, and stagescredit=1. - Transaction T2 concurrently reads both properties, observes
credit=0, and stagesdebit=1. - Under
optimistic_multi_writer, the closed merge rules treat these as different properties, so both may publish after rebase. - The committed generation can contain
credit=1anddebit=1, violating the application invariant even though neither writer observed the other’s write.
Therefore public docs must classify optimistic mode as optimistic snapshot / conflict semantics, never as SSI or serializable isolation. Preventing write-skew requires a separately approved SSI design outside M6.
Idempotency, retry, cancellation, unknown outcome
Section titled “Idempotency, retry, cancellation, unknown outcome”| Situation | Required behavior |
|---|---|
| Exact retry of the same operation identity and content after acknowledgement | Return the prior receipt / committed generation without restaging |
| Same operation identity with changed content | GF_IDEMPOTENCY_CONFLICT with zero mutation |
| Cancellation before staging or before linearization | Prior generation remains authoritative; peer operations are unaffected |
Failure before CURRENT replacement |
committed: false; parent remains authoritative |
Failure after CURRENT replacement (including post-linearization API errors) |
Writer rereads validated CURRENT under the writer lock and reports committed: true; the generation is not rolled back |
| Crash or I/O ambiguity before acknowledgement | Reopen selects only an exact valid prior or new CURRENT; unknown third states are not returned to callers |
Publication vocabulary (shared with M5 interchange)
Section titled “Publication vocabulary (shared with M5 interchange)”Import, export, bulk construction, and portable project surfaces that publish a generation MUST use this vocabulary:
- stage — write private participants without moving
CURRENT; - validate — domain and composite checks against pinned parent plus staged bytes;
- durable generation — flushed participants + flushed manifest + completed platform-native namespace barriers for the generation tree (and optimistic promotion when applicable);
- linearize — atomic
CURRENTreplacement or first creation; - acknowledge — linearize plus the project-root platform-native namespace durability barrier;
- publish / published — acknowledged-durable success visible to new opens;
- abort — abandon staged work without moving
CURRENT; - recover — reopen/classification that never elects authority from journals or directory scans.
M5 issues #738, #742, and #745 consume these terms; they do not redefine them.
Observability and privacy
Section titled “Observability and privacy”Safe fields: operation, phase, transaction/generation/parent UUIDs, lock-owner UUID, capability and record-family IDs, counts, duration, filesystem class, and recovery classification. Forbidden: graph properties, assertion/evidence text, vector contents, credentials, user-controlled absolute paths, hostnames, or raw lock metadata beyond machine-owned IDs.
Consequences
Section titled “Consequences”- Callers can determine acknowledged durability and isolation without reading implementation comments.
- M6 fault modeling (#749), recovery-on-open (#750), deltas (#752), transactions (#754), and certification (#756) share one vocabulary and coverage matrix.
- Optimistic throughput remains available without false serializability claims.
- Unsupported filesystems stay fail-closed.
Rejected alternatives
Section titled “Rejected alternatives”| Alternative | Reason |
|---|---|
Treat CURRENT replacement alone as acknowledgement |
Omits the platform-native namespace durability barrier required against power loss |
| Claim SSI because readers pin snapshots | Write-skew remains possible under optimistic property merge |
| Best-effort mode on network filesystems | Flush and replacement semantics are not proven |
| Let journals elect authority after crash | Turns advisory cleanup into an election protocol |
| Silently revise ADR 0013/0015 text for M6 behavior changes | Semantic change requires an amending ADR |
Required verification
Section titled “Required verification”- Contract schema validation and documentation link checks via
scripts/ci/durability-isolation-gate.py. - Matrix maps crash phases and anomalies to covered evidence or later M6 owner issues (#749–#756).
- Public docs reference this ADR and do not claim generic ACID or SSI.