Skip to content

fix(deps): update module github.com/go-crdt/crdt to v0.35.0 - #10

Merged
tannevaled merged 1 commit into
mainfrom
renovate/github.com-go-crdt-crdt-0.x
Aug 30, 2026
Merged

fix(deps): update module github.com/go-crdt/crdt to v0.35.0#10
tannevaled merged 1 commit into
mainfrom
renovate/github.com-go-crdt-crdt-0.x

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Change Age Confidence
github.com/go-crdt/crdt v0.31.0v0.35.0 age confidence

Release Notes

go-crdt/crdt (github.com/go-crdt/crdt)

v0.35.0

Compare Source

Doc.Collect and List.Collect, shipped in v0.33.0, are withdrawn. They left
two replicas holding different documents.

What was wrong

They re-pointed a survivor that named a collected run at the nearest character
still alive before it
. Still alive is relative to what a replica happens to
hold: two replicas collecting against the same version hold different characters
above it, re-point the same survivor at different anchors, and disagree from
then on about what it was inserted after.

A chaos harness diverged them on every seed, and narrowed it: collecting the
maps alone is green on every seed; collecting the text alone, or the list alone,
diverges on every seed. The maps are the parts that re-point nothing.

Re-pointing at the dead run's own origin instead is agreed by every replica and
cannot be written down — it makes the survivor sort earlier than it does in the
live document, so Load refuses the snapshot. Both anchors were tried.

Breaking, and un-breaking

TextAt, LenAt, ChangesSince, List.ValuesAt, List.LenAt and OpsSince
go back to their v0.32.0 signatures, along with Floor, CanReplay and
ErrCollected, which existed only for the withdrawn feature. If you are on
v0.32.0 or earlier, nothing changes for you. If you took v0.33.0 or v0.34.0, the
error returns go away again.

What stays

Map.Collect with its clock guard, Composite.Collect (maps only),
Diagram.Sweep, Diagram.Collect, and Rewritten.

Snapshot formats are unchanged — a text at version 6, a list at version 2 — and
now refuse a snapshot whose collection tables are not empty, because nothing
sound could have written one.

What a next attempt should build

Yjs's shape, read from Item.gc: replace a deleted item in place with
GC(id, length) — same identity, same length, contents discarded — so nothing
is re-pointed and no causal stability is needed, because an operation that names
it finds it. Measured here at 16.2% of a real document's snapshot against
the 59.4% the wrong answer promised. Smaller, and sound.

The chaos harness that found this ships with it.

v0.34.0

Compare Source

OpsSince refuses a difference below the collection floor.

Below Floor() it used to hand back a difference with holes in its sequence
numbers
, which a peer applying it could only park — silently, because every
operation in the batch is well-formed and the one that would let the rest
through is never coming.

Breaking
was is
Doc.OpsSince(v) []Op ([]Op, error)
List.OpsSince(v) []ListOp ([]ListOp, error)
Composite.OpsSince(v) []PartOps ([]PartOps, error)

Map.OpsSince is unchanged: a map gives back a sequence number without its
operation as a matter of course, so the span that stands in for a collected
tombstone is one a peer already knew how to apply.

The eight structured types that delegate to Composite.OpsSinceBlobs,
Blocks, Diagram, Document, Ink, Proposals, RichText, Sheet
change with it. The six backed by a map do not.

Migrating

A replica that never collects never refuses, so the common migration is
mechanical:

ops := doc.OpsSince(v)              // before
ops, err := doc.OpsSince(v)         // after

If you do collect, ask CanReplay(v) first and send a snapshot when it says no:
a peer below the floor has to be re-seeded, not caught up.

Also

Proposals.Draft and Proposals.Preview now refuse on a document that has
collected. A proposal is that document's history replayed, and what collection
dropped is not in it.

v0.33.0

Compare Source

Replicas can give back what nobody can be confused by any more.

Doc, List and Map collect the tombstones every replica has seen the
deletion of, and Composite collects the parts a caller vouches for. On the
automerge paper 87% of a text's tombstones go and the snapshot falls from
478 474 to 194 203 bytes. A collected replica still merges with the replicas
it came from
, which is the whole difference between this and Rewritten.

A diagram takes two deliberate steps: Diagram.Sweep turns the records of
removed nodes into tombstones, Diagram.Collect gives them back — 2.0x on a
diagram of two hundred nodes half of which were tried and removed.

Breaking

Five readers now return an error, and give ErrCollected below Floor()
rather than a past with characters missing from it:

was is
Doc.TextAt(v) string (string, error)
Doc.LenAt(v) int (int, error)
Doc.ChangesSince(v) []Change ([]Change, error)
List.ValuesAt(v) [][]byte ([][]byte, error)
List.LenAt(v) int (int, error)

A document that never collects has an empty floor and never refuses, so the
migration is to take the error and treat it as impossible — or to handle it, if
you collect.

Snapshot formats

Text 6, list 2, map 2. Every older version still loads, and re-encoding
writes the current one.

v0.32.0

Compare Source

Apply can say what it absorbed

ApplyAbsorbed on Doc, List, Map and Composite, beside the Apply that
is unchanged. It reports the operations a call actually integrated — including
any that had been parked waiting for them
, which were in no batch anybody sent.

Anything that passes operations on needs exactly that set. More, and two
replicas following each other talk for ever. Less, and a peer ends up missing an
operation nobody will send it again, because everybody else has it by then.

Working the set out afterwards does not do: asking what a replica holds that a
version does not walks the document — 383µs on sixty thousand operations against
90ns for a version that is current — and inferring it from a counter fails in
both directions, since the number waiting cannot see a release that coincides
with a park and the size of the gain cannot see past duplicates.

The replica knows without looking, and collecting what it integrates costs an
append. A replica nobody is asking carries nothing for the question: the
collector is a parameter, not a field, so Doc is the 168 bytes it always was.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Enabled.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR has been generated by Mend Renovate CLI.

@tannevaled
tannevaled merged commit c51626c into main Aug 30, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant