MovableTreeGcCoordinator
Coordinator that drives move-log GC for a MovableTree CRDT replicated via Quilter, gated by the eviction-safe causal-stability barrier (ADR-003 addendum v3, #262).
Role
The MovableTree move-log grows without bound unless causally-stable, superseded ops are periodically garbage-collected. A MoveOp on node n is eligible for GC once:
Causally stable —
op.seq ≤ stableCut[op.replica].Superseded — a later stable op on
nis the winning placement; this op is not.Not a creation op (
value != null) still referenced by any live op.Frontier-complete —
delivered.dominates(frontierMax).
This coordinator observes Quilter.cutFrontier (for cut and frontier) and Quilter.deliveredLocal (this replica's contiguous delivered VV), then calls MovableTree.compact in a loop until nothing further qualifies. Each resulting MoveTreeCompact is broadcast as a Patch via applyCompaction so peers trim their own logs.
Loop-until-stable
Compaction runs in a loop until MovableTree.compact returns null. A single pass may unlock a second (e.g., removing a superseded move reveals another). Quilter.apply updates state synchronously, so state.value reflects each application before the next iteration.
Triggering
Re-evaluates on EITHER trigger: a cutFrontier change (cut advanced or frontier changed) OR a state change (a new move op appeared that may now be compactable). Without the state trigger, an op applied while the cut already covers it would never be re-evaluated (the StateFlow deduplicates unchanged cuts).
Parameters
live MovableTree state (updated by Quilter on every incoming delta).
the atomically-published causal-stability cut + frontier from Quilter.cutFrontier.
this replica's contiguous delivered VV from Quilter.deliveredLocal.
called with each compaction Patch; wire this to Quilter.apply so the delta propagates to all peers.
the CoroutineScope for background coroutines.