# Recovery status

Persistent `Database::open(path)` holds the database's advisory exclusive lock,
reads `wal.bin`, verifies complete record framing/checksums and strictly
sequential transaction IDs, then replays each record against a private graph
candidate. The candidate becomes visible only after every operation in that
record succeeds. Derived label, type, adjacency and property indexes are
rebuilt by the normal graph mutation methods.

A short final header or a valid header whose record extends beyond EOF is
treated as an incomplete tail. Open rewrites the file from its validated
complete-record prefix through a same-directory temporary file and synchronizes
the database directory after replacement, so the next commit is not hidden
behind stale tail bytes. A complete header is checked for a valid operation
count and sequential transaction ID even when its body is truncated. Complete
records with invalid framing, operation payloads or checksums are fatal and
return structured file, offset and transaction context.

Initialization creates only the final database directory, so its immediate
parent must already exist. It synchronizes the new WAL file, the database
directory, and its immediate parent. Directory synchronization uses the
platform-specific access mode required by the installed native filesystem
runtime; a synchronization failure is returned as an I/O error.

Persistent commits are async. Each commit appends one transaction record and
calls `File::sync(only_data=true)` before publishing the in-memory candidate.
If an append or synchronization failure could have written part or all of the
record, the current handle enters a recovery-required state and returns
`CommitOutcomeUnknown`; close and reopen determine whether recovery sees it.
An error while opening the WAL for append is reported as a definite I/O error.

The current recovery implementation loads the complete WAL into memory and
supports files smaller than 2 GiB. It has no snapshot/checkpoint fallback or
log compaction, so recovery time and disk use grow with the WAL. The lock is
advisory and is documented by `moonbitlang/async` as synchronization for
cooperating processes using that library; other lock implementations are not
covered.

Native integration tests cover graph operation replay, indexes, Unicode and
binary values, ID continuity, an incomplete tail followed by a later commit,
complete-record checksum corruption, lock exclusivity, and preventing close or
rollback during an in-flight commit. Initialization with a missing immediate
parent is rejected. Snapshot boundaries and injected
append/sync failures remain unimplemented test areas.
