Revisions:
This branch changes no production code.
Round 4 applied 53 plausible maintenance mistakes to unchanged main: 23 in state.ts, 15 in sync.ts, 6 in mutations.ts, 4 in lifecycle.ts, 3 in sync-receipt.ts, and one each in the lazy state getters of live-query-observer.ts and live-query-window-controller.ts. Each mutant ran against the full @tanstack/db suite with --bail=1. Of the 53, 44 failed the suite and 9 survived. Each survivor received a probe that runs on main, a code argument, or full-suite instrumentation.
Triage also found a bug on main. A sync transaction begun inside an open one could commit first and make the rebuild lose last-write-wins row metadata. #2048 fixes it, and its record corrects the round 3 claim that the history was unreachable.
| Mutant | Change | Verdict |
|---|---|---|
| SY8 | write stages writes into a transaction that a replay invalidated | Gap. Closed here. |
| SY13 | A stale sync run's commit returns false | Gap. Closed here. |
| X3 | A receipt with no acceptance moment counts as accepted at once | Gap. Closed here. |
| X4 | The observer snapshot builds a new state map on every read | Gap. Closed here. |
| X5 | The window snapshot copies state on every read | Gap. Closed here. |
| ST27 | Dropping a held row keeps its pending local origin | Equivalent. The branch is unreachable. See below. |
| ST16 | Recompute copies rowOrigins instead of keeping a reference | Equivalent by argument. Recompute writes no origin before it reads the reference. |
| ST17 | The origin snapshot stores undefined for keys without an origin | Equivalent. Every reader treats a missing origin as remote. |
| SY18 | A missing loadSubset returns false | Equivalent. The caller resolves a non-promise result the same way. |
Writes after invalidation (SY8). After a replay invalidates the open transaction, write returns early. Without that guard, a late write enters the pending projection, and a newer transaction classifies its own inserts against it. A probe on main showed the effect. A late insert of key 9 made a newer, valid insert of key 9 throw DuplicateKeySyncError, and row 9 was lost. Two witnesses in collection-state-retention-oracle.property.test.ts write a late insert and a late delete. The newer transaction must see only the accepted row. Both fail under SY8.
A stale run's commit (SY13). The retention oracle's lifecycle driver committed from an old run only while that run was still current. Now the old run also writes and commits a row after cleanup ends it, at two points: after a plain restart, and while a reentrant restart's new transaction is still open. The commit must return true, and the row must not appear. The fixed and random retention campaigns fail under SY13. A mutant that routes a stale run's write into the new run's open transaction fails both pinned afterOldReturn histories, because the row appears. After a plain restart no transaction is open, so there a stale write fails only by throwing.
Acceptance of an abandoned receipt (X3). A commit that core never accepts, because its signal was aborted or a replay invalidated it, returns a bare receipt with no acceptance moment. whenSyncAccepted must then wait for that receipt, which rejects. The optimistic-history oracle's aborted batches and the retention oracle's invalidation replay now check that the acceptance moment rejects too. Under X3, the invalidation replay, the open-at-settlement histories, and the fixed and random optimistic-history campaigns fail. X3 also reaches every adapter that calls whenSyncAccepted on a core receipt: SQLite persistence, Query DB, Electric, and PowerSync. Under X3 those callers would treat an abandoned write as accepted. This review did not run their suites under X3. The core witnesses above own the fallback rule.
State identity (X4, X5). Before #2043, state was a field of the snapshot. The lazy getter must keep that identity: one snapshot has one state map. Consumers that compare by identity, such as a React dependency list, rely on it. The snapshot oracle now checks that a second read of data and state returns the first read's objects. Five of its six tests fail under X4. The sixth is the calibration test, which replaces the state getter. data is a plain field today, so its identity check has no demonstrated kill. It guards a future lazy data. A witness in live-query-window-controller.test.ts checks the window snapshot and fails under both X4 and X5.
The second review found X6, which shares one map per observer and refills it on every read. Each read then looks right on its own, so X6 passed. Now the oracle saves every map it reads and checks each one again after all reads. Snapshots with different rows must also have different maps. The window witness fetches a page and checks that the newer revision has its own map and the older map is unchanged. Under X6, five of the six snapshot tests and the window witness fail.
A third review found X7, a single cache entry on the observer. It builds a new map whenever a different snapshot is read, so a later read of an earlier snapshot returns a new map with the same rows. X7 passed every check above. Now the oracle reads each snapshot again after all reads and requires the map that its first read returned. The window witness reads the older snapshot again after the newer one. Under X7, the same six tests fail.
recomputeOptimisticState keeps a completed optimistic row only while a committed, queued sync transaction touches its key. It drops a held row whose key no longer has such a transaction, and ST27 skips the origin delete in that branch. A committed transaction leaves the queue only by applying, and the commit path then clears the held row and the pending local origin for each of its keys. A cleanup reset clears both maps. So no reached history takes the branch. Full-suite instrumentation agrees. A log write in the branch fired 0 times across 249 files and 8,578 tests. A log write at the loop entry fired 55 times in two of those files. The branch is a code-weight candidate. Its removal belongs with the cuts, with this argument as its evidence.
The window snapshot's state is the observer's state, so it includes the peek-ahead row that data omits. useLiveInfiniteQuery in React returns that map. This predates #2043. This review does not decide whether state should match data.
A medium code review found eight items. Each is fixed here.
A second code review, of 5a24b9025, found two items.
A third review, of 7bc58cfab, found one item. The saved-map checks did not read a snapshot again after another snapshot was read, so X7 passed. Fixed: the identity checks under "State identity" above.
CodeRabbit then asked for the same recheck of data. The oracle now saves each first read's data too and requires it on the later read. data is a plain field today, so this check has no demonstrated kill.