CORE-2: Map primitive + Element_id (UUID v5) + Value#5
Merged
Conversation
- Element_id: UUID v5 derived from (parent_id, key) via uuidm; root = nil (Uuidm.nil). Concurrent SDK helpers on same (parent, key) compute the same id, so LWW collapses to one winning Element with identical id on both sides -- no orphan. - Value: closed sum -- Scalar (String | Int int64 | Float | Bool | Null) or Element of Element_id.t. Blob constructor lands with the blob subsystem. - Map: Stdlib.Map.Make(String) of Live/Tomb slots; per-slot LWW with (lamport, op_id) tiebreak; tombstones survive Delete so stale Sets cannot resurrect a deleted key. Wire-level ops trimmed to just Set and Delete (initOnce / replace dropped -- they were SDK guardrails that became dead surface once deterministic Element_id removed the concurrent-init orphan footgun). - apply returns (t', released_ids) -- both displaced Live Element refs and stranded losers from losing Set ops with unique Element values. Doc layer decides orphan status by cross-checking reachability. ARCHITECTURE.md: Map section rewritten around the deterministic id + Map Slot Safety reframed to drop replace/initOnce/throws guard. Closed op kind enum updated to map.set | map.delete. 49 new tests across element_id (17), value (11), map (22). Total suite now 122 tests across 10 binaries, all green. Closes CORE-2.
.ocamlformat: margin 100 -> 90, add break-infix=fit-or-vertical. Re-run ocamlformat across the tree to match.
Local opam pins qcheck-core 0.25 where the top-level helper is QCheck.string_of_size. CI installs a newer release where that helper is renamed (QCheck.string_size) and the old name is a deprecated alert - which CI's OCaml escalates to an error. Use QCheck.make over Gen.string_size directly. Stable surface across both versions, no deprecated alert.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Element_id(UUID v5 derived from(parent_id, key)),Value(closed sum: Scalar | Element), andMap(LWW per slot with Live/Tomb tombstones).Set+Deleteonly.initOnce/replacewere SDK guardrails that became dead surface once deterministic Element_id removed the concurrent-init orphan footgun.applyreturns(t', released_ids)— both displaced Live Element refs and stranded losers from losing Set ops with unique Element values. Doc layer decides orphan status by cross-checking reachability.kindenum trimmed.Modules
Element_idroot,derive ~parent ~key,to_string/of_string,to_bytes/of_bytes,compare,equal,ppUuidm.v5. Root =Uuidm.nil. Two clients computing same(parent, key)arrive at same id without coordination.Valuescalar = String | Int int64 | Float | Bool | Null;t = Scalar of scalar | Element of Element_id.t;pp/equalBlobconstructor lands with the blob subsystem.Mapempty ~element_id,element_id,apply ~op ~op_id ~lamport -> t * released,get/keys/entries/cardinal,equal/pp(lamport, op_id)tiebreak. Tombstones surviveDeleteso stale Sets cannot resurrect deleted keys.Released-ref semantics
Across both win and lose branches of
apply:Commits
d232125CORE-2: Map primitive + Element_id (UUID v5) + Value2965b4dstyle: ocamlformat sweep (margin 90, break-infix fit-or-vertical)Test plan
(parent, key), nested chain, v5 version-nibble check, malformed/wrong-length rejects, 4 qcheck properties)Summary by cubic
Implements CORE-2 by adding a Map CRDT with deterministic
Element_id(UUID v5 from(parent_id, key)) and reducing wire ops tomap.set/map.deleteso concurrent slot init converges without orphans. Also updates tests forqcheck-coreAPI changes to keep CI passing.New Features
Element_id: UUID v5 from(parent_id, key);rootis nil. Same(parent, key)→ same id across clients.Value:Scalar(String,Int int64,Float,Bool,Null) orElement of Element_id.Map: LWW per key withlamport/op_idtiebreak; deletes leave tombstones.apply ~op ~op_id ~lamportreturns(t', released_ids)for displaced or stranded Element refs. SDK get-or-create helpers (e.g.Map.text key) derive the id and emitSet, so concurrent init converges.map.setandmap.delete.Migration
map.setandmap.deleteon the wire;initOnce/replacemove to SDK sugar.released_idsfromMap.applyin the doc layer to decide orphaning via reachability.Written for commit 2f01725. Summary will update on new commits. Review in cubic