I Shipped...

6 August 2026 (5PRs)

reliable reconnects after permission changes

I fixed reconnects after a user's permissions changed while they were offline. The server now uses a complete membership response when a fast data-progress marker could miss an old-but-newly-visible row or a revoked row, while keeping the fast path for ordinary updates.

deterministic cleanup for local query outputs

I fixed local one-shot reads and streams retaining internal listeners after they had finished or been dropped. They now clean themselves up immediately, including error paths, while shared streams stay alive until their last owner leaves; regression tests cover these cases.

correct relation data across the browser boundary

I fixed browser-worker transport dropping the hidden column-name information needed to decode relation results. Without it, a relation could be matched with the wrong scalar value; the information is now preserved across the boundary, with regression coverage for nested relations.

a repeatable benchmark for scaling routed subscriptions

I added a repeatable benchmark that measures how a large number of routes affects attaching subscriptions, processing writes, retained data, and memory. It covers 1 to 1,000 routes and gives the next optimisation work a trustworthy baseline without changing runtime behaviour.

a benchmark comparing maintained and rebuilt views

I added a benchmark comparing a saved query result that is already kept up to date with one rebuilt from storage. The maintained result stayed at one read and about 494 encoded bytes, while rebuilding grew from 401 to 40,001 reads as the source grew from 100 to 10,000 rows, while producing the same result.

4 August 2026 (13PRs)

phase-level receipts for the R3 first-read benchmark

I added optional timing receipts to our benchmark for reopening a saved database and running its first query. They split the wait into recognisable steps: preparing the query, running it, turning the returned data into rows, and finishing the result. This shows where an optimisation actually helps, while normal reads do not pay the profiling cost.

lower-allocation native UUID formatting

The native code turns binary IDs into UUID strings while it loads data. It was creating several temporary arrays and strings for every ID, so I replaced that with a small reusable table and direct string building, reducing garbage without changing the UUIDs consumers receive.

a faster path for full-snapshot relation joins

When a relationship query starts with two complete sets of data, the old code treated them as if each side had changed separately. That made it do the join twice and then throw away the duplicate work, so I added a special path that builds the answer once; ordinary incremental updates still use the old behaviour.

cached relation target schemas during hydration

When loading a large set of related records, the code kept looking up and copying the same table description for each record. I cache that description for the duration of the load, so the query does less repeated work while returning exactly the same records.

a benchmark for known-state payload scaling

I added a benchmark for a sync case where the server already knows some of the client’s data. It keeps the amount of data being rebuilt constant, varies how much is already known, and checks that the client still receives the exact right versions; this lets us measure how much network data can be avoided.

attributed receipts for persisted R3 reads

I added a way for the persisted-read benchmark to say where its time went: opening the storage, opening Jazz, preparing the query, and building the first result. The existing benchmark result is still recorded, so the new numbers explain the old measurement rather than replacing it.

a fix for indexing split exclusive transactions

Some transactions arrive in several messages. If the final message arrived after earlier pieces had been cached, the code could rebuild the current view from just that last piece, so old rows disappeared; I now reload all pieces before rebuilding the indexes.

a fix for duplicate authorisation proofs consuming result slots

An access-control query can prove that a row is allowed through more than one policy route. Those repeated proofs were being counted as separate result rows before sorting and limiting the answer, so I changed the query to keep one proof per row and policy route before applying the limit; normal joins still keep their duplicate rows when duplicates are meaningful.

refreshed the performance benchmark roadmap

I refreshed the performance roadmap to match the investigations and benchmark receipts that now exist. It separates completed work from open questions and records the places where measurements showed no improvement.

recorded how selective data loading scales

I added a repeatable benchmark that measures how a selective lookup behaves as the underlying table grows. It records the exact result, the number of rows read during each phase, and the current implementation's known limit, so future improvements can be checked against a clear baseline.

lowered work for filtered relation lookups

I changed lookups that follow linked records so safe filters are applied earlier, reducing the rows carried through the rest of the query. When limits or offsets make reordering unsafe, the lookup keeps its original order.

kept indexed single reads from scanning whole tables

I fixed a query path that found the right index but then lost that choice and scanned the whole table. The lookup now keeps the specific index binding, so it reads the small candidate set while returning the same ordered results.

aligned native runtime error types

I made the browser and native versions of Jazz agree on the shape of a couple of error values. This keeps optional remote stack traces intact and gives WebSocket callbacks the types the code actually uses.

24 July 2026 (1PR)

removed stale permission examples from the docs

I removed old permission examples from the Jazz docs that no longer matched how the system works. This keeps the documentation from teaching people an outdated way of checking permissions.

23 July 2026 (1PR)

patched Next.js versions across Classic Jazz

I updated the Next.js versions used by the examples, tests, and homepage to the patched 15.5.21 release. I also tidied the surrounding dependency pins and formatting issues so the change could be checked consistently.