Repository navigation
Conversation
* test: check public DB API after minification * test: harden minified DB check setup * docs: qualify minified query row comparison --------- Co-authored-by: Isaac <isaac@example.com>
* Fix continuation after overlapping window outcomes * Preserve overlap oracle failures during cleanup * Document DEC-03 oracle evidence * Add changeset for window overlap fix * Fix continuation after subscribed source updates * Document subscribed continuation oracle evidence * fix(db): refresh failed window continuation on snapshot reads * refactor(db): remove dead synchronous continuation write * docs(db): record detached window continuation evidence * docs(db): describe detached continuation follow-up * docs(db): bound sync window error evidence * docs(db): record latest main merge validation --------- Co-authored-by: Isaac <isaac@example.com>
* fix(db): publish held row moves after value reversions * test(db): handle optional previous callback value * docs(db): explain first retraction retention * docs(db): clarify held layout notification example --------- Co-authored-by: Isaac <isaac@example.com>
* test: protect draft native collection order * docs: record draft order oracle review --------- Co-authored-by: Isaac <isaac@example.com>
* test: add paced mutation timeline oracle * docs: record paced mutation oracle review * test: preserve paced oracle cleanup failures * docs: close paced oracle cleanup review * fix: settle paced queue overflow and trailing throttle * docs: record paced mutation fix and changeset * fix: preserve custom queue admission compatibility * fix: honor omitted leading trailing throttle * fix: drain admitted queue work on cleanup * fix: settle paced throttle drops and cleanup * fix: settle pending debounce mutations on cleanup and drops * docs: record paced oracle review at implementation commit --------- Co-authored-by: Isaac <isaac@example.com>
* fix: honor collection collation in ordered reads * perf: resolve scan collation once per clause * docs: record PR 1920 oracle review evidence --------- Co-authored-by: Isaac <isaac@example.com>
* fix(db-ivm): preserve Temporal group key identity * docs(db-ivm): record Temporal group-key oracle evidence * fix(db-ivm): keep spoofed Temporal tags in plain object groups * docs(db-ivm): record Temporal tag review follow-up * test(db-ivm): specify supported Temporal group keys --------- Co-authored-by: Isaac <isaac@example.com>
* test: cover ordered Effect startup changes * chore: document ordered Effect oracle coverage * test: tighten ordered Effect startup oracle --------- Co-authored-by: Isaac <isaac@example.com>
…anStack#1921) * fix: isolate publication deferrals across cleanup * test: preserve deferral oracle failures during cleanup * docs: record publication deferral oracle evidence * test: compare deferred publication payloads * docs: record deferral payload oracle review * fix: release retired deferral queues * docs: record retired deferral queue review --------- Co-authored-by: Isaac <isaac@example.com>
…ack#1923) * fix(db): preserve aggregate query semantics during predicate pushdown * docs: record aggregate pushdown oracle evidence and release note --------- Co-authored-by: Isaac <isaac@example.com>
* Fix build-dependent binary query identities * Document binary query identity repair * Fix named query identity replay registration * Stabilize built-in index resolver metadata * Read intrinsic bytes for query view identity * Fix binary query identity across equality and realms * Record executable binary identity review evidence * Guard Buffer identity against shadowed length * Record reviewed binary identity follow-up head * fix: preserve binary query identity across view realms and tags * Keep custom index resolvers compatible with metadata naming --------- Co-authored-by: Isaac <isaac@example.com>
* test(db): specify per-key change event order * docs(db): record callback order oracle review * chore: add change event order changeset * ci: apply automated fixes --------- Co-authored-by: Isaac <isaac@example.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* fix: stop released subscription snapshots * test: cover status reentry unsubscribe * ci: apply automated fixes --------- Co-authored-by: Isaac <isaac@example.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
…#1929) * test: verify equality identity partitions agree * docs: record equality identity oracle evidence * test(db): strengthen equality identity oracle coverage * docs: record equality oracle review follow-up * docs: record post-merge oracle verification * docs: record current-main identity oracle verification --------- Co-authored-by: Isaac <isaac@example.com>
…alias for typecheck (TanStack#1540) fix(query-db-collection, trailbase-db-collection): add electric path alias Both packages alias @tanstack/db-collection-e2e, whose index re-exports suites that import @tanstack/electric-db-collection — but neither tsconfig had an alias for electric, so fresh-clone typecheck fails. Co-authored-by: Isaac <isaac@example.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…anStack#1946) * test(db): check partial and exclusive BTree range scans BTreeIndex.rangeQuery calls forRange with arbitrary bounds and both includeHigh modes. The Map oracle only checked full inclusive scans, so wrong designs that ignore includeHigh or the low bound passed it. Co-authored-by: Isaac <no-reply@databricks.com> * refactor(db): trim vendored BTree to the operations BTreeIndex uses BTreeIndex uses get, set, delete, clear, minKey, maxKey, forRange, nextHigherPair and nextLowerPair. Remove the vendored sorted-btree features that nothing calls: editRange, early-break callbacks, callback counters, in-place value edits, the overwrite flag and reused result arrays. Delete now removes the point range directly. has() stays as a wrapper over get() so the Map oracle keeps its membership observation. Point ranges keep their single-search fast path, which delete relies on. Full @tanstack/db entry, minified with esbuild: -1,148 B min, -387 B gzip, -361 B brotli. Co-authored-by: Isaac <no-reply@databricks.com> * Apply code simplification and review fixes - Remove the unreachable right-sibling shift in BNodeInternal.set and both takeFromLeft methods. The child is chosen as the first whose max key is >= the new key, so a key above child.maxKey() only reaches the last child, which has no right sibling. - Correct the header: has() and the get() fallback stay because the Map oracle observes them. - Add executable range-law calibration: range scans that ignore includeHigh or the low bound must fail the refinement check. Co-authored-by: Isaac <no-reply@databricks.com> * docs: record BTree trim oracle review Ties the range-scan law extension, its source mutants and executable controls, the performance check, and ORC-001 to ORC-012 outcomes to reviewed revision 067fbd9. Co-authored-by: Isaac <no-reply@databricks.com> * chore: add changeset for BTree trim Co-authored-by: Isaac <no-reply@databricks.com> * ci: apply automated fixes --------- Co-authored-by: Isaac <isaac@example.com> Co-authored-by: Isaac <no-reply@databricks.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
) * fix(query-db): retain errors until retry succeeds * fix(query-db): keep errors until result application * fix(query-db): count only terminal retry errors * fix(query-db): retain newer errors after held result application * docs: record held-result error oracle review --------- Co-authored-by: Isaac <isaac@example.com>
* fix: keep collection GC on a monotonic clock * docs: record GC clock oracle evidence * docs: add GC clock changeset * fix: read current monotonic clock for collection GC * docs: record GC timer clock replacement review * docs: bound GC clock oracle across timer providers --------- Co-authored-by: Isaac <isaac@example.com>
* fix: suggest manual indexes for large collections * test: record index suggestion oracle evidence * docs: record full index suggestion baseline replay * docs: clarify index advice helper * chore: add index suggestion changeset * test: provide index maps in compiler collection fixtures --------- Co-authored-by: Isaac <isaac@example.com>
* fix(vue-db): publish coherent live query data snapshots * docs: record Vue publication oracle review * docs: clarify Vue publication mutant result * refactor(vue-db): inline atomic data publication * docs: align Vue review record with simplified fix * chore: add Vue live query fix changeset * docs: note Vue live query array reference change --------- Co-authored-by: Isaac <isaac@example.com>
…k#1944) * fix(angular-db): defer required input query evaluation * docs: record Angular required input review evidence * chore: add Angular required input changeset * docs: clarify Angular changeset behavior --------- Co-authored-by: Isaac <isaac@example.com>
…#1879) * fix(offline-transactions): allow replay in visible web tabs * test: prove visible replay reaches persisted outbox work --------- Co-authored-by: Isaac <isaac@example.com>
…anStack#1951) * test(db-ivm): add a MultiSet consolidation oracle MultiSet.consolidate had no oracle owner. The existing example tests check primitive and string values only. They do not state the identity rules for object references, join tuples, the unkeyed fallback, the retained record, or input immutability. The model derives identity independently of production helpers and hash. Keyed values exclude cross-type primitives with the same text, which collide today (TanStack#1948). Co-authored-by: Isaac <no-reply@databricks.com> * refactor(db-ivm): share one loop for keyed and unkeyed consolidation MultiSet.consolidate had two hand-written loops: one for keyed [key, value] multisets and one for unkeyed data. Both summed multiplicities per identity, kept the first record, and dropped zero sums. One consolidateBy(inner, identityOf) helper now does that for both paths. Only the identity function differs. The internal ObjectIdGenerator class becomes a getStringId function with the same encoding. The internal DefaultMap class and its unit tests are removed; nothing else used it. Neither was exported. Identity rules do not change, including the cross-type collision tracked in TanStack#1948. Full @tanstack/db entry, minified with esbuild: -997 B min, -363 B gzip, -273 B brotli. Standalone db-ivm: -988 / -345 / -370. Co-authored-by: Isaac <no-reply@databricks.com> * docs: record MultiSet consolidation oracle review Ties the new consolidation oracle, its source mutants, the performance check, and ORC-001 to ORC-012 outcomes to reviewed revision f46dd42e, and adds the oracle as a coverage-map owner. Co-authored-by: Isaac <no-reply@databricks.com> * chore: add changeset for MultiSet consolidation Co-authored-by: Isaac <no-reply@databricks.com> * Apply code simplification and review fixes - Return the Map key on the single-primitive path, so a -0 record comes back as 0 as it did before the shared consolidation loop. - Build the result with one loop over the map entries. - Remove the redundant empty-input guard. - Restore a contract comment on MultiSet.consolidate() and point the oracle's authority at it. - Oracle: add -0 and NaN to the number and keyed grammars, pin the normalization, declare every known TanStack#1948 keyed collision, and check input record contents. Co-authored-by: Isaac <no-reply@databricks.com> * docs: record MultiSet consolidation review fixes Tie the review record to cca2c1c1: restored -0 normalization, the N7 and N8 mutants, updated bytes and timings, and every known TanStack#1948 keyed collision as a declared oracle limit. Co-authored-by: Isaac <no-reply@databricks.com> * docs: tie MultiSet consolidation review to the rebased head Record the rebase onto bef12e2, the new commit ids, bytes against the new base, and verification on a27600f. Update the changeset size and note that -0 still consolidates to 0. Co-authored-by: Isaac <no-reply@databricks.com> * docs: escape the delimiter pipe in the coverage table Co-authored-by: Isaac <no-reply@databricks.com> --------- Co-authored-by: Isaac <isaac@example.com> Co-authored-by: Isaac <no-reply@databricks.com>
* perf(db): defer SortedMap ordering for bulk sync publications * chore: add changeset for bulk sync ordering * docs: record bulk ordering oracle review * docs: keep oracle review record technical --------- Co-authored-by: Isaac <isaac@example.com>
* fix(db): keep NaN-ranked live windows congruent * test(db): include NaN in primitive equality generators --------- Co-authored-by: Isaac <isaac@example.com>
…ns (TanStack#1955) * feat(db): expose persisted readiness for initial live queries * chore: add persisted initial render changeset * fix(frameworks): preserve persisted error identity
* fix(db): reject invalid BTree comparator results * docs: record invalid comparison oracle evidence * fix(db): validate insertion comparisons before mutation * docs: record insertion comparison review evidence * fix(db): enforce valid index comparators in one shared checkpoint Reject invalid custom-comparator results on both BTreeIndex and BasicIndex, not just the vendored B+ tree. A comparator that returns NaN or a non-number (for example a boolean from `(a, b) => a > b`) is a caller programming error, so the index crashes: the operation that calls it throws a TypeError. BaseIndex now builds every index's comparator through a single `makeCheckedComparator` wrapper, so the two index constructors no longer duplicate comparator setup and the tree can trust its comparator again. This removes the tree's own NaN/invalid-result branch and the post-split comparison, leaving production code net negative. The index oracle proves the law across both index types, three comparators, seven probe operations, and accepted prefixes including the empty prefix (whose first write compares nothing and must succeed). The BTree Map owner keeps the split-placement histories. Collection/index-state atomicity after a comparator break is a programming-error path and stays out of scope. Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): crash the collection when an index update rejects a write An invalid custom comparator could throw from updateIndexes after the Collection had written rows but before it published change events. The collection stayed usable while holding rows its subscribers never saw, and the write reported failure: partial success, which main did not have. updateIndexes now calls the existing lifecycle.markError and rethrows, so the collection moves to `error` and later mutations throw. BasicIndex add and remove, and BTreeIndex remove, now finish their comparisons before they change the index, so a rejected write leaves the index unchanged. The index owner adds Collection-boundary cells for both index types and both write paths, and an accepted-row refinement check after rejected add/remove. The review record and coverage map now state the earlier regression and the remaining sync-writes-after-crash limit. Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): check custom collation results in indexes A `compareOptions` custom collation (`stringSort: 'custom'`) reached the index unchecked, so a `compare` that returned NaN silently merged BTreeIndex buckets and misordered BasicIndex, the same corruption as issue TanStack#1945. BaseIndex now checks every comparator result, not only a user `compareFn`. This removes a branch; the check costs one type test per comparison. Found by CodeRabbit review 5368545679. The index owner adds a custom collation cell for both index types; checking only `compareFn` fails it. Co-authored-by: Isaac <no-reply@databricks.com> * ci: apply automated fixes * fix(db): replace a BTreeIndex representative before changing the bucket Removing a bucket's representative deleted its tree entry and re-inserted the next exact value afterward. If that comparison was invalid, the remove threw after the entry was gone, so ordered reads lost the surviving rows. Overwrite the tree entry with the replacement key first; a comparator-equal `set` replaces the stored key in place and compares before it changes anything. This drops a tree delete from the path. Found by CodeRabbit review 5369042794. The index owner adds a witness where only the promoted value's comparison is invalid. Co-authored-by: Isaac <no-reply@databricks.com> --------- Co-authored-by: Isaac <isaac@example.com> Co-authored-by: Isaac <no-reply@databricks.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
…1 KB min) (TanStack#1961) * test(db-ivm): add a structural hash identity oracle equalHashValues had no direct test. The new oracle checks hash and equalHashValues against one independent identity model over constructed value pairs, including near-miss mutations and cyclic values. The work cap tests pin how headers of arrays and regular expressions count. Both land on unchanged production code before the hash dispatch refactor. Co-authored-by: Isaac <no-reply@databricks.com> * refactor(db-ivm): share one type dispatch for hashing and equality hashObject and equalHashValues each had their own six-way type dispatch. objectParts now returns one [marker, header, body] view that both use. The four per-type hashers, the duplicate getCachedHash path, and the unreachable typeof default are removed. Behavior is unchanged: headers of arrays and regular expressions still count toward the work cap, header-only types still compare before the pair memo, and arrays from another realm keep their length check. Co-authored-by: Isaac <no-reply@databricks.com> * test(db-ivm): cover Set and Map carriers, Temporal tags, and other realms Mutants on the refactor showed three gaps: Sets never held entry pairs, no near miss changed only a Map key, and no value shared Temporal text across types. Sets now hold generated values, Map keys can change, and pinned cases hold the Temporal tag and the cross-realm length check. The extended oracle passes on the base code and on the refactor. Co-authored-by: Isaac <no-reply@databricks.com> * docs: record the hash dispatch oracle review Record bytes, the kept prototype behaviors, W and N mutant outcomes, timings with an A/A control, and verification on b745595. Add the hash-identity owner to the coverage map and a db-ivm changeset. Co-authored-by: Isaac <no-reply@databricks.com> * Apply code simplification and review fixes - Check the equality revisit memo before objectParts copies Map or Set entries, so a revisited shared or cyclic pair costs O(1) again. - Compare headers with ===, as the base code did. A RegExp whose lastIndex is NaN is unequal to its copy again. Invalid dates get one header value so they stay equal. - Oracle: split a shared child against two different copies, mutate values under symbol keys, build cycles through Sets, pin RegExp lastIndex comparison, and drop an unused model parameter. - Work law: equality copies a shared Map's entries once per distinct pair. Co-authored-by: Isaac <no-reply@databricks.com> * docs: record hash dispatch review fixes Tie the review record to 6073cb1. Split mutant evidence into identity oracle, work-cap pins, and pre-existing tests; record the revisit and header fixes, N12 to N16, and a 15-process timing run with an A/A control. Declare the oracle's model-only kinds and narrow the coverage claim for cyclic values. Co-authored-by: Isaac <no-reply@databricks.com> * fix(db-ivm): separate equality type identity from hash markers * docs: record hash marker collision review evidence * test(db-ivm): run the hash identity oracle with colliding type markers The pinned marker-collision test forced one Map/Set collision by draw position. Every oracle law now also runs in a second module copy whose initialization draws are all equal, so every type marker has the same hash number regardless of declaration order. Only the sampled distinct-hash control is skipped there. Map keys are now any generated value. The model merges primitive keys and shared references under SameValueZero, as Map and Set do, and a near miss can repeat a key. Half of the mutations target a container node. The witness requires non-string, container, and merged keys. Co-authored-by: Isaac <no-reply@databricks.com> * docs: record the colliding-marker oracle and current bytes Tie the review record to 3b8412f on base 0122da8: the marker identity fix, the colliding module copy, generated Map keys, container-biased mutations, N17, rerun mutant tables, bytes, and verification. Update the coverage note and the changeset gzip figure. Co-authored-by: Isaac <no-reply@databricks.com> --------- Co-authored-by: Isaac <isaac@example.com> Co-authored-by: Isaac <no-reply@databricks.com>
…nup (TanStack#2257) * fix(electric): release the shape stream's visibility listener on cleanup The Electric client registers a visibilitychange listener (AppState on React Native) when a ShapeStream is constructed and removes it only in unsubscribeAll(). Sync cleanup unsubscribed its own callback and aborted the stream, so the listener stayed registered and kept the dead stream reachable. Each cleanup and restart, and each managed cache rotation, retained one more stream. Cleanup now calls stream.unsubscribeAll(). The stream is private to the sync session, so this affects no other consumer. A generated lane in the installed-SDK delivery oracle checks that cleanup leaves no listener from a run's streams across eager, on-demand and progressive runs that stay open, give up, abort or restart. The managed cache rotation case checks the retired session. * test(electric): name the source-release property in the oracle replay manifest * docs(electric): name the client versions that register an AppState listener * test(electric): cover incomplete stream cleanup and test teardown Generate row-only delivery and HTTP 409 reset before cleanup, with replay examples across modes and terminal outcomes. Release recorded listeners after failed comparisons and preserve the primary error. Law: completed sync runs release their resources (CleanupFn and creator guide). Reach: fixed 40-history campaign reaches partial cleanup in 4 histories and reset cleanup in 10; the random campaign reaches both too. RED: old recorder fails actual event dispatch after teardown; original missing release and both conditional-release variants fail expanded cleanup checks. GREEN: 1,130 package tests pass with no type errors; lint and builds pass. Calibration: direct seed/path replay fails each incorrect cleanup variant and passes unchanged PR production. Production source is unchanged. Limits: installed SDK, controlled HTTP and jsdom; no native/live-service claim. Full review evaluation and exact-source evidence: docs/contributing/oracle-reviews/2026-10-26-pr-2257.md * test(electric): enforce listener identity and pending-decision cleanup Track visibility listeners by ordered callback/capture registrations and calibrate the recorder against native dispatch over 8,421 bounded histories. Generate cleanup during held async error decisions, including partial/reset neighbors and subsequent runs, without changing production behavior. Law: completed sync cleanup releases adapter-owned resources (CleanupFn and creator guide); EventTarget identity includes callback and capture. Reach: pending-decision cleanup reached 18 fixed and 21 random histories; all eight declared states reached in both normal 40-history campaigns. RED: native-dispatch calibration fails old recorder; old campaigns accept wrong-capture SDK removal and conditional pending-decision release. GREEN: Electric package 1,131 tests pass, one skipped, no type errors; 8,421 calibration histories, lint and formatting pass. Variant: final oracle rejects wrong-capture SDK removal and four incorrect release conditions at cleanup assertions; direct replay pairs fail incorrect code and pass unchanged production. Limits: installed SDK/jsdom/controlled HTTP and bounded capture histories; no actual native-host or whole-version-range claim. Retired-session disposal deadline during rotation remains an explicit policy question, not a new rule. * test(electric): check rotation release before replacement State the approved Electric resource-release deadline and observe the real SDK constructor before replacement listeners can register. Generate 1-3 sequential rotations and three retired-response timings. Check both retained demands stay pending until replacement source responses; preserve assertion-time values. Law: user-approved release before replacement construction; existing Electric scoped-recovery promise completes each demand after its source snapshot applies. Reach: six rotation states reached in both 40-history campaigns, including retirement of replacement sessions and demands awaiting replacement. RED: earlier late comparisons accepted after-constructor disposal and retired demand early fulfillment. Final comparisons reject both; initial-only disposal also fails the second rotation. Diagnostic comparison payload drift reproduced. GREEN: Electric 1133 runtime tests pass, one skipped, no type errors; final owner 49 runtime + 3 type tests pass; core replay/manifest 38 tests pass. Variant: exact replay failure/pass pairs; legal disposal after cache rotation but before construction passes both campaigns. Captured values survive cleanup. Limits: installed browser SDK/jsdom, controlled HTTP and Map-backed cache, sequential 1-3 rotations; no native, overlapping, full SDK range or every private resource claim. Production code is unchanged. See docs/contributing/oracle-reviews/2026-10-26-pr-2257.md for evidence. * test(electric): satisfy package lint for oracle helpers * docs: correct oracle review evidence spacing * test(electric): observe retired cache targets after rotation * test(db): distinguish initiating request failures during replay * refactor(electric): release private streams without callback handle state * chore: remove passing persistence snippet from failure baseline --------- Co-authored-by: Kyle Mathews <mathews.kyle@gmail.com> Co-authored-by: Test <test@test.com>
…Stack#2237) * feat(oracles): lint oracle files and return reports to coding agents Add an ESLint plugin for oracle files (scripts/eslint-plugin-oracle/) with the oracle/no-disabled rule. It reports `.only`, a `.skip` or `.todo` that switches off a body, `.skipIf`/`.runIf` conditions that name no replay, Playwright's unconditional runtime skips, and generated properties limited to one unpinned run. The rules have no inline exception. scripts/agent-lint-feedback.mjs runs the oracle rules in a pass that ignores inline directives, so an eslint-disable comment cannot silence them. It serves the Claude Code, Codex, Gemini CLI, and Copilot hooks after each edit and at the end of a turn, `pnpm lint:changed`, `pnpm lint:oracles` in CI, and the pre-commit hook. Pre-commit ESLint now runs in chunks to avoid running out of memory on large commits. Remove the skipped random campaign for the TanStack#2139 known gap; the fixed-seed `it.fails` cell still pins it, and its replay name is unregistered so a replay request fails loudly. Refs TanStack#2234 Co-authored-by: Isaac <no-reply@databricks.com> * fix(oracles): pin one-run and replay exemptions structurally Replace the rule's "names a replay" heuristic, which wrong-branch conditionals, inverted conditions, the node:path module, and environment seeds all passed. A one-run options object now needs a `path` (a recorded counterexample) or a seed that is a number literal or a constant bound to one. `skipIf` accepts only `oracleReplayRequested()` from oracle-config, and `runIf` is always reported. The rule also reports Playwright `fixme`, context skips such as `ctx.skip()`, and runtime skips other than `browserName !== '<browser>'`. oraclePropertyOptions now returns one run for a path replay, so six campaign sites drop their own one-run conditional. The OPFS diagnostics oracle uses the shared replay variables, and the replay calibration fixture overrides its run count after the replay options are chosen. The enforcing pass lists tracked oracle files with git, so it skips untracked worktrees, and it fails when no main branch is available to compare against. CONTRIBUTING.md states that lint:changed runs only the oracle rules. Stale prose about the removed replay name is corrected. Refs TanStack#2234 Co-authored-by: Isaac <no-reply@databricks.com> * test(oracles): expect one run from a path replay in the run-config test The previous commit made oraclePropertyOptions run a path replay once. utils.test.ts still expected the base run count for the named property. Refs TanStack#2234 Co-authored-by: Isaac <no-reply@databricks.com> * fix(oracles): resolve replay helper and test context through scope Address CodeRabbit's review of 012f406: - oracleReplayRequested() means a path replay. A seed without a path only reseeds the random campaigns, so fixed tests keep running. - The OPFS diagnostics oracle takes its replay and random options from oraclePropertyOptions, so a path replay runs once and a seed-only run reseeds only the random campaign. - oracle/no-disabled accepts skipIf(oracleReplayRequested()) only when the helper is imported from oracle-config, so a local function of that name does not pass. - The context-skip check resolves the binding through scope, so a context captured by a nested callback is reported and a shadowing variable is not. Refs TanStack#2234 Co-authored-by: Isaac <no-reply@databricks.com> * feat(oracles): report aliased, guarded, and unscaled oracle tests oracle/no-disabled now requires every replay selection to go through the shared oracle-config helpers, so a selection cannot switch an oracle test off in an ordinary run: - A test modifier used as a value (`replaying ? it.skip : it`) is reported. oracleReplayTest(registration, ...properties) selects a replay instead. - A test registered under a condition, or after an early return, is reported unless the condition is oracleReplayRequested() or oracleReplaySkips(...properties), or a negation of one. An early throw fails loudly, so it guards nothing. Calls such as Playwright's test.info() are not registrations. - skipIf accepts oracleReplaySkips(...properties) as well. - A one-run path must be recorded: a literal, or a failure's counterexamplePath. A replay from configuration uses oraclePropertyOptions. - A literal run count above 1 that pins no history is reported, because it does not scale with TANSTACK_DB_ORACLE_RUNS_MULTIPLIER. oracle-config gains oracleReplaySkips and oracleReplayTest, both of which accept several properties. The registry adds the names that the migrated oracles select: numbered utils and comparison families, IndexedDB cross-tab history, OPFS diagnostics, and the package oracles that had their own replay variables. Refs TanStack#2234 Co-authored-by: Isaac <no-reply@databricks.com> * refactor(oracles): select every oracle replay through shared helpers Move each oracle's replay selection onto oracleReplayRequested, oracleReplaySkips, oracleReplayTest, and oraclePropertyOptions, so the extended oracle/no-disabled rule passes everywhere. In an ordinary run, every file runs the same tests with the same seeds and run counts; literal run counts are wrapped in oracleRuns, which equals the count at multiplier 1. Replay changes: - Oracles with their own replay variables now use TANSTACK_DB_ORACLE_SEED, _PATH, and _PROPERTY: utils (utils.N), comparison (comparison.N), IndexedDB cross-tab history, db-ivm index refinement, offline KeyScheduler, Electric history and pg-serializer, SQLite core adapter, Node expression index, and op-sqlite. ORACLE.md and the coverage map show the new replay commands. - A seed without a path reseeds random campaigns and keeps fixed ones, matching the shared semantics. - Some fixed or unit tests now stand aside during any path replay, where they used to run during some replays. - Calibration witnesses register through oracleReplayTest and keep TANSTACK_DB_ORACLE_CALIBRATION as their fault switch. Other changes: - The replay guard's calibration fixture is renamed to replay-guard-calibration.fixture.test.ts. It is a fixture, not an oracle. - The includes transition matrix registers over a filtered table, not under `continue`. - The PowerSync correctness oracle now fails loudly when no SQLite binding loads, where it used to skip itself silently. Refs TanStack#2234 Co-authored-by: Isaac <no-reply@databricks.com> * fix(oracles): close rule bypasses and replay gaps from review Address the medium code review and CodeRabbit's review of 95c16bb: - oracle/no-disabled checks a registration made through oracleReplayTest or oracleCalibrationTest as a registration of its test, so `.only`, `.skip`, `.skipIf`, and guards through the helper are reported. - A guard is accepted only when the test registers while the replay helper is false: under `!helper()`, in the `else` of `helper()`, or after `if (helper()) return`. A test that registers only during a replay is reported, and so is `if (!helper()) return`. - `continue` and `break` filter a loop's data, so they guard nothing; the two loop rewrites they forced are reverted. - The numRuns exemption matches only packages/db/tests/oracle-config.ts. - Replay-only lanes in the Electric, pg-serializer, SDK delivery, descriptor isolation, and includes-optimistic oracles become fixed and random campaigns through the shared helpers. - oracleCalibrationTest skips a calibration witness during a replay without its fault, where it used to pass without checking anything. - readOracleRunConfig rejects retired replay variables, so an old replay command fails instead of running the full campaigns. - The interruption-settlement oracle uses the shared replay variables, and the offline config no longer accepts TANSTACK_DB_OFFLINE_ORACLE_*. - The replay manifest names owners for the new replay properties, and its owner check covers the numbered utils and comparison families. - Fixed-campaign run counts scale with the multiplier in the queued-presence, binding-capacity, and IndexedDB cross-tab campaigns. - Comments and the coverage map describe the shared replay variables. Refs TanStack#2234 Co-authored-by: Isaac <no-reply@databricks.com> * ci: apply automated fixes * fix(oracles): own every replay property and select the rest through shared variables Replay manifest: - Every registered property now names an owner file. A manifest entry may name the module that spells a property separately from the host file that runs it, which gives the SQLite core-adapter contract and the ordinary-transaction module honest owners. The owner check accepts any string quotes. - A new test fails when a registered property has no owner. - The 42 registered names that no file used are removed, so a replay request for one fails with `unknown oracle property` instead of running nothing. This reverses the earlier choice to keep unavailable names registered. Rule: - oracle/no-disabled resolves a run count held in a constant bound to a number, as it already did for seeds. The shared-driver fairness and proxy native-method fixed campaigns now scale with the run multiplier. Shared replay variables: - The offline fifo-retry, leadership, serializer, and settlement oracles, proxy revert and native methods, db-ivm hash identity and multiset consolidation, IndexedDB pending history, and the proxy callback campaign move to TANSTACK_DB_ORACLE_SEED/PATH/PROPERTY, with registered names and manifest owners. Their old variables are retired and fail loudly. - The retired-variable error also names TANSTACK_DB_ORACLE_RUNS_MULTIPLIER. - The replay witness documents that the guarded runner witnesses async properties. A synchronous owner fails loudly under it; replay such an owner directly. Refs TanStack#2234 Co-authored-by: Isaac <no-reply@databricks.com> * fix(oracles): share one run budget across offline fixed and random lanes The random lane scaled its reserved fixed examples by the run multiplier, so the two lanes ran different budgets above multiplier 1. Both lanes now run the scaled base plus the reserved examples, and a path replay keeps its single run. Refs TanStack#2234 Co-authored-by: Isaac <no-reply@databricks.com> * feat(oracles): report test-function aliases and conditional test tables oracle/no-disabled now also reports: - A test function passed around as a value (`const t = it`, `[it, test]`, a function argument) unless it goes to oracleReplayTest or oracleCalibrationTest. A local variable with the same name is not one. - A loop or `.each()` table whose items depend on a condition: a ternary with an empty-array branch, `cond && cases`, a constant bound to either, or a `.filter` callback that reads process.env. The condition must be a shared replay helper under which the items still register in ordinary runs. The OPFS worker diagnostics oracle registers its fixed and random lanes through oracleReplayTest, not a conditional table. The offline lane options note why a path replay keeps fixed examples: fast-check counts them in the recorded path. Refs TanStack#2234 Co-authored-by: Isaac <no-reply@databricks.com> * feat(oracles): witness synchronous and fc.check replays; share the Playwright replay interface Guarded replay: - A synchronous property rejects fast-check's async reporter before it runs. The guarded runner now retries once in a synchronous witness mode when an attempt records no witness and fails, so synchronous owners can be witnessed. Async owners keep the async reporter and its native formatting. - recordOracleReplay records a witness for an owner that runs its property with fc.check, which never calls a reporter. comparison-oracle and the SQLite core-adapter binding campaign use it. - The calibration fixture has a synchronous variant, and the guard suite checks that it is witnessed in the synchronous mode. Playwright: - The IndexedDB cross-tab browser spec selects replays through oracleReplayTest and oraclePropertyOptions, with the registered property indexeddb.browser-native-history. TANSTACK_INDEXEDDB_BROWSER_* is retired. - The manifest gives Playwright owners the browser-replay status and a Playwright command. The guarded Vitest runner rejects them instead of claiming evidence it cannot collect. Refs TanStack#2234 Co-authored-by: Isaac <no-reply@databricks.com> * feat(oracles): report lowered oracle run budgets A generated property checks more histories with a larger budget, and a lowered budget is still a valid count, so no lint rule can see one. scripts/check-oracle-run-counts.mjs compares each changed oracle file with its merge-base version. It reports every run budget that went down, keyed by registered property or by fixed seed. A lowered budget can be deliberate, so the report is a tripwire: CI prints it as a warning, and the end-of-turn hook and `pnpm lint:changed` return it to the agent. Refs TanStack#2234 Co-authored-by: Isaac <no-reply@databricks.com> * fix(oracles): keep failed merge-base attempts out of hook reports The changed-files lookup tried every main branch eagerly, and git printed "fatal: Not a valid object name main" to the inherited stderr whenever a clone had origin/main but no local main. Claude and Codex return a hook's stderr to the model, so the line reached the agent as part of the report. Share the run-budget script's short-circuiting mergeBase, and pipe git's stderr so a failed attempt prints nothing. Document that Codex reads project hooks from the main checkout of a linked worktree. Co-authored-by: Isaac <no-reply@databricks.com> * feat(oracles): report private replay variables in oracle files The migration to the shared replay helpers followed the rule's reports, and the rule could not see a seed, path, or run count read from a private variable. Eleven oracles still selected their replays that way, so the replay manifest, the guarded runner, and the retired-variable check could not see them. oracle/no-disabled now reports a process.env read, dotted, computed, or destructured, whose name ends in _SEED, _PATH, _PROPERTY, or _RUNS, other than the shared TANSTACK_DB_ORACLE_* variables. oracle-config.ts, which implements replay selection, may read any of them. Co-authored-by: Isaac <no-reply@databricks.com> * test(oracles): move the remaining private replays onto the shared helpers Eleven oracles read their own replay variables: the OPFS page-lifecycle and request-order oracles, five db-ivm oracles, the SQLite boolean-arity oracle, the BTree map and IndexedDB persistence oracles, and the LIKE evaluator oracle. Each property is now registered and has a manifest owner, and its old variables are retired, so an old replay command fails loudly. Ordinary runs keep the same tests, fixed seeds, and run counts. The LIKE oracle draws cases from its own seeded generator and has no shrink path. Its manifest status is the new seed-replay: TANSTACK_DB_ORACLE_SEED reseeds its random campaign directly, and the guarded runner, which witnesses a seed and path, names that command when it rejects the target. The mixed-graph campaign checked that it reached the graph check exactly 150 times, so it failed under TANSTACK_DB_ORACLE_RUNS_MULTIPLIER=2. It now compares with the campaign's run count. Co-authored-by: Isaac <no-reply@databricks.com> * fix(oracles): see registrations through aliased test functions The migration bound many registrations to constants, such as `const fixedCase = oracleReplayTest(it)`, and some files import a test function under another name, such as `import { it as vitestIt }`. The rule recognized a test only by its name, so `fixedCase.skip(...)`, `fixedCase.only(...)`, and a guarded `fixedCase(...)` passed, and so did `vitestIt.skip(...)`. The rule now resolves the root of a registration through renamed imports and through constants bound to a test function by a selection helper or a member chain. No current oracle file had such a disabled test. Co-authored-by: Isaac <no-reply@databricks.com> --------- Co-authored-by: Test <test@test.com> Co-authored-by: Isaac <no-reply@databricks.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
…estores (TanStack#2266) - Grouped live queries key null, undefined, boolean, and bigint group values with strings in the reserved normalized-key namespace; sync.write rejects keys outside string | number. - SortedMap restores large deferred batches by membership and one merge pass, matches NaN slots as Map does, and is no longer exported (forEach removed). - Keyed MultiSet.consolidate uses nested Maps; the commit loop skips unused work. - New grouped result key oracle; the SortedMap oracle adds in-place row changes, NaN keys, and a comparator-call work law. Co-authored-by: Isaac <no-reply@databricks.com>
…Stack#2103) The OPFS worker no longer inlines wa-sqlite's WebAssembly as a base64 data URL. The package ships the worker and wa-sqlite.wasm as separate assets, and Vite, webpack 5, and Next.js load both without configuration. - Under a Vite dev server, a pre-bundled copy asks the dev server for its assets; production builds drop that lookup. Installs that Vite cannot resolve, and a worker from another installed version, report PersistenceUnavailableError, and the README documents optimizeDeps.exclude. - The open deadline and abort cover asset resolution. Asset, WASM, worker-start, and OPFS storage-root failures report PersistenceUnavailableError. - The built worker is a classic script started as a classic worker. Fixes TanStack#2101 Co-authored-by: Isaac <no-reply@databricks.com>
…cleanup admission (TanStack#2168) Splits Collection status from sync health, adds reload() and cleanup admission, folds persisted readiness into one status, and adds the health tree with request errors. See the PR description for the contract, laws, verification, size, and limits. Fixes TanStack#672 Closes TanStack#2141 Co-authored-by: Isaac <no-reply@databricks.com>
…TanStack#2230) * feat: add Convex collection with pagination and durable replay * fix: isolate Convex options and defer pagination row copying * fix: resolve offline types from source before package builds * fix: require Convex source acceptance and strengthen integration evidence * docs: record exact-head Convex oracle reviews * test: admit the dedicated offline codec replay selector * test: exercise Convex public apps and authenticated permissions * docs: record independent Convex public app review * test: use patched supported Convex Auth dependencies * docs: record patched auth fixture validation * docs: escape auth peer range in review table * test: let Convex allocate available local backend ports * docs: record Convex startup collision regression evidence * test(convex-db-collection): close oracle observation and reach gaps Retain typed keys and every pagination observation, exercise later source acceptance rejection, and preserve independently captured codec operands. Enforce separate fixed/random reach and direct replay, including backend acknowledgement cuts. Clarify that the adapter currently uses eager sync. Law: complete keyed snapshots, terminal source rejection until restart, and durable operand preservation follow the existing public collection and offline transaction guides. No production implementation changes. Reach: snapshot 150/state/lane; ownership, pagination and offline 60/state/lane; codec 60 and Convex values 100 histories/lane; 36 actual backend crash histories. RED: the repaired owners reject five previously surviving wrong designs, adjacent threshold/stale/delete-key variants, and in-place original corruption at intended comparisons. Unchanged production has no baseline RED to repair. GREEN: 64 adapter, 302 offline, 7 helper and 18 real-backend tests pass; the final captured-original follow-up passes 51 serializer tests and Convex values. Offline typecheck/build, configured ESLint and required oracle lint pass. Mutant: private stringification, prefix/stale publication, adjacent default caps, conditional terminality and operand-routing/corruption edits rejected; restored direct replays pass. Omitted uncertain histories fail declared reach. Limits: bounded eager queries and controlled source revisions; receiving completeness does not establish all scheduling, native fault or cloud laws. Exact-source evidence follows in the versioned October 26 review record. * docs: record exact-source Convex oracle repair evidence * fix: validate Convex docs and budget expanded oracle campaigns Fix relative app imports in the doc checker using import-specifier rewriting, retain real dependency types, and remove three baseline entries that now pass. Complete the Convex insert example and retain generated query row types. Give the expanded fixed/random campaigns 30 seconds under CI coverage without reducing their histories or comparisons. Validation: 767 doc examples, zero new failures; 64 adapter checks; 302 offline tests; 18 real-backend tests; adapter typecheck and build; lint:changed and configured ESLint. Published runtime sources are unchanged. * feat(convex): add on-demand paging and scientific oracle coverage Implement fixed-query lazy paging through syncMode on-demand, retaining exact coverage, applied-receipt settlement and obsolete-callback fencing. Add independent models and generated receivers for ownership, optimistic app writes, backend truth, failure equivalence and replay noninterference. Law: fixed-query coverage and publication, LoadSubsetFn/SyncAppliedReceipt, optimistic settlement, and the maintainer decision in issue TanStack#2269. Versioned law, authority and source bindings are in docs/contributing/oracle-reviews/. Reach: fixed/random campaigns enforce all declared states; final app deep campaigns reach all 14 states in 12 histories each, and outcome/abort deep campaigns reach every state in 90 histories each. RED: the recorded pre-repair ownership history rejects a retired native error callback at seed 20261027, path 0:0:0. Later coverage additions pass unchanged production and do not claim a new product-defect RED. GREEN: 112 package cases, 23 actual-backend cases, deeper campaigns, types, ESLint, formatting, oracle lint and diff checks pass. Mutant: final calibration rejects early handler settlement, swallowed sync throw, active retired subscription and a misdirected backend write; shrunk failures replay and pass after exact restoration. Limits: bounded fixed-query histories; zero-acceptance cancellable abort is covered, while prospective immediate core abort for uncooperative adapters remains TanStack#2168 integration. Final review is self-review. No new TLA+ run. Keep the local baseline-browser-mapping override and lockfile workaround out of this commit. Preserve earlier frozen evidence and versioned review records. * fix: type React paging cleanup and Electric guide rows * test: honor unindexed joined refetch fallback in work oracle * fix: regenerate minifier cache for Convex fixture names --------- Co-authored-by: Kyle Mathews <mathews.kyle@gmail.com>
…nStack#2278) Adds the oracle/no-unit-test lint rule and migrates every oracle file to declare its check, model, and grammar with declareOracle. Tests that reach no declared part move unchanged to ordinary test files (TanStack#2264 tracks the laws left with fixed tests only). The run-count script follows imports and reports removed budgets, the lint selection re-lints dependent and .js oracle files, and replay owners are checked. Generated oracles cover the rule (lint.part-reach), the run-count script (scripts.run-budget), and the lint selection (scripts.lint-scope). Refs TanStack#2234. Co-authored-by: Isaac <no-reply@databricks.com>
…ons (TanStack#2265) Every persisted hydration now waits for a reserved sync transaction to end instead of breaking its commit. Electric abandons its open transaction when its stream stops, peer resets apply in arrival order, and a peer's commit no longer changes a tab's status. Closes TanStack#2226. Co-authored-by: Isaac <no-reply@databricks.com>
…anStack#2262) * fix(db): read a sync session's accepted writes and apply transactions in commit order A sync-side metadata read now includes every accepted and open sync transaction, so a held transaction's writes stay visible to the source (issue TanStack#2220). Sync transactions stay independent: a commit is accepted after every accepted transaction and ahead of every open one, so a hold cannot change commit order and an outer transaction can still write after an inner commit. Hydration seeds follow decisions D4 and D5: they supply only objects no committed source write has written, and a source insert replaces a seeded row. The persisted wrapper reads every open transaction, stages an insert's implicit clear, and defers late seeds to its staged transactions. Query DB no longer falls back to applied metadata. Oracles: sync metadata session oracle (core and persisted drivers), the row-metadata composition oracle's held-window and held nested lanes, the hydration authority oracle, and Query ownership witnesses. Evidence is in docs/contributing/oracle-reviews/2026-10-22-sync-metadata-session.md. Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): cover conflicting nested sync transactions and treat persisted cache rows as seeds The sync metadata session grammar now generates every nested history. Core judges an outer insert against commit order: a different source row makes its transaction invalid, so no read includes it, later writes (a truncate included) are ignored, and its commit rejects. The replay projection records keys that accepted source transactions wrote, so a hold no longer changes whether a seed exempts a row. In the persisted wrapper a cached row is a snapshot older than every source write, and a source insert replaces whatever row it lands on, in core and in storage. After an inner commit, the wrapper re-judges an enclosing transaction's implicit metadata clears, as core's replay does. Oracles: nested-conflicts, persisted-cached, and persisted-conflicts lanes with declared reach; receiving expectations in the persisted, Electric, Browser, and Electron tests. Evidence is in docs/contributing/oracle-reviews/2026-10-22-sync-metadata-session.md. Co-authored-by: Isaac <no-reply@databricks.com> * perf(db): ship hydration seed authority only with DbClient The per-object seed authority lookups read Collection state but serve only DbClient hydration, so they move from the state class into client.ts as a module function and are tree-shaken from apps without DbClient. The typical app scenario's growth on this branch falls from 240 to 111 bytes gzip. A shared helper now states the rule that a partial update merges and an insert replaces its row, and the wrapper drops a parameter only its removed hidden-row check used. Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): keep seed authority off metadata a source only kept, and reject duplicates under seeds and holds Prep-PR review round for the sync metadata session (TanStack#2220). - Persisted wrapper hands inserts to core as whole-row updates (originalSyncType 'insert'), so an equal re-insert keeps metadata without writing it and a later seed still governs it (decision 9). - Rows read from storage become seeds unless core holds them as source rows, so a later DbClient seed replaces a cached row (decision 9). - The wrapper re-judges every insert without metadata of a key after an inner commit, and an update without metadata keeps that judgment. - Core's classifier exempts only seed-only rows; an insert over a row the source wrote is a duplicate whatever holds or seeds there are. - The wrapper keeps each transaction's staged row per key, so a write-time read is linear (4000 inserts: 48 ms before, 2.6 ms after). - DbClient's deleted-key and metadata-key sets become one map. Oracles: persisted-nested-seeds lane with reach "seed under a kept re-insert"; conflictingInsert action with duplicate-insert reach states; seven new witnesses; hydration-authority delete/update cells. RED on 743a6ce for every behavioral repair; hostile mutants fail. Co-authored-by: Isaac <no-reply@databricks.com> * chore: add changeset for sync metadata session reads Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): update the private-member mangle cache for this branch appliedAdapterDeletedKeys and hasAppliedAdapterTruncate are no longer private; mangle the three new private methods. Write the staged-row loop without spreading transactionStack, which the cache check reads as a property use and would stop mangling the transaction stack. Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): snapshot the projection only for open transactions A committed transaction never becomes invalid, so the rebuild no longer copies the projection for it. List all six lanes in the persisted session driver's header. Co-authored-by: Isaac <no-reply@databricks.com> * test(db): let a merged write carry a later seed into an insert's kind Decision 10: a seed accepted after an insert was written does not by itself change the insert's kind, nor does an open transaction's own write over it, but a transaction that commits after the seed and merges over it does. Core and the persisted wrapper already judge this way; the model now agrees. A random persisted campaign (seed 50521469) found the disagreement, which also reproduced in core on 743a6ce. Witnesses lateSeedUnderMergedUpdate and heldLateSeedUnderMergedUpdate (core) and a persisted witness pin it. The held witness rejects a core that keeps the insert's kind after the accepted inner write. Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): keep peer writes from seed authority, judge enclosing inserts after an abandoned transaction, and drop Query's owner supplement - A storage read marks a row as a seed only when no peer notice named it as written in this run; after a notice or catch-up that needs a full reload, it marks none (decision 9 applies to previous runs' rows only). - When a deferred transaction leaves the wrapper without reaching the Collection, the wrapper judges enclosing inserts again without it. - Query's rowToQueries supplement in getOwnersFromMetadata covered owner sets that held transactions hid from reads; the commit-order reader makes it redundant. Witnesses in persisted-oracle.test.ts fail on e4b5624 and pass now; a mutant that ignores a full-reload notice fails its witness. Co-authored-by: Isaac <no-reply@databricks.com> * test(query-db-collection): give retention witnesses time for their GC timer on loaded runners Two retention-cleanup witnesses timed out on CI at the default 1 s wait (they pass locally every run). Their cleanup runs on a real 10 ms GC timer, a storage scan, and a sync transaction; they now wait up to 5 s. Co-authored-by: Isaac <no-reply@databricks.com> * test(query-db-collection): start retention witnesses' expiry count from a clean queue The witnesses counted pending sync transactions just after their loads resolved. On a loaded runner a load's result transaction could still be pending then and apply later, so the expiry's held transaction only brought the count back to its baseline and the wait never ended. They now wait for an empty queue before the blocker starts. Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): judge storage reads per key, treat peer writes as source writes, and let transactions span storage reads Second high review of the sync metadata session (TanStack#2220), decisions 11 and 12. - A storage read stays one transaction, and core keeps seed status only for its seed keys (seed status is now a key set on every seed transaction). Validation deletes and rows written in this run keep source authority. - The runtime keeps one record of keys written in this run, fed by this tab's handoffs and peers' notices. A DbClient seed holds against stored rows only until a source writes its key in this run (decision 11). - Writes during a storage read follow the ordinary metadata rules (last write wins), and a transaction binds to a read when it commits (decision 12). The deferred hydration metadata path is gone. - Each implicit insert keeps its transaction's row from just before it, so re-judgment after an inner commit is linear and no longer walks operations. - An internal syncWriteNamesWholeRow symbol replaces the public originalSyncType field for the wrapper's whole-row updates. - Metadata readers skip invalidated transactions without copying the queue. Oracles: persisted-reads lane with a read hold (reach "write during a storage read"); witnesses for peer metadata, peer deletes, held seeds, equal re-inserts of peer rows, writes during a read, and public messages. Thirty-one persisted-oracle cases that encoded the retired rules were rewritten, each citing its decision. Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): count peers' metadata writes, mark missed notices unknown, and judge open inserts after internal writes Third high review of the sync metadata session (TanStack#2220). - The runtime records row keys and metadata keys written in this run apart, so a peer's metadata-only notice is a source write for that object. - The gap-recovery fallback and term-fence reloads mark the run's writes unknown, since the missed notices named no keys. - Every internal apply judges the open source transactions' inserts again, so an open insert follows the row a peer's notice or a storage read lands. - A storage read marks seeds only when begin opened its transaction; the staged-row fold iterates the queues in place; re-judgment looks up the inner transaction's keys; syncWriteNamesWholeRow is @internal. Oracles: a peer-route witness grid (metadata-only notice, missed notice, peer row during an open insert), each RED on ff80a31; the persisted-reads lane's read hold now returns stored rows, with reach "storage read of a stored row". Two copied-sync-config cases create their Collection per branch so vitest's typecheck never resolves overloads against a union. Co-authored-by: Isaac <no-reply@databricks.com> * test(query-db-collection): let retention witnesses report a stuck wait Their waits allow 5 s and so did the test timeout, so a slow CI run ended with a bare test timeout. The six witnesses that wait on a retention GC timer now run for up to 20 s, so a stuck wait fails with its own observation. Co-authored-by: Isaac <no-reply@databricks.com> * docs: record decision 13, unknown authorship keeps what a tab holds Co-authored-by: Isaac <no-reply@databricks.com> * fix(persistence): keep previous-run rows seeds across a peer's reload A reload that a peer's notice causes is a storage read, not a source write. While this run's source writes are known it now validates rows in place, so a row no source wrote keeps its seed status and a later DbClient seed still replaces it (decisions 9 and 11). It no longer counts the reload's truncate as a source truncate. After a full-reload notice or a reset, it still replaces the snapshot. An internal apply now re-judges only the open inserts of keys it wrote. The authority lookup drops a redundant deleted-key check, and the projection's accepted-source keys are a local of its rebuild. The reload-route grid in the persisted oracle failed three of four variants before the change. Co-authored-by: Isaac <no-reply@databricks.com> * ci: apply automated fixes * fix(persistence): count only changed stored rows as source writes after a reload A reload that a peer's notice causes is a storage read. Its transaction marks the rows that stay seeds: rows no source wrote in this run, and, when this run's writes are unknown, seeds that storage left unchanged (decision 15). Core keeps source authority and seed status across the truncate of such a transaction, and the reload deletes each dropped row as a source write. The truncate stays, because it republishes each durable row's exact shape. A reset still truncates as a source write. Record decision 14: a commit fixes its inserts' kinds as the transaction read them. The reload-route grid's full-reload variants failed before the change. Co-authored-by: Isaac <no-reply@databricks.com> * docs: record the held-cleanup retention witness race as open Co-authored-by: Isaac <no-reply@databricks.com> * test(query-db-collection): trace Query DB timers in retention witnesses The held-cleanup retention witnesses sometimes see no expiry transaction on CI. A fixture with a retention time-to-live now records each timer Query DB sets (where it was set, whether it fired, what cleared it) and each Query fetch, and a failed retention wait reports that trace. Co-authored-by: Isaac <no-reply@databricks.com> * docs: record the maintainer's deferral of the retention witness race Co-authored-by: Isaac <no-reply@databricks.com> * test(db): select session oracle replays through the shared helpers The oracle/no-disabled rule from main rejects campaigns registered under a replay condition. Fixed campaigns and reach checks now stand aside through oracleReplayTest, and each random campaign serves its own path replay. Co-authored-by: Isaac <no-reply@databricks.com> * test(oracles): name the session oracle owners in the replay manifest The replay manifest from main needs an owner for every registered property, and the owner must spell each name. The session lanes now name their campaign and paired-formulation properties literally. Co-authored-by: Isaac <no-reply@databricks.com> * test(sync-metadata): add a peer to the persisted session grammar The persisted-peers lane plays another tab's source through the persistence coordinator: named row, metadata-only, delete, and full-reload notices, crossed with seeds, the cached row, nesting, and holds. The model keeps what a reload restores for each row. It found and the wrapper now fixes: - a reload truncated over a seed accepted during a hold; - a notice for a row held transactions wrote merged over it, unless the notice leaves the row as it is (a commit this tab already knows); - an in-place reload merged a stored row over a held seed; - a reload deleted again a row a held delete removed; - staging counted a reload's preserved keys as source writes (F5). Decisions 16 to 18: reloads keep row metadata of a missing row, an update over a DbClient seed stores the row this tab shows, and a reload after a named notice keeps a seed's metadata. A storage read's truncate keeps row metadata in core, and an unchanged row keeps its status. Co-authored-by: Isaac <no-reply@databricks.com> * fix(persistence): reload a held row unless a notice carries exactly that row A peer's whole row that kept a held row's value and dropped its other fields merged over it and kept them. The peer grammar's row notice gains a variant that keeps this tab's value, and a replay pins the held case. A notice that carries exactly the held row, a commit this tab already knows, still rereads nothing. Co-authored-by: Isaac <no-reply@databricks.com> * docs: point the open two-coordinator receiving witness at TanStack#2272 Co-authored-by: Isaac <no-reply@databricks.com> * fix(sqlite-persistence): store full-row updates whole and make an on-demand reset a truncate that keeps held seeds Under rowUpdateMode 'full' the wrapper stored every sync update as a merge, so a reload or restart brought back fields an update dropped. Only a partial update now merges in storage. A reset beside a held DbClient seed skipped its truncate: it kept orphan row metadata and held rows and skipped the demand replay. An on-demand reset is now a source truncate that keeps held seeds' rows, metadata, and authority (decisions 19 and 20). A reload deletes every accepted row that storage lacks, held rows included. The peer lanes gain a reset notice, a full-row lane, an on-demand lane, and a settled storage-rows check. Co-authored-by: Isaac <no-reply@databricks.com> * fix(sqlite-persistence): make a full reload's unknown writes per key A notice that requires a full reload marked this run's writes unknown for the rest of the run, so a later named notice that the wrapper turns into a reload erased a seed's row metadata (decision 18). Writes are now unknown per key until a storage read judges the key's row or this tab's source writes it; a row such a read finds changed is recorded as this run's source write. This tab's own truncate still makes every later write unknown. Co-authored-by: Isaac <no-reply@databricks.com> * refactor(sqlite-persistence): drop read knowledge from per-key unknown writes A storage read already records each row it finds changed, so marking the keys it read as known changed no outcome; the mutant survived every case. Co-authored-by: Isaac <no-reply@databricks.com> * fix(sqlite-persistence): keep a reset's stored metadata and a seed beneath an abandoned commit A schema reset's reload read metadata through its own open truncate, so it skipped writing stored metadata equal to what the truncate then deleted. The session reader now stops at a source truncate for keys it does not keep, and rows storage holds after a reset count as this run's source writes. A commit abandoned before the wrapper hands it to core no longer counts as this run's write, so a seed beneath it still holds (decision 21). A seed that arrives after a buffered write stays judged against it (decision 22). Dead seed checks in targeted invalidation and an unreachable hydration-cycle check are removed. The peer grammar gains a reset that rewrites its rows; a new persisted-reads-seeds lane aborts a buffered commit's signal. Co-authored-by: Isaac <no-reply@databricks.com> * docs: record the final code weight and each fix commit's size Co-authored-by: Isaac <no-reply@databricks.com> --------- Co-authored-by: Test <test@test.com> Co-authored-by: Isaac <no-reply@databricks.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
… network-first (TanStack#2263) * docs: propose persisted demand coverage for offline on-demand queries On-demand persisted sources, including progressive Electric, cannot use network-first initial rendering. Offline, a query whose exact demand was loaded in an earlier session keeps waiting for the network even though its rows are in SQLite. Add a design proposal (issue TanStack#2102, item 1): the persisted wrapper may settle a demand's load from SQLite when that exact demand has persisted demand coverage in the current cache generation. Readiness, Suspense, ordered-window publication, and later demand follow from existing laws. Sources combine for network-first: eager restore plus settled on-demand demands. - Draft law and oracle design, plus an interactive explainer. - ARCHITECTURE.md and glossary wording for the proposed contract. These describe unimplemented behavior and must land with the implementation. - Two witness tests that pin current offline behavior for a generic on-demand source and for progressive Electric over node:sqlite. Expiry of coverage is tracked separately in TanStack#2211. Co-authored-by: Isaac <no-reply@databricks.com> * test(persistence): add persisted demand coverage oracle Add the oracle for the proposed persisted demand coverage law before any production change. An opted-in wrapper over an on-demand source may settle a demand's load from SQLite only when that exact demand has persisted demand coverage, after the configured wait or an upstream failure. The oracle enumerates 40 two-session histories over the real wrapper and SQLiteCorePersistenceAdapter on node:sqlite, plus a joined eager and on-demand case for the network-first wait. Session 1 creates coverage only through real loads. A pure trace model predicts status, preload state, and exact rows at five checkpoints. Model-level calibration shows that eight wrong designs each differ from the law at a named cell. The law is not implemented yet, so the 42 production cases fail at setup: initialRender rejects syncMode 'on-demand'. With the opt-in removed, main passes every uncovered cell and fails covered cells at the checkpoint where the law first applies. Co-authored-by: Isaac <no-reply@databricks.com> * test(persistence): close review gaps in demand coverage oracle Address the first round of oracle review findings: - Add an other-window session-1 history (same filter, orderBy rank, limit 1) so coverage keyed by filter alone is rejected, and a positive witness that session 1 did or did not send the session-2 demand key. - Add late empty-answer and late-failure schedules (72 cells total). - Add a fixed lifecycle history: each load measures its own wait, release aborts the upstream load, and coverage survives release. - Give the joined case a model and a wrong-design control. - Preserve the primary failure when cleanup also fails (ORC-010). - State ORC-004 ablation and ORC-009 vocabulary mapping beside the grammar and model. Revise the law draft: an on-demand source registers only that it opted in; the network-first gate reads settlement from the live query. Record the point-in-time and readiness-versus-freshness documentation requirements. Co-authored-by: Isaac <no-reply@databricks.com> * test(persistence): close second-round demand coverage review gaps - Add a rejected durable row-write history: SQLite rejects the transaction that writes the answered rows, so neither rows nor coverage may become durable. Rejects coverage written separately from its rows. - Compare exact rows in the lifecycle history, so a load cannot settle on a sibling demand's restore. - Add a restore checkpoint witness that every durable row matching the demand is visible before the deadline is tested. - Rename the joined observation fields and state lifecycle ablation. The law draft now states that the three conditions may arrive in any order, that a failure after a settlement from SQLite stays invisible to the query, and that optimistic writes on a covered demand are out of scope. The coverage map records the deferred offset/cursor key gap. Co-authored-by: Isaac <no-reply@databricks.com> * test(persistence): add order-only demand history to coverage oracle Round 3 of the oracle review converged with no new survivors at the current head. Close its remaining small items: - Add an other-order session-1 history (same filter, orderBy rank, no limit). SQLite then holds every row that matches the session-2 demand, but the ordered demand key differs, so the demand stays uncovered. Rejects a key that drops orderBy while honoring limit. - State that coverage needs the rows durably committed in condition 2. - Record in the law draft that the network-first gate needs a new query-level settlement signal that the live query does not expose yet. Co-authored-by: Isaac <no-reply@databricks.com> * feat(persistence): settle covered on-demand demands from SQLite under network-first Remove the on-demand rejection for initialRender network-first. A successful upstream load records persisted demand coverage for its exact demand key in a metadata-only source transaction that persists after the load's rows; a source truncate clears it. A covered demand of an opted-in on-demand Collection settles once its persisted subset restore is visible and the wait since the load started has passed, or its upstream load failed. The upstream load keeps running. The network-first gate combines eager restores with the live query's settled initial demands for on-demand sources. Co-authored-by: Isaac <no-reply@databricks.com> * test(persistence): end persisted demand coverage at a source truncate Add a settled-then-truncated session-1 history and its wrong design to the coverage oracle. A production mutant that keeps coverage across the truncate now fails at the first truncated cell. Co-authored-by: Isaac <no-reply@databricks.com> * docs: describe network-first initial rendering for on-demand Collections Co-authored-by: Isaac <no-reply@databricks.com> * docs: record persisted demand coverage implementation review Co-authored-by: Isaac <no-reply@databricks.com> * test(query-db): receive persisted demand coverage offline Two sessions over node:sqlite with a fresh QueryClient each: a covered on-demand query settles on its restored rows at the network-first wait or when queryFn rejects, and a never-loaded query keeps loading. Co-authored-by: Isaac <no-reply@databricks.com> * test(react-db,solid-db): receive the joined on-demand network-first gate Suspense keeps the first render of a query that joins a restored eager source and an on-demand source until the on-demand load settles. The eager readiness fixtures now declare onDemand: false. Co-authored-by: Isaac <no-reply@databricks.com> * test(persistence): check the joined gate through a nested live query Part 3 now reads rows when the network-first wait releases and adds an outer live query over the joined query. A gate that reads the root query's demands instead of the owning inner query's fails the nested cells. Co-authored-by: Isaac <no-reply@databricks.com> * test(persistence): reach resets during a load and a source with two readers Extend the persisted demand coverage oracle with three session-1 histories in which a reset lands after a load's rows but before it settles (a source truncate, a completed scoped recovery that rotates the cache generation, and a scoped recovery still pending), and with a joined shape that reaches the on-demand source through two live queries. Law: an obsolete acquisition cannot satisfy current demand (law 6 in packages/db/src/query/live/ARCHITECTURE.md), so it records no persisted demand coverage; persisted readiness needs every demand the query reaches on an on-demand source to settle (docs/contributing/glossary.md); recording coverage does not fail a load whose rows applied (prior behavior). RED on 5c0e04d: npx vitest run tests/persisted-demand-coverage-oracle.test.ts gives 35 failed | 137 passed (172), all assertion failures at a named checkpoint: 22 cells where an obsolete load recorded coverage, 12 cells where the session-1 query went to error, and the two-reader inner-only history released the wait. Co-authored-by: Isaac <no-reply@databricks.com> * fix(persistence): record coverage only for current loads and gate every reader A load records persisted demand coverage only if no lifecycle change, reset (cache rotation or scoped recovery), or source truncate happened since it was forwarded upstream. The network-first gate gives an on-demand source one entry for each live query that reads it. Multi-tab coordinators are outside the law by maintainer decision; the guide, law record, coverage map, and architecture paragraph state the limit. - Law: an obsolete acquisition cannot satisfy current demand (law 6, packages/db/src/query/live/ARCHITECTURE.md); persisted readiness needs every demand the query reaches on an on-demand source to settle (docs/contributing/glossary.md); recording coverage does not fail a load whose rows applied (prior behavior). - Reach: bounded enumeration, not a generated property (ORC-015 does not apply); every in-flight cell has a positive witness that the reset reacquired D while its first load was pending, and the two-reader history witnesses one or two distinct session-1 demand keys. - RED (parent e4add5b): npx vitest run tests/persisted-demand-coverage-oracle.test.ts gives 35 failed | 137 passed (172), all assertion failures at named checkpoints. - GREEN: the same command gives 172 passed; db 13462, persistence 1535, react-db 601, and the Electric and Query DB witnesses pass. - Mutant: restoring "any settled load records coverage" fails 34 in-flight cells; checking only for a truncate fails 23 scoped-recovery cells; restoring "the first live query reached stands for all" fails the two-reader inner-only history. All by assertion. - Limits: no multi-tab coordinator, cache-claim expiry, must-refetch, or schema reset history; Solid did not run because the registry proxy blocks a test dependency. Whole-PR size vs main: app +209 B gzip, db-sqlite-persistence-core +279 B gzip. Co-authored-by: Isaac <no-reply@databricks.com> * perf(db): ship the on-demand network-first gate only with persistence Move the per-source local-render predicate and the demand-settlement subscription into persisted-readiness.ts. The persistence adapter supplies the predicate on its readiness capability (permitsInitialRender), as it already supplies the wait, so an app without a persisted Collection does not ship them. The builder's settled check and listener stay in core because they read the builder's private state. Size vs origin/main (pnpm size:compare): the app scenario goes from +209 to +55 B gzip; db-sqlite-persistence-core is +299 B gzip. No behavior change: the persisted demand coverage oracle passes 172 of 172, and the gate mutants "an on-demand opt-in permits fallback alone" (7 joined cases) and "the first live query reached stands for all" (the two-reader inner-only case) still fail it. db 13462, persistence 1399, react-db 601, solid-db 99, and the Electric and Query DB witnesses pass. Co-authored-by: Isaac <no-reply@databricks.com> * docs(changeset): state the coordinator limit and obsolete-load rule Co-authored-by: Isaac <no-reply@databricks.com> * test(persistence): reach row deletes, rotation, refetch, and source failure Extend the persisted demand coverage oracle with a session-1 history that deletes a row of D after D settles, two same-session histories (a load of D after a cache rotation, and a refetch load of D), and a joined history in which the on-demand source fails before its demand is covered. Add a Query DB retention-expiry history to its receiving witness, and serialize the Electric witness driver as the driver contract requires. Record the illegal open-truncate history as excluded. RED on 3de5a92 production: the oracle fails 14 of 188 (11 row-deletion cells, and the refetch, rotation, and source-failure histories), and the Query DB witness fails its retention history, all by assertion. Co-authored-by: Isaac <no-reply@databricks.com> * fix(persistence): end coverage with lost rows, rotation, and refetch A coverage record now stores the cache generation's storage ID and how many rows the demand reads from SQLite. It counts only for the current generation and while the restore returns at least that many rows, so a source delete or Query DB retention expiry ends it. A refetch load never settles from coverage, an aborted load clears its wait timer, and a load rewrites its record only when the count changes. The network-first gate requires on-demand settlement on the network-failure path too, and the observer keeps one entry for each source and owning live query. - Law: an obsolete or refetch acquisition cannot settle from coverage (ARCHITECTURE.md law 6 and "Demand grouping and ownership"); coverage belongs to one generation and counts only while the restore returns at least its recorded rows (maintainer decision, 2026-10-26); a failure alone never releases an unsettled on-demand demand (glossary). - Reach: bounded enumeration; positive witnesses check the delete, the rotation's new load, the refetch's demand key, and Query DB's eviction. - RED (parent 06e0285, with the production code of 3de5a92): 14 oracle cases and the Query DB retention history fail by assertion. - GREEN: oracle 188/188, Query DB witness 4/4; db 13462, persistence 1415, react-db 601, solid-db 99, electric 719, query-db 929. - Mutant: dropping the row count, requiring more rows, ignoring the generation, letting a refetch settle, or letting a network failure bypass on-demand settlement each fail named cases. - Limits: an order change in a covered window without a delete; a truncate that lists a pending coverage record before an abort (not reproduced); each load reads its row count, so a synchronous result settles asynchronously. Size vs main: app +78 B gzip, persistence core +475 B. Co-authored-by: Isaac <no-reply@databricks.com> * test(persistence): reach a failure at once and a synchronous answer Add the session-2 schedule fails-at-once and a Part 2b history in which a demand already hydrated in the sync run gets a synchronous answer. RED on f62f9dc: the synchronous-answer history fails by assertion (the second request reports a promise). The fails-at-once cells pass with this driver, whose reads answer within one microtask turn; a real-timer probe reproduced the unhandled rejection instead. Co-authored-by: Isaac <no-reply@databricks.com> * fix(persistence): observe fast failures and keep synchronous answers settleCoveredDemand observes the upstream rejection before its coverage read, so a load that fails while SQLite counts rows does not raise an unhandled rejection. A literal true from the source returns unchanged; its coverage record follows in commit order without holding the load. - Law: a synchronous answer for an already hydrated demand stays synchronous (behavior before the opt-in); library code raises no unhandled rejection. - RED: the synchronous-answer history (oracle, assertion); a real-timer probe raised one unhandledRejection. - GREEN: oracle 215/215, persistence 1442, electric 719, query-db 929; the probe raises none. - Limits: the oracle cannot drive timer-delayed reads under its fake clock. Co-authored-by: Isaac <no-reply@databricks.com> * test(query-db): give the retention witness time on a loaded runner The positive witness waited 2 s for Query DB retention to delete the released rows. On CI the eviction had not happened by then, so the case stopped before its law check. Wait up to 10 s and give the case 20 s. Co-authored-by: Isaac <no-reply@databricks.com> * test(persistence): close open coverage-oracle limits Turn open limits of the persisted demand coverage oracle into enforced histories and receiving witnesses: - The driver checks that the wrapper forwards a load only after its persisted subset restore is visible, so no upstream event can precede it. - Session 2 reads answer on a later macrotask, so a load that fails at once reaches the coverage read; removing the wrapper's early rejection handler now raises unhandled rejections in the run. - Part 2b adds a schema reset over an empty covered demand and an optimistic write that rolls back on a demand settled from SQLite. - Part 4 adds ordered windows: a row deleted, moved out, or moved in after the window settled keeps it loading, a swap keeps it covered, and a page move settles offline only when session 1 loaded that page, with a cursor. - The Electric witness adds on-demand must-refetch and checks that Electric writes nothing from a snapshot that answers after release. - React and Solid Suspense render a covered demand from a real persisted on-demand Collection on node:sqlite, and keep the fallback otherwise. Mutants: removing the early rejection handler, keeping collection metadata across a schema reset, a coverage key that ignores cursor and offset, and ignoring coverage in React and Solid each fail these cases. Co-authored-by: Isaac <no-reply@databricks.com> * test(persistence): reach an expired cache claim around coverage reads Add two Part 2b histories: the cache claim expires after a load's rows committed and before its coverage record, and after the restore and before the coverage check. RED on 0949bc4 production: the session-1 query enters error in the first history and D enters error in the second, both by assertion. With coverage recording disabled, the first history stays ready. Co-authored-by: Isaac <no-reply@databricks.com> * fix(persistence): recover an expired claim instead of failing coverage reads A coverage row-count read that fails under an expired cache claim now returns no count and schedules claim recovery: no coverage is recorded, and a load that would settle from SQLite waits for the network. Other read failures still propagate. Record the closed limits and the remaining ones. - Law: a load whose rows applied does not fail because coverage could not be read, and an expired claim cannot vouch for restored rows. - RED: both claim histories enter error on the parent's production code. - GREEN: oracle 227/227; persistence 1454, electric 722, query-db 929, react-db 603, solid-db 101. - Limits: X7 (a truncate that lists a pending coverage record before an abort) is not reproduced; coverage relies on source settlement (law 7). Co-authored-by: Isaac <no-reply@databricks.com> * test(persistence): share one serialized node:sqlite test driver Move the serialized node:sqlite driver that five persisted demand coverage tests copied into packages/db-sqlite-persistence-core/tests/helpers, so every witness follows the driver contract's serialization rule from one place. Record the maintainer's approval of the ordered-window rule in the law record. Co-authored-by: Isaac <no-reply@databricks.com> * docs: relate persisted demand coverage to the cache eviction design Record, in the cache eviction design and the Electric guide, how persisted demand coverage relates to resume authority: it is per-demand evidence in one cache generation, not a key-set certificate, and rotation, truncate, schema reset, and lost rows end it. This meets TanStack#2102's completion criterion for the cache design and user-facing Electric documentation. Co-authored-by: Isaac <no-reply@databricks.com> * test: extend the demand coverage oracle for the fifth review round Add Part 2c (coverage bookkeeping: a failed coverage read after the load or during the check, a synchronous answer followed by a deletion, and a release during the coverage check), source failures before a covered demand's wait ends in Part 3, and two window histories in Part 4 that keep the tie-boundary key: a row replaced below the boundary and a row gained by an under-full window. Each new case fails on this commit's parent production code. Co-authored-by: Isaac <no-reply@databricks.com> * fix: keep coverage bookkeeping from changing a load, and windows exact - A source failure no longer blocks a settled on-demand demand from permitting the network-first render. The error state still blocks markReady, and settleDemand notifies settlement listeners because a query in error runs no graph. - A failed coverage read proves nothing and keeps the load's outcome. - The coverage read waits on the apply mutex, so it counts every committed source transaction's rows durably, also for a synchronous answer. - Coverage of a limited load records its row keys; the restore must return the same keys. - A load released during the coverage check owns no wait. Co-authored-by: Isaac <no-reply@databricks.com> * test: pin the row-count rule for unordered demands The maintainer kept the row count for unordered demands over recording every row key. A fixed history checks that an unordered demand that loses one row and gains another stays covered; a mutant that records keys for every demand fails it. The law record, review record, and coverage map record the decision. Co-authored-by: Isaac <no-reply@databricks.com> * test: cover window ties and unindexed page moves in the demand coverage oracle Part 4 adds four histories with a tie at the window boundary, modelled as a limited demand (same rows) plus an unordered tie-boundary demand (row count), and runs both page-move histories without an index, where core reloads the longer prefix. Mutants that ignore the tie-boundary row count or record keys for every demand each fail a named tie history. Co-authored-by: Isaac <no-reply@databricks.com> * test: add a generated campaign and failure and release sweeps for demand coverage Move the fixed oracle's production driver into a shared helper and add persisted-demand-coverage-campaign-oracle.test.ts: - Part 1, a fixed and a random campaign (40 histories each) over two unordered demands with load starts, answers, partial settlements, failures, releases, live writes and deletes, truncates, and a source that may ignore abort. Its model replays the source's own event log, so it judges coverage without predicting wrapper scheduling. - Part 2 fails each adapter call of a covered history once, before or after its effect, in either session. - Part 3 releases the demand after every step of a load. Mutants that ignore the row count, let an obsolete load record coverage, keep coverage across a truncate, let a failed coverage read fail the load, or keep a released load's wait each fail it. Co-authored-by: Isaac <no-reply@databricks.com> * test: receive persisted demand coverage in Vue, Svelte, and Angular Each adapter reads a real persisted on-demand Collection on node:sqlite in two sessions: a demand covered online is ready offline on its restored rows, and a never-loaded one stays loading. These adapters have no network-first render gate, so the witnesses check readiness and rows. A 60 s session-2 wait fails each covered history. Co-authored-by: Isaac <no-reply@databricks.com> * test: receive persisted demand coverage on better-sqlite3 and OPFS The Node witness runs five histories over better-sqlite3 through createNodeSQLitePersistence. The Chromium OPFS witness runs the same five over wa-sqlite in two page loads, in the OPFS Playwright matrix, plus one history that pins the guide's statement that a multi-tab coordinator waits for the network. The coverage map and review record describe the new campaign, sweeps, and witnesses. Co-authored-by: Isaac <no-reply@databricks.com> * test: kill mutation-sweep survivors in the demand coverage oracle A scripted sweep of 63 mutants left survivors. Five new fixed histories kill five of them: an empty covered demand after a rotation, a truncate during an empty load's coverage read, a synchronous answer whose durable write lands late, claim recovery after an expired claim, and a coverage read that outlasts the wait. The release sweep also checks that no upstream load starts after a release. The review record classifies every remaining survivor. Co-authored-by: Isaac <no-reply@databricks.com> * fix: follow main's Electric cleanup and persisted option types The Electric witness's ShapeStream mock gains `unsubscribeAll`, which cleanup now calls (TanStack#2257). The guide's on-demand example composes `persistedCollectionOptions` without explicit type arguments, as TanStack#2259 documents. Co-authored-by: Isaac <no-reply@databricks.com> * test: close the remaining demand coverage and readiness mutant gaps - persisted-readiness-oracle: six histories kill the surviving observer and gate mutants (restore held past the deadline, gate read before the preload, a retried observer after a failed client stream or a started preload, two readers of one on-demand source, and a network answer before a long deadline). - demand coverage oracle: hold the load's own cache-claim renewal past the wait, which kills a wait measured from after the renewal. - campaign: generate the window `group = 'a' orderBy rank limit 2` over tied ranks and judge each session-2 load against its exact demand's record, so the tie-boundary load is covered without predicting when core sends it. Co-authored-by: Isaac <no-reply@databricks.com> * test: receive persisted demand coverage on Electron, Cloudflare, and mobile hosts Five-history witnesses run on Electron, Cloudflare Durable Objects, React Native, Expo, Tauri, and Capacitor through each host's existing local harness. A 60 s session-2 wait fails each witness's covered histories. The Cloudflare wrangler helpers move to a shared fixture. The coverage map and review record describe the hosts, their CI jobs, and the native paths that need cargo or a device. Co-authored-by: Isaac <no-reply@databricks.com> * test: share the demand coverage judgment and add offline premises to the campaign Move the campaign's model and comparison into persisted-demand-coverage-model-oracle.ts, so the campaign and every receiving driver feed one judgment and none states expected results (ORC-014, ORC-016). The judgment takes the online session's source log, each offline query's spec, sent loads, and observation, and the offline premises. The campaign now generates a 0 or 1000 ms wait and a source that never answers or fails at once, observing before and after the deadline. Its calibration feeds wrong observations to the shared judgment. A mutant that settles a covered load without waiting fails both campaigns. Co-authored-by: Isaac <no-reply@databricks.com> * chore: commit the demand coverage mutant harness and a nightly oracle job scripts/demand-coverage-mutants.mjs applies each of 66 hostile mutants in memory through vitest.mutants.config.ts, so no file on disk changes, and requires every non-equivalent mutant to fail a named test in the owner oracles. Equivalent mutants carry the reason no observation tells them apart, and a mutant that never reaches the code is an error. The nightly workflow runs the generated campaign with ten times its PR budget and the full mutant list. Co-authored-by: Isaac <no-reply@databricks.com> * test: judge the fixed demand coverage histories with the shared model ORC-016: an oracle file holds no example tests with their own expected answers. Parts 2 to 4 of persisted-demand-coverage-oracle.test.ts now record the source's own log (rows, deletes, truncates, generation ends, and each settled or failed load, current or obsolete), each query's sent loads and observation, and the premises a history injects, and pass that record to the shared judgment. The hand-written expectation tables and functions are gone. The shared model gains what these histories exercise: per-load waits (a window's tie-boundary load starts after its limited load settles), generation ends for a rotation or schema reset, refetch loads that are never covered, failed coverage reads and checks, settlement by the upstream load in the same session, cursors and offsets, `in` filters, and a join of an eager and an on-demand Collection. Two pinned checks whose laws other owners hold move to persisted-demand-coverage-supplemental.test.ts. The release during a coverage check is swept in the campaign file instead. Co-authored-by: Isaac <no-reply@databricks.com> * test: run every receiving witness through one shared contract and judgment The demand coverage receiving witnesses (Node, OPFS, Electron, Cloudflare, React Native, Expo, Tauri, Capacitor) and the five framework witnesses now share one contract in tests/contracts/persisted-demand-coverage-receiving-contract.ts. The history data holds only session-1 steps and session-2 premises; each host records the source log, the session-2 loads, and checkpoint observations, and the shared model judges them, so no witness states an expected result (ORC-014, ORC-016). A sixth history, a covered demand whose upstream fails at once, now reaches every host. Every host passes. With the contract's session-2 source forced to a 60 s wait and an upstream that never answers, every host reports violations on its covered histories. Co-authored-by: Isaac <no-reply@databricks.com> * docs: record the shared demand coverage judgment and receiving contract The coverage map describes the receiving witnesses as production drivers for one judgment, with the six contract histories and their per-host CI jobs and limits. The review record adds the ORC-016 conversion, the per-load wait correction, the campaign premises, the receiving contract, and the mutation harness with its nightly job. Co-authored-by: Isaac <no-reply@databricks.com> * test: register the campaign's replay owner and export the contract's model types Main's replay manifest (TanStack#2237) requires every registered property to name its owner file; persisted-demand-coverage.history now names the campaign. The receiving contract re-exports the model types its framework drivers record with, so their type checks pass. Co-authored-by: Isaac <no-reply@databricks.com> * test: make the campaign reach a window that holds other rows A random campaign on CI reached no history in which a covered window later holds other rows, so its declared reach failed without any law violation. A compound step now covers a two-row window and then ranks a third row below them, which makes that state likely in every campaign. Four random campaigns and the fixed one reach every declared state. Co-authored-by: Isaac <no-reply@databricks.com> * test(persistence): observe the on-demand Collection's status in demand coverage Mutant P39, which lets openFromDisk open an on-demand Collection after the deadline, survived the first sweep after the TanStack#2168 merge: no oracle observed the guide's promise that only the load settles from SQLite. The shared model now judges the Collection's own status, and the campaign, the fixed lifecycle history, and the joined histories supply it. P39 is killed by the fixed campaign; the full sweep matches all 38 expected outcomes. Co-authored-by: Isaac <no-reply@databricks.com> * docs: type the progressive Electric example's rows After the TanStack#2168 merge the snippet check infers `unknown` rows for the untyped example, so `getKey` no longer type-checks. Co-authored-by: Isaac <no-reply@databricks.com> * fix(persistence): end demand coverage at snapshots, aborts, and scoped recovery A high review of 83b10fd found three ways coverage outlived the rows it described. Each oracle extension failed on the parent production code first. - A snapshot transaction replaces every row, as a truncate does, but kept coverage. The campaign gains a `snapshot` command; the wrapper's truncate and snapshot begin now share one coverage deletion. - A load that core aborted recorded coverage when its source resolved it. The campaign's source may now stop and resolve an aborted load, with a declared reach for a demand settled only by an aborted load. - During scoped recovery, a load could settle from SQLite and record coverage, though the restore publishes no rows and the generation is uncertified. Part 2b gains a load after scoped recovery reloaded the demand; the coverage read now proves nothing during recovery. The shared model now judges demand identity from the request data instead of core's demand key, and the storage sweep checks that a failed coverage write crashes the Collection. Mutants P40-P42 and K01 are killed. Co-authored-by: Isaac <no-reply@databricks.com> * test(electric): judge progressive offline reads with the shared model The guide says a query that never ran renders offline once a progressive full sync completed, but no witness checked it. The shared model gains a `full sync completes` source event: such a source settles every later load at once from its complete rows (ARCHITECTURE.md). The Electric witness's network-first block becomes a production driver for the model, recording Electric's own loads, over three session-1 histories and two session-2 demands, one of them never loaded. All pass; mutant E01 (a complete resume that does not resolve its full snapshot) fails only the never-loaded history. The 2026-10-28 review ledger is recorded in the review log. Co-authored-by: Isaac <no-reply@databricks.com> * test(persistence): close the final review's coverage questions - A synchronous empty answer followed by a truncate during its coverage read now distinguishes the reset-epoch check (mutant P33 killed). - A source that truncates inside its own load call, and an expired claim over an empty covered demand, run as regression histories. - P05 and P10 are equivalent in the single-process scope: every in-run reset that can meet a record goes through scoped recovery, where coverage reads prove nothing. - The review log records the final high review (S1-S10): no production change, with same-path probes for S1 and S5. Co-authored-by: Isaac <no-reply@databricks.com> --------- Co-authored-by: Isaac <isaac@example.com> Co-authored-by: Isaac <no-reply@databricks.com> Co-authored-by: Test <test@test.com>
…#2247) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…Stack#2282) The agent hooks ran the script of the checkout where the session started and linted that checkout, even when the agent worked in a worktree under `.worktrees/`. Plain directories under `.worktrees/` also counted as new work on the launch checkout's branch, so the stop hook returned thousands of reports for files that no session changed. The hooks now resolve the checkout from the hook input's `cwd`. When that checkout has its own copy of the script, the copy runs, so a branch is linted by its own rule. `.gitignore` lists `/.worktrees/`. The lint-scope oracle now draws a launch or worktree session and a scratch directory, runs the stop hook as well, and checks that every report comes from the session checkout's own rule. Fixes TanStack#2280. Co-authored-by: Test <test@test.com> Co-authored-by: Isaac <no-reply@databricks.com>
…anStack#2285) The guide said initialRender requires an eager Collection, which contradicted the on-demand section that TanStack#2263 added. Point the eager readiness rules at that section instead. Co-authored-by: Test <test@test.com> Co-authored-by: Isaac <no-reply@databricks.com>
…truncate replay (TanStack#2231) * fix(db): keep a lazy demand that fails a truncate replay from ending its live query A marker truncate privately moved an ordered window, so an include's demand changed keys. The demand controller released the old segment first; that was the subscription's last demand, so its truncate replay ended, and the new demand started outside it. Its failure then ended the query with error 228. - Start new demand coverage before releasing retired coverage. - Open a replay at every truncate for a query subscription that acquires, even without demand, so demand the commit starts belongs to it. - A demand failure that fails its subscription's replay records the error and keeps the last publication instead of ending the query. The replay-refinement oracle now classifies recovery loads by request data (replay loads ask again with the same `where`) and by the commit window, and requires the campaigns to reach replay loads. Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): treat aborted lazy demand as obsolete and keep initial failures fatal Review fixes on top of the replay-failure change: - A recovery failure before the live query first becomes ready still ends it, so preload no longer waits for a later truncate. - A replay opened without demand starts no recovery work and reports no loading, so a zero-limit ordered window issues no full-source read. - A rejected aborted lazy demand request is obsolete demand: its truncate replay decides it even when the replay settled first, and a segment that newer coverage replaced settles. Both cases ended the query on main when the adapter rejected an aborted request with AbortError. The replay-refinement oracle now generates histories during the initial preload, aborted marker loads that reject, and newest-first settles, with an initial-readiness law; the ordered-lifecycle oracle checks truncate work. Co-authored-by: Isaac <no-reply@databricks.com> * chore: add changeset for live-query replay failure fix Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): repair an ordered window whose refetched page moved before its tie A signalling refetch can move the first page's end earlier, to a rank that a row the provider never sent ties with. The loader ignored the report because an installed row past the page sorted after the new end, but that row was there only because an older tie request read it. A later window move then read local rows and skipped the unsent row. The loader now takes an installed row past the page as evidence only when its own page read it or when it read the tie of the page's current end. The ordered-work oracle keeps the trace as a named replay; the request-authority oracle gains an unproven-report law. Both fail on main. Co-authored-by: Isaac <no-reply@databricks.com> * docs: split a long changeset sentence Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): read only the moved end's tie after a refetch, and apply moves deferred behind a discarded replay Ordered loading after a signalling refetch: - When a report shows that the first page's end moved, the loader now reads only the new end's tie on the next pass instead of a bounded repair, and releases the older tie without treating its deletes as an order change. Query DB's top-N insert costs one tie request and keeps its active Query count. The loader repairs instead when it already owes a repair, has work in flight, a replay is pending, or no cursor can express the end. Window moves: - A window move deferred behind a replay that a newer truncate discards now waits for every replay that holds publication and then applies. Before, it rejected with that replay's AbortError and an older move's window stayed. Oracles: - The ordered-work refetch law now allows the documented full-source refill of an unindexed plan whose bounded prefix leaves the window short. - The replay-refinement oracle keeps the discarded-replay trace; one older replay now checks the moved window instead of an AbortError. - Query DB's first-page witness requires exactly the end's tie request and an unchanged active Query count; the request-authority unproven-report law accepts the end's tie as the proof step. Co-authored-by: Isaac <no-reply@databricks.com> * chore(db): update the mangle cache for renamed loader members Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): keep an undefined page end from matching the no-tie sentinel Address CodeRabbit review: an undefined order value at the page's end matched lastBoundary's no-tie sentinel, so a report was ignored without a proof. Also reword a named replay's comment to state the fixed behavior. Co-authored-by: Isaac <no-reply@databricks.com> * test(db): judge an unproven report after joined work defers its pass The request-authority unproven-report law judged the report's own pass, but pending joined work defers ordinary loading, so the owed repair or tie runs on the pass after that work finishes. test:dist found the case. The law now judges that later pass, and a named replay fails under the old law. Co-authored-by: Isaac <no-reply@databricks.com> * test(db): void a deferred report proof when a later step changes what it owes Address CodeRabbit review: a truncate, disposal, failure, or move between a deferred unproven report and the pass after joined work can change or end what the report owes. Only quiet steps now keep the obligation; a named replay with a truncate failed under the old law. Co-authored-by: Isaac <no-reply@databricks.com> * test(db): cover nullish ranks, staged end ties, and proven reports in the ordered oracles - The ordered-work model now sorts undefined ranks with null ones, as the comparator does, and its histories generate both. A random campaign then found that its in-flight order check treated null and undefined as different ranks; nullish ranks now tie there too. - Peers over a refetching provider stay in the grammar for source filters (TanStack#2155 excludes only joined filters). A proven-report law requires that a report which changes nothing the first query's own page and end tie prove starts no request for it and leaves it owing no repair. A campaign fixed to that premise must reach it; a loader that repairs every report fails it. - A request-authority named replay reaches a staged end tie that pending joined work defers; a loader that drops the staged tie fails it. - The held fixed campaign moves to a seed that keeps its reach after the row generator changed. Co-authored-by: Isaac <no-reply@databricks.com> * refactor(db): drop the equivalent undefined-end guard from report revalidation An `undefined` page end matched the loader's no-tie value, and the guard kept that match from ignoring a report. No cursor expresses a nullish end, so the loader has started or will start a full-source read, which covers the window either way. A mutant without the guard survived every ordered oracle. Saves 18 B gzip in @tanstack/db. Co-authored-by: Isaac <no-reply@databricks.com> * test(db): judge law 9 retention in generated replay histories The replay refinement oracle's generated section excused the window whenever a load failed, so only fixed tests judged that a failed replay keeps the last publication, and one named replay pinned a demand that fails while its replay is open. A retention law and a failed-replay campaign now judge both. - Law: ARCHITECTURE.md law 9 and "Replay participants and failure". A truncate raises a publication barrier; every load its source starts while it is up joins it; a participant's failure fails the replay while its logical demand is held; while any barrier is up the query shows the rows it showed when the first one rose. A participant counts as recovery for the terminal-error law. - Reach (createOracleReach, minimum 5, 200 histories each): judged failed replay fixed 72 / random 78; judged held replay 75 / 79; demand started after the commit failing while its replay loads 33 / 46; the include form 18 / 22. - RED: the branch's production files reverted to origin/main fail both failed-replay campaigns (convergence: terminal error). - GREEN: load-subset-replay-refinement-oracle.test.ts, 34 tests. - Mutants: a failed replay that releases the publication gate fails both failed-replay campaigns ("retained publication"); a subscriber that ends the query when a demand fails while its replay still loads fails both ("convergence: terminal error"). - Model corrections found while building it, each from ARCHITECTURE.md: a failure is scoped to its logical demand, so a retired failed demand and its replacement publish; an ordered tie request is not lazy demand, which had let a failed tie request excuse a terminal error. - Limits: sources with two subscriptions in the query (a peer, or a query that both joins and includes markers) are not judged; barriers that rise before the first ready are not judged; a barrier where a release threw during the replay's start is skipped. Co-authored-by: Isaac <no-reply@databricks.com> * test(db): generate replay recovery during the initial preload Two production repairs in this branch were killed only by named replays: the deferred window move that a discarded replay rejected, and the guard that keeps a failure before the first ready fatal. A preload-recovery campaign now generates both. - Law: ARCHITECTURE.md "Replay participants and failure" (a deferred move waits for every replay that holds publication and applies unless one failed) and the initial-readiness law (a failure before the first ready ends the query, so the preload settles). - Reach (createOracleReach, minimum 5, 300 histories each): lazy demand in a replay failing before the first ready, fixed 37 / random 32-40; a window move behind a replay a newer truncate discards, 41 / 39-49. - RED: the builder from 21fbd03 fails the fixed campaign ("settled window"); the builder without the initial-failure guard fails it ("initial preload settles"). The fixed seed kills both; each dies in about half of 200-run seeds. - GREEN: load-subset-replay-refinement-oracle.test.ts, 36 tests, three runs. - Model correction: a replay completes when its last participant's rows apply, so the retention model now settles a participant after its commit and lowers the barrier then. Demand that a deferred move starts afterwards no longer joins a finished replay. The gate-release and pending-replacement mutants still fail both failed-replay campaigns. - Limits: the random campaign kills each named mutant about half the time. Co-authored-by: Isaac <no-reply@databricks.com> * test(db): declare reach for the ordered report laws this branch added The unproven-report law was reached in about one broad-campaign history in 10,000, the proven-report random campaign counted no reach, and the refetch law's unindexed short-refill exception had no reach on either side. - Law: ARCHITECTURE.md ordered recovery. A report over a first page that no row the loader read proves starts a repair or reads the end's tie; a report the first page proves starts nothing; a signalling refetch's repair reads no full source except the refill of an unindexed plan whose bounded prefix leaves the window short. - Reach (createOracleReach): unproven-report campaign, 300 histories, minimum 20: fixed 104, random 84. Unindexed-refetch campaign, minimum 5: law applied fixed 16 / random 18, short refill excused 26 / 24. Proven-report random campaign: at least one history. - RED: main's loader fails both unproven-report campaigns. - GREEN: ordered-source-loader-state and ordered-work oracles, 354 tests. - Mutant: a loader that answers every report with a full-source read fails the unindexed-refetch fixed seed and random runs; about half of fixed seeds kill it, and this one does. - Limits: the unproven-report campaign starts every history with the same three commands; the fake's installed rows ignore a read's limit, so the first-page ignore branch stays with the ordered-work oracle. Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): share one replay barrier across a query's subscriptions to a collection A query that joins and includes the same on-demand source has two subscriptions to it, each with its own truncate replay. A truncate's commit can retire one subscription's only demand privately; its emptied replay then closed while the other's still loaded, and demand the graph started for rows the commit exposed fell outside every replay. Its failure ended the query with error 228, the TanStack#2224 class through a second subscription. A subscription's replay now neither publishes nor closes as empty while a sibling subscription of the same collection in the graph still has replay work, so demand started meanwhile joins it. - Law: ARCHITECTURE.md "Replay participants and failure", revised here: a live query's subscriptions to one collection share one publication barrier. The replay oracle's participant rule and retention law now apply to them (previously skipped as unattributable). - Reach (createOracleReach, minimum 5): a sibling-demand branch of the failed-replay campaign declares a demand that fails inside the shared barrier of a query that joins and includes markers. - RED: the parent commit's production code fails the named replay "a demand that fails while a sibling subscription's replay still loads" and both failed-replay campaigns at "convergence: terminal error". - GREEN: replay refinement oracle 37 tests; @tanstack/db 285 files / 13,816 tests; test:dist 323; query-db 42 files. - Mutants: removing the hold at replay completion, or the hold for an emptied replay, each fails the named replay and both campaigns. - Size: +175 B gzip in @tanstack/db (+276 to +451). - Limits: histories with a peer query stay unjudged by the retention law. Co-authored-by: Isaac <no-reply@databricks.com> * chore: describe the shared replay barrier in the changeset Co-authored-by: Isaac <no-reply@databricks.com> * test(db): drop the one-run replay override from this branch's campaigns A path replay now runs once inside oraclePropertyOptions, so the override is redundant, and oracle/no-disabled rejects it. Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): grow a short unindexed prefix instead of reading the full source An ordered, limited window over a source without an order index used to read the whole source when its bounded prefix left the window short. That read is unbounded. Maintainer decision: the prefix grows instead, doubling the last prefix while growth still adds rows, and never reads the full source. Law: after a signalling refetch, a repair starts no full-source request unless a bounded page ended on a null or NaN rank (ARCHITECTURE.md, "an underfilled unindexed prefix grows"). A pending optimistic departure from such a window refills it before settlement with a bounded request. Reach: `ordered-work.eager-unindexed-refetch` (fixed and random) declares windows the prefix fills and windows it leaves short. Sixteen new optimistic-window cases cover an unindexed one-row window behind a joined or anti-joined filter that installs only its grown prefix, so a departure leaves the replacement row missing. RED (unchanged production): the named short-window replay and the fixed unindexed-refetch campaign fail with "full-source requests after a signalling refetch at step N: expected 2 to be +0". GREEN: ordered-work oracle 293/293; @tanstack/db 284 of 285 files pass, and oracle-replay.test.ts fails only on child-run timeouts (spawnSync ETIMEDOUT) under a concurrent mutant sweep. Mutants: no stop rule -> "ordered loading did not reach a fixed point"; stopping whenever the prefix reads short -> the 16 optimistic cases leave the window empty after settlement ("settled rows: [] instead of [2]"). Expectation changes (maintainer approved): the existing unindexed optimistic cases behind a joined filter now allow, without requiring, a request when the replacement row is installed, because growth proves rows only up to its prefix. The optimistic refill bound for unindexed plans is twice the installed prefix plus the overlaid row. Limits: the new optimistic cases are fixed histories with one-row windows; growth's request count is bounded but not minimal (an answer with new rows resets the growth record, so a short source can cost one more read). Size: @tanstack/db +468 B gzip vs main (+17 B for this change). Co-authored-by: Isaac <no-reply@databricks.com> * refactor(db): drop the implied short-read term from the growth stop rule A prefix stops growing once its last growth added no rows. The rule also required the prefix to read fewer rows than it asked for, but that follows: the growth record holds the rows before the last doubling, so a count that did not exceed it is below the doubled prefix. Every path that shrinks the prefix count resets the record too. Evidence: a deletion sweep found the term's mutant surviving the ordered, replay, include, pagination, subset, and join oracles. With the term removed, the ordered-work, pagination, and ordered source loader oracles pass (659 tests), and the full @tanstack/db suite passes (285 files, 13,833 tests). The 16 optimistic-window cases that kill a stop rule without the growth record still pass. Co-authored-by: Isaac <no-reply@databricks.com> * test(db): judge every loaded-subset report against a faithful fake source A deletion sweep found the report path's guards surviving every oracle: deleting the moved-end early return in revalidateLoadedCoverage, its past-window term, or loadMovedEndTie's owed-debt term changed nothing that an oracle saw. Law (ARCHITECTURE.md, the loaded-subset report paragraph): a loader whose coverage is a held full-source read, or whose window its own first page proves, ignores the report, so it reads nothing for it. A loader that already owes a repair repairs instead, so no tie or page comes before the repair's prefix. Oracle changes: - The fake subscription reads installed rows as the subscription does, applying a tie's equality, a cursor, and a limit. Its reads ignored the limit, which made the first-page ignore branch unreachable. - The cost law's per-chain bound of three requests was an artifact of that fake. It is now a prefix plus a tie and a page per installed row, and it also judges an unindexed window that still needs rows, which now grows its prefix instead of reading the full source. - A head insert moves the page's end before the tie the loader read, and a window that shrinks counts as quiet: it shows only rows already proven. - The ignored-report law forbids any request, not only authoritative ones, and a new owed-repair law judges reports while a repair is owed. Reach: `ordered-loader.unproven-report` (600 runs) starts each history with one of seven prefixes and requires at least 20 histories for each premise: a proven first page, a page past a shrunk window, a held full-source read, a moved end, an owed repair, authoritative work in flight, and an older tie's release that truncates or disposes the loader. RED (hostile mutants; production is unchanged): - the early return removed: "ignored report: request 2 was read for a report over coverage that the loader's own first page proves"; - its past-window term removed: the same failure; - the owed-debt term removed: "owed repair: request 2, an ordinary read, came before the repair a report owed". GREEN: ordered-source-loader-state oracle 77/77. Limits: the unexpressible-end term (the fake's comparator cannot place NaN or nulls), the stale-run check after the older tie's release (the fake's dataNeeded ignores its boundary), and the in-flight-work, staged- continuation, and covered-source checks, each decided by another check in every reached state, still survive. Recorded in the coverage map. Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): judge a lazy demand's failure against its source's shared barrier A query that joins and includes one source has two subscriptions to it, which share one replay barrier. When one subscription's replay failed and the other's finished, demand that the finished subscription then started and lost ended the query: the subscriber judged the failure by its own subscription's replay. Law (ARCHITECTURE.md, "Replay participants and failure", now extended): a failed replay of any of a query's subscriptions to one collection fails the shared barrier. Until a later replay of the source succeeds, a lazy demand of any of them that fails keeps the last publication and does not end the query. Reach: the failed-replay campaign's sibling branch may now move a row and fail the oldest held load; `failedSiblingDemandFailures` (minimum 5) is reached 11 to 19 times per 400-run campaign. RED (unchanged production): the random campaign found seed -1842904352, path 22:1:0:0:0:0:2:0:2:5:5:5 (also on the loader before prefix growth); the named replay "a demand that fails after the sibling subscription's replay failed" and both campaigns fail at "convergence: terminal error". GREEN: load-subset replay oracle 38/38; @tanstack/db 285 files, 13,834 tests. Mutant: the per-subscription rule is the RED above. A rule that judged against any failed replay of the query, across collections, survives: the convergence check follows an explicit window move, which retries a failed row demand. ARCHITECTURE.md keeps that failure fatal; recorded as a limit. Also, the report path's own `!active` checks go: dispose() advances the generation that the stale-run check after the older tie's release reads, and a disposed loader issues nothing. The report campaign gains a dispose-before-report prefix (reached 69 to 90 times per campaign), and it runs 1,000 histories, since at 600 a random seed could miss a minimum. GREEN: ordered source loader state oracle 77/77, query-db refetch oracle 40/40, ordered and replay oracles 1093/1093. Size: @tanstack/db +501 B gzip vs main (the barrier check costs about 33 B after a smaller form replaced a shared lookup helper). Co-authored-by: Isaac <no-reply@databricks.com> * refactor(db): close the report path's remaining guards with laws or deletions A deletion sweep left five guards on the loaded-subset report path that no oracle distinguished. Each now has a law that fails without it, or it is deleted because another check decides every state it covered. Laws (ARCHITECTURE.md, the loaded-subset report and truncate rules; `canExpressCursorOrder`): - the loader never reads the tie of a rank no cursor can express; - the first finite request after a truncate starts with no cursor; - a report over coverage that a held full-source read completed issues no request, whatever came before, since such coverage owes nothing. Oracle changes: a head insert can carry a `-Infinity` rank; the fake's dataNeeded counts the window rows not at or before the boundary it gets, where it returned a constant; and the report campaign gains prefixes for an unexpressible end, a release that truncates before the window grows, and a paged loader whose repair fell back to a full-source read. The cost law's premise now asks whether every installed rank is expressible. Reach: each new report state reaches its minimum of 20 with a margin (the lowest state across six random seeds was 47). RED (hostile mutants; production unchanged for these): - the stale-run check after the older tie's release removed: "reset after step 4: request 2, the first finite request after a truncate, continued from a cursor" in the fixed and random report campaigns; - loadBoundary's unexpressible-value check removed: "unexpressible tie after the drain: request 3 read the tie of rank -Infinity"; - the work-slot term in loadMovedEndTie removed (with the deletions below): "ignored report: request 2 was read for a report over coverage that a full-source read proves"; - the unexpressible-end term in loadMovedEndTie removed: the unindexed-refetch random campaign found seed 1620448148, where an unindexed loader reads the full source through the boundary fallback; a named replay of that trace fails with "full-source requests after a signalling refetch at step 2: expected 2 to be +0". Deleted, with the oracles green: the covered-source early return in revalidateLoadedCoverage (the work-slot check decides it) and the staged-continuation term in loadMovedEndTie (a staged finite continuation cannot coexist with a proven end; a staged repair step owes a repair). GREEN: ordered source loader state 77/77; ordered, replay, and pagination oracles 1095/1095; query-db refetch oracle 40/40; @tanstack/db 285 files, 13,835 tests. Limits: the moved-end check's `Object.is` term still survives; it decides differently from its fallback only while other work is in flight, which the ignored-report law's quiet premise excludes. Size: @tanstack/db +497 B gzip vs main (was +501). Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): fail a truncate replay when a demand's load throws inside it A source whose loadSubset throws instead of rejecting could end a live query during a truncate replay: the subscription dropped the thrown demand, so its failure did not fail the replay, and the subscriber treated the synchronous throw as fatal. Law (ARCHITECTURE.md, "Replay participants and failure", extended by maintainer decision): a load that throws while it starts inside an open replay fails that replay as a rejected load does. The demand stays with the subscription, so the replay keeps the last publication, a later replay retries it, and its owner releases it; the adapter acquired nothing, so it receives no unloadSubset for that load. Oracle: `markerThrows` arms the next marker load to throw while it starts. A thrown load joins a replay only if that replay still has a participant or a failure, and an armed throw expires before the drain. Reach: `thrownReplayDemands` (a throw into a replay that is only loading) reaches 10 to 24 per 600-run failed-replay campaign. RED (parent code): the named replays "a replay load that throws while it starts" and "a demand whose load throws inside its open replay", both failed-replay campaigns, and the broad fixed campaign fail at "convergence: terminal error". GREEN: load-subset replay oracle 41/41; @tanstack/db 285 files, 13,838 tests; query-db, electric, powersync, trailbase, and react suites pass. Mutant: unloading the never-acquired load fails with "adapter release: marker unloadSubset released a demand it did not hold". The subscriber-side alternative (judge the thrown demand against a failed replay) fails like the parent code and was dropped. Expectation changes (maintainer decision): the subscription replay oracle's `new request throws` case and five live-query cases expected a thrown new demand to roll back; they now expect the rejection outcome. Model corrections: a thrown demand's retirement is invisible at the adapter boundary, so the retention law skips the marker barrier from a throw to the end of its episode, a barrier raised later in an ambiguous episode inherits that (which also corrects the existing release-throw case), and a failure after the throw's command may end the query. Limits: a design that dropped a thrown demand and let its replay succeed passes wherever only retention could see it. Open: a retention counterexample with no throw, found at five times the run count and failing on the parent code too, is pinned with it.fails and recorded in the coverage map. Size: @tanstack/db +498 B gzip vs main. Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): hold publication for a demand that fails behind a sibling's failed replay A query that joins and includes one source has two subscriptions to it. When one subscription's replay failed and the other's finished, a demand on the finished subscription that then failed was kept from ending the query, but it held nothing. A later change that retired the first replay's failed demand discarded that replay and published the window without the second demand's rows. Law (ARCHITECTURE.md, "Replay participants and failure"): until a later replay of the source succeeds, a lazy demand of any of the query's subscriptions to it that fails keeps the last publication. Such a failure now fails its own subscription's replacement too; it holds until a later replay succeeds or that subscription's last demand retires. Reach: `partialFailureRetirements` (a failed demand retires while another failure still holds the barrier) reaches 17 to 27 per failed-replay campaign. RED (parent code, also before this branch): the failed-replay random campaign at five times its run count found the trace; its named replay fails with "retained publication after mark: expected [] to deeply equal [ { id: 4, rank: 40, ... } ]". The campaigns do not fail without the hold, so the named replay is the witness. GREEN: load-subset replay oracle 41/41 three times; @tanstack/db 285 files, 13,839 tests. Mutant: the subscriber without the hold is the RED above. Also keeps the trace from TanStack#2221 as a named replay in the ordered-work oracle: the loader before prefix growth reads the full source twice after a signalling refetch past a NaN boundary ("expected 2 to be 1"); with growth it reads none. Size: @tanstack/db +570 B gzip vs main (+72 B for this fix). Fixes TanStack#2221 Co-authored-by: Isaac <no-reply@databricks.com> * test(db): keep the two traces reported on TanStack#2221 as named replays Both traces failed on main and pass on this branch, each fixed by an earlier commit here rather than by a shared marker-filter cause. - Trace 1 (ordered-work oracle): an ascending unindexed window behind an anti-joined marker read the full source twice after a signalling refetch. The loader before prefix growth (944a0c0) fails it with "full-source requests after a signalling refetch at step 1: expected 2 to be +0"; prefix growth (fe48cbe) reads none. - Trace 2 (load-subset replay oracle): a one-row move deferred behind a marker replay that a source truncate discards was dropped, so the window kept three rows. main's production fails it with "settled window: expected [ 10, 20, 30 ] to deeply equal [ 10 ]"; bisection places the fix at ce1299d, which applies a deferred move after the newer replay. Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): keep a window move from settling on behalf of itself A window move whose load throws while it starts bumps the request generation before it replaces the latest request. An older move that still waited then saw itself as superseded and returned the latest request, which was its own promise: a chaining cycle, which escaped as a TypeError. Law (the oracle's newest-move rule; ARCHITECTURE.md, window moves): a superseded move resolves after the newest move settled. A newer move that threw while it started has settled, failed, so the older move resolves on its own. Oracle: a move whose setWindow throws counts as a settled, failed move in the model, as a rejected move does; it stayed pending before, so the newest-move law misjudged the older move. Reach: `thrownMoves`. RED (parent code): the broad random campaign at five times its run count found the trace; its named replay fails with "escaped error: only injected faults may surface", caused by "TypeError: Chaining cycle detected for promise". It fails the same way before this branch's synchronous-throw and hold changes. GREEN: load-subset replay oracle 43/43; @tanstack/db 285 files, 13,842 tests. Mutant: the settlement without the self guard is the RED above. Limits: a move deferred behind a replay has the same shape when a newer move throws between the replay settling and the deferred move resuming. The grammar flushes between commands, so it never reaches that microtask gap; a guard there had no witness and was left out. Recorded in the coverage map. Size: @tanstack/db +588 B gzip vs main (+18 B). Co-authored-by: Isaac <no-reply@databricks.com> * refactor(db): derive the live query's error state and share loading-promise release - collection-subscriber: the loading-promise map stores the bare resolve function, and the resolve-then-delete sequence that the status handler and the unsubscribe cleanup each repeated is one helper. - collection-config-builder: `isInErrorState` is a getter over the two flags it always mirrored (a fatal error, or any unrecovered source error), which removes four assignments. No behavior change: setErrorState is reached only through a fatal transition or a source error, recovery clears the source id, and sync start and clear reset both flags together. Evidence: @tanstack/db 285 files, 13,842 tests; ordered, replay, and subscription oracles pass. Size: -12 B gzip. Co-authored-by: Isaac <no-reply@databricks.com> * refactor(db): repair a report whose page end moved instead of reading its tie When a refetch report moves the end of a loader's own first page before the tie it read, the window is no longer proven. The loader read only the new end's tie; it now starts the bounded repair it already used for every other unproven report. Maintainer decision: the tie-only read saved one page request per such refetch but cost shipped bytes. The correctness half of that earlier fix stays: a report is ignored only when the page read past the window or the loader read the tie of the current end, so a row that ties with a moved end is still never skipped. Oracle: the report campaign drops its two prefixes whose release of the older tie truncated or disposed the loader, since a report no longer releases anything; the ignored-report, owed-repair, unexpressible-tie, and reset laws still run, and the unproven-report law accepts the repair. The query-db replay of an insert into a top-N list now expects the repair's bounded prefix and the new end's tie, with the Query count unchanged. Evidence: @tanstack/db 285 files, 13,842 tests; query-db 21 files, 925 tests; the query-db refetch random campaign passes at three times its run count. Size: @tanstack/db +449 B gzip vs main (was +576 after the previous commit; -127 B). Co-authored-by: Isaac <no-reply@databricks.com> * refactor(db): fold the shared-barrier failure check into the builder - The builder's failDemand takes the failing subscription and collection and decides there whether a failed replay of that collection holds the shared barrier; the subscriber's wrapper and the public hasFailedReplay query go. A superseded demand, whose generation is stale, no longer installs a failed replacement. - siblingReplays receives the subscriber's collection instead of finding it by source id. - failReplacement drops a check its only caller cannot fail: every live-query subscription has the replay-publication control. - failDemand's `fatal` default goes with its last defaulting caller. Measured one at a time (gzip): -21, -6, -5, -18. A getter that returned a replay's completion only while it had work grew gzip by 4 B and was dropped. Evidence: @tanstack/db 285 files, 13,842 tests; the replay, subscription, and ordered oracles pass. Size: @tanstack/db +399 B gzip vs main (was +449). Co-authored-by: Isaac <no-reply@databricks.com> * test(db): require a failed demand outside every recovery to end the query The convergence check runs after an explicit window move, which retries a failed demand, so it accepted a query that stayed alive after a lazy demand failed outside any recovery. A design that let any failed replay of the query, across collections, cover such a failure passed both failed-replay campaigns. Law (ARCHITECTURE.md, error 228): a lazy demand load that fails outside a replay ends the query; no barrier keeps the incomplete join side or bucket private. The oracle now requires that, skipping only failures the model cannot place: after a load threw, and, with a peer, a load that joined a replay, which may belong to either query's recovery. An aborted request's rejection and the failure of a load whose segment newer coverage released are not failures, as ARCHITECTURE.md states. RED (hostile mutant): judging the shared barrier against any failed replay of the query fails both failed-replay campaigns with "terminal error: a failed demand outside recovery". GREEN: load-subset replay oracle 43/43 five times; @tanstack/db 285 files, 13,842 tests. Also corrects ARCHITECTURE.md, which still described the old rule that a synchronous loadSubset throw rolls its owner back and ends the query; a throw now follows the same rules as a rejection. Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): let no window move wait on itself after a newer move throws An independent review found that a move deferred behind a replay could hang forever: a newer move that throws while it starts bumps the request generation without recording a settlement, so the deferred move, resuming after the replay, awaited the latest request, which was still itself. The plain path's version of the same shape was fixed earlier with a self check on its settlement. A call now clears the latest request as soon as it bumps the generation. An older move then waits only on a settlement the newer call records, or on nothing if that call threw, so neither path can wait on itself, and the earlier self check goes. Evidence: the named replay "a window move whose load throws while an older move waits" passes, and without the reset fails with "TypeError: Chaining cycle detected for promise". The deferred path needs a newer move inside the microtask gap between a replay settling and the deferred move resuming, which the grammar does not reach; it shares the mechanism the replay witnesses. @tanstack/db 285 files, 13,842 tests. Also corrects revalidateLoadedCoverage's comment: coverage that a full-source read completed still runs the invalidation, which starts no request for it, so the loader ignores the report. Size: @tanstack/db +386 B gzip vs main (-13 B). Co-authored-by: Isaac <no-reply@databricks.com> * test(db): tighten the oracles an independent review found too permissive An oracle review of this branch found excusals broader than their reasons, a false coverage claim, and gaps in reach and in ORC-016. Production is unchanged; each change was checked against the current code and, where a mutant applies, against that mutant. load-subset replay oracle: - The aborted-move branch was unreachable (no history reached it, though the coverage map said a named replay did) and quietly accepted a move rejected with AbortError. A truncate never aborts a move now (ARCHITECTURE.md), so such a rejection is an escaped error; the branch and its counter are gone. - The terminal-error excuse after a synchronous throw applied to every later failure in the history. It now applies only to the thrown error itself ending the query after the step that threw. Rolling a thrown demand back, the old behavior, again fails both named replays and the broad and failed-replay campaigns. - A load that fails outside every barrier after a failed demand of its source retired is unplaced: the failed replay may still hold through the subscription's other demands, which the adapter cannot attribute. - The window check requires each shown row to be eligible and to appear once, so a tie cannot hide a row the join rejects. ordered source loader state oracle: - The unproven-report law no longer accepts a full-source read as proof when every installed rank is expressible. A mutant that forced full debt after a report passed before and now fails ("issued no bounded repair and no tie of its end"). - The cost law rejects a repeated read within one invalidation. ordered-work oracle: an optimistic refill that a case allows but does not require is still bounded. A mutant that made the growth read unbounded fails 24 cases ("an allowed refill used an unbounded provider request"). query-db refetch oracle: the campaigns declare reach with createOracleReach (minimum 3), including a refetch that reported while the window was full (12 to 21 histories per campaign); the named replay asserts bounded reads and an unchanged Query count instead of an exact request shape. ORC-016: the zero-limit truncate example moves from the ordered-lifecycle oracle to live-query-collection.test.ts. Limit recorded: with a peer, the strict terminal-error law accepts either query ending, since the adapter cannot attribute a load to a query. Evidence: @tanstack/db 285 files, 13,842 tests; query-db 21 files, 925 tests; the replay oracle passes six runs in a row. Co-authored-by: Isaac <no-reply@databricks.com> * test(db): judge request errors and per-query initial failures in the replay oracle A high review of the TanStack#2168 merge flagged the replay oracle's law-16 rewrite: it had no positive request-error assertion, no check that request errors clear after recovery, and a global initial-failure excuse that a peer could satisfy for `live`. Law (ARCHITECTURE.md law 16 and law 17; glossary, "request error"): after initial-query readiness, a failed lazy demand load is a request error on the query's own node until its demand retires, a later load of the source covers its keys, or a later replay of the source completes. A failure inside a replay is decided by that replay's verdict, once every participant settled. A terminal error needs a load that failed while that same query was loading. Oracle changes: - A request-error account, independent of production's bookkeeping, owes each such failure. At every history cut, the query's node must list each owed failure by its injected error object; after recovery replays every source, no request error may stay. - Initial failures are recorded per query. - A thrown load joins a replay that no participant joined yet (a replay still starting its loads holds publication); without this the model booked a recovery that had not happened. - The failed-sibling branch generates a held retry, and a new branch starts another demand of the source while a failed demand's retry is held. Reach: `requestErrorChecks` reaches the declared minimum of 5 in each failed-replay campaign. RED: the review's mutant classes. Demand outcomes that never report an error fail 9 tests ("after settle: request error for "marker load failed" on keys 1"); a successful replay that keeps its request errors fails 22 ("convergence: stale request error"). GREEN: production unchanged; replay oracle 53/53 at 1x and 3x budget; @tanstack/db 300 files, 15,528 tests. Limits: the account shares one replay across a query's subscriptions to a source, so it cannot judge one subscription's request error while another load of that source is held. The reviewer's failReplacement finding (its replay can complete when a joined demand settles, then clear the failed demand's request error) stays open in the coverage map; a trace confirms the flush, but no reached history showed a cleared owed error. Which failure a deferred move rejects with, when its replay has several, is not specified and awaits a decision. Also: - ARCHITECTURE.md now states what the deferred move compares: the sync runs, only when a replay it waits on rejects. - setReadyIfIdle uses hasTruncateReplayWork, and siblingReplays is required again (-14 B gzip; @tanstack/db +355 B vs main). Co-authored-by: Isaac <no-reply@databricks.com> * fix(db): reject a deferred window move with its replay's own error A window move that waits behind a truncate replay caught the replay's rejection, re-entered setWindow, and rejected with the query's latest subset error. When the replay had two failures, the move reported the later one, not the replay's own error. Law (ARCHITECTURE.md, window moves; maintainer decision): a deferred move whose replay fails rejects with that replay's own error, its earliest failure that still decides it. Law 17 decides which failures count: one whose demand retired, one that a later failure of the same request replaced, and one whose keys a later load of the replay covers no longer count. A replay that a newer truncate or a retired demand discards still lets the move apply. Production: the deferred move's loop rethrows a replay rejection unless it is a LoadSubsetOperationAbortedError from a discarded replay in an unchanged sync run. Oracle: an identity law judges deferred moves that start before any failure of the replays they wait on, with the named replay "a window move behind a replay with two failed loads". The request-error account now decides failures per key: a later load of the source that asks for some of a failure's keys decides them (law 16: a key's latest load decides it). Reach: `deferredMoveRejections` reaches the declared minimum of 5 in each failed-replay campaign. RED (parent code): both failed-replay campaigns and the named replay fail with "replay error identity: move 1 rejected with "load failed", not its replay's earliest failure that still decides it". GREEN: replay oracle 54/54; @tanstack/db 300 files, 15,529 tests. Limits: the law does not judge a move that starts after a replay it waits on already failed. Open, recorded in the coverage map with its trace: after a marker replay fails, a marker change can publish over the retained window; it fails on the parent code too and is not yet classified. Co-authored-by: Isaac <no-reply@databricks.com> * test(db): let a loaded retry decide a failed replay load in the replay oracle The held-retry branch found, at three times the run budget, a marker change publishing over a window that the model retained after a failed marker replay. The model was wrong: the query retries a failed demand at once, and when that retry loads the failed keys, the failure no longer counts. Law (ARCHITECTURE.md law 17): a replay's failure counts for a key that no later held load loaded. The model now clears a replay failure when a later load of the replay loads its keys. Trace: hold, truncateMarkers, settle ok, settle ok, settle fail newest, mark 3 off (ascending, offset 1, limit 1, includes over on-demand markers): "retained publication once loading is quiet: expected [ { id: 3 ... } ] to deeply equal []" on the parent model, with the same result on the code before the identity fix. Production is unchanged. GREEN: replay oracle 42/42 at the normal budget and three times at three times it. Mutant: production without failReplacement still fails "keeps the last publication when a marker change follows two failed replay loads" with "retained publication after mark". Co-authored-by: Isaac <no-reply@databricks.com> * test(db): pin a failed replacement's hold ending when a later demand settles A review of the TanStack#2168 merge found that the replay `failReplacement` creates records no failed demand. A later demand that joins it and settles completes it as a success: it publishes, and a live query's `succeed` clears the failed demand's request error. Law (ARCHITECTURE.md, "Replay participants and failure"): the hold lasts until a later replay of the source succeeds or the subscription's last demand retires. RED: as a plain test, "expected "spy" to not be called at all, but actually been called 1 times". Maintainer decision: pinned with it.fails and left unfixed for now; the coverage map records it. The replay oracle cannot reach it until its model gives each subscription its own verdict. Co-authored-by: Isaac <no-reply@databricks.com> * test(db): judge each subscription's replay verdict in the request-error account The replay oracle's request-error account treated a query's subscriptions to a source as one replay, so it could not judge one subscription's request error while another subscription's load was held, which is where a failed replacement's hold can end early. Law (ARCHITECTURE.md law 17): a subscription's replay fails once its own participants settled; only a success also waits for the query's other subscriptions to the source. Each load names the subscription that issued it (LoadSubsetOptions.subscription), so the account waits for that subscription's loads. Publication retention stays shared. Reach: `successWhileSubscriptionOwes` (a subscription's load succeeds while a failure of that subscription is still owed) is declared in the failed-replay campaigns; 30 fixed and 24 random histories at the normal budget. GREEN: replay oracle 42/42 at the normal budget and at three times it. Mutant: a failed replacement's hold that completes when any participant settles fails both failed-replay campaigns with "retained publication after mark". Also: the replay-error identity law skips peer histories, since the adapter cannot tell which query a failed load served. Limits: no reached live-query history violates the request-error or retention law at that premise, because the failed demand's own retry joins the hold; the pinned subscription test asserts the hold law's stricter wording. Co-authored-by: Isaac <no-reply@databricks.com> * docs(db): end a failed replacement's hold when its joined loads load A review found that the replay `failReplacement` creates records no failed demand, so it completes once every demand that joined it settles. A pinned subscription test asserted a stricter law: no joined demand may end the hold. Law (ARCHITECTURE.md, "Replay participants and failure"; maintainer decision): the hold lasts until every demand load that joins it loads, a later replay of the source succeeds, or the subscription's last demand retires. The query retries the failed demand at once and the retry joins the hold, so the hold cannot publish without the failed demand's rows. Production unchanged; the failReplacement doc comment states the revised law. The pinned `it.fails` test, which asserted the earlier wording, is removed. The coverage map records the revision. Reach: `successWhileSubscriptionOwes` (a subscription's load succeeds while that subscription's failure is still owed), 30 fixed and 24 random histories at the normal budget. GREEN: replay oracle and subscription replay tests, 107 passed; tsc and pnpm lint:changed clean. Mutant: completing such a hold when any participant settles fails both failed-replay campaigns at the retention law (from 446ad1f). Limits: the oracle drives the hold only through live-query histories, where the retry always joins. Co-authored-by: Isaac <no-reply@databricks.com> * test(persistence): pin two known gaps that random campaigns found The multi-tab Electric campaign (seed -1660880085) and the persisted-peers-full session campaign (seed 654774679, which failed CI on this branch) each found a history that reproduces on main. Pin both with `it.fails` replays through each oracle's own driver and checks, and record them in the coverage map, tracked with TanStack#2281. Co-authored-by: Isaac <no-reply@databricks.com> --------- Co-authored-by: Test <test@test.com> Co-authored-by: Isaac <no-reply@databricks.com>
…anStack#2245) chore(deps): update dependency trailbase>uuid to v13.0.1 [security] Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Test <test@test.com>
…2252) chore(deps): update dependency @angular/common to v20.3.34 [security] Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Test <test@test.com>
* chore(deps): update dependency vite to v7.3.7 [security] * Align projects example with patched Vite version --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Test <test@test.com>
* chore(deps): update dependency zod to v4 [security] * Remove schema snippet waiver resolved by Zod 4 * Regenerate Zod lockfile without repeat-install shortcut --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Test <test@test.com>
…nStack#2250) * chore(deps): update dependency vitest to v4 [security] * chore(deps): update dependency zod to v4 [security] * Remove schema snippet waiver resolved by Zod 4 * Finish Vitest migration for Browser and Query test harnesses * Type React source replacement fixture for both collections --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Test <test@test.com>
…ring (TanStack#2288) * docs(persistence): document networkTimeoutMs: 0 for cache-first rendering Co-authored-by: Isaac <no-reply@databricks.com> * docs(persistence): make the networkTimeoutMs: 0 example parse The doc-snippet check rejected the bare object-property fence. Show the option inside a persistedCollectionOptions call instead. Co-authored-by: Isaac <no-reply@databricks.com> --------- Co-authored-by: Test <test@test.com> Co-authored-by: Isaac <no-reply@databricks.com>
* feat: prepare first 1.0 release candidate
* feat: automate 1.0 RC patch and stable releases
* ci: apply automated fixes
* fix: migrate remaining RC API consumers
* fix: update todo example RC usage
* fix: preserve pending stable changesets on RC exit
* ci: check release versioning on PRs
* docs: add 1.0 RC migration guide
* docs: clarify accidental 1.0 releases
* docs: cover recent breaking changes in RC migration guide
* fix: address first RC review findings
* fix: order offline transaction peer dependencies
* test: migrate merged ordered-query oracles to RC APIs
* Remove automatic Query Collection mutation refetch
* ci: apply automated fixes
* docs: update RC migration guide for recent API changes
* docs: state removal of live-query isError explicitly
* chore: include Preact and Convex in the first RC
* docs: cover latest persistence and hydration migrations
* docs: record RC review evidence and decisions
* ci: apply automated fixes
* docs: record verification of latest main merge
* docs: note toolchain and CI formatting integration
* fix: close RC metadata and cached query review gaps
Require the RC DB peer in Query Collection, preserve metadata through recursive
queries and nullable spreads, reject incompatible cached row options, and guard
external transaction completion. Repair stale tests and examples, remove dead
warning APIs and the unused window recompile mode, and hoist functional aliases.
Law: published metadata follows projection shape; independent same-identity
callers reject conflicting row options while mounted hooks retain theirs;
external completion admits pending transactions only. Authority: VirtualRowProps,
the live-query architecture, transaction admission, and maintainer decisions.
Reach: metadata single/joined pending 200 each, collisions 320, direct functions
16, QueryRef 250, union 150, null later spread 2; cache 144 core/24 React;
completion three histories per state across pending/persisting/completed/failed.
RED: initial metadata grammar 92 failures; final grammar on original compiler
220 failures. Cache core36/React6 fail. Unguarded completion fails four states.
GREEN: metadata409/cache144/outcomes27/census19 pass; Query978, React765,
Offline302, replay/ordered534, release11, TypeScript and lint pass.
Mutant: first-spread metadata fails four; removed cache guard fails36core/6React;
unguarded completion fails four state histories at primary comparisons.
Limits: finite composition and admission grammars; no arbitrary SSR schedules,
native provider E2E, or whole-PR CI readiness claim. Versioned audit follows.
Size: repairs add84gzip DB/39app; PR versus merged main saves340gzip across
measured packages and276gzip in the app. Removed warning state and per-row work.
* docs: record second RC review evidence and dispositions
* test: strengthen RC oracles and repair bounded release failures
Law: preserve the approved publication, cache claim, mutation confirmation,
restored settlement and published row contracts.
Reach: all declared states in the independently accepted release campaigns.
RED: unchanged implementation fails the versioned pre-repair comparisons.
GREEN: release owners and neighbors pass; clean locked build and all25packs pass.
Mutant: original and isolated wrong implementations fail intended comparisons.
Limits: installed API receivers and final CI remain pending; broader discoveries
are preserved on the follow-up snapshot with their unchanged laws.
See docs/contributing/oracle-reviews/2026-10-29-pr-2171-release-repairs.md
and the adjacent chunk, release-gate and deferral records for exact evidence.
* test: make RC receiving fixtures portable across CI hosts
* test: make required RC histories and schema fixtures reliable
* ci: apply automated fixes
* test: guarantee source-health reach and named replay registration
* docs: update transaction settlement comments
Replace four stale isPersisted mentions with when('settled'). Executable TypeScript is identical with comments removed; oracle laws, histories, assertions and run budgets are unchanged. Verified with lint:changed and git diff --check.
* test: remove stale RC API references
Update current guides and test descriptions, preserve historical replay coordinates, and stop treating removed metadata as virtual in test helpers. Keep ownership schedules, identity controls, registrations, and run budgets intact.
Validation: 496 Query ownership tests; 56 SQLite, 63 Electric, and 21 selected React runtime tests passed. Two independent oracle reviews found no actionable cleanup issues. Changed-file lint and formatting passed. Runtime checks used the existing Vitest 3.2.4 installation; cross-package rootDir setup errors prevented the SQLite/Electric automatic typecheck.
---------
Co-authored-by: Isaac <isaac@example.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Test <test@test.com>
The open-ended >= range lets solid-js 2.0.0 satisfy the peer once it is released, but these adapters are built for Solid 1. Claude-Session: https://claude.ai/code/session_0117BoWMMJ6aTSA3MvtgJWG2 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* ci: Version Packages * test: align release checks with first RC versions --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Test <test@test.com>
* docs: anchor RC migration guidance to DB 0.9.2 * docs: clarify error-handling upgrade instructions * docs: simplify SSR migration introduction --------- Co-authored-by: Test <test@test.com>
ci: stage RC publishing while new packages await npm access Co-authored-by: Test <test@test.com>
* ci: restore release tests after staged RC publication * ci: restore public package release eligibility --------- Co-authored-by: Test <test@test.com>
Co-authored-by: Test <test@test.com>
…its (TanStack#2298) Interleaved eager Electric commits could leave shared SQLite rows inconsistent with the saved resume position. A restarted source then skipped the correction. Carry opaque predecessor evidence through the persistence capability and check it atomically with row writes. Keep source-progress incompatibility sticky until authoritative replacement. Reuse snapshot recovery for uncertified progress, including explicit caller positions and interrupted eager reset recovery. Keep cached rows visible by separating source continuity from key integrity. Law: after successful recovery and complete delivery, durable rows and every open eager Collection equal the source head. Authority: Browser Law 2, TanStack#2209, the coverage map, and Electric's existing fresh-baseline/reset contract. Reach: SQLite and Browser properties retain 80 fixed/80 random inputs. Browser inputs pair uninterrupted and interrupted recovery, varying membership, source roles, empty/disjoint advancement, draining, restart/reopen, provenance and five caller forms. All declared reaches pass; 30 real PostgreSQL/Electric SDK/Chromium OPFS receiving cells reach their required premises. Full counts and commands are in docs/contributing/oracle-reviews/issue-2281-investigation.md. RED: extended Browser and SQLite owners reject original production 82c5f99; real receiving has four semantic failures with four draining controls. Later explicit-position and interrupted-reset extensions reject unchanged production before their corresponding fixes. Final interrupted replay: seed 2281, path 1:0:0. GREEN: full Browser owner 312 passes plus 2 tracked expected failures; SQLite 61; all 30 real receiving cells; Electric 336 and 97 across two commands; types, lint, capability, Electron IPC and replay validation pass. All four reported traces pass. Final PR-head CI and review status are recorded on TanStack#2298. Mutant: ignoring gaps, rejecting duplicate endpoints, bypassing provenance, preserving explicit positions during recovery, omitting SDK-give-up abandonment, and omitting final reset restart each fail the relevant shared comparison. Exact source restoration and corresponding replays pass. No comparison, exclusion or run budget was relaxed. Limits: bounded same-version eager sources sharing SQLite. No mixed-version, monotonic live ordering, on-demand, optimistic, native-host, receipt or teardown claim. Permanent-close TanStack#2209, partial-update composition TanStack#2267 and seed metadata remain separate. The maintainer kept the independently reproduced observer cleanup/restart failure and aggregate chance-reach gap TanStack#2289 outside this PR. Final CI on 381f41e: all 25 checks pass after one failed-job retry. The ordered-replay retention counterexample at seed -1707950093, path 218:1:1:8:9:9:9:13 also fails on clean main; TanStack#2294 tracks that separate retention boundary. Its exact witness remains in the PR description. CodeRabbit is paused at 124ce66; later eager reset semantics have independent local review and executed generated, real receiving, and mutant evidence.
|
Size Change: +112 kB (+88.73%) 🆘 Total Size: 238 kB 📦 View Changed
ℹ️ View Unchanged
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Tiny change, anyone around for a quick +1?