Skip to content

feat(C3 partial): OR-Set semilattice property tests (3 more)#1423

Merged
AceHack merged 2 commits intomainfrom
otto/c3-orset-semilattice-properties-2026-05-03
May 3, 2026
Merged

feat(C3 partial): OR-Set semilattice property tests (3 more)#1423
AceHack merged 2 commits intomainfrom
otto/c3-orset-semilattice-properties-2026-05-03

Conversation

@AceHack
Copy link
Copy Markdown
Member

@AceHack AceHack commented May 3, 2026

Summary

Three FsCheck properties for OrSet, mirroring the G-Counter / PN-Counter pattern:

  • OR-set merge value is commutative
  • OR-set merge value is idempotent
  • OR-set merge value is associative

Compares on the observable element-set (Set.ofSeq orset.Value), not on internal entry weights — OrSet.Merge is ZSet.add, so weights can vary by merge order but the externally-visible CRDT set must be invariant under semilattice laws.

Math-proofs assessment matrix C3 row: closes 3 more after PN-Counter's 3 (PR #1420). Running total: 6 of ~15 target.

Test plan

  • All 3 properties pass: dotnet test --filter "FullyQualifiedName~OR-set" → 3 passed in 160ms.
  • dotnet build clean.

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings May 3, 2026 15:26
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds FsCheck semilattice-law property tests for OrSet in the math invariants test suite, aligning coverage with the existing CRDT property-testing pattern used for counters while comparing on the externally observable element set.

Changes:

  • Added FsCheck properties asserting commutativity, idempotence, and associativity of OrSet.Merge on the observable Value set.
  • Introduced helpers to build OR-sets from generated inputs and compare OR-set observable values via Set.ofSeq.

Comment thread tests/Tests.FSharp/Properties/Math.Invariants.Tests.fs Outdated
Comment thread tests/Tests.FSharp/Properties/Math.Invariants.Tests.fs
Three FsCheck properties for OrSet, mirroring the G-Counter / PN-Counter
pattern:
- OR-set merge value is commutative
- OR-set merge value is idempotent
- OR-set merge value is associative

Compares on the OBSERVABLE element-set (Set.ofSeq orset.Value), not on
internal entry weights — OrSet.Merge is ZSet.add, so weights can
vary by merge order but the externally-visible CRDT set must be
invariant under semilattice laws.

Math-proofs assessment matrix C3 row: closes 3 more after PN-Counter's
3 (running total: 6 of ~15 target).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@AceHack AceHack force-pushed the otto/c3-orset-semilattice-properties-2026-05-03 branch from a228fe5 to 1fdf0bf Compare May 3, 2026 15:29
Reviewer-flagged P1 on #1423: buildOrSet used OrSet.Add which calls
Guid.NewGuid() per step, making FsCheck shrinking non-replayable
and taking O(N^2) work via repeated linear merges.

Fix: build Entries directly via ZSet.ofSeq with index-derived Guid
tags. Deterministic across runs (same input list → same Entries),
O(N log N) construction, and FsCheck can now shrink reproducibly.

P2 (rename to "merge VALUE is...") was already addressed in the
original commit's property names — no rename needed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 3, 2026 15:30
@AceHack AceHack merged commit be27543 into main May 3, 2026
28 of 29 checks passed
@AceHack AceHack deleted the otto/c3-orset-semilattice-properties-2026-05-03 branch May 3, 2026 15:33
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 2 comments.

Comment on lines +112 to +149
// Tag derivation: deterministic Guid from list-index so FsCheck
// shrinking is replayable. Naive `OrSet.Empty.Add x` would call
// Guid.NewGuid() on every step, which (a) makes shrinking
// non-replayable and (b) takes O(N^2) work via repeated linear
// merges. Building Entries directly via ZSet.ofSeq is deterministic
// and O(N log N).

let private orSetValueEqual<'T when 'T : comparison>
(a: OrSet<'T>) (b: OrSet<'T>) =
Set.ofSeq a.Value = Set.ofSeq b.Value

let private buildOrSet (xs: int list) : OrSet<int> =
let entries =
xs
|> List.mapi (fun i x ->
// Encode list-index into a 16-byte Guid for determinism;
// index is unique per call so no collisions across xs.
let bytes = Array.zeroCreate<byte> 16
(System.BitConverter.GetBytes i).CopyTo(bytes, 0)
(x, System.Guid bytes), 1L)
{ Entries = ZSet.ofSeq entries }

[<Property>]
let ``OR-set merge value is commutative`` (xs: int list) (ys: int list) =
let a = buildOrSet xs
let b = buildOrSet ys
orSetValueEqual (OrSet<int>.Merge a b) (OrSet<int>.Merge b a)

[<Property>]
let ``OR-set merge value is idempotent`` (xs: int list) =
let a = buildOrSet xs
orSetValueEqual (OrSet<int>.Merge a a) a

[<Property>]
let ``OR-set merge value is associative`` (xs: int list) (ys: int list) (zs: int list) =
let a = buildOrSet xs
let b = buildOrSet ys
let c = buildOrSet zs
Comment on lines +107 to +110
// weights can vary across merge orderings (Merge a a doubles weights),
// but the *observable* set (elements with ANY weight > 0) is the
// CRDT contract. These properties pin the laws on the observable
// set, which is the layer external code consumes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants