crdt example: property-based law checks over the executable merge - #32
Conversation
Follow-up to the CRDT example. Tier (b) previously checked the semilattice laws on a handful of hand-picked states — instance checking. This adds property-based checking over 2000 random states each (seeded PRNG, so a failure prints a reproducible counterexample), which is the cheapest way to narrow the model<->code gap the README's tier (c) is honest about not closing: the SHIPPED merges are now exercised on thousands of random inputs, not four. - crdt.shen: add the doc-* semilattice laws (doc-eq? via an order/duplicate- independent effective-field comparison, plus doc-idempotent?/commutative?/ associative?). The LWW-Map is the CRDT the demo actually uses, so proving its merge laws is the highest-value part; previously only gc-* and lww-* had them. - selftest.lua: a "2b. laws by PROPERTY" section generating random G-Counters, registers, and docs over small alphabets (so ids/keys collide and clocks tie — the adversarial corners), running all nine laws over 2000 cases each. 18000 checks; verified the harness can go red (a non-law reports a counterexample). - README: tier (b) reframed as property-based; the honest-scope note now says the property run *narrows* the model<->code gap without claiming a universal proof over the representation. Port suite green (467/467); crdt selftest green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CC5aXvUTaaNgfGHQJjGsvX
The arity dispatch in `prop` used an `and`/`or` chain:
local r = (arity == 1 and fn(build(sa)))
or (arity == 2 and fn(build(sa), build(sb)))
or fn(build(sa), build(sb), build(sc))
Lua's `and`/`or` fall through on a FALSE value, so for arity 1 and 2 a law
returning `false` — precisely the counterexample the harness exists to report —
skipped the `if r ~= true` branch and instead applied the predicate to three
arguments, aborting the run with an opaque Shen "attempt to apply a
non-function" error object and no counterexample. Only arity-3 laws reported
correctly. The green path was unaffected, which is why it went unnoticed.
Dispatch on arity explicitly. Verified by temporarily replacing
`gc-commutative?`'s body with a non-law: the run now prints
gc-commutative? FAIL @ case 1
a = gc{A:1}
b = gc{A:4,C:3,C:1}
and continues to the remaining laws, instead of crashing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Verified against current Ran on the merged tree (luajit 2.1):
One fix pushed on top (833bd10): the property harness's failure path was broken. |
Follow-up to the CRDT example. Its README is honest that tier (c)'s proofs are
about an axiomatized
join, not the shipped merge (the model↔code gap), andtier (b) only checked the laws on a handful of hand-picked states. This PR does
the cheapest thing that narrows that gap: property-based checking of the
executable merge over thousands of random states.
Changes
crdt.shen— add thedoc-*semilattice laws. The LWW-Map (doc) is theCRDT the demo actually uses, but only
gc-*andlww-*had law predicates.Adds
doc-eq?(order- and duplicate-independent, via an effective-fieldcomparison mirroring
gc-eq?) plusdoc-idempotent?/doc-commutative?/doc-associative?. Still loads clean under(tc +).selftest.lua— a new2b. laws by PROPERTYsection. A seeded PRNGgenerates random G-Counters, registers, and docs over small alphabets (so ids
and keys collide and clocks tie — the adversarial corners), then runs all nine
laws over 2000 cases each (18k checks). On failure it prints a reproducible
counterexample. I confirmed the harness can go red (pointing it at a non-law
reports a violation immediately), so the green result is meaningful.
README.md— tier (b) reframed as property-based; the honest-scope notenow says the property run narrows the model↔code gap without claiming a
universal proof over the representation.
Why this and not a full inductive proof
Re-deriving the semilattice axioms from
doc-merge's definition (induction overthe field-list representation) would close the gap, but that's real proof
engineering and out of scope for an example. Property-testing the shipped merge
on thousands of random states is the high-value, low-cost step — and it's the
exact bug class (silent replica divergence on inputs no one hand-picked) these
laws exist to catch.
Port suite green (467/467);
luajit examples/crdt/selftest.luagreen.🤖 Generated with Claude Code
Generated by Claude Code