Context
While verifying PR #75, a full property-test run produced a falsifying case in VectorClockProperties.Merge returns the upper bound when one input dominates:
A focused rerun of the property passed, so this is not blocking the UUIDv7 RNG PR. The failure points at an adjacent VectorClock canonicalization edge case.
Likely cause
VectorClock.Compare treats missing entries as counter 0, which is mathematically correct for vector clocks. However VectorClock.Parse / canonical construction can preserve explicit zero-counter entries, and structural equality can distinguish a clock with node:0 from the equivalent clock where that node is absent.
That means an upper-bound property can fail when one side carries an explicit zero entry:
(11:704,16:0) and (11:704) compare as equal
Merge(a,b) may preserve the explicit zero entry
- structural equality then sees a difference even though vector-clock semantics do not
Proposed fix
Pick one invariant and enforce it consistently:
- Prefer canonical representation: drop zero-counter entries in parse/read/canonical construction and possibly merge results.
- Alternatively, adjust equality/hash semantics to treat missing and explicit-zero entries as equivalent.
Canonicalizing away zero entries is likely simpler and more allocation-friendly for the sparse sorted-array representation.
Acceptance criteria
- Add a unit test showing
VectorClock.Parse("11:704,16:0") is equivalent to VectorClock.Parse("11:704") under equality or canonical output.
- Update property generators to avoid invalid/non-canonical zero-counter entries, or assert canonicalization removes them.
Merge returns the upper bound when one input dominates is stable across repeated property-test runs.
- Preserve existing sparse-array performance characteristics.
Context
While verifying PR #75, a full property-test run produced a falsifying case in
VectorClockProperties.Merge returns the upper bound when one input dominates:A focused rerun of the property passed, so this is not blocking the UUIDv7 RNG PR. The failure points at an adjacent VectorClock canonicalization edge case.
Likely cause
VectorClock.Comparetreats missing entries as counter0, which is mathematically correct for vector clocks. HoweverVectorClock.Parse/ canonical construction can preserve explicit zero-counter entries, and structural equality can distinguish a clock withnode:0from the equivalent clock where that node is absent.That means an upper-bound property can fail when one side carries an explicit zero entry:
(11:704,16:0)and(11:704)compare as equalMerge(a,b)may preserve the explicit zero entryProposed fix
Pick one invariant and enforce it consistently:
Canonicalizing away zero entries is likely simpler and more allocation-friendly for the sparse sorted-array representation.
Acceptance criteria
VectorClock.Parse("11:704,16:0")is equivalent toVectorClock.Parse("11:704")under equality or canonical output.Merge returns the upper bound when one input dominatesis stable across repeated property-test runs.