Expand description
WAL frame substrate (Phase 14.6 — DS-14-storage Q1+Q3+Q5 locks, M4.A 2026-05-10).
On-disk frame format consumed by Graph::restore_snapshot({ mode:"diff" })
(M4.E). Each frame decomposes a single graph diff into one DS-14
BaseChange<T> envelope per structural-or-value change, scoped by
Lifecycle so callers can narrow rewinds.
The TS reference impl lives at
packages/pure-ts/src/extra/storage/wal.ts. Field names + checksum
algorithm are parity-locked — both impls produce byte-identical hex SHA-256
over the same canonical-JSON encoding of a frame body.
§Canonical-JSON parity
TS’s stableJsonString (packages/pure-ts/src/extra/storage/core.ts:22-39)
is “recursively sort object keys, then JSON.stringify(_, undefined, 0)”.
The Rust port mirrors this by routing the frame body through
serde_json::to_value (lands in serde_json::Map which is BTreeMap by
default — sorted iteration) then serde_json::to_string. Output is
byte-identical to TS for the WAL frame schema: ASCII keys, integer
numerics, no floats.
Parity caveats (lift when a real consumer surfaces):
- String VALUES containing surrogate-pair code points (≥ U+10000): JS sorts
keys by UTF-16 code-unit order; Rust
BTreeMapsorts by UTF-8 byte order. For ASCII keys these agree; for non-BMP keys they don’t. The frame schema’s keys are ASCII so this can only bite ifpathorchange.structurecontains non-BMP code points — neither is expected for graph identifiers. - Float-typed user payloads: JS
JSON.stringifyuses IEEE 754 with shortest-decimal-round-trip; Rust’sserde_jsonusesryuwhich agrees on finite f64 in safe range but may diverge on subnormals. WAL frames typically carry integer-only data; if a user puts a float inchange.change, document the constraint.
§Checksum
SHA-256 over canonical-JSON of the frame body (everything except the
checksum field itself), encoded as a 64-char lowercase hex string.
Spec-locked at GRAPHREFLY-SPEC.md:1201-1206 — original BLAKE3 lock was
revised to SHA-256 so the TS impl could stay zero-dep (no BLAKE3 in
WebCrypto). Rust matches via sha2 + hex.
Structs§
- WALFrame
- On-disk WAL frame (DS-14-storage Q1 lock).
- WalTag
- Singleton-string discriminator for the bridge wire-format tag. Always
serializes / deserializes as
"c"; rejects any other value at parse time.
Enums§
- Checksum
Error - Errors surfaced by checksum compute / verify.
Constants§
- REPLAY_
ORDER - Cross-scope replay order (DS-14 PART 4 lock —
Spec → Data → Ownership). Exported so the replay implementation and parity tests share one source of truth. - WAL_
FRAME_ SEQ_ PAD - Pad width for
frame_seqin WAL keys. 20 digits keeps lex-ASC string sort = numeric ASC up toframe_seq < 10^20(well pastu64::MAX). - WAL_
KEY_ SEGMENT - Default WAL prefix segment relative to a
graph.name. Frames land at${graph.name}/${WAL_KEY_SEGMENT}/${frame_seq:020}.
Functions§
- graph_
wal_ prefix - Default WAL key prefix for a graph by its
name. - verify_
wal_ frame_ checksum - Verify a frame’s
checksumfield matches its body. Replay invokes this at the WAL tail (drop on mismatch by default) and mid-stream (abort on mismatch by default) per Q3. - wal_
frame_ checksum - Compute the SHA-256 checksum over a frame’s body (sans
checksum), returning a 64-char lowercase hex string. Parity-locked with TSwalFrameChecksum. - wal_
frame_ key - Build the canonical WAL frame key.
prefixis the WAL-prefix portion (e.g."my-graph/wal");frame_seqis the per-frame cursor. Zero-padded so lex-ASC string sort equals numeric ASC sort.