Skip to content

fix(deps): update dependencies - #439

Merged
tannevaled merged 1 commit into
mainfrom
renovate/deps
Aug 31, 2026
Merged

fix(deps): update dependencies#439
tannevaled merged 1 commit into
mainfrom
renovate/deps

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Change Age Confidence Type Update
github.com/go-crdt/collab v0.33.0v0.36.1 age confidence require minor
github.com/go-crdt/crdt v0.35.0v0.37.0 age confidence require minor
github.com/go-images/images 4a50ef57e8189d age confidence require digest
github.com/go-opentype/fonts v0.6.0v0.9.0 age confidence require minor
github.com/go-opentype/opentype v0.5.0v0.12.0 age confidence require minor
github.com/go-richdoc/richdoc v0.2.0v0.3.0 age confidence require minor
github.com/go-widgets/mvvm v0.5.0v0.9.0 age confidence require minor
github.com/go-widgets/painter v0.11.0v0.12.0 age confidence require minor
github.com/go-widgets/toolkit v0.290.0v0.298.0 age confidence require minor

Release Notes

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

v0.36.1

Compare Source

Selects crdt v0.37.0. No code changed here.

The root module asked for crdt v0.36.0, so anyone depending on collab and nothing else selected a crdt without the sortIDs fix — the duplicate-deletion table sorted quadratically on every Snapshot() call, which is what a store does every time it writes one.

v0.36.0

Compare Source

A participant that has only ever read is now remembered across an eviction,
by a store that can keep them.

The participant set a server can work out from a document alone is the sites
its own version vector names — everyone who has written. It does not name a
reader: nothing a reader did is in any version vector. So a document that was
evicted and loaded again came back not knowing a reader had been there, the
collection floor moved past them, and they came back holding a value everybody
else had removed.

The shape

A store that can keep a little more says so by implementing SiteStore.
What it is given is opaque — the server owns the encoding, a store keeps the
bytes and gives them back. MemoryStore and DirStore keep them.

A store that does not implement it loses nothing it had: the server falls
back to the sites the document names. A test pins that fallback rather than
describing it, so it stays a fact.

Checked against the field before it was designed: Automerge keeps a SyncState
per peer rather than per writer, with readOnly as first-class state and
encodeSyncState so the record can outlive the process holding it.

Two decisions worth knowing about

DirStore keeps them in a directory beside the documents, not a file among
them: Documents and Idle skip directories, and archiving is built on Idle
— a file with a decorated name would have been archived as though it were a
document of its own.

Tiered and MultiStore do not implement it, and say so in their
documentation. Go has no way to implement an interface only when what is
underneath does, so either of them declaring the methods would keep nothing
whenever its stores could not — silently, which is worse than not offering it.
Delegating properly has a policy decision in it and is left for later.

v0.35.0

Compare Source

Config.CollectEvery did nothing at all in a federation, silently. It works
now.

A link is a participant of the document it follows — it joins that server, and
Server.Follow joins its own as well — and a participant that never says
anything holds a document's floor at nothing for ever.

It says it now, after applying what the peer sent: the version and the
clocks
of its own replica.

Why that is safe

What a link promises is what its own server has applied, not what everybody
behind it has. That is enough: the people behind a link are participants of
that server's document, and that document's own floor protects them. A peer
collecting up to what this server holds takes away nothing anybody here will
later ask this server for, because this server still holds it.

TestAParticipantBehindALinkIsNotCollectedPast is that end to end — a reader on
the following server goes away, the followed server deletes a key and gives its
tombstone back, the following server keeps its own, and the reader comes
back to a key that is gone.

Two unstable tests, made deterministic

TestAParticipantThatCannotKeepUpIsDropped said it opened a session that never
read again, and then read from it in a goroutine while the writer wrote. Whether
the queue ever overflowed was a race between the two, lost about one run in
six
under -race and once on a Windows runner.

The writes happen first now, and there are enough of them to fill gRPC's
flow-control window
rather than a backlog of one — which is what actually has
to fill. Two hundred operations of sixty-four characters went nowhere near it;
two thousand does it every time.

whole suite, -race
before 2–3 failures in 8
after 0 in 10

The federation tests added here had the same fault in reverse: they asked for
collection once, and a link acknowledges when it has applied what it was sent,
so the first ask can be too early. They ask until it happens, and both still
fail with the acknowledgement reverted — which is the only reason to keep them.

v0.34.1

Compare Source

v0.34.0 is retracted. Take this instead.

v0.34.0 added the acknowledgement clocks and made the internal wire encoding
require them, so a participant built before that release was refused as a
protocol error on every transport that uses it — Pipe, WebSocket,
BroadcastChannel. The two ends of a session are not deployed at the same
moment, so that was a break rather than an upgrade.

gRPC was unaffected: a protobuf field is absent rather than missing.

The field is optional on this wire now, as it already was there. A participant
that says nothing about its clocks holds the collection floor back — which is
exactly what one that says nothing about its version has always done.

Everything else in v0.34.0 stands; its notes are worth reading.

v0.34.0

Compare Source

A server collected against the participants it had a connection to, rather
than every participant it had ever had. A participant that was away came back
holding a value everybody else had removed, with a version vector equal to the
server's — so nothing was left for a rejoin to send.

Four faults of one family, all about giving space back without knowing what can
still arrive.

Collecting against the sessions open now

Server.Stable is the meet over the participants connected now, and its own
documentation says so — "this is not yet a version anything may be collected
against"
. The server collected against it anyway. A participant whose carrier
drops is out of that meet within milliseconds, and it is exactly the participant
that has not delivered what the others have.

crdt.Map.OpsSince answers for a collected stretch with a superseded run, so
that participant's version vector advances over a deletion without it ever
learning what the operation did
.

collecting against wedged
the sessions open now 23 / 30
every site the document has seen 0 / 30

Measured after cutting every link and waiting two minutes: nought behind, nought
ahead, nothing parked, and two different documents.

The same fault through a second door

The site set lived only in memory. A document that was let go of — evicted for
idleness, or lost to a restart — came back remembering nobody, and the first
participant to return and acknowledge became the whole of the answer. It is
seeded now from the document's own version vector, which is durable and names
everyone who has written here.

A participant that has only ever read is still outside that: #​85.

Collecting needs a clock, not only a version

See go-crdt/crdt v0.36.0. The floor cannot be worked out from what the server
holds — a participant that has seen everything and written nothing keeps it
pinned at whatever it last wrote. Measured: Ada wrote to clock 40, Grace deleted
from 41 to 60, and nothing was ever collectable again.

So Acknowledge carries the clocks beside the version (bytes clocks = 2;
an older participant simply sends none and holds the answer back rather than
making it wrong). A document in use now gives everything back: forty keys
written, twenty deleted, no tombstones left.

A federated server still collects nothing, and that is pinned down by a test
rather than left to be discovered: a link is a participant that never
acknowledges, so it says no clock either. Whether it should is #​88.

The harness could not arrange any of this

  • A cut carrier is an interruption, not an absence. A supervised participant
    redials in milliseconds, before the room has done anything it would need to be
    told about. With absences: the fault goes from 1 run in 12 to 10 in 10
    at the size the lane runs at.
  • No chaos test set EvictAfter, so the server never let go of a document
    and the second door stayed shut. It does now, and a blackout empties the
    room outright — everyone closes the tab on a Friday and one of them opens it
    on Monday.
  • Three chaos tests existed and CI ran two. The missing one was the only test
    that breaks collecting, which is the one thing this server does that takes away
    what somebody wrote. It has a lane now, at sixty participants, under -race.
  • The real-browser lane ran nowhere. It runs on a pinned Chrome for Testing,
    and a missing browser is a failure rather than a skip.

Also

  • A settling budget that scales with the crowd, and knobs to attribute a chaos
    failure to one source of breakage rather than four.
  • A stalled WebRTC connection names the state it stalled in.
go-crdt/crdt (github.com/go-crdt/crdt)

v0.37.0

Compare Source

A release that does nothing, on purpose.

A text and a list learn to accept an operation that names nothing and does
nothing but account for a run of sequence numbers — exactly what a map's
MapSuperseded has always done. Nothing here produces one, and no behaviour
changes.

Why publish it

A peer that does not know a kind refuses it, and the two ends of a session
are not deployed at the same moment. That was the lesson of go-crdt/collab
v0.34.0 a few hours ago, where a new field was made required and broke every
transport that was not gRPC — enough to need a retraction.

So: understand first, send later. The only thing this release buys is the months
it spends published before the other half of #​80 lands, and every one of them is
a month of peers that will not have to be refused.

What a run may stand in for

Only operations nothing else names. A deletion is named by nothing, so a
losing one — two replicas deleted the same character, and only one of them is
that character's recorded deletion — can go. An insertion cannot: it is
named by whatever was inserted after it, and a peer sent a run over one would
park everything that followed, waiting for an origin it will never be given.

The encoding

One meaning, one encoding: the clock is the sequence number, and the two
fields a deletion spends on its target hold the span and a zero. Truncated,
carrying anything in the field this kind does not use, or decoding into a span
that reaches back past sequence number one — all refused, because these arrive
from a peer.

dupDeletes is still there and still built and sorted on every snapshot.
Removing it is #​80's other half, and that needs the compatibility plan the issue
now carries.

v0.36.1

Compare Source

sortIDs said the lists were short and sorted them accordingly. They usually
are. They are not always: every one of them holds the duplicate deletions a
document is carrying — two replicas deleting the same character make one apiece
— and a document being undone and redone makes a great many. 34 044 in a
room of twenty replicas.

Insertion sort is quadratic, and the map they come out of hands them over in a
random order, which is its worst case.

     2.55s 81.21% 81.21%      2.86s 91.08%  crdt.sortIDs
                                             2.86s   100% |   crdt.(*Doc).Snapshot

So the one type of document that undoes things was a hundred times slower than
every other — slow enough that it went untested at any size worth testing,
which is the part that mattered.

20 replicas × 50 rounds
before 2 seeds in 92.7 s
after 40 seeds in 43.7 s

The short case keeps its insertion sort. The long one gets a real sort, in the
same order — asserted against a second insertion sort written out in the test
rather than borrowed from the code under test.

Also

Map.Collect now says what Yjs does instead, read in its source rather than
recalled. It collects a deleted item by replacing the content and keeping the
item, so the identity always survives and an operation arriving late still finds
something to lose to; causal delivery is the whole precondition there. A map's
tombstone has no content — the record is the space — so v0.36.0's clock floor
is the price of an economy Yjs does not attempt, rather than the repair of a
mistake.

sort.Slice is not new API; nothing here changes shape.

v0.36.0

Compare Source

Map.Collect asked for a version every replica had delivered. That is not
what a tombstone needs, and two replicas that had applied the same operations
ended up holding different documents.

What was wrong

A version says which operations everybody holds. It says nothing about the
clocks of the operations still in flight — a site that has seen nothing writes
at clock one
, however far along everyone else is.

So: site 3 writes k, site 2 deletes k, neither having seen the other. Both
are at clock 1 and the tie goes to the higher site, so the write beats the
deletion. The deletion reaches everybody, which makes it collectable by the rule
as stated. The write is still on its way.

  • The replica that kept the tombstone compares the write against it, the
    write wins, and the key comes back.
  • The replica that collected has nothing left to compare against, refuses
    the write, and the key stays gone.
the replica that collected      : held=false value=""
the one that kept the tombstone : held=true  value="v"

Same operations. Different documents. This is the shape of the fault that
withdrew the text and list collection in v0.35.0, in the one collection that was
kept.

Breaking

Map.Collect, Composite.Collect and structured.Diagram.Collect take a
clock floor as well: a promise that no operation with a clock at or under it
can still arrive. A tombstone goes when its own clock is at or under that floor
— everything still to come is then strictly later, and a write strictly later
beats it, brings the key back, and wants no comparison. What needed the
tombstone was a write at or below its clock.

A replica cannot work the floor out alone: it does not know who is out
there, and the site whose write is on its way is exactly the one it has never
heard from. Map.LastClocks, Map.Clock and Composite.Clocks are what it
offers whoever can promise one. CompositeClocks carries one floor per part,
because each map has a clock of its own.

go-crdt/collab v0.34.0 computes such a floor: participants acknowledge their
clocks as well as their version.

Also

  • Composite.Apply reports what a map part refuses instead of dropping it.
    The comment said a part's Apply could not fail once the batch had validated;
    that stopped being true when Map.Collect introduced ErrStranded, which is
    about the state of this replica rather than the batch. Dropping it meant the
    operation was refused, the caller was told the batch had been applied, and
    everything that site sent afterwards waited for a predecessor that never
    arrived — 63 thrown away in one chaos run, one replica left holding 1 555
    operations back for good
    .

  • The clock a map remembers collecting under is the floor it was asked with,
    not the highest it happened to drop. What a replica drops depends on what it
    was holding, so remembering that put replica-relative state in a snapshot that
    promises to be canonical: two replicas with the same 782 keys, the same 160
    tombstones, and different bytes.

  • The structured chaos collects, which is how all of this was found — every
    replica reaches its composite, the floor is the meet over all of them, and
    they all collect with the same version so their bytes stay comparable. Twenty
    seeds, eighty-six collections, seventeen thousand records given back, every
    type still agreeing at the end.

  • The floors are fuzzed and have a lane of their own.

go-opentype/fonts (github.com/go-opentype/fonts)

v0.9.0: — Tinos, and a licence that lives elsewhere

Compare Source

Tinos is the metric-compatible stand-in for Times, as Arimo is for Helvetica and Cousine for Courier. Two of the three were bundled and the third was not.

It was skipped because its google/fonts directory ships no OFL.txt, though its METADATA.pb names the licence and the text sits in the upstream project. A seed may now say where the real text is — the rule is untouched: no licence, no bundle.

48 families.

v0.8.0

Compare Source

v0.7.0

Compare Source

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

v0.12.0

Compare Source

v0.11.0

Compare Source

v0.10.0

Compare Source

v0.9.0: — PostScript Type 1, and the dots back on the letters

Compare Source

ParseType1 reads the format a PDF carries as a FontFile: eexec, the private dictionary, /Encoding, /Subrs, /CharStrings, and an interpreter for the Type 1 charstring operators — accent composition, flex, hint replacement and arithmetic included.

Two defects the corpus found:

  • hint replacement called the wrong subroutine, so glyphs using it came out empty;
  • dotsection — a Type 1 hint operator that fonts converted to CFF still carry — was refused as unknown, which took the dot off every i, j, colon and semicolon in such a font.

Measured over 118 863 real PDFs: 14 606 of 14 614 Type 1 programs read and 15 178 of 15 178 bare CFF ones; every glyph that does not draw is one with nothing to draw.

Details in #​30.

v0.8.0: — a bare CFF program, addressed by name

Compare Source

ParseCFF reads a Compact Font Format program with no sfnt container around it — how a PDF carries one as FontFile3/Type1C. Font.GlyphName, Font.GlyphIndexByName and Font.GlyphIndexByCode address a font the way a document does: by name, or by a byte through the program's own built-in encoding.

The 391 names every CFF font shares are checked rather than believed: against this package's own Standard Encoding table, and against 38 real OpenType fonts whose character maps say independently which character each named glyph is — 559 names, all agreeing.

Details in #​29.

v0.7.0: — a font need not carry a character map

Compare Source

A font subset embedded in a PDF as a CIDFontType2 is addressed by glyph number through the document's own map, so the subset that gets embedded routinely leaves its cmap table out. Parse required one, and refused every such font outright — leaving every glyph in it undrawn with nothing to say why.

cmap is no longer required. A font without one parses, reports the new Font.HasCharacterMap() == false, and answers GlyphIndex with ok=false rather than dereferencing a table it has not got. Outlines and metrics by glyph number — everything such a font is for — work unchanged.

Details in #​28.

v0.6.0

Compare Source

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

v0.3.0

Compare Source

go-widgets/mvvm (github.com/go-widgets/mvvm)

v0.9.0

Compare Source

v0.8.0

Compare Source

v0.7.0

Compare Source

v0.6.0

Compare Source

go-widgets/painter (github.com/go-widgets/painter)

v0.12.0

Compare Source

go-widgets/toolkit (github.com/go-widgets/toolkit)

v0.298.0

Compare Source

v0.297.0

Compare Source

v0.296.0

Compare Source

v0.295.0

Compare Source

v0.294.0

Compare Source

v0.293.0

Compare Source

v0.292.0

Compare Source

v0.291.0

Compare Source


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.

👻 Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.


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

This PR has been generated by Mend Renovate CLI.

@tannevaled

Copy link
Copy Markdown
Contributor Author

ℹ️ Artifact update notice

File name: go.mod

In order to perform the update(s) described in the table above, Renovate ran the go get command, which resulted in the following additional change(s):

  • 4 additional dependencies were updated

Details:

Package Change
github.com/andybalholm/brotli v1.2.2 -> v1.2.3
golang.org/x/net v0.49.0 -> v0.58.0
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 -> v0.0.0-20260526163538-3dc84a4a5aaa
google.golang.org/grpc v1.80.0 -> v1.83.2
File name: rougelex/go.mod

In order to perform the update(s) described in the table above, Renovate ran the go get command, which resulted in the following additional change(s):

  • 12 additional dependencies were updated

Details:

Package Change
github.com/andybalholm/brotli v1.2.2 -> v1.2.3
github.com/go-crdt/collab v0.33.0 -> v0.36.1
github.com/go-crdt/crdt v0.35.0 -> v0.37.0
github.com/go-images/images v0.0.0-20260815210351-4a50ef5ab734 -> v0.0.0-20260830151046-7e8189dc2288
github.com/go-opentype/fonts v0.6.0 -> v0.9.0
github.com/go-opentype/opentype v0.5.0 -> v0.12.0
github.com/go-richdoc/richdoc v0.2.0 -> v0.3.0
github.com/go-widgets/mvvm v0.5.0 -> v0.9.0
github.com/go-widgets/painter v0.11.0 -> v0.12.0
golang.org/x/net v0.49.0 -> v0.58.0
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 -> v0.0.0-20260526163538-3dc84a4a5aaa
google.golang.org/grpc v1.80.0 -> v1.83.2

@tannevaled
tannevaled merged commit 0e8a399 into main Aug 31, 2026
1 check passed
@tannevaled
tannevaled deleted the renovate/deps branch August 31, 2026 11:55
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