test(incremental): delta maintenance == rebuild, retraction + split (WP-0011 T2)

Verify change-driven maintenance keeps the equivalence index equal to a
from-scratch rebuild under add / edit / remove: an edit into a new bucket
retracts the stale edge, an edit into equivalence adds one, and removing a
connector node propagates a retraction that splits a chorus. Equality checked
against a fresh build() oracle on every operation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-16 02:14:32 +02:00
parent 0b3ab2086f
commit d7d046cac0

View File

@@ -0,0 +1,84 @@
"""Incremental maintenance == rebuild, with retraction + propagation (SHARD-WP-0011 T2)."""
from shard_wiki.incremental import EquivalenceIndex
from shard_wiki.model import Identity, Page
from shard_wiki.provenance import ProvenanceEnvelope
def _page(shard, key, body):
return Page(
identity=Identity(shard, key),
body=body,
envelope=ProvenanceEnvelope(source_shard=shard),
)
def _rebuilt(pages, curator=()):
idx = EquivalenceIndex()
idx.build(pages, curator)
return idx
def _equal(a, b):
return a.edges() == b.edges() and set(a.groups()) == set(b.groups())
def test_add_keeps_index_equal_to_rebuild():
pages = [_page("A", "Foo", "same content here"), _page("B", "Bar", "same content here")]
idx = EquivalenceIndex()
for p in pages:
idx.add(p)
assert _equal(idx, _rebuilt(pages))
assert idx.groups() # the two collapse
def test_remove_keeps_index_equal_to_rebuild():
pages = [
_page("A", "Foo", "same content here"),
_page("B", "Bar", "same content here"),
_page("C", "Baz", "unrelated isolated material entirely"),
]
idx = _rebuilt(pages)
idx.remove(Identity("B", "Bar"))
assert _equal(idx, _rebuilt([pages[0], pages[2]]))
def test_edit_into_new_bucket_retracts_stale_edge():
a = _page("A", "Foo", "shared identical body text")
b = _page("B", "Bar", "shared identical body text")
idx = _rebuilt([a, b])
assert idx.groups() # A ≡ B initially
# Edit B to something completely different: it exits A's buckets, the edge is retracted.
b2 = _page("B", "Bar", "now totally divergent unrelated prose about nothing")
idx.update(b2)
assert idx.groups() == () # stale edge gone
assert _equal(idx, _rebuilt([a, b2]))
def test_edit_into_equivalence_adds_edge():
a = _page("A", "Foo", "target body to converge on later")
b = _page("B", "Bar", "initially completely separate writing here")
idx = _rebuilt([a, b])
assert idx.groups() == ()
b2 = _page("B", "Bar", "target body to converge on later") # now identical to A
idx.update(b2)
assert idx.equivalent_to(Identity("A", "Foo")) == frozenset(
{Identity("A", "Foo"), Identity("B", "Bar")}
)
assert _equal(idx, _rebuilt([a, b2]))
def test_removing_connector_splits_a_chorus():
# Curator chain A—B—C (no direct A—C): one group of three.
a, b, c = (_page("A", "X", "aaa"), _page("B", "Y", "bbb"), _page("C", "Z", "ccc"))
idx = EquivalenceIndex()
for p in (a, b, c):
idx.add(p)
idx.bind(a.identity, b.identity)
idx.bind(b.identity, c.identity)
assert idx.equivalent_to(a.identity) == {a.identity, b.identity, c.identity}
# Removing the connector B retracts/propagates: the chorus splits.
idx.remove(b.identity)
assert idx.groups() == ()
chain = [(a.identity, b.identity), (b.identity, c.identity)]
assert _equal(idx, _rebuilt([a, c], curator=chain))