0.36.1 2026-10-06
Post-release gap analysis on v0.36.0. One user-visible regression the release introduced, plus documentation that had drifted from the code it describes.
Fixed
- A slow op response could drop a shape the user had just drawn. Adopting the server's shape list from a POST response looked harmless — it carries the version the server assigned, which a later edit needs as
baseVersion— but concurrent POST responses are not ordered relative to each other, so a late response for an older op could overwrite a newer local shape (draw X, draw Z, X's response lands last, Z disappears locally until the next SSE event). The list is only authoritative when it arrives over the SSE stream, which is a single ordered channel. Both the success path and the 409 path now take only what they need: the success path copies the assigned version onto the local shape in place, and the conflict path corrects only the conflicting shape rather than replacing the list mid-session. This is the same reasoning behind the server's no-echo broadcast.
- Doc drift:
docs/async-layers.mdoverstated the whiteboard's replay safety. It claimed the whiteboard "avoids this entirely: Loro CRDT ops already carry unique IDs and converge idempotently on their own". Measured, only half of that holds: replay is state-idempotent, but a duplicateaddis now refused with 409, and the doc said nothing about the per-shape version contract or its scope. Rewritten to match the code.
- Doc drift: the gate lists were stale in three places.
docs/local-ci.md's parity table is an explicit contract ("when a step is added to one, add it to the other") and did not mentioncheck-stelow-drift;docs/code-quality.md'sci-localchain was missing four steps it actually runs;docs/stack-layers.mdenumerated 31 of the 33 enabled linters (unparamwas absent) and did not list the two new maintenance workflows.
Verification
make ci-localgreen end-to-end (both smoke tests);make site-checkgreen (22 pages, all anchors);node --checkon the edited JS- Checked rather than assumed: the lefthook
stelow-driftglob really fires (probed with a staged file), the vendored copy still matches its pin, nomorpheus/neo-*references remain outside intentional history, the site output is gitignored and rebuilt in CI, the JS is served with a content-hash ETag so the edit cannot be cached stale, and all 33 linters now match.golangci.ymlexactly