Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-optimistic-field-reconciliation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/db': patch
---

Preserve whole-row optimistic snapshots through sync and truncate. Fix insert-dependent update settlement, local origin tracking, and rollback publication while sibling requests remain pending. Keep source updates beneath an optimistic live-query delete when queued sync batches apply, without changing sync queue timing.
17 changes: 16 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,8 @@ const dependentBuilders = [] // Accurately describes dependents

### Always Add Tests for Bugs

**Key Principle:** If you're fixing a bug, add a unit test that reproduces the bug before fixing it. This ensures:
**Key Principle:** Reproduce a bug in a test before fixing it. Prefer extending
an oracle as described below over adding an isolated unit test. This ensures:

- The bug is actually fixed
- The bug doesn't regress in the future
Expand All @@ -389,6 +390,20 @@ classifier, fixture, or assertion that let it pass. Use that analysis to suggest
the smallest test or oracle improvement that would catch the same class of bug,
not only the reported example.

### Prefer Oracle Coverage Over Isolated Regressions

An oracle that checks general laws across generated states and histories is a
stronger form of coverage than a unit test for one specific example. Prefer
extending an existing oracle when it can cover the behavior. Add the missing
model rule, generator dimension, state transition, or observable assertion;
adding more pinned examples alone does not generalize the oracle.

Use a focused regression to isolate and shrink a failure, then keep it as a
replay example for the broader oracle where possible. Verify that the expanded
oracle fails without the fix and passes with it. Keep valuable unit tests, but
do not treat them as a substitute for applicable oracle coverage. If an oracle
is not practical for the behavior, explain why a focused test is sufficient.

### Name Tests After Behavior

Test names should state the behavior they prove. Do not put issue or pull
Expand Down
12 changes: 7 additions & 5 deletions packages/db/src/collection/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,13 @@ export class CollectionMutationsManager<
return `KEY::${this.id}/${key}`
}

private markPendingLocalOrigins(
private markPendingLocalChanges(
mutations: Array<PendingMutation<TOutput>>,
): void {
for (const mutation of mutations) {
this.state.pendingLocalOrigins.add(mutation.key as TKey)
// The handler can sync synchronously before its transaction is registered.
// This is provisional; only completed mutations retain a local origin.
this.state.pendingLocalChanges.add(mutation.key as TKey)
}
}

Expand Down Expand Up @@ -267,7 +269,7 @@ export class CollectionMutationsManager<

// Apply mutations to the new transaction
directOpTransaction.applyMutations(mutations)
this.markPendingLocalOrigins(mutations)
this.markPendingLocalChanges(mutations)
// Errors still reject tx.isPersisted.promise; this catch only prevents global unhandled rejections
directOpTransaction.commit().catch(() => undefined)

Expand Down Expand Up @@ -464,7 +466,7 @@ export class CollectionMutationsManager<

// Apply mutations to the new transaction
directOpTransaction.applyMutations(mutations)
this.markPendingLocalOrigins(mutations)
this.markPendingLocalChanges(mutations)
// Errors still hit tx.isPersisted.promise; avoid leaking an unhandled rejection from the fire-and-forget commit
directOpTransaction.commit().catch(() => undefined)

Expand Down Expand Up @@ -568,7 +570,7 @@ export class CollectionMutationsManager<

// Apply mutations to the new transaction
directOpTransaction.applyMutations(mutations)
this.markPendingLocalOrigins(mutations)
this.markPendingLocalChanges(mutations)
// Errors still reject tx.isPersisted.promise; silence the internal commit promise to prevent test noise
directOpTransaction.commit().catch(() => undefined)

Expand Down
Loading
Loading